def setup_ui(self): """create UI widgets """ self.resize(447, 546) self.setWindowTitle("TimeLog Dialog") # ----------------------------- # Setup Main Layout self.main_layout = QtWidgets.QHBoxLayout(self) # create the left column self.left_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(self.left_layout) # create the right column # this will list the previously entered time logs self.right_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(self.right_layout) # ----------------------------- # Setup Form Layout self.form_layout = QtWidgets.QFormLayout() self.form_layout.setFieldGrowthPolicy( QtWidgets.QFormLayout.AllNonFixedFieldsGrow ) self.left_layout.addLayout(self.form_layout) # ----------------------------- # Tasks self.tasks_label = QtWidgets.QLabel(self) self.tasks_label.setText("Task") self.form_layout.setWidget( 0, QtWidgets.QFormLayout.LabelRole, self.tasks_label ) from anima.ui.widgets import TaskComboBox self.tasks_combo_box = TaskComboBox(self) self.tasks_combo_box.setObjectName("tasks_combo_box") self.form_layout.setWidget( 0, QtWidgets.QFormLayout.FieldRole, self.tasks_combo_box ) # ----------------------------- # Tasks Progress Bar self.task_progress_bar = QtWidgets.QProgressBar(self) self.task_progress_bar.setProperty("value", 24) self.form_layout.setWidget( 1, QtWidgets.QFormLayout.FieldRole, self.task_progress_bar ) # ----------------------------- # Resources self.resources_label = QtWidgets.QLabel(self) self.resources_label.setText("Resource") self.form_layout.setWidget( 2, QtWidgets.QFormLayout.LabelRole, self.resources_label ) self.resource_combo_box = QtWidgets.QComboBox(self) self.form_layout.setWidget( 2, QtWidgets.QFormLayout.FieldRole, self.resource_combo_box ) # ----------------------------- # Calendar, Start & End Time # Label self.date_label = QtWidgets.QLabel(self) self.date_label.setText("Date") self.form_layout.setWidget( 3, QtWidgets.QFormLayout.LabelRole, self.date_label ) # Calendar Widget self.calendar_widget = QtWidgets.QCalendarWidget(self) self.calendar_widget.setFirstDayOfWeek(QtCore.Qt.Monday) self.form_layout.setWidget( 3, QtWidgets.QFormLayout.FieldRole, self.calendar_widget ) # Start Time self.start_time_label = QtWidgets.QLabel(self) self.start_time_label.setText("Start") self.form_layout.setWidget( 4, QtWidgets.QFormLayout.LabelRole, self.start_time_label ) from anima.ui.widgets import TimeEdit self.start_time_edit = TimeEdit(self, resolution=timing_resolution) self.start_time_edit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection ) self.start_time_edit.setCalendarPopup(True) self.start_time_edit.setObjectName("start_time_edit") self.start_time_edit.setWrapping(True) self.form_layout.setWidget( 4, QtWidgets.QFormLayout.FieldRole, self.start_time_edit ) self.start_time_edit.setDisplayFormat("HH:mm") # End Time self.end_time_label = QtWidgets.QLabel(self) self.end_time_label.setText("End") self.form_layout.setWidget( 5, QtWidgets.QFormLayout.LabelRole, self.end_time_label ) self.end_time_edit = TimeEdit(self, resolution=timing_resolution) self.end_time_edit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection ) self.end_time_edit.setCalendarPopup(True) self.end_time_edit.setObjectName("end_time_edit") self.end_time_edit.setWrapping(True) self.form_layout.setWidget( 5, QtWidgets.QFormLayout.FieldRole, self.end_time_edit ) self.end_time_edit.setDisplayFormat("HH:mm") # ----------------------------- # Time Left Info self.info_area_label = QtWidgets.QLabel(self) self.info_area_label.setText("INFO") self.form_layout.setWidget( 6, QtWidgets.QFormLayout.FieldRole, self.info_area_label ) # ----------------------------- # Revision self.revision_label = QtWidgets.QLabel(self) self.revision_label.setText( """<html> <head/> <body> <p align=\"right\"> Revision <br/> Type </p> </body> </html> """ ) self.form_layout.setWidget( 7, QtWidgets.QFormLayout.LabelRole, self.revision_label ) self.revision_type_combo_box = QtWidgets.QComboBox(self) self.form_layout.setWidget( 7, QtWidgets.QFormLayout.FieldRole, self.revision_type_combo_box ) # ----------------------------- # Description self.description_label = QtWidgets.QLabel(self) self.description_label.setText("Description") self.form_layout.setWidget( 8, QtWidgets.QFormLayout.LabelRole, self.description_label ) self.description_plain_text_edit = QtWidgets.QPlainTextEdit(self) self.form_layout.setWidget( 8, QtWidgets.QFormLayout.FieldRole, self.description_plain_text_edit ) # ----------------------------- # Status self.status_label = QtWidgets.QLabel(self) self.status_label.setText("Status") self.form_layout.setWidget( 9, QtWidgets.QFormLayout.LabelRole, self.status_label ) self.not_finished_yet_radio_button = QtWidgets.QRadioButton(self) self.not_finished_yet_radio_button.setText("Not Finished Yet") self.not_finished_yet_radio_button.setChecked(True) self.form_layout.setWidget( 9, QtWidgets.QFormLayout.FieldRole, self.not_finished_yet_radio_button ) self.set_as_complete_radio_button = QtWidgets.QRadioButton(self) self.set_as_complete_radio_button.setText("Set As Complete") self.form_layout.setWidget( 10, QtWidgets.QFormLayout.FieldRole, self.set_as_complete_radio_button ) self.submit_for_final_review_radio_button = \ QtWidgets.QRadioButton(self) self.submit_for_final_review_radio_button.setText( "Submit For Final Review" ) self.form_layout.setWidget( 11, QtWidgets.QFormLayout.FieldRole, self.submit_for_final_review_radio_button ) # ----------------------------- # Dialog Button Box self.dialog_button_box = QtWidgets.QDialogButtonBox(self) self.dialog_button_box.setOrientation(QtCore.Qt.Horizontal) self.dialog_button_box.setStandardButtons( QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok ) self.left_layout.addWidget(self.dialog_button_box) # create the right column which is the current date details self.formatted_date_label = QtWidgets.QLabel(self) self.formatted_date_label.setText("") self.right_layout.addWidget(self.formatted_date_label) # create the right column which is the current date details self.time_log_info_label = QtWidgets.QLabel(self) self.time_log_info_label.setText("") self.right_layout.addWidget(self.time_log_info_label) # set stretch self.main_layout.setStretchFactor(self.left_layout, 0) self.main_layout.setStretchFactor(self.right_layout, 1) # setup signals self._setup_signals() # setup defaults self._set_defaults() # center window self.center_window()
class MainDialog(QtWidgets.QDialog, AnimaDialogBase): """The TimeLog Dialog """ def __init__(self, parent=None, task=None, timelog=None): logger.debug("initializing the interface") super(MainDialog, self).__init__(parent) # store the logged in user self.logged_in_user = None self.no_time_left = False self.extended_hours = None self.extended_minutes = None self.timelog = timelog self.timelog_created = False self.task = task # ui element storage self.main_layout = None self.left_layout = None self.right_layout = None self.form_layout = None self.tasks_label = None self.task_progress_bar = None self.resources_label = None self.resource_combo_box = None self.date_label = None self.calendar_widget = None self.start_time_label = None self.end_time_label = None self.info_area_label = None self.revision_label = None self.revision_type_combo_box = None self.description_label = None self.description_plain_text_edit = None self.status_label = None self.not_finished_yet_radio_button = None self.set_as_complete_radio_button = None self.submit_for_final_review_radio_button = None self.dialog_button_box = None self.tasks_combo_box = None self.start_time_edit = None self.end_time_edit = None self.formatted_date_label = None self.time_log_info_label = None self.setup_ui() def setup_ui(self): """create UI widgets """ self.resize(447, 546) self.setWindowTitle("TimeLog Dialog") # ----------------------------- # Setup Main Layout self.main_layout = QtWidgets.QHBoxLayout(self) # create the left column self.left_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(self.left_layout) # create the right column # this will list the previously entered time logs self.right_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(self.right_layout) # ----------------------------- # Setup Form Layout self.form_layout = QtWidgets.QFormLayout() self.form_layout.setFieldGrowthPolicy( QtWidgets.QFormLayout.AllNonFixedFieldsGrow ) self.left_layout.addLayout(self.form_layout) # ----------------------------- # Tasks self.tasks_label = QtWidgets.QLabel(self) self.tasks_label.setText("Task") self.form_layout.setWidget( 0, QtWidgets.QFormLayout.LabelRole, self.tasks_label ) from anima.ui.widgets import TaskComboBox self.tasks_combo_box = TaskComboBox(self) self.tasks_combo_box.setObjectName("tasks_combo_box") self.form_layout.setWidget( 0, QtWidgets.QFormLayout.FieldRole, self.tasks_combo_box ) # ----------------------------- # Tasks Progress Bar self.task_progress_bar = QtWidgets.QProgressBar(self) self.task_progress_bar.setProperty("value", 24) self.form_layout.setWidget( 1, QtWidgets.QFormLayout.FieldRole, self.task_progress_bar ) # ----------------------------- # Resources self.resources_label = QtWidgets.QLabel(self) self.resources_label.setText("Resource") self.form_layout.setWidget( 2, QtWidgets.QFormLayout.LabelRole, self.resources_label ) self.resource_combo_box = QtWidgets.QComboBox(self) self.form_layout.setWidget( 2, QtWidgets.QFormLayout.FieldRole, self.resource_combo_box ) # ----------------------------- # Calendar, Start & End Time # Label self.date_label = QtWidgets.QLabel(self) self.date_label.setText("Date") self.form_layout.setWidget( 3, QtWidgets.QFormLayout.LabelRole, self.date_label ) # Calendar Widget self.calendar_widget = QtWidgets.QCalendarWidget(self) self.calendar_widget.setFirstDayOfWeek(QtCore.Qt.Monday) self.form_layout.setWidget( 3, QtWidgets.QFormLayout.FieldRole, self.calendar_widget ) # Start Time self.start_time_label = QtWidgets.QLabel(self) self.start_time_label.setText("Start") self.form_layout.setWidget( 4, QtWidgets.QFormLayout.LabelRole, self.start_time_label ) from anima.ui.widgets import TimeEdit self.start_time_edit = TimeEdit(self, resolution=timing_resolution) self.start_time_edit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection ) self.start_time_edit.setCalendarPopup(True) self.start_time_edit.setObjectName("start_time_edit") self.start_time_edit.setWrapping(True) self.form_layout.setWidget( 4, QtWidgets.QFormLayout.FieldRole, self.start_time_edit ) self.start_time_edit.setDisplayFormat("HH:mm") # End Time self.end_time_label = QtWidgets.QLabel(self) self.end_time_label.setText("End") self.form_layout.setWidget( 5, QtWidgets.QFormLayout.LabelRole, self.end_time_label ) self.end_time_edit = TimeEdit(self, resolution=timing_resolution) self.end_time_edit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection ) self.end_time_edit.setCalendarPopup(True) self.end_time_edit.setObjectName("end_time_edit") self.end_time_edit.setWrapping(True) self.form_layout.setWidget( 5, QtWidgets.QFormLayout.FieldRole, self.end_time_edit ) self.end_time_edit.setDisplayFormat("HH:mm") # ----------------------------- # Time Left Info self.info_area_label = QtWidgets.QLabel(self) self.info_area_label.setText("INFO") self.form_layout.setWidget( 6, QtWidgets.QFormLayout.FieldRole, self.info_area_label ) # ----------------------------- # Revision self.revision_label = QtWidgets.QLabel(self) self.revision_label.setText( """<html> <head/> <body> <p align=\"right\"> Revision <br/> Type </p> </body> </html> """ ) self.form_layout.setWidget( 7, QtWidgets.QFormLayout.LabelRole, self.revision_label ) self.revision_type_combo_box = QtWidgets.QComboBox(self) self.form_layout.setWidget( 7, QtWidgets.QFormLayout.FieldRole, self.revision_type_combo_box ) # ----------------------------- # Description self.description_label = QtWidgets.QLabel(self) self.description_label.setText("Description") self.form_layout.setWidget( 8, QtWidgets.QFormLayout.LabelRole, self.description_label ) self.description_plain_text_edit = QtWidgets.QPlainTextEdit(self) self.form_layout.setWidget( 8, QtWidgets.QFormLayout.FieldRole, self.description_plain_text_edit ) # ----------------------------- # Status self.status_label = QtWidgets.QLabel(self) self.status_label.setText("Status") self.form_layout.setWidget( 9, QtWidgets.QFormLayout.LabelRole, self.status_label ) self.not_finished_yet_radio_button = QtWidgets.QRadioButton(self) self.not_finished_yet_radio_button.setText("Not Finished Yet") self.not_finished_yet_radio_button.setChecked(True) self.form_layout.setWidget( 9, QtWidgets.QFormLayout.FieldRole, self.not_finished_yet_radio_button ) self.set_as_complete_radio_button = QtWidgets.QRadioButton(self) self.set_as_complete_radio_button.setText("Set As Complete") self.form_layout.setWidget( 10, QtWidgets.QFormLayout.FieldRole, self.set_as_complete_radio_button ) self.submit_for_final_review_radio_button = \ QtWidgets.QRadioButton(self) self.submit_for_final_review_radio_button.setText( "Submit For Final Review" ) self.form_layout.setWidget( 11, QtWidgets.QFormLayout.FieldRole, self.submit_for_final_review_radio_button ) # ----------------------------- # Dialog Button Box self.dialog_button_box = QtWidgets.QDialogButtonBox(self) self.dialog_button_box.setOrientation(QtCore.Qt.Horizontal) self.dialog_button_box.setStandardButtons( QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok ) self.left_layout.addWidget(self.dialog_button_box) # create the right column which is the current date details self.formatted_date_label = QtWidgets.QLabel(self) self.formatted_date_label.setText("") self.right_layout.addWidget(self.formatted_date_label) # create the right column which is the current date details self.time_log_info_label = QtWidgets.QLabel(self) self.time_log_info_label.setText("") self.right_layout.addWidget(self.time_log_info_label) # set stretch self.main_layout.setStretchFactor(self.left_layout, 0) self.main_layout.setStretchFactor(self.right_layout, 1) # setup signals self._setup_signals() # setup defaults self._set_defaults() # center window self.center_window() def close(self): logger.debug('closing the ui') QtWidgets.QDialog.close(self) def show(self): """overridden show method """ logger.debug('MainDialog.show is started') self.logged_in_user = self.get_logged_in_user() if not self.logged_in_user: self.close() return_val = None else: return_val = super(MainDialog, self).show() logger.debug('MainDialog.show is finished') return return_val def _setup_signals(self): """sets up the signals """ logger.debug("start setting up interface signals") # Dialog buttons QtCore.QObject.connect( self.dialog_button_box, QtCore.SIGNAL("accepted()"), self.accept ) QtCore.QObject.connect( self.dialog_button_box, QtCore.SIGNAL("rejected()"), self.reject ) # tasks_combo_box QtCore.QObject.connect( self.tasks_combo_box, QtCore.SIGNAL('currentIndexChanged(QString)'), self.tasks_combo_box_index_changed ) # start_time_edit QtCore.QObject.connect( self.start_time_edit, QtCore.SIGNAL('timeChanged(QTime)'), self.start_time_changed ) # end_time_edit QtCore.QObject.connect( self.end_time_edit, QtCore.SIGNAL('timeChanged(QTime)'), self.end_time_changed ) # resource changed QtCore.QObject.connect( self.resource_combo_box, QtCore.SIGNAL('currentIndexChanged(QString)'), self.resource_changed ) # calendar day selected QtCore.QObject.connect( self.calendar_widget, QtCore.SIGNAL("selectionChanged()"), self.calendar_widget_selection_changed ) def _set_defaults(self): """sets the defaults for the ui """ logger.debug("started setting up interface defaults") # Set Default Value for time current_time = QtCore.QTime.currentTime() # round the minutes to the resolution minute = current_time.minute() hour = current_time.hour() minute = int(minute / float(timing_resolution)) * timing_resolution current_time = QtCore.QTime(hour, minute) self.start_time_edit.setTime(current_time) self.end_time_edit.setTime(current_time.addSecs(timing_resolution * 60)) self.calendar_widget.resource_id = -1 # enter revision types revision_types = [ 'Yetistiremedim', 'Ajans', 'Yonetmen', 'Ic Revizyon', ] self.revision_type_combo_box.addItems(revision_types) if not self.logged_in_user: self.logged_in_user = self.get_logged_in_user() # fill the tasks comboBox from stalker import Status, Task status_wfd = Status.query.filter(Status.code == 'WFD').first() status_cmpl = Status.query.filter(Status.code == 'CMPL').first() status_prev = Status.query.filter(Status.code == 'PREV').first() if not self.timelog: # dialog is in create TimeLog mode # if a task has been given just feed that task to the comboBox if self.task: all_tasks = [self.task] else: # no Task is given nor updating a TimeLog # show all the suitable tasks of the logged_in_user all_tasks = Task.query \ .filter(Task.resources.contains(self.logged_in_user)) \ .filter(Task.status != status_wfd) \ .filter(Task.status != status_cmpl) \ .filter(Task.status != status_prev) \ .all() # sort the task labels all_tasks = sorted( all_tasks, key=lambda task: '%s | %s' % ( task.project.name.lower(), ' | '.join(map(lambda x: x.name.lower(), task.parents)) ) ) else: # dialog is working in update TimeLog mode all_tasks = [self.timelog.task] self.tasks_combo_box.setSizeAdjustPolicy( QtWidgets.QComboBox.AdjustToContentsOnFirstShow ) self.tasks_combo_box.setFixedWidth(360) self.tasks_combo_box.clear() self.tasks_combo_box.addTasks(all_tasks) self.tasks_combo_box.setSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum ) # if a time log is given set the fields from the given time log if self.timelog: # first update Task try: self.tasks_combo_box.setCurrentTask(self.timelog.task) except IndexError as e: return # set the resource # and disable the tasks_combo_box and resource_combo_box self.tasks_combo_box.setEnabled(False) self.resource_combo_box.setEnabled(False) # set the start and end time from anima.utils import utc_to_local start_date = utc_to_local(self.timelog.start) end_date = utc_to_local(self.timelog.end) # set the date self.calendar_widget.setSelectedDate( QtCore.QDate(start_date.year, start_date.month, start_date.day) ) # first reset the start and end time values self.start_time_edit.setTime(QtCore.QTime(0, 0)) self.end_time_edit.setTime(QtCore.QTime(23, 50)) # now set the timelog time self.start_time_edit.setTime( QtCore.QTime( start_date.hour, start_date.minute ) ) self.end_time_edit.setTime( QtCore.QTime( end_date.hour, end_date.minute ) ) self.fill_calendar_with_time_logs() # also trigger an update to the side info bar self.calendar_widget_selection_changed() def fill_calendar_with_time_logs(self): """fill the calendar with daily time log info """ resource_id = self.get_current_resource_id() # do not update if the calendar is showing the same user if self.calendar_widget.resource_id == resource_id or resource_id == -1: return tool_tip_text_format = u'{start:%H:%M} - {end:%H:%M} | {task_name}' # import time # start = time.time() # get all the TimeLogs grouped daily sql = """-- TOP DOWN SEARCH -- select "TimeLogs".start::date as date, array_agg(task_rec_data.full_path) as task_name, array_agg("TimeLogs".start) as start, array_agg("TimeLogs".end) as end, sum(extract(epoch from ("TimeLogs".end - "TimeLogs".start))) from "TimeLogs" join ( with recursive recursive_task(id, parent_id, path_names) as ( select task.id, task.project_id, -- task.project_id::text as path, ("Projects".code || '') as path_names from "Tasks" as task join "Projects" on task.project_id = "Projects".id where task.parent_id is NULL union all select task.id, task.parent_id, -- (parent.path || '|' || task.parent_id:: text) as path, (parent.path_names || ' | ' || "Parent_SimpleEntities".name) as path_names from "Tasks" as task inner join recursive_task as parent on task.parent_id = parent.id inner join "SimpleEntities" as "Parent_SimpleEntities" on parent.id = "Parent_SimpleEntities".id ) select recursive_task.id, "SimpleEntities".name || ' (' || recursive_task.path_names || ')' as full_path from recursive_task join "SimpleEntities" on recursive_task.id = "SimpleEntities".id ) as task_rec_data on "TimeLogs".task_id = task_rec_data.id -- getting all the data is as fast as getting one, so get all the TimeLogs of this user where "TimeLogs".resource_id = :resource_id group by cast("TimeLogs".start as date) order by cast("TimeLogs".start as date) """ from sqlalchemy import text from stalker.db.session import DBSession result = DBSession.connection().execute( text(sql), resource_id=resource_id ).fetchall() # end = time.time() # print('getting data from sql: %0.3f sec' % (end - start)) from anima.utils import utc_to_local time_shifter = utc_to_local import stalker # TODO: Remove this in a later version from distutils.version import LooseVersion if LooseVersion(stalker.__version__) >= LooseVersion('0.2.18'): def time_shifter(x): return x for r in result: calendar_day = r[0] year = calendar_day.year month = calendar_day.month day = calendar_day.day daily_logged_seconds = r[4] daily_logged_hours = daily_logged_seconds // 3600 daily_logged_minutes = \ (daily_logged_seconds - daily_logged_hours * 3600) // 60 tool_tip_text_data = [ u'Total: %i h %i min logged' % (daily_logged_hours, daily_logged_minutes) if daily_logged_hours else u'Total: %i min logged' % daily_logged_minutes ] for task_name, start, end in sorted( zip(r[1], r[2], r[3]), key=lambda x: x[1]): time_log_tool_tip_text = tool_tip_text_format.format( start=time_shifter(start), end=time_shifter(end), task_name=task_name ) tool_tip_text_data.append(time_log_tool_tip_text) merged_tool_tip = u'\n'.join(tool_tip_text_data) date_format = QtGui.QTextCharFormat() bg_brush = QtGui.QBrush() bg_brush.setColor( QtGui.QColor(0, 255.0 / 86400.0 * daily_logged_seconds, 0) ) date_format.setBackground(bg_brush) date_format.setToolTip(merged_tool_tip) date = QtCore.QDate(year, month, day) self.calendar_widget.setDateTextFormat(date, date_format) def calendar_widget_selection_changed(self): """runs when selection changed """ selected_date = self.calendar_widget.selectedDate() assert isinstance(selected_date, QtCore.QDate) date_format = u"""<div> <div style="width: 100px; height: 100px; font-size: 60pt; float: initial"> <span><strong>{day_number}</strong></span> </div> <div style="position: relative; left: 60px; top: -95px;"> <div> <span style="font-size: 24pt;"><strong>{month_name} {year}</strong></span> <br> <span style="font-size: 32pt;">{day_name}</span> </div> </div> </div>""".format( day_number=selected_date.day(), day_name=selected_date.longDayName(selected_date.dayOfWeek()), month_name=selected_date.longMonthName(selected_date.month()), year=selected_date.year() ) assert isinstance(self.formatted_date_label, QtWidgets.QLabel) self.formatted_date_label.setText(date_format) # create new labels for time logs in that day date = self.calendar_widget.selectedDate() date_text_format = self.calendar_widget.dateTextFormat() try: time_log_data = date_text_format[date].toolTip() self.time_log_info_label.setText(time_log_data) except KeyError: self.time_log_info_label.setText('') def tasks_combo_box_index_changed(self, task_label): """runs when another task has been selected """ # task = self.get_current_task() task = self.tasks_combo_box.currentTask() self.update_percentage() self.update_info_text() # update resources comboBox resource_names = [] if self.logged_in_user in task.resources: resource_names = [self.logged_in_user.name] for r in task.resources: if r != self.logged_in_user: resource_names.append(r.name) self.resource_combo_box.clear() self.resource_combo_box.addItems(resource_names) def get_current_resource(self): """returns the current resource """ resource_name = self.resource_combo_box.currentText() from stalker import User return User.query.filter(User.name == resource_name).first() def get_current_resource_id(self): """returns the current resource """ resource_name = self.resource_combo_box.currentText() from stalker import User from stalker.db.session import DBSession return DBSession.query(User.id)\ .filter(User.name == resource_name)\ .first() def start_time_changed(self, q_time): """validates the start time """ end_time = self.end_time_edit.time() if q_time >= end_time: self.end_time_edit.setTime(q_time.addSecs(timing_resolution * 60)) self.update_percentage() self.update_info_text() def end_time_changed(self, q_time): """validates the end time """ start_time = self.start_time_edit.time() if q_time <= start_time: if q_time.hour() == 0 and q_time.minute() == 0: start_time = q_time end_time = q_time.addSecs(timing_resolution * 60) self.end_time_edit.setTime(end_time) else: start_time = q_time.addSecs(-timing_resolution * 60) self.start_time_edit.setTime(start_time) self.update_percentage() self.update_info_text() def resource_changed(self, resource_name): """runs when the selected resource has changed """ self.fill_calendar_with_time_logs() def calculate_percentage(self): # task = self.get_current_task() task = self.tasks_combo_box.currentTask() if not task: return 0 schedule_seconds = task.schedule_seconds logged_seconds = task.total_logged_seconds start_time = self.start_time_edit.time() end_time = self.end_time_edit.time() time_log_seconds = start_time.secsTo(end_time) percentage = \ (logged_seconds + time_log_seconds) / float(schedule_seconds) * 100 return percentage def update_percentage(self): """updates the percentage """ percentage = self.calculate_percentage() self.task_progress_bar.setMinimum(0) self.task_progress_bar.setMaximum(100) self.task_progress_bar.setValue(percentage) def update_info_text(self): """updated the info text """ # task = self.get_current_task() task = self.tasks_combo_box.currentTask() if not task: return schedule_seconds = task.schedule_seconds logged_seconds = task.total_logged_seconds start_time = self.start_time_edit.time() end_time = self.end_time_edit.time() time_log_seconds = start_time.secsTo(end_time) remaining_seconds = \ schedule_seconds - (logged_seconds + time_log_seconds) # if a time log given add its seconds if self.timelog: remaining_seconds += self.timelog.total_seconds # calculate the remaining minutes and hours self.no_time_left = False if remaining_seconds < 0: self.no_time_left = True remaining_seconds = abs(remaining_seconds) hours = remaining_seconds // 3600 minutes = (remaining_seconds - hours * 3600) // 60 if self.no_time_left: self.info_area_label.setText( '<b style="color: red;">%i h %i min</b> extra time.' % (hours, minutes) ) self.show_revision_fields() self.extended_hours = hours self.extended_minutes = minutes else: self.hide_revision_fields() self.info_area_label.setText( '<b style="color: green;">%i h %i min</b> will remain to ' 'complete this task.' % (hours, minutes) ) self.extended_hours = None self.extended_minutes = None def show_revision_fields(self): """shows the revision fields """ self.revision_label.show() self.revision_type_combo_box.show() def hide_revision_fields(self): """hides the revision fields """ self.revision_label.hide() self.revision_type_combo_box.hide() def accept(self): """overridden accept method """ # get the info task = self.tasks_combo_box.currentTask() resource = self.get_current_resource() # war the user if the resource is not the logged_in_user if resource != self.logged_in_user: msg_box = QtWidgets.QMessageBox(self) msg_box.setWindowTitle( 'Entering TimeLog On Behalf of Somebody Else' ) msg_box.setText( "You're entering a TimeLog on behalf of somebody else???" ) accept_button = msg_box.addButton( 'Accept the responsibility', QtWidgets.QMessageBox.AcceptRole ) cancel_button = msg_box.addButton( 'Cancel', QtWidgets.QMessageBox.RejectRole ) msg_box.setDefaultButton(cancel_button) msg_box.exec_() clicked_button = msg_box.clickedButton() msg_box.deleteLater() if clicked_button == cancel_button: return description = self.description_plain_text_edit.toPlainText() revision_cause_text = \ self.revision_type_combo_box.currentText().replace(' ', '_') is_complete = self.set_as_complete_radio_button.isChecked() submit_to_final_review = \ self.submit_for_final_review_radio_button.isChecked() # get the revision Types from stalker import Type revision_type = Type.query\ .filter(Type.target_entity_type == 'Note')\ .filter(Type.name == revision_cause_text)\ .first() date = self.calendar_widget.selectedDate() start = self.start_time_edit.time() end = self.end_time_edit.time() # construct proper datetime.DateTime instances import datetime start_date = datetime.datetime( date.year(), date.month(), date.day(), start.hour(), start.minute() ) end_date = datetime.datetime( date.year(), date.month(), date.day(), end.hour(), end.minute() ) today_midnight = datetime.datetime.now().replace( hour=23, minute=59, second=59, microsecond=999 ) # raise an error if the user is trying to enter a TimeLog to the future if start_date > today_midnight or end_date > today_midnight: QtWidgets.QMessageBox.critical( self, 'Error', 'Gelecege TimeLog giremezsiniz!!!', ) return # convert them to utc from anima.utils import local_to_utc utc_start_date = local_to_utc(start_date) utc_end_date = local_to_utc(end_date) # create a TimeLog # print('Task : %s' % task.name) # print('Resource : %s' % resource.name) # print('utc_start_date: %s' % utc_start_date) # print('utc_end_date : %s' % utc_end_date) # now if we are not using extra time just create the TimeLog from stalker import TimeLog from stalker.db.session import DBSession from stalker.exceptions import (OverBookedError, DependencyViolationError) utc_now = local_to_utc(datetime.datetime.now()) # TODO: Remove this in a later version import stalker from distutils.version import LooseVersion if LooseVersion(stalker.__version__) >= LooseVersion('0.2.18'): # inject timezone info import pytz utc_start_date = utc_start_date.replace(tzinfo=pytz.utc) utc_end_date = utc_end_date.replace(tzinfo=pytz.utc) utc_now = utc_now.replace(tzinfo=pytz.utc) from sqlalchemy.exc import IntegrityError if not self.timelog: try: new_time_log = TimeLog( task=task, resource=resource, start=utc_start_date, end=utc_end_date, description=description, date_created=utc_now ) except (OverBookedError, DependencyViolationError) as e: # inform the user that it can not do that QtWidgets.QMessageBox.critical( self, 'Error', '%s' % e ) DBSession.rollback() return try: DBSession.add(new_time_log) DBSession.commit() self.timelog_created = True except IntegrityError as e: DBSession.rollback() QtWidgets.QMessageBox.critical( self, 'Error', 'Database Error!!!' '<br>' '%s' % e ) return else: # just update the date values self.timelog.start = utc_start_date self.timelog.end = utc_end_date self.timelog.date_updated = utc_now DBSession.add(self.timelog) DBSession.commit() if self.no_time_left: # we have no time left so automatically extend the task from stalker import Task schedule_timing, schedule_unit = \ task.least_meaningful_time_unit( task.total_logged_seconds ) if schedule_timing != 0: task.schedule_timing = schedule_timing task.schedule_unit = schedule_unit # also create a Note from stalker import Note new_note = Note( content='Extending timing of the task <b>%s h %s min.</b>' % ( self.extended_hours, self.extended_minutes ), type=revision_type, created_by=self.logged_in_user, date_created=utc_now ) DBSession.add(new_note) task.notes.append(new_note) try: DBSession.commit() except IntegrityError as e: QtWidgets.QMessageBox.critical( self, 'Error', 'Database Error!!!' '<br>' '%s' % e ) DBSession.rollback() return if is_complete: # set the status to complete from stalker import Type, Status status_cmpl = Status.query.filter(Status.code == 'CMPL').first() forced_status_type = \ Type.query.filter(Type.name == 'Forced Status').first() # also create a Note from stalker import Note new_note = Note( content='%s has changed this task status to Completed' % resource.name, type=forced_status_type, created_by=self.logged_in_user, date_created=utc_now ) DBSession.add(new_note) task.notes.append(new_note) task.status = status_cmpl DBSession.commit() elif submit_to_final_review: # clip the Task timing to current time logs from stalker import Task schedule_timing, schedule_unit = \ task.least_meaningful_time_unit( task.total_logged_seconds ) task.schedule_timing = schedule_timing task.schedule_unit = schedule_unit DBSession.add(task) try: DBSession.commit() except IntegrityError as e: QtWidgets.QMessageBox.critical( self, 'Error', 'Database Error!!!' '<br>' '%s' % e ) DBSession.rollback() return # request a review reviews = task.request_review() for review in reviews: review.created_by = review.updated_by = self.logged_in_user review.date_created = utc_now review.date_updated = utc_now DBSession.add_all(reviews) # and create a Note for the Task request_review_note_type = \ Type.query\ .filter(Type.target_entity_type == 'Note')\ .filter(Type.name == 'Request Review')\ .first() from stalker import Note request_review_note = Note( type=request_review_note_type, created_by=self.logged_in_user, date_created=utc_now ) DBSession.add(request_review_note) DBSession.add(task) task.notes.append(request_review_note) try: DBSession.commit() except IntegrityError as e: DBSession.rollback() QtWidgets.QMessageBox.critical( self, 'Error', 'Database Error!!!' '<br>' '%s' % e ) return # Fix statuses from anima import utils utils.fix_task_statuses(task) try: DBSession.commit() except IntegrityError as e: DBSession.rollback() QtWidgets.QMessageBox.critical( self, 'Error', 'Database Error!!!' '<br>' '%s' % e ) return # if nothing bad happens close the dialog super(MainDialog, self).accept()
def setup_ui(self): """create UI widgets """ self.resize(447, 546) self.setWindowTitle("TimeLog Dialog") # ----------------------------- # Setup Main Layout self.main_layout = QtWidgets.QHBoxLayout(self) # create the left column self.left_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(self.left_layout) # create the right column # this will list the previously entered time logs self.right_layout = QtWidgets.QVBoxLayout() self.main_layout.addLayout(self.right_layout) # ----------------------------- # Setup Form Layout self.form_layout = QtWidgets.QFormLayout() self.form_layout.setFieldGrowthPolicy( QtWidgets.QFormLayout.AllNonFixedFieldsGrow ) self.left_layout.addLayout(self.form_layout) i = 0 # ----------------------------- # Tasks self.tasks_label = QtWidgets.QLabel(self) self.tasks_label.setText("Task") self.form_layout.setWidget( i, QtWidgets.QFormLayout.LabelRole, self.tasks_label ) from anima.ui.widgets import TaskComboBox self.tasks_combo_box = TaskComboBox(self) self.tasks_combo_box.setObjectName("tasks_combo_box") self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.tasks_combo_box ) # ----------------------------- # Tasks Progress Bar i += 1 self.task_progress_bar = QtWidgets.QProgressBar(self) self.task_progress_bar.setProperty("value", 24) self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.task_progress_bar ) # ----------------------------- # Resources i += 1 self.resources_label = QtWidgets.QLabel(self) self.resources_label.setText("Resource") self.form_layout.setWidget( i, QtWidgets.QFormLayout.LabelRole, self.resources_label ) self.resource_combo_box = QtWidgets.QComboBox(self) self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.resource_combo_box ) # ----------------------------- # Calendar, Start & End Time i += 1 # Label self.date_label = QtWidgets.QLabel(self) self.date_label.setText("Date") self.form_layout.setWidget( i, QtWidgets.QFormLayout.LabelRole, self.date_label ) # Calendar Widget self.calendar_widget = QtWidgets.QCalendarWidget(self) self.calendar_widget.setFirstDayOfWeek(QtCore.Qt.Monday) self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.calendar_widget ) # ----------------------------- # Start Time from anima.ui.widgets import TimeEdit i += 1 self.start_time_label = QtWidgets.QLabel(self) self.start_time_label.setText("Start") self.form_layout.setWidget( i, QtWidgets.QFormLayout.LabelRole, self.start_time_label ) from anima import TIMING_RESOLUTION self.start_time_edit = TimeEdit(self, resolution=TIMING_RESOLUTION) self.start_time_edit.setCurrentSection(QtWidgets.QDateTimeEdit.MinuteSection) self.start_time_edit.setCalendarPopup(True) self.start_time_edit.setObjectName("start_time_edit") self.start_time_edit.setWrapping(True) self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.start_time_edit ) self.start_time_edit.setDisplayFormat("HH:mm") # ----------------------------- # End Time i += 1 self.end_time_label = QtWidgets.QLabel(self) self.end_time_label.setText("End") self.form_layout.setWidget( i, QtWidgets.QFormLayout.LabelRole, self.end_time_label ) self.end_time_edit = TimeEdit(self, resolution=TIMING_RESOLUTION) self.end_time_edit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection ) self.end_time_edit.setCalendarPopup(True) self.end_time_edit.setObjectName("end_time_edit") self.end_time_edit.setWrapping(True) self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.end_time_edit ) self.end_time_edit.setDisplayFormat("HH:mm") # ----------------------------- # Effort i += 1 # Label self.effort_label = QtWidgets.QLabel(self) self.effort_label.setText("Effort") self.form_layout.setWidget( i, QtWidgets.QFormLayout.LabelRole, self.effort_label ) effort_layout = QtWidgets.QHBoxLayout(self) self.form_layout.setLayout( i, QtWidgets.QFormLayout.FieldRole, effort_layout ) self.effort_time_edit = TimeEdit(self, resolution=TIMING_RESOLUTION) self.effort_time_edit.setCurrentSection(QtWidgets.QDateTimeEdit.MinuteSection) self.effort_time_edit.setCalendarPopup(True) self.effort_time_edit.setObjectName("effort_time_edit") self.effort_time_edit.setWrapping(True) # self.form_layout.setWidget( # i, # QtWidgets.QFormLayout.FieldRole, # self.effort_time_edit # ) effort_layout.addWidget(self.effort_time_edit) self.effort_time_edit.setDisplayFormat("HH:mm") # ----------------------------- # Advanced Time Controls self.show_advanced_time_controls_check_box = QtWidgets.QCheckBox(self) self.show_advanced_time_controls_check_box.setText('Advanced Controls') effort_layout.addWidget(self.show_advanced_time_controls_check_box) # ----------------------------- # Time Left Info i += 1 self.info_area_label = QtWidgets.QLabel(self) self.info_area_label.setText("INFO") self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.info_area_label ) # ----------------------------- # Status i += 1 self.status_label = QtWidgets.QLabel(self) self.status_label.setText("Status") self.form_layout.setWidget( i, QtWidgets.QFormLayout.LabelRole, self.status_label ) self.not_finished_yet_radio_button = QtWidgets.QRadioButton(self) self.not_finished_yet_radio_button.setText("Not Finished Yet") self.not_finished_yet_radio_button.setChecked(True) self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.not_finished_yet_radio_button ) i += 1 self.set_as_complete_radio_button = QtWidgets.QRadioButton(self) self.set_as_complete_radio_button.setText("Set As Completed") self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.set_as_complete_radio_button ) i += 1 self.submit_for_final_review_radio_button = \ QtWidgets.QRadioButton(self) self.submit_for_final_review_radio_button.setText( "Submit For Final Review" ) self.form_layout.setWidget( i, QtWidgets.QFormLayout.FieldRole, self.submit_for_final_review_radio_button ) # ----------------------------- # Dialog Button Box self.dialog_button_box = QtWidgets.QDialogButtonBox(self) self.dialog_button_box.setOrientation(QtCore.Qt.Horizontal) self.dialog_button_box.setStandardButtons( QtWidgets.QDialogButtonBox.Cancel | QtWidgets.QDialogButtonBox.Ok ) self.left_layout.addWidget(self.dialog_button_box) # create the right column which is the current date details self.formatted_date_label = QtWidgets.QLabel(self) self.formatted_date_label.setText("") self.right_layout.addWidget(self.formatted_date_label) # create the right column which is the current date details self.time_log_info_label = QtWidgets.QLabel(self) self.time_log_info_label.setText("") self.right_layout.addWidget(self.time_log_info_label) # set stretch self.main_layout.setStretchFactor(self.left_layout, 0) self.main_layout.setStretchFactor(self.right_layout, 1) # setup signals self._setup_signals() # setup defaults self._set_defaults() # center window self.center_window()
def __init__(self, parent=None, task=None, timelog=None): logger.debug("initializing the interface") # store the logged in user self.logged_in_user = None self.no_time_left = False self.extended_hours = None self.extended_minutes = None self.timelog = timelog self.timelog_created = False self.task = task super(MainDialog, self).__init__(parent) self.setupUi(self) # customize the ui elements from anima.ui.widgets import TaskComboBox self.tasks_comboBox = TaskComboBox(self) self.tasks_comboBox.setObjectName("tasks_comboBox") self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.tasks_comboBox) # self.start_timeEdit.deleteLater() from anima.ui.widgets import TimeEdit self.start_timeEdit = TimeEdit(self, resolution=timing_resolution) self.start_timeEdit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection) self.start_timeEdit.setCalendarPopup(True) self.start_timeEdit.setObjectName("start_timeEdit") self.start_timeEdit.setWrapping(True) self.formLayout.insertRow(4, self.label, self.start_timeEdit) self.start_timeEdit.setDisplayFormat("HH:mm") # self.end_timeEdit.deleteLater() self.end_timeEdit = TimeEdit(self, resolution=timing_resolution) self.end_timeEdit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection) self.end_timeEdit.setCalendarPopup(True) self.end_timeEdit.setObjectName("end_timeEdit") self.end_timeEdit.setWrapping(True) self.formLayout.insertRow(5, self.label_2, self.end_timeEdit) self.end_timeEdit.setDisplayFormat("HH:mm") current_time = QtCore.QTime.currentTime() # round the minutes to the resolution minute = current_time.minute() hour = current_time.hour() minute = int(minute / float(timing_resolution)) * timing_resolution current_time = QtCore.QTime(hour, minute) self.start_timeEdit.setTime(current_time) self.end_timeEdit.setTime(current_time.addSecs(timing_resolution * 60)) self.calendarWidget.resource_id = -1 # setup signals self._setup_signals() # setup defaults self._set_defaults() # center window self.center_window()
class MainDialog(QtWidgets.QDialog, time_log_dialog_UI.Ui_Dialog, AnimaDialogBase): """The TimeLog Dialog """ def __init__(self, parent=None, task=None, timelog=None): logger.debug("initializing the interface") # store the logged in user self.logged_in_user = None self.no_time_left = False self.extended_hours = None self.extended_minutes = None self.timelog = timelog self.timelog_created = False self.task = task super(MainDialog, self).__init__(parent) self.setupUi(self) # customize the ui elements from anima.ui.widgets import TaskComboBox self.tasks_comboBox = TaskComboBox(self) self.tasks_comboBox.setObjectName("tasks_comboBox") self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.tasks_comboBox) # self.start_timeEdit.deleteLater() from anima.ui.widgets import TimeEdit self.start_timeEdit = TimeEdit(self, resolution=timing_resolution) self.start_timeEdit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection) self.start_timeEdit.setCalendarPopup(True) self.start_timeEdit.setObjectName("start_timeEdit") self.start_timeEdit.setWrapping(True) self.formLayout.insertRow(4, self.label, self.start_timeEdit) self.start_timeEdit.setDisplayFormat("HH:mm") # self.end_timeEdit.deleteLater() self.end_timeEdit = TimeEdit(self, resolution=timing_resolution) self.end_timeEdit.setCurrentSection( QtWidgets.QDateTimeEdit.MinuteSection) self.end_timeEdit.setCalendarPopup(True) self.end_timeEdit.setObjectName("end_timeEdit") self.end_timeEdit.setWrapping(True) self.formLayout.insertRow(5, self.label_2, self.end_timeEdit) self.end_timeEdit.setDisplayFormat("HH:mm") current_time = QtCore.QTime.currentTime() # round the minutes to the resolution minute = current_time.minute() hour = current_time.hour() minute = int(minute / float(timing_resolution)) * timing_resolution current_time = QtCore.QTime(hour, minute) self.start_timeEdit.setTime(current_time) self.end_timeEdit.setTime(current_time.addSecs(timing_resolution * 60)) self.calendarWidget.resource_id = -1 # setup signals self._setup_signals() # setup defaults self._set_defaults() # center window self.center_window() def close(self): logger.debug('closing the ui') QtWidgets.QDialog.close(self) def show(self): """overridden show method """ logger.debug('MainDialog.show is started') self.logged_in_user = self.get_logged_in_user() if not self.logged_in_user: self.close() return_val = None else: return_val = super(MainDialog, self).show() logger.debug('MainDialog.show is finished') return return_val def _setup_signals(self): """sets up the signals """ logger.debug("start setting up interface signals") # tasks_comboBox QtCore.QObject.connect(self.tasks_comboBox, QtCore.SIGNAL('currentIndexChanged(QString)'), self.tasks_combo_box_index_changed) # start_timeEdit QtCore.QObject.connect(self.start_timeEdit, QtCore.SIGNAL('timeChanged(QTime)'), self.start_time_changed) # end_timeEdit QtCore.QObject.connect(self.end_timeEdit, QtCore.SIGNAL('timeChanged(QTime)'), self.end_time_changed) # resource changed QtCore.QObject.connect(self.resource_comboBox, QtCore.SIGNAL('currentIndexChanged(QString)'), self.resource_changed) def _set_defaults(self): """sets the defaults for the ui """ logger.debug("started setting up interface defaults") # enter revision types revision_types = [ 'Yetistiremedim', 'Ajans', 'Yonetmen', 'Ic Revizyon', ] self.revision_type_comboBox.addItems(revision_types) if not self.logged_in_user: self.logged_in_user = self.get_logged_in_user() # fill the tasks comboBox from stalker import Status, Task status_wfd = Status.query.filter(Status.code == 'WFD').first() status_cmpl = Status.query.filter(Status.code == 'CMPL').first() status_prev = Status.query.filter(Status.code == 'PREV').first() if not self.timelog: # dialog is in create TimeLog mode # if a task has been given just feed that task to the comboBox if self.task: all_tasks = [self.task] else: # no Task is given nor updating a TimeLog # show all the suitable tasks of the logged_in_user all_tasks = Task.query \ .filter(Task.resources.contains(self.logged_in_user)) \ .filter(Task.status != status_wfd) \ .filter(Task.status != status_cmpl) \ .filter(Task.status != status_prev) \ .all() # sort the task labels all_tasks = sorted( all_tasks, key=lambda task: '%s | %s' % (task.project.name.lower(), ' | '.join( map(lambda x: x.name.lower(), task.parents)))) else: # dialog is working in update TimeLog mode all_tasks = [self.timelog.task] self.tasks_comboBox.setSizeAdjustPolicy( QtWidgets.QComboBox.AdjustToContentsOnFirstShow) self.tasks_comboBox.setFixedWidth(360) self.tasks_comboBox.clear() self.tasks_comboBox.addTasks(all_tasks) self.tasks_comboBox.setSizePolicy( QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum) # if a time log is given set the fields from the given time log if self.timelog: # first update Task try: self.tasks_comboBox.setCurrentTask(self.timelog.task) except IndexError as e: return # set the resource # and disable the tasks_comboBox and resource_comboBox self.tasks_comboBox.setEnabled(False) self.resource_comboBox.setEnabled(False) # set the start and end time from anima.utils import utc_to_local start_date = utc_to_local(self.timelog.start) end_date = utc_to_local(self.timelog.end) # set the date self.calendarWidget.setSelectedDate( QtCore.QDate(start_date.year, start_date.month, start_date.day)) # first reset the start and end time values self.start_timeEdit.setTime(QtCore.QTime(0, 0)) self.end_timeEdit.setTime(QtCore.QTime(23, 50)) # now set the timelog time self.start_timeEdit.setTime( QtCore.QTime(start_date.hour, start_date.minute)) self.end_timeEdit.setTime( QtCore.QTime(end_date.hour, end_date.minute)) self.fill_calendar_with_time_logs() def fill_calendar_with_time_logs(self): """fill the calendar with daily time log info """ resource_id = self.get_current_resource_id() # do not update if the calendar is showing the same user if self.calendarWidget.resource_id == resource_id or resource_id == -1: return tool_tip_text_format = u'{start:%H:%M} - {end:%H:%M} | {task_name}' # import time # start = time.time() # get all the TimeLogs grouped daily sql = """-- TOP DOWN SEARCH -- select "TimeLogs".start::date as date, array_agg(task_rec_data.full_path) as task_name, array_agg("TimeLogs".start) as start, array_agg("TimeLogs".end) as end, sum(extract(epoch from ("TimeLogs".end - "TimeLogs".start))) from "TimeLogs" join ( with recursive recursive_task(id, parent_id, path_names) as ( select task.id, task.project_id, -- task.project_id::text as path, ("Projects".code || '') as path_names from "Tasks" as task join "Projects" on task.project_id = "Projects".id where task.parent_id is NULL union all select task.id, task.parent_id, -- (parent.path || '|' || task.parent_id:: text) as path, (parent.path_names || ' | ' || "Parent_SimpleEntities".name) as path_names from "Tasks" as task inner join recursive_task as parent on task.parent_id = parent.id inner join "SimpleEntities" as "Parent_SimpleEntities" on parent.id = "Parent_SimpleEntities".id ) select recursive_task.id, "SimpleEntities".name || ' (' || recursive_task.path_names || ')' as full_path from recursive_task join "SimpleEntities" on recursive_task.id = "SimpleEntities".id ) as task_rec_data on "TimeLogs".task_id = task_rec_data.id -- getting all the data is as fast as getting one, so get all the TimeLogs of this user where "TimeLogs".resource_id = :resource_id group by cast("TimeLogs".start as date) order by cast("TimeLogs".start as date) """ from sqlalchemy import text from stalker.db.session import DBSession result = DBSession.connection().execute( text(sql), resource_id=resource_id).fetchall() # end = time.time() # print('getting data from sql: %0.3f sec' % (end - start)) from anima.utils import utc_to_local time_shifter = utc_to_local import stalker from distutils.version import LooseVersion if LooseVersion(stalker.__version__) >= LooseVersion('0.2.18'): def time_shifter(x): return x for r in result: calendar_day = r[0] year = calendar_day.year month = calendar_day.month day = calendar_day.day daily_logged_seconds = r[4] daily_logged_hours = daily_logged_seconds // 3600 daily_logged_minutes = \ (daily_logged_seconds - daily_logged_hours * 3600) // 60 tool_tip_text_data = [ u'Total: %i h %i min logged' % (daily_logged_hours, daily_logged_minutes) if daily_logged_hours else u'Total: %i min logged' % daily_logged_minutes ] for task_name, start, end in sorted(zip(r[1], r[2], r[3]), key=lambda x: x[1]): time_log_tool_tip_text = tool_tip_text_format.format( start=time_shifter(start), end=time_shifter(end), task_name=task_name) tool_tip_text_data.append(time_log_tool_tip_text) merged_tool_tip = u'\n'.join(tool_tip_text_data) date_format = QtGui.QTextCharFormat() bg_brush = QtGui.QBrush() bg_brush.setColor( QtGui.QColor(0, 255.0 / 86400.0 * daily_logged_seconds, 0)) date_format.setBackground(bg_brush) date_format.setToolTip(merged_tool_tip) date = QtCore.QDate(year, month, day) self.calendarWidget.setDateTextFormat(date, date_format) def tasks_combo_box_index_changed(self, task_label): """runs when another task has been selected """ # task = self.get_current_task() task = self.tasks_comboBox.currentTask() self.update_percentage() self.update_info_text() # update resources comboBox resource_names = [] if self.logged_in_user in task.resources: resource_names = [self.logged_in_user.name] for r in task.resources: if r != self.logged_in_user: resource_names.append(r.name) self.resource_comboBox.clear() self.resource_comboBox.addItems(resource_names) def get_current_resource(self): """returns the current resource """ resource_name = self.resource_comboBox.currentText() from stalker import User return User.query.filter(User.name == resource_name).first() def get_current_resource_id(self): """returns the current resource """ resource_name = self.resource_comboBox.currentText() from stalker import User from stalker.db.session import DBSession return DBSession.query(User.id)\ .filter(User.name == resource_name)\ .first() def start_time_changed(self, q_time): """validates the start time """ end_time = self.end_timeEdit.time() if q_time >= end_time: self.end_timeEdit.setTime(q_time.addSecs(timing_resolution * 60)) self.update_percentage() self.update_info_text() def end_time_changed(self, q_time): """validates the end time """ start_time = self.start_timeEdit.time() if q_time <= start_time: if q_time.hour() == 0 and q_time.minute() == 0: start_time = q_time end_time = q_time.addSecs(timing_resolution * 60) self.end_timeEdit.setTime(end_time) else: start_time = q_time.addSecs(-timing_resolution * 60) self.start_timeEdit.setTime(start_time) self.update_percentage() self.update_info_text() def resource_changed(self, resource_name): """runs when the selected resource has changed """ self.fill_calendar_with_time_logs() def calculate_percentage(self): # task = self.get_current_task() task = self.tasks_comboBox.currentTask() schedule_seconds = task.schedule_seconds logged_seconds = task.total_logged_seconds start_time = self.start_timeEdit.time() end_time = self.end_timeEdit.time() time_log_seconds = start_time.secsTo(end_time) percentage = \ (logged_seconds + time_log_seconds) / float(schedule_seconds) * 100 return percentage def update_percentage(self): """updates the percentage """ percentage = self.calculate_percentage() self.task_progressBar.setMinimum(0) self.task_progressBar.setMaximum(100) self.task_progressBar.setValue(percentage) def update_info_text(self): """updated the info text """ # task = self.get_current_task() task = self.tasks_comboBox.currentTask() schedule_seconds = task.schedule_seconds logged_seconds = task.total_logged_seconds start_time = self.start_timeEdit.time() end_time = self.end_timeEdit.time() time_log_seconds = start_time.secsTo(end_time) remaining_seconds = \ schedule_seconds - (logged_seconds + time_log_seconds) # if a time log given add its seconds if self.timelog: remaining_seconds += self.timelog.total_seconds # calculate the remaining minutes and hours self.no_time_left = False if remaining_seconds < 0: self.no_time_left = True remaining_seconds = abs(remaining_seconds) hours = remaining_seconds // 3600 minutes = (remaining_seconds - hours * 3600) // 60 if self.no_time_left: self.info_area_label.setText( 'You need <b>%i h %i min</b> extra time. ' 'If you enter this time log, time of the task will be ' 'extended. Are you sure?' % (hours, minutes)) self.show_revision_fields() self.extended_hours = hours self.extended_minutes = minutes else: self.hide_revision_fields() self.info_area_label.setText( 'If you enter this time log, ' '<b>%i h %i min</b> will remain to complete this task.' % (hours, minutes)) self.extended_hours = None self.extended_minutes = None def show_revision_fields(self): """shows the revision fields """ self.revision_label.show() self.revision_type_comboBox.show() def hide_revision_fields(self): """hides the revision fields """ self.revision_label.hide() self.revision_type_comboBox.hide() def accept(self): """overridden accept method """ # get the info task = self.tasks_comboBox.currentTask() resource = self.get_current_resource() # war the user if the resource is not the logged_in_user if resource != self.logged_in_user: msg_box = QtWidgets.QMessageBox(self) msg_box.setWindowTitle('Baskasi Adina TimeLog Giriyorsun') msg_box.setText('Baskasi adina TimeLog giriyorsun???') accept_button = msg_box.addButton('Sorumlulugu Kabul Ediyorum', QtWidgets.QMessageBox.AcceptRole) cancel_button = msg_box.addButton('Cancel', QtWidgets.QMessageBox.RejectRole) msg_box.setDefaultButton(cancel_button) msg_box.exec_() clicked_button = msg_box.clickedButton() if clicked_button == cancel_button: return description = self.description_plainTextEdit.toPlainText() revision_cause_text = \ self.revision_type_comboBox.currentText().replace(' ', '_') is_complete = self.set_as_complete_radioButton.isChecked() submit_to_final_review = \ self.submit_for_final_review_radioButton.isChecked() # get the revision Types from stalker import Type revision_type = Type.query\ .filter(Type.target_entity_type == 'Note')\ .filter(Type.name == revision_cause_text)\ .first() date = self.calendarWidget.selectedDate() start = self.start_timeEdit.time() end = self.end_timeEdit.time() # construct proper datetime.DateTime instances import datetime start_date = datetime.datetime(date.year(), date.month(), date.day(), start.hour(), start.minute()) end_date = datetime.datetime(date.year(), date.month(), date.day(), end.hour(), end.minute()) today_midnight = datetime.datetime.now().replace(hour=23, minute=59, second=59, microsecond=999) # raise an error if the user is trying to enter a TimeLog to the future if start_date > today_midnight or end_date > today_midnight: QtWidgets.QMessageBox.critical( self, 'Error', 'Gelecege TimeLog giremezsiniz!!!', ) return # convert them to utc from anima.utils import local_to_utc utc_start_date = local_to_utc(start_date) utc_end_date = local_to_utc(end_date) # create a TimeLog # print('Task : %s' % task.name) # print('Resource : %s' % resource.name) # print('utc_start_date: %s' % utc_start_date) # print('utc_end_date : %s' % utc_end_date) # now if we are not using extra time just create the TimeLog from stalker import TimeLog from stalker.db.session import DBSession from stalker.exceptions import OverBookedError utc_now = local_to_utc(datetime.datetime.now()) import stalker from distutils.version import LooseVersion if LooseVersion(stalker.__version__) >= LooseVersion('0.2.18'): # inject timezone info import pytz utc_start_date = utc_start_date.replace(tzinfo=pytz.utc) utc_end_date = utc_end_date.replace(tzinfo=pytz.utc) utc_now = utc_now.replace(tzinfo=pytz.utc) from sqlalchemy.exc import IntegrityError if not self.timelog: try: new_time_log = TimeLog(task=task, resource=resource, start=utc_start_date, end=utc_end_date, description=description, date_created=utc_now) except OverBookedError: # inform the user that it can not do that QtWidgets.QMessageBox.critical( self, 'Error', 'O saatte baska time log var!!!') DBSession.rollback() return try: DBSession.add(new_time_log) DBSession.commit() self.timelog_created = True except IntegrityError as e: DBSession.rollback() QtWidgets.QMessageBox.critical( self, 'Error', 'Database hatasi!!!' '<br>' '%s' % e) return else: # just update the date values self.timelog.start = utc_start_date self.timelog.end = utc_end_date self.timelog.date_updated = utc_now DBSession.add(self.timelog) DBSession.commit() if self.no_time_left: # we have no time left so automatically extend the task from stalker import Task schedule_timing, schedule_unit = \ task.least_meaningful_time_unit( task.total_logged_seconds ) if schedule_timing != 0: task.schedule_timing = schedule_timing task.schedule_unit = schedule_unit # also create a Note from stalker import Note new_note = Note( content='Extending timing of the task <b>%s h %s min.</b>' % (self.extended_hours, self.extended_minutes), type=revision_type, created_by=self.logged_in_user, date_created=utc_now) DBSession.add(new_note) task.notes.append(new_note) try: DBSession.commit() except IntegrityError as e: QtWidgets.QMessageBox.critical( self, 'Error', 'Database hatasi!!!' '<br>' '%s' % e) DBSession.rollback() return if is_complete: # set the status to complete from stalker import Type, Status status_cmpl = Status.query.filter(Status.code == 'CMPL').first() forced_status_type = \ Type.query.filter(Type.name == 'Forced Status').first() # also create a Note from stalker import Note new_note = Note( content='%s has changed this task status to Completed' % resource.name, type=forced_status_type, created_by=self.logged_in_user, date_created=utc_now) DBSession.add(new_note) task.notes.append(new_note) task.status = status_cmpl DBSession.commit() elif submit_to_final_review: # clip the Task timing to current time logs from stalker import Task schedule_timing, schedule_unit = \ task.least_meaningful_time_unit( task.total_logged_seconds ) task.schedule_timing = schedule_timing task.schedule_unit = schedule_unit DBSession.add(task) try: DBSession.commit() except IntegrityError as e: QtWidgets.QMessageBox.critical( self, 'Error', 'Database hatasi!!!' '<br>' '%s' % e) DBSession.rollback() return # request a review reviews = task.request_review() for review in reviews: review.created_by = review.updated_by = self.logged_in_user review.date_created = utc_now review.date_updated = utc_now DBSession.add_all(reviews) # and create a Note for the Task request_review_note_type = \ Type.query\ .filter(Type.target_entity_type == 'Note')\ .filter(Type.name == 'Request Review')\ .first() from stalker import Note request_review_note = Note(type=request_review_note_type, created_by=self.logged_in_user, date_created=utc_now) DBSession.add(request_review_note) DBSession.add(task) task.notes.append(request_review_note) try: DBSession.commit() except IntegrityError as e: DBSession.rollback() QtWidgets.QMessageBox.critical( self, 'Error', 'Database hatasi!!!' '<br>' '%s' % e) return # # Fix statuses # task.update_parent_statuses() # for dep_task in task.dependent_of: # dep_task.update_status_with_dependent_statuses() fix_task_statuses(task) try: DBSession.commit() except IntegrityError as e: DBSession.rollback() QtWidgets.QMessageBox.critical( self, 'Error', 'Database hatasi!!!' '<br>' '%s' % e) return # if nothing bad happens close the dialog super(MainDialog, self).accept()