~jonatan-cloutier/gtimelog/addTags

« back to all changes in this revision

Viewing changes to src/gtimelog/main.py

  • Committer: Jonatan Cloutier
  • Date: 2013-02-16 21:48:26 UTC
  • Revision ID: cloutier.jo@gmail.com-20130216214826-3ac6hfrgh4r42yhw
add tags feature

Show diffs side-by-side

added added

removed removed

Lines of Context:
350
350
        duration = stop - start
351
351
        return start, stop, duration, entry
352
352
 
353
 
    def grouped_entries(self, skip_first=True):
 
353
    def grouped_entries(self, skip_first=True, trimTags=True):
354
354
        """Return consolidated entries (grouped by entry title).
355
355
 
356
356
        Returns two list: work entries and slacking entries.  Slacking
360
360
        work = {}
361
361
        slack = {}
362
362
        for start, stop, duration, entry in self.all_entries():
 
363
            if trimTags:
 
364
                entry = entry.split(" '")[0]
363
365
            if skip_first:
364
366
                skip_first = False
365
367
                continue
411
413
                    None, datetime.timedelta(0)) + duration
412
414
        return entries, totals
413
415
 
 
416
    def tagged_work_entries(self, skip_first=True):
 
417
        """Return consolidated work entries grouped by tag.
 
418
 
 
419
        Tag is a string following an apostrophe (') at the end of an entry.
 
420
 
 
421
        Return two dicts:
 
422
          - {<tag>: <entry list>}, where <tag> is a tag string
 
423
            and <entry list> is a sorted list that contains tuples (start,
 
424
            entry, duration); entry is stripped of its tag. An entry can
 
425
            be present in more than one tag.
 
426
          - {<tag>: <total duration>}, where <total duration> is the
 
427
            total duration of work in the <category>.
 
428
        """
 
429
 
 
430
        work, slack = self.grouped_entries(skip_first=skip_first, trimTags=False)
 
431
        entries = {}
 
432
        totals = {}
 
433
        for start, entry, duration in work:
 
434
            if " '" in entry:
 
435
                splitedEntries = entry.split(" '")
 
436
                tags = splitedEntries[1:]
 
437
                clipped_entry = splitedEntries[0]
 
438
                for tag in tags:
 
439
                    entry_list = entries.get(tag, [])
 
440
                    entry_list.append((start, clipped_entry, duration))
 
441
                    entries[tag] = entry_list
 
442
                    totals[tag] = totals.get(tag, datetime.timedelta(0)) + duration
 
443
        return entries, totals
 
444
 
414
445
    def totals(self):
415
446
        """Calculate total time of work and slacking entries.
416
447
 
637
668
        print >> output
638
669
        print >> output, "By category:"
639
670
        print >> output
640
 
 
641
 
        items = categories.items()
 
671
        
 
672
        self._report_groups(output, categories)
 
673
 
 
674
    def _report_tags(self, output, tags):
 
675
        """A helper method that lists time spent per tag.
 
676
 
 
677
        Use this to add a section in a report looks similar to this:
 
678
 
 
679
        Administration:  2 hours 1 min
 
680
        Coding:          18 hours 45 min
 
681
        Learning:        3 hours
 
682
 
 
683
        tags is a dict of entries (<tag name>: <duration>).
 
684
        """
 
685
        print >> output
 
686
        print >> output, "By tag:"
 
687
        print >> output
 
688
        
 
689
        self._report_groups(output, tags)
 
690
        
 
691
    def _report_groups(self, output, groups):
 
692
        """A helper method that lists time spent per grouped entries,
 
693
         either categories or tags.
 
694
        """
 
695
        items = groups.items()
642
696
        items.sort()
643
697
        for cat, duration in items:
644
698
            if not cat:
647
701
            print >> output, u"%-62s  %s" % (
648
702
                cat, format_duration_long(duration))
649
703
 
650
 
        if None in categories:
 
704
        if None in groups:
651
705
            print >> output, u"%-62s  %s" % (
652
 
                '(none)', format_duration_long(categories[None]))
 
706
                '(none)', format_duration_long(groups[None]))
653
707
        print >> output
654
708
 
655
709
    def _plain_report(self, output, email, who, subject, period_name,
674
728
            print >> output, "                time"
675
729
        work, slack = window.grouped_entries()
676
730
        total_work, total_slacking = window.totals()
677
 
        categories = {}
678
731
        if work:
679
732
            work = [(entry, duration) for start, entry, duration in work]
680
733
            work.sort()
682
735
                if not duration:
683
736
                    continue # skip empty "arrival" entries
684
737
 
685
 
                if ': ' in entry:
686
 
                    cat, task = entry.split(': ', 1)
687
 
                    categories[cat] = categories.get(
688
 
                        cat, datetime.timedelta(0)) + duration
689
 
                else:
690
 
                    categories[None] = categories.get(
691
 
                        None, datetime.timedelta(0)) + duration
692
 
 
693
738
                entry = entry[:1].upper() + entry[1:]
694
739
                if estimated_column:
695
740
                    print >> output, (u"%-46s  %-14s  %s" %
701
746
        print >> output, ("Total work done this %s: %s" %
702
747
                          (period_name, format_duration_long(total_work)))
703
748
 
 
749
        entries, categories = window.categorized_work_entries()
704
750
        if categories:
705
751
            self._report_categories(output, categories)
706
752
 
 
753
        entries, tags = window.tagged_work_entries()
 
754
        if tags:
 
755
            self._report_tags(output, tags)
 
756
 
707
757
    def weekly_report_categorized(self, output, email, who,
708
758
                                  estimated_column=False):
709
759
        """Format a weekly report with entries displayed  under categories."""
766
816
        print >> output
767
817
        work, slack = window.grouped_entries()
768
818
        total_work, total_slacking = window.totals()
769
 
        categories = {}
770
819
        if work:
771
820
            for start, entry, duration in work:
772
821
                entry = entry[:1].upper() + entry[1:]
773
822
                print >> output, u"%-62s  %s" % (entry,
774
823
                                                format_duration_long(duration))
775
 
                if ': ' in entry:
776
 
                    cat, task = entry.split(': ', 1)
777
 
                    categories[cat] = categories.get(
778
 
                        cat, datetime.timedelta(0)) + duration
779
 
                else:
780
 
                    categories[None] = categories.get(
781
 
                        None, datetime.timedelta(0)) + duration
782
824
 
783
825
            print >> output
784
826
        print >> output, ("Total work done: %s" %
785
827
                          format_duration_long(total_work))
786
828
 
787
 
        if len(categories) > 0:
 
829
        entries, categories = window.categorized_work_entries()
 
830
        if categories:
788
831
            self._report_categories(output, categories)
789
832
 
 
833
        entries, tags = window.tagged_work_entries()
 
834
        if tags:
 
835
            self._report_tags(output, tags)
 
836
 
790
837
        print >> output, 'Slacking:\n'
791
838
 
792
839
        if slack:
1053
1100
    spreadsheet = 'xdg-open %s'
1054
1101
    chronological = True
1055
1102
    summary_view = False
 
1103
    tags_view = False
1056
1104
    show_tasks = True
1057
1105
 
1058
1106
    enable_gtk_completion = True  # False enables gvim-style completion
1111
1159
        config.set('gtimelog', 'spreadsheet', self.spreadsheet)
1112
1160
        config.set('gtimelog', 'chronological', str(self.chronological))
1113
1161
        config.set('gtimelog', 'summary_view', str(self.summary_view))
 
1162
        config.set('gtimelog', 'tags_view', str(self.tags_view))
1114
1163
        config.set('gtimelog', 'show_tasks', str(self.show_tasks))
1115
1164
        config.set('gtimelog', 'gtk-completion',
1116
1165
                   str(self.enable_gtk_completion))
1140
1189
        self.spreadsheet = config.get('gtimelog', 'spreadsheet')
1141
1190
        self.chronological = config.getboolean('gtimelog', 'chronological')
1142
1191
        self.summary_view = config.getboolean('gtimelog', 'summary_view')
 
1192
        self.tags_view = config.getboolean('gtimelog', 'tags_view')
1143
1193
        self.show_tasks = config.getboolean('gtimelog', 'show_tasks')
1144
1194
        self.enable_gtk_completion = config.getboolean('gtimelog',
1145
1195
                                                       'gtk-completion')
1440
1490
        self.chronological = (settings.chronological
1441
1491
                              and not settings.summary_view)
1442
1492
        self.summary_view = settings.summary_view
 
1493
        self.tags_view = settings.tags_view
1443
1494
        self.show_tasks = settings.show_tasks
1444
1495
        self.looking_at_date = None
1445
1496
        self.entry_watchers = []
1454
1505
        chronological_menu_item.set_active(self.chronological)
1455
1506
        summary_menu_item = builder.get_object('summary')
1456
1507
        summary_menu_item.set_active(self.summary_view)
 
1508
        tags_menu_item = builder.get_object('tags')
 
1509
        tags_menu_item.set_active(self.tags_view)
1457
1510
        show_task_pane_item = builder.get_object('show_task_pane')
1458
1511
        show_task_pane_item.set_active(self.show_tasks)
1459
1512
        # Now hook up signals.
1568
1621
            where = buffer.get_end_iter()
1569
1622
            where.backward_cursor_position()
1570
1623
            buffer.place_cursor(where)
 
1624
        elif self.tags_view:
 
1625
            entries, totals = window.tagged_work_entries()
 
1626
            for category, duration in sorted(totals.items()):
 
1627
                self.write_group(category or 'no category', duration)
 
1628
            where = buffer.get_end_iter()
 
1629
            where.backward_cursor_position()
 
1630
            buffer.place_cursor(where)
1571
1631
        else:
1572
1632
            work, slack = window.grouped_entries()
1573
1633
            for start, entry, duration in work + slack:
1830
1890
        """View -> Chronological"""
1831
1891
        self.chronological = True
1832
1892
        self.summary_view = False
 
1893
        self.tags_view = False
1833
1894
        self.populate_log()
1834
1895
 
1835
1896
    def on_grouped_activate(self, widget):
1836
1897
        """View -> Grouped"""
1837
1898
        self.chronological = False
1838
1899
        self.summary_view = False
 
1900
        self.tags_view = False
1839
1901
        self.populate_log()
1840
1902
 
1841
1903
    def on_summary_activate(self, widget):
1842
1904
        """View -> Summary"""
1843
1905
        self.chronological = False
1844
1906
        self.summary_view = True
 
1907
        self.tags_view = False
 
1908
        self.populate_log()
 
1909
 
 
1910
    def on_tags_activate(self, widget):
 
1911
        """View -> Tags"""
 
1912
        self.chronological = False
 
1913
        self.summary_view = False
 
1914
        self.tags_view = True
1845
1915
        self.populate_log()
1846
1916
 
1847
1917
    def daily_window(self, day=None):