Example #1
0
 def __init__(self, filename=None, parent=None):
     super(Workspace, self).__init__(parent)
     self.setWindowTitle(APP_NAME)
     if filename is None:
         filename = ":memory:"
     self._db = LogDb(filename, bounds=60, units='minutes')
     # Widgets
     self.filter_panel = FilterPanel(self)
     self.list_panel = ListPanel(self._db, self)
     self.graph_panel = GraphPanel(self._db, self)
     self.tabs = widgets.QTabWidget()
     # self.tabs.setTabPosition(widgets.QTabWidget.West)
     self.tabs.addTab(self.list_panel, get_icon('list.png'), 'List')
     self.tabs.addTab(self.graph_panel, get_icon('pie.png'), 'Graph')
     # Layout
     main = widgets.QVBoxLayout()
     top_section = widgets.QHBoxLayout()
     top_section.addStretch(0)
     top_section.addWidget(self.filter_panel, 1)
     top_section.addStretch(0)
     main.addLayout(top_section)
     main.addWidget(self.tabs, 1)
     self.setLayout(main)
     # Connections
     self.filter_panel.apply_filter.connect(self.apply_filter)
     # self.tabs.currentChanged.connect(self.tab_change)
     # Initialize
     last_entry = self._db.filter(last=True)
     if last_entry is not None:
         last_entry = last_entry['start']
     self.filter_panel.initial_state(last_entry)
Example #2
0
 def start_end_edited(self):
     current_duration = LogDb.parse_duration(self.duration_field.text())
     if current_duration is None:
         current_duration = self.seconds
     if (self.link.isChecked() or current_duration is None
             or current_duration > self.start_end_seconds):
         self.seconds = self.start_end_seconds
     else:
         self.seconds = current_duration
     duration_text = LogDb.format_duration(self.seconds)
     self.duration_field.setText(duration_text)
Example #3
0
 def duration_edited(self):
     new = LogDb.parse_duration(self.duration_field.text())
     if new is None:
         if self.link.isChecked() or self.seconds is None:
             new = self.start_end_seconds
         else:
             new = self.seconds
     if self.link.isChecked() or new > self.start_end_seconds:
         self.end.setDateTime(self.start.dateTime().toPyDateTime() +
                              datetime.timedelta(seconds=new))
     self.seconds = new
     self.duration_field.setText(LogDb.format_duration(new))
Example #4
0
 def _plot(self, figure, database, activity, start, end):
     axes = figure.add_subplot('111')
     start, end = self.bracket(database, '', start, end)
     chunk_size = LogDb.parse_duration(self.sample_field.text())
     level = self.level.value()
     ranked_activities = self.ranked_activities(database, activity, start,
                                                end)
     allowed_activities = [n[0] for n in ranked_activities]
     midpoints, activities = database.span_slices(
         start=start,
         span=end - start,
         chunk_size=chunk_size,
         level=level,
         unrecorded=False,
     )
     # Mutate chunk_size so it's understandable by matplotlib
     chunk_size = chunk_size / datetime.timedelta(days=1).total_seconds()
     self._graph_data(activities, figure, axes, allowed_activities,
                      midpoints, chunk_size, activity)
     axes.xaxis.set_ticks([
         datetime.datetime(start.year, start.month, start.day, 0, 0, 0) +
         datetime.timedelta(days=n) for n in range(
             int(round((end - start) / datetime.timedelta(days=1))))
     ],
                          minor=True)
     axes.grid(self.day_boundary_lines.isChecked(), 'minor', 'x')
     figure.autofmt_xdate()
Example #5
0
 def set_entry(self, entry):
     self._id = entry['id']
     self.activity.setText(entry['activity'])
     self.start.setDateTime(entry['start'])
     span = (entry['end'] - entry['start']).total_seconds()
     self.link.setChecked(abs(span - entry['duration']) < 0.01)
     self.end.setDateTime(entry['end'])
     self.duration_field.setText(LogDb.format_duration(entry['duration']))
     self.activity.setFocus()
Example #6
0
 def value(self):
     self._duration_edited()
     seconds = LogDb.parse_duration(self.duration.text())
     if seconds is None:
         return seconds
     elif self.forward_box.isChecked():
         return datetime.timedelta(seconds=seconds)
     elif self.backward_box.isChecked():
         return datetime.timedelta(seconds=0 - seconds)
Example #7
0
def get_database():
    """
    Return a LogDb instance based on the current user's application storage
    location.
    """
    # This can't be in the helper module because it creates circular imports.
    # So it's here.
    app = widgets.QApplication.instance() or widgets.QApplication(sys.argv)
    app.setApplicationName(APP_NAME)
    app.setOrganizationName(ORG_NAME)
    app_dir = core.QStandardPaths.writableLocation(
        core.QStandardPaths.AppDataLocation)
    # app_dir = gui.QDesktopServices.storageLocation(
    #     gui.QDesktopServices.DataLocation
    # )
    filename = os.path.join(app_dir, "user_data.sqlite")
    return LogDb(filename)
Example #8
0
 def entry(self):
     if self.activity.hasFocus():
         self.activity.editingFinished.emit()
     if self.duration_field.hasFocus():
         self.duration_field.editingFinished.emit()
     activity = self.activity.text().strip()
     if activity == '':
         activity = None
     start = self.start.dateTime().toPyDateTime()
     end = self.end.dateTime().toPyDateTime()
     if abs((end - start).seconds - self.seconds) < 60:
         duration = None
     else:
         duration = LogDb.parse_duration(self.duration_field.text())
     entry = {
         'id': self._id,
         'activity': activity,
         'start': start,
         'end': end,
         'duration': duration,
     }
     return entry
Example #9
0
 def _plot(self, figure, database, activity, start, end):
     figure.set_tight_layout(False)
     start = datetime.datetime.fromordinal(start.date().toordinal())
     end = datetime.datetime.fromordinal(
         (end + datetime.timedelta(days=1)).date().toordinal())
     level = self.level.value()
     if activity != 'unrecorded':
         start, end = self.bracket(database, '', start, end)
     activities = dict()
     day_labels = list()
     day_count = (end - start).days
     seconds_per_day = datetime.timedelta(days=1).total_seconds()
     day_totals = [
         0,
     ] * day_count
     for day_offset in range(day_count):
         chunk_start = start + datetime.timedelta(days=day_offset)
         day_labels.append(stringify_date(chunk_start))
         chunk_activities = database.slice_activities(
             start=chunk_start,
             end=chunk_start + datetime.timedelta(days=1),
             level=level,
             unrecorded=False,
             activity=activity,
         )
         for k, v in chunk_activities.items():
             if k not in activities.keys():
                 activities[k] = [
                     0,
                 ] * day_count
             activities[k][day_offset] = v * seconds_per_day
         day_totals[day_offset] = sum(
             [v[day_offset] for v in activities.values()])
     activity_totals = dict([(k, sum(v)) for k, v in activities.items()])
     activity_labels = sorted(list(activity_totals.keys()),
                              key=lambda k: activity_totals[k],
                              reverse=True)
     #run_pdb()
     axes = figure.add_subplot('111')
     celltext = list()
     for k in activity_labels:
         if self.verbose_times.isChecked():
             celltext.append(
                 [LogDb.format_duration(v) for v in activities[k]] + [
                     LogDb.format_duration(activity_totals[k]),
                 ])
         else:
             data = activities[k] + [activity_totals[k]]
             data = [int(round(n / 60.0)) for n in data]
             celltext.append(data)
     if self.verbose_times.isChecked():
         celltext.append([LogDb.format_duration(v) for v in day_totals] +
                         [LogDb.format_duration(sum(day_totals))])
     else:
         data = day_totals + [
             sum(day_totals),
         ]
         data = [int(round(n / 60.0)) for n in data]
         celltext.append(data)
     with open(os.path.expanduser('~/Desktop/debug.csv'), 'w',
               newline='') as fout:
         writer = csv.writer(fout)
         writer.writerow([
             '',
         ] + day_labels + [
             'TOTALS',
         ])
         for i, row in enumerate(celltext):
             if i < len(activity_labels):
                 writer.writerow([
                     activity_labels[i],
                 ] + row)
             else:
                 writer.writerow([''] + row)
         #writer.writerows(celltext)
     bg_color = mpl.rcParams['axes.facecolor']
     axes.table(cellText=celltext,
                rowLabels=activity_labels + [
                    'TOTALS',
                ],
                colLabels=day_labels + [
                    'TOTALS',
                ],
                cellColours=[[
                    bg_color,
                ] * (day_count + 1)] * len(celltext),
                rowColours=[
                    bg_color,
                ] * (len(activity_labels) + 1),
                colColours=[
                    bg_color,
                ] * (len(day_labels) + 1),
                loc='center')
Example #10
0
 def _sample_edited(self):
     new = LogDb.parse_duration(self.sample_field.text())
     self.sample_field.setText(LogDb.format_duration(new))
Example #11
0
 def sample(self):
     return LogDb.parse_duration(self.sample_field.text())
Example #12
0
 def _plot(self, figure, database, activity, start, end):
     weekly = self.weekly
     weekdays = None
     if hasattr(self, 'weekday_selector'):
         weekdays = self.weekday_selector.selection()
     axes = figure.add_subplot('111', xmargin=0, ymargin=0)
     start, end = self.bracket(database, '', start, end)
     chunk_size = LogDb.parse_duration(self.sample_field.text())
     level = self.level.value()
     ranked_activities = self.ranked_activities(database, activity, start,
                                                end)
     allowed_activities = [n[0] for n in ranked_activities]
     midpoints, activities = database.stacked_slices(
         start=start,
         span=end - start,
         chunk_size=chunk_size,
         level=level,
         unrecorded=False,
         weekdays=weekdays,
         weekly=weekly,
     )
     self._graph_data(activities, figure, axes, allowed_activities,
                      midpoints, chunk_size, activity)
     if weekly:
         axes.set_xlim((0, datetime.timedelta(days=7).total_seconds()))
         axes.xaxis.set_label_text("Weekdays")
         axes.xaxis.set_ticks((
             0,
             datetime.timedelta(days=1).total_seconds(),
             datetime.timedelta(days=2).total_seconds(),
             datetime.timedelta(days=3).total_seconds(),
             datetime.timedelta(days=4).total_seconds(),
             datetime.timedelta(days=5).total_seconds(),
             datetime.timedelta(days=6).total_seconds(),
             datetime.timedelta(days=7).total_seconds(),
         ))
         axes.xaxis.set_ticklabels(
             [core.QDate.shortDayName(n) for n in range(1, 8)]
             #('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')
         )
         axes.grid(self.day_boundary_lines.isChecked(), 'major', 'x')
     else:
         axes.set_xlim((0, datetime.timedelta(days=1).total_seconds()))
         axes.xaxis.set_label_text("Time of day")
         axes.xaxis.set_ticks([
             datetime.timedelta(hours=h).total_seconds()
             for h in range(0, 30, 6)
         ])
         axes.xaxis.set_ticks([
             datetime.timedelta(hours=h).total_seconds() for h in range(25)
         ],
                              minor=True)
         axes.xaxis.set_ticklabels(
             ('Midnight', '6am', 'Noon', '6pm', 'Midnight'))
         axes.grid(True, 'minor', 'x')
     axes.yaxis.set_ticklabels(list())
     date_fmt_string = core.QLocale().dateFormat()
     title = ("{0} Activities Between {1} and {2}".format(
         "Weekly" if weekly else "Daily", stringify_date(start),
         stringify_date(end)))
     if hasattr(self, 'weekday_selector'):
         if len(self.weekday_selector.selection()) < 7:
             title += "\n(" + str(self.weekday_selector) + ")"
     axes.set_title(title)