Пример #1
0
    def set_title(self):
        dates_dict = stuff.dateDict(self.start_date, "start_")
        dates_dict.update(stuff.dateDict(self.end_date, "end_"))
        
        if self.start_date.year != self.end_date.year:
            # overview label if start and end years don't match
            # letter after prefixes (start_, end_) is the one of
            # standard python date formatting ones- you can use all of them
            overview_label = _(u"Overview for %(start_B)s %(start_d)s, %(start_Y)s – %(end_B)s %(end_d)s, %(end_Y)s") % dates_dict
        elif self.start_date.month != self.end_date.month:
            # overview label if start and end month do not match
            # letter after prefixes (start_, end_) is the one of
            # standard python date formatting ones- you can use all of them
            overview_label = _(u"Overview for %(start_B)s %(start_d)s – %(end_B)s %(end_d)s, %(end_Y)s") % dates_dict
        else:
            # overview label for interval in same month
            # letter after prefixes (start_, end_) is the one of
            # standard python date formatting ones- you can use all of them
            overview_label = _(u"Overview for %(start_B)s %(start_d)s – %(end_d)s, %(end_Y)s") % dates_dict

        if self.week_view.get_active():
            dayview_caption = _("Week")
        else:
            dayview_caption = _("Month")
        
        self.get_widget("overview_label").set_markup("<b>%s</b>" % overview_label)
        self.get_widget("dayview_caption").set_markup("%s" % (dayview_caption))
Пример #2
0
    def do_charts(self, facts):
        all_categories = self.popular_categories
        
        
        #the single "totals" (by category) bar
        category_sums = stuff.totals(facts, lambda fact: fact["category"],
                      lambda fact: stuff.duration_minutes(fact["delta"]) / 60.0)
        category_totals = [category_sums.get(cat, 0)
                                                      for cat in all_categories]
        category_keys = ["%s %.1f" % (cat, category_sums.get(cat, 0.0))
                                                      for cat in all_categories]
        self.category_chart.plot([_("Total")],
                                 [category_totals],
                                 stack_keys = category_keys)
        
        # day / category chart
        all_days = [self.start_date + dt.timedelta(i)
                    for i in range((self.end_date - self.start_date).days  + 1)]        

        by_date_cat = stuff.totals(facts,
                                   lambda fact: (fact["date"],
                                                 fact["category"]),
                                   lambda fact: stuff.duration_minutes(fact["delta"]) / 60.0)
        res = [[by_date_cat.get((day, cat), 0)
                                 for cat in all_categories] for day in all_days]

        #show days or dates depending on scale
        if (self.end_date - self.start_date).days < 20:
            day_keys = [day.strftime("%a") for day in all_days]
        else:
            day_keys = [_("%(m_b)s %(m_d)s") %  stuff.dateDict(day, "m_") for day in all_days]

        self.day_chart.plot(day_keys, res, stack_keys = all_categories)


        #totals by activity, disguised under a stacked bar chart to get category colors
        activity_sums = stuff.totals(facts,
                                     lambda fact: (fact["name"],
                                                   fact["category"]),
                                     lambda fact: stuff.duration_minutes(fact["delta"]))
        by_duration = sorted(activity_sums.items(),
                             key = lambda x: x[1],
                             reverse = True)
        by_duration_keys = [entry[0][0] for entry in by_duration]

        category_sums = [[entry[1] / 60.0 * (entry[0][1] == cat)
                            for cat in all_categories] for entry in by_duration]
        self.activity_chart.plot(by_duration_keys,
                                 category_sums,
                                 stack_keys = all_categories)
Пример #3
0
    def _write_fact(self, report, fact):
        end_time = fact["end_time"]
        
        # ongoing task in current day
        end_time_str = ""
        if end_time:
            end_time_str = end_time.strftime('%H:%M')

        category = ""
        if fact["category"] != _("Unsorted"): #do not print "unsorted" in list
            category = fact["category"]

        description = fact["description"] or ""            
            
        # fact date column in HTML report
        report.write("""<tr class="row%d">
                            <td class="smallCell">%s</td>
                            <td class="largeCell">%s</td>
                            <td class="smallCell">%s</td>
                            <td class="smallCell">%s</td>
                            <td class="smallCell">%s</td>
                            <td class="smallCell">%s</td>
                            <td class="largeCell">%s</td>
                        </tr>
                       """ % (int(self.even_row),
                              _("%(report_b)s %(report_d)s, %(report_Y)s") % stuff.dateDict(fact["start_time"], "report_"),
                              fact["name"],
                              category, 
                              fact["start_time"].strftime('%H:%M'),
                              end_time_str,
                              stuff.format_duration(fact["delta"]) or "",
                              description))

        self.even_row = not self.even_row            


        # save data for summary table
        if fact["delta"]:
            id_string = "<td class=\"smallCell\">%s</td><td class=\"largeCell\">%s</td>" % (fact["category"], fact["name"])
            self.sum_time[id_string] = self.sum_time.get(id_string, 0) + fact["delta"]
Пример #4
0
    def fill_tree(self, facts):
        day_dict = {}
        for day, facts in groupby(facts, lambda fact: fact["date"]):
            day_dict[day] = sorted(list(facts), key=lambda fact:fact["start_time"])
        
        
        
        for i in range((self.end_date - self.start_date).days  + 1):
            current_date = self.start_date + dt.timedelta(i)
            
            # date format in overview window fact listing
            # prefix is "o_",letter after prefix is regular python format. you can use all of them
            fact_date = _("%(o_A)s, %(o_b)s %(o_d)s") %  stuff.dateDict(current_date, "o_")
            
            day_total = dt.timedelta()
            for fact in day_dict.get(current_date, []):
                day_total += fact["delta"]

            day_row = self.fact_store.append(None, [-1,
                                                    fact_date,
                                                    stuff.format_duration(day_total),
                                                    current_date.strftime('%Y-%m-%d'),
                                                    "",
                                                    "",
                                                    None])

            for fact in day_dict.get(current_date, []):
                self.fact_store.append(day_row,
                                       [fact["id"],
                                        fact["start_time"].strftime('%H:%M') + " " +
                                        fact["name"],
                                        stuff.format_duration(fact["delta"]),
                                        fact["start_time"].strftime('%Y-%m-%d'),
                                        fact["description"],
                                        fact["category"],
                                        fact
                                        ])

        self.fact_tree.expand_all()
Пример #5
0
    def stats(self, year = None):
        facts = self.stat_facts
        if year:
            facts = filter(lambda fact: fact["start_time"].year == year,
                           facts)

        if not facts or (facts[-1]["start_time"] - facts[0]["start_time"]) < dt.timedelta(days=6):
            self.get_widget("statistics_box").hide()
            self.get_widget("explore_controls").hide()
            label = self.get_widget("not_enough_records_label")

            if not facts:
                label.set_text(_("""There is no data to generate statistics yet.
A week of usage would be nice!"""))
            else:
                label.set_text(_("Still collecting data - check back after a week has passed!"))

            label.show()
            return
        else:
            self.get_widget("statistics_box").show()
            self.get_widget("explore_controls").show()
            self.get_widget("not_enough_records_label").hide()

        # All dates in the scope
        self.timeline.draw(facts)


        # Totals by category
        categories = stuff.totals(facts,
                                  lambda fact: fact["category"],
                                  lambda fact: fact['delta'].seconds / 60 / 60.0)
        category_keys = sorted(categories.keys())
        categories = [categories[key] for key in category_keys]
        self.chart_category_totals.plot(category_keys, categories)
        
        # Totals by weekday
        weekdays = stuff.totals(facts,
                                lambda fact: (fact["start_time"].weekday(),
                                              fact["start_time"].strftime("%a")),
                                lambda fact: fact['delta'].seconds / 60 / 60.0)
        
        weekday_keys = sorted(weekdays.keys(), key = lambda x: x[0]) #sort 
        weekdays = [weekdays[key] for key in weekday_keys] #get values in the order
        weekday_keys = [key[1] for key in weekday_keys] #now remove the weekday and keep just the abbreviated one
        self.chart_weekday_totals.plot(weekday_keys, weekdays)


        split_minutes = 5 * 60 + 30 #the mystical hamster midnight
        
        # starts and ends by weekday
        by_weekday = {}
        for date, date_facts in groupby(facts, lambda fact: fact["start_time"].date()):
            date_facts = list(date_facts)
            weekday = (date_facts[0]["start_time"].weekday(),
                       date_facts[0]["start_time"].strftime("%a"))
            by_weekday.setdefault(weekday, [])
            
            start_times, end_times = [], []
            for fact in date_facts:
                start_time = fact["start_time"].time()
                start_time = start_time.hour * 60 + start_time.minute
                if fact["end_time"]:
                    end_time = fact["end_time"].time()
                    end_time = end_time.hour * 60 + end_time.minute
                
                    if start_time < split_minutes:
                        start_time += 24 * 60
                    if end_time < start_time:
                        end_time += 24 * 60
                    
                    start_times.append(start_time)
                    end_times.append(end_time)
            if start_times and end_times:            
                by_weekday[weekday].append((min(start_times), max(end_times)))


        for day in by_weekday:
            by_weekday[day] = (sum([fact[0] for fact in by_weekday[day]]) / len(by_weekday[day]),
                               sum([fact[1] for fact in by_weekday[day]]) / len(by_weekday[day]))

        min_weekday = min([by_weekday[day][0] for day in by_weekday])
        max_weekday = max([by_weekday[day][1] for day in by_weekday])


        weekday_keys = sorted(by_weekday.keys(), key = lambda x: x[0])
        weekdays = [by_weekday[key] for key in weekday_keys]
        weekday_keys = [key[1] for key in weekday_keys] # get rid of the weekday number as int

        
        # starts and ends by category
        by_category = {}
        for date, date_facts in groupby(facts, lambda fact: fact["start_time"].date()):
            date_facts = sorted(list(date_facts), key = lambda x: x["category"])
            
            for category, category_facts in groupby(date_facts, lambda x: x["category"]):
                category_facts = list(category_facts)
                by_category.setdefault(category, [])
                
                start_times, end_times = [], []
                for fact in category_facts:
                    start_time = fact["start_time"]
                    start_time = start_time.hour * 60 + start_time.minute
                    if fact["end_time"]:
                        end_time = fact["end_time"].time()
                        end_time = end_time.hour * 60 + end_time.minute
                        
                        if start_time < split_minutes:
                            start_time += 24 * 60
                        if end_time < start_time:
                            end_time += 24 * 60

                        start_times.append(start_time)
                        end_times.append(end_time)

                if start_times and end_times:            
                    by_category[category].append((min(start_times), max(end_times)))

        for cat in by_category:
            by_category[cat] = (sum([fact[0] for fact in by_category[cat]]) / len(by_category[cat]),
                                sum([fact[1] for fact in by_category[cat]]) / len(by_category[cat]))

        min_category = min([by_category[day][0] for day in by_category])
        max_category = max([by_category[day][1] for day in by_category])

        category_keys = sorted(by_category.keys(), key = lambda x: x[0])
        categories = [by_category[key] for key in category_keys]


        #get starting and ending hours for graph and turn them into exact hours that divide by 3
        min_hour = min([min_weekday, min_category]) / 60 * 60
        max_hour = max([max_weekday, max_category]) / 60 * 60

        self.chart_weekday_starts_ends.plot_day(weekday_keys, weekdays, min_hour, max_hour)
        self.chart_category_starts_ends.plot_day(category_keys, categories, min_hour, max_hour)


        #now the factoids!
        summary = ""

        # first record        
        if not year:
            #date format for case when year has not been selected
            first_date = _("%(first_b)s %(first_d)s, %(first_Y)s") % \
                               stuff.dateDict(facts[0]["start_time"], "first_")
        else:
            #date format when year has been selected
            first_date = _("%(first_b)s %(first_d)s") % \
                               stuff.dateDict(facts[0]["start_time"], "first_")

        summary += _("First activity was recorded on %s.") % \
                                                     ("<b>%s</b>" % first_date)
        
        # total time tracked
        total_delta = dt.timedelta(days=0)
        for fact in facts:
            total_delta += fact["delta"]
        
        if total_delta.days > 1:
            summary += " " + _("""Time tracked so far is %(human_days)s human days \
(%(human_years)s years) or %(working_days)s working days \
(%(working_years)s years).""") % \
            ({"human_days": ("<b>%d</b>" % total_delta.days),
              "human_years": ("<b>%.2f</b>" % (total_delta.days / 365.0)),
              "working_days": ("<b>%d</b>" % (total_delta.days * 3)), # 8 should be pretty much an average working day
              "working_years": ("<b>%.2f</b>" % (total_delta.days * 3 / 365.0))})
        

        # longest fact
        max_fact = None
        for fact in facts:
            if not max_fact or fact["delta"] > max_fact["delta"]:
                max_fact = fact

        datedict = stuff.dateDict(max_fact["start_time"], "max_")
        datedict["hours"] = "<b>%.1f</b>" % (max_fact["delta"].seconds / 60 / 60.0
                                                  + max_fact["delta"].days * 24)

        summary += "\n" + _("Longest continuous work happened on \
%(max_b)s %(max_d)s, %(max_Y)s and was %(hours)s hours.") % datedict

        # total records (in selected scope)
        summary += " " + _("There are %s records.") % ("<b>%d</b>" % len(facts))


        early_start, early_end = dt.time(5,0), dt.time(9,0)
        late_start, late_end = dt.time(20,0), dt.time(5,0)
        
        
        fact_count = len(facts)
        def percent(condition):
            matches = [fact for fact in facts if condition(fact)]
            return round(len(matches) / float(fact_count) * 100)
        
        
        early_percent = percent(lambda fact: early_start < fact["start_time"].time() < early_end)
        late_percent = percent(lambda fact: fact["start_time"].time() > late_start or fact["start_time"].time() < late_end)
        short_percent = percent(lambda fact: fact["delta"] <= dt.timedelta(seconds = 60 * 15))

        if fact_count < 100:
            summary += "\n\n" + _("Hamster would like to observe you some more!")
        elif early_percent >= 20:
            summary += "\n\n" + _("With %s percent of all facts starting before \
9am you seem to be an early bird." % ("<b>%d</b>" % early_percent))
        elif late_percent >= 20:
            summary += "\n\n" + _("With %s percent of all facts starting after \
11pm you seem to be a night owl." % ("<b>%d</b>" % late_percent))
        elif short_percent >= 20:
            summary += "\n\n" + _("With %s percent of all tasks being shorter \
than 15 minutes you seem to be a busy bee." % ("<b>%d</b>" % short_percent))

        self.explore_summary.set_text(summary)
Пример #6
0
    def __init__(self, path, start_date, end_date):
        ReportWriter.__init__(self, path, datetime_format = None)

        dates_dict = stuff.dateDict(start_date, "start_")
        dates_dict.update(stuff.dateDict(end_date, "end_"))
        if start_date.year != end_date.year:
            self.title = _(u"Overview for %(start_B)s %(start_d)s, %(start_Y)s – %(end_B)s %(end_d)s, %(end_Y)s") % dates_dict
        elif start_date.month != end_date.month:
            self.title = _(u"Overview for %(start_B)s %(start_d)s – %(end_B)s %(end_d)s, %(end_Y)s") % dates_dict
        else:
            self.title = _(u"Overview for %(start_B)s %(start_d)s – %(end_d)s, %(end_Y)s") % dates_dict
    
        if start_date == end_date:
            self.title = _(u"Overview for %(start_B)s %(start_d)s, %(start_Y)s") % dates_dict

        self.sum_time = {}
        self.even_row = True

        """TODO bring template to external file or write to PDF"""
        
        self.file.write("""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta name="author" content="hamster-applet" />
    <title>%(title)s</title>
    <style type="text/css">
        body {
            font-family: "Sans";
            font-size: 12px;
            padding: 12px;
            color: #303030;
            
        }
        h1 {
            border-bottom: 2px solid #303030;
            padding-bottom: 4px;
        }
        h2 {
            margin-top: 2em;
            border-bottom: 2px solid #303030;
        }
        table {
            width:800px;
        }
        th {
            font-size: 14px;
            text-align: center;
            padding-bottom: 6px;
        }
  
        .smallCell {
            text-align: center;
            width: 100px;
            padding: 3px;
        }

        .largeCell {
            text-align: left;
            padding: 3px 3px 3px 5px;
        }     
        .row0 {
                background-color: #EAE8E3;
        }

        .row1 {
                background-color: #ffffff;
        }   

    </style>
</head>
<body>
<h1>%(title)s</h1>""" % {"title": self.title} + """
<table>
    <tr>
        <th class="smallCell">""" + _("Date") + """</th>
        <th class="largeCell">""" + _("Activity") + """</th>
        <th class="smallCell">""" + _("Category") + """</th>
        <th class="smallCell">""" + _("Start") + """</th>
        <th class="smallCell">""" + _("End") + """</th>
        <th class="smallCell">""" + _("Duration") + """</th>
        <th class="largeCell">""" + _("Description") + """</th>
    </tr>""")