Exemple #1
0
    def set_facts(self, facts):
        totals = defaultdict(lambda: defaultdict(dt.timedelta))
        for fact in facts:
            for key in ('category', 'activity'):
                totals[key][getattr(fact, key)] += fact.delta

            for tag in fact.tags:
                totals["tag"][tag] += fact.delta

        for key, group in totals.items():
            totals[key] = sorted(group.items(),
                                 key=lambda x: x[1],
                                 reverse=True)
        self.totals = totals

        self.activities_chart.set_values(totals['activity'])
        self.categories_chart.set_values(totals['category'])
        self.tag_chart.set_values(totals['tag'])

        self.stacked_bar.set_items([(cat, delta.total_seconds() / 60.0)
                                    for cat, delta in totals['category']])

        grand_total = sum(delta.total_seconds() / 60
                          for __, delta in totals['activity'])
        self.category_totals.markup = "<b>Total: </b>%s; " % stuff.format_duration(
            grand_total)
        self.category_totals.markup += ", ".join(
            "<b>%s:</b> %s" %
            (stuff.escape_pango(cat), stuff.format_duration(hours))
            for cat, hours in totals['category'])
Exemple #2
0
 def update_label(self):
     if self.last_activity and self.last_activity.end_time is None:
         delta = dt.datetime.now() - self.last_activity.start_time
         duration = delta.seconds /  60
         label = "%s %s" % (self.last_activity.activity,
                            stuff.format_duration(duration, False))
         self.button.set_text(self.last_activity.activity,
                              stuff.format_duration(duration, False))
     else:
         label = "%s" % _(u"No activity")
         self.button.set_text(label, None)
Exemple #3
0
    def update_label(self):
        '''Override for menu items sensitivity and to update the menu'''
        if self.project.last_activity:
            # Let's see if activity is an attribute and cache the result.
            # This is only required for backwards compatibility
            if self._activity_as_attribute == None:
                self._activity_as_attribute = hasattr(self.project.last_activity,
                                                      'activity')
            if self._activity_as_attribute:
                start_time = self.project.last_activity.start_time
                end_time = self.project.last_activity.end_time
                last_activity_name = self.project.last_activity.activity
            else:
                start_time = self.project.last_activity['start_time']
                end_time = self.project.last_activity['end_time']
                last_activity_name = self.project.last_activity['name']
        self.project.load_day()
        facts = self.project.todays_facts
        today_duration = 0
        if facts:
            for fact in facts:
                today_duration += 24 * 60 * fact.delta.days + fact.delta.seconds / 60
#        if self.duration:
#            today_duration += self.duration
        today_duration = "%s:%02d" % (today_duration/60, today_duration%60)
        if self.project.last_activity and end_time is None:
            self._set_activity_status(1)
            delta = dt.datetime.now() - start_time
            duration = delta.seconds /  60
            label = "%s %s" % (last_activity_name,
                               stuff.format_duration(duration, False))
            self.set_activity_text(last_activity_name,
                                 stuff.format_duration(duration, False))
            indicator_label = "%s %s / %sh" % (self._clamp_text(self.activity,
                                         length=self._label_length,
                                         with_ellipsis=False),
                                         self.duration,
                                         today_duration)
        else:
            self._set_activity_status(0)
            label = "%s" % _(u"New activity")
            self.set_activity_text(label, None)
            indicator_label = self._get_no_activity_label(today_duration)

        # Update the indicator label, if needed
        if self._show_label:
            self.indicator.set_label(indicator_label)
        else:
            self.indicator.set_label("")

        # Update the menu or the new activity text won't show up
        self.refresh_menu()
Exemple #4
0
    def update_label(self):
        '''Override for menu items sensitivity and to update the menu'''
        if self.project.last_activity:
            # Let's see if activity is an attribute and cache the result.
            # This is only required for backwards compatibility
            if self._activity_as_attribute == None:
                self._activity_as_attribute = hasattr(
                    self.project.last_activity, 'activity')
            if self._activity_as_attribute:
                start_time = self.project.last_activity.start_time
                end_time = self.project.last_activity.end_time
                last_activity_name = self.project.last_activity.activity
            else:
                start_time = self.project.last_activity['start_time']
                end_time = self.project.last_activity['end_time']
                last_activity_name = self.project.last_activity['name']
        self.project.load_day()
        facts = self.project.todays_facts
        today_duration = 0
        if facts:
            for fact in facts:
                today_duration += 24 * 60 * fact.delta.days + fact.delta.seconds / 60
#        if self.duration:
#            today_duration += self.duration
        today_duration = "%s:%02d" % (today_duration / 60, today_duration % 60)
        if self.project.last_activity and end_time is None:
            self._set_activity_status(1)
            delta = dt.datetime.now() - start_time
            duration = delta.seconds / 60
            label = "%s %s" % (last_activity_name,
                               stuff.format_duration(duration, False))
            self.set_activity_text(last_activity_name,
                                   stuff.format_duration(duration, False))
            indicator_label = "%s %s / %sh" % (self._clamp_text(
                self.activity, length=self._label_length,
                with_ellipsis=False), self.duration, today_duration)
        else:
            self._set_activity_status(0)
            label = "%s" % _(u"New activity")
            self.set_activity_text(label, None)
            indicator_label = self._get_no_activity_label(today_duration)

        # Update the indicator label, if needed
        if self._show_label:
            self.indicator.set_label(indicator_label)
        else:
            self.indicator.set_label("")

        # Update the menu or the new activity text won't show up
        self.refresh_menu()
Exemple #5
0
    def _draw(self, context, opacity, matrix):
        g = graphics.Graphics(context)
        g.save_context()
        g.translate(self.x, self.y)

        for i, (label, value) in enumerate(self.values):
            g.set_color("#333")
            duration_str = stuff.format_duration(value, human=False)
            markup = stuff.escape_pango('{}, {}'.format(label, duration_str))
            self.layout.set_markup(markup)
            label_w, label_h = self.layout.get_pixel_size()

            bar_start_x = 150  # pixels
            margin = 10  # pixels
            y = int(i * label_h * 1.5)
            g.move_to(bar_start_x - margin - label_w, y)
            pangocairo.show_layout(context, self.layout)

            if self._max > dt.timedelta(0):
                w = ceil((self.alloc_w - bar_start_x) * value.total_seconds() /
                         self._max.total_seconds())
            else:
                w = 1
            g.rectangle(bar_start_x, y, int(w), int(label_h))
            g.fill("#999")

        g.restore_context()
Exemple #6
0
def fact_dict(fact_data, with_date):
    fact = {}
    if with_date:
        fmt = '%Y-%m-%d %H:%M'
    else:
        fmt = '%H:%M'

    fact['start'] = fact_data.start_time.strftime(fmt)
    if fact_data.end_time:
        fact['end'] = fact_data.end_time.strftime(fmt)
    else:
        end_date = stuff.hamster_now()
        fact['end'] = ''

    fact['duration'] = stuff.format_duration(fact_data.delta)

    fact['activity'] = fact_data.activity
    fact['category'] = fact_data.category
    if fact_data.tags:
        fact['tags'] = ' '.join('#%s' % tag for tag in fact_data.tags)
    else:
        fact['tags'] = ''

    fact['description'] = fact_data.description

    return fact
Exemple #7
0
    def set_last_activity(self):
        activity = self.last_activity
        #sets all the labels and everything as necessary
        self.get_widget("stop_tracking").set_sensitive(activity != None)


        if activity:
            self.get_widget("switch_activity").show()
            self.get_widget("start_tracking").hide()

            delta = dt.datetime.now() - activity.start_time
            duration = delta.seconds /  60

            if activity.category != _("Unsorted"):
                self.get_widget("last_activity_name").set_text("%s - %s" % (activity.activity, activity.category))
            else:
                self.get_widget("last_activity_name").set_text(activity.activity)

            self.get_widget("last_activity_duration").set_text(stuff.format_duration(duration) or _("Just started"))
            self.get_widget("last_activity_description").set_text(activity.description or "")
            self.get_widget("activity_info_box").show()

            self.tag_box.draw(activity.tags)
        else:
            self.get_widget("switch_activity").hide()
            self.get_widget("start_tracking").show()

            self.get_widget("last_activity_name").set_text(_("No activity"))

            self.get_widget("activity_info_box").hide()

            self.tag_box.draw([])
Exemple #8
0
    def check_user(self):
        """check if we need to notify user perhaps"""
        if not self.notification or self.notify_interval <= 0 or self.notify_interval >= 121:
            return

        now = dt.datetime.now()
        message = None

        # update duration of current task
        if self.last_activity:
            delta = now - self.last_activity.start_time
            duration = delta.seconds /  60

            if duration and duration % self.notify_interval == 0:
                message = _(u"Working on <b>%s</b>") % self.last_activity.name

            self.get_widget("last_activity_duration").set_text(stuff.format_duration(duration) or _("Just started"))

        if not self.last_activity and self.notify_on_idle:
            #if we have no last activity, let's just calculate duration from 00:00
            if (now.minute + now.hour *60) % self.notify_interval == 0:
                message = _(u"No activity")

        if message:
            self.notification.update(_("Time Tracker"), message, "hamster-applet")
            self.notification.show()
Exemple #9
0
    def _write_fact(self, fact):
        # no having end time is fine
        end_time_str, end_time_iso_str = "", ""
        if fact.end_time:
            end_time_str = fact.end_time.strftime('%H:%M')
            end_time_iso_str = fact.end_time.isoformat()

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

        data = dict(
            date=fact.date.strftime(
                # date column format for each row in HTML report
                # Using python datetime formatting syntax. See:
                # http://docs.python.org/library/time.html#time.strftime
                C_("html report", "%b %d, %Y")),
            date_iso=fact.date.isoformat(),
            activity=fact.activity,
            category=category,
            tags=", ".join(fact.tags),
            start=fact.start_time.strftime('%H:%M'),
            start_iso=fact.start_time.isoformat(),
            end=end_time_str,
            end_iso=end_time_iso_str,
            duration=stuff.format_duration(fact.delta) or "",
            duration_minutes="%d" % (stuff.duration_minutes(fact.delta)),
            duration_decimal="%.2f" %
            (stuff.duration_minutes(fact.delta) / 60.0),
            description=fact.description or "")
        self.fact_rows.append(
            Template(self.fact_row_template).safe_substitute(data))
Exemple #10
0
    def _draw(self, context, opacity, matrix):
        g = graphics.Graphics(context)
        g.save_context()
        g.translate(self.x, self.y)
        # arbitrary 3/4 total width for label, 1/4 for histogram
        hist_width = self.alloc_w // 4;
        margin = 10  # pixels
        label_width = self.alloc_w - hist_width - margin
        self.layout.set_width(label_width * pango.SCALE)
        label_h = self.label_height
        bar_start_x = label_width + margin
        for i, (label, value) in enumerate(self.values):
            g.set_color("#333")
            duration_str = stuff.format_duration(value, human=False)
            markup_label = stuff.escape_pango(str(label))
            markup_duration = stuff.escape_pango(duration_str)
            self.layout.set_markup("{}, <i>{}</i>".format(markup_label, markup_duration))
            y = int(i * label_h * 1.5)
            g.move_to(0, y)
            pangocairo.show_layout(context, self.layout)
            if self._max > dt.timedelta(0):
                w = ceil(hist_width * value.total_seconds() /
                         self._max.total_seconds())
            else:
                w = 1
            g.rectangle(bar_start_x, y, int(w), int(label_h))
            g.fill("#999")

        g.restore_context()
Exemple #11
0
    def _write_fact(self, fact):
        # no having end time is fine
        end_time_str, end_time_iso_str = "", ""
        if fact.end_time:
            end_time_str = fact.end_time.strftime('%H:%M')
            end_time_iso_str = fact.end_time.isoformat()

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


        data = dict(
            date = fact.date.strftime(
                   # date column format for each row in HTML report
                   # Using python datetime formatting syntax. See:
                   # http://docs.python.org/library/time.html#time.strftime
                   C_("html report","%b %d, %Y")),
            date_iso = fact.date.isoformat(),
            activity = fact.activity,
            category = category,
            tags = fact.tags,
            start = fact.start_time.strftime('%H:%M'),
            start_iso = fact.start_time.isoformat(),
            end = end_time_str,
            end_iso = end_time_iso_str,
            duration = stuff.format_duration(fact.delta) or "",
            duration_minutes = "%d" % (stuff.duration_minutes(fact.delta)),
            duration_decimal = "%.2f" % (stuff.duration_minutes(fact.delta) / 60.0),
            description = fact.description or ""
        )
        self.fact_rows.append(Template(self.fact_row_template).safe_substitute(data))
Exemple #12
0
 def current(self, *args):
     """prints current activity. kinda minimal right now"""
     facts = self.storage.get_todays_facts()
     if facts and not facts[-1].end_time:
         print("{} {}".format(
             str(facts[-1]).strip(),
             stuff.format_duration(facts[-1].delta, human=False)))
     else:
         print((_("No activity")))
Exemple #13
0
    def __init__(self, width, fact, color, **kwargs):
        graphics.Sprite.__init__(self, **kwargs)
        self.width = width
        self.height = 27
        self.natural_height = 27
        self.fact = fact
        self.color = color

        self.interactive = True
        self.mouse_cursor = gdk.CursorType.XTERM

        self.fact_labels = graphics.Sprite()

        self.start_label = graphics.Label("", color="#333", size=11, x=10, y=5, interactive=True,
                                          mouse_cursor=gdk.CursorType.XTERM)
        self.start_label.text = "%s - " % fact.start_time.strftime("%H:%M")
        self.fact_labels.add_child(self.start_label)

        self.end_label = graphics.Label("", color="#333", size=11, x=65, y=5, interactive=True,
                                        mouse_cursor=gdk.CursorType.XTERM)
        if fact.end_time:
            self.end_label.text = fact.end_time.strftime("%H:%M")
        self.fact_labels.add_child(self.end_label)

        self.activity_label = graphics.Label(fact.activity, color="#333", size=11, x=120, y=5, interactive=True,
                                             mouse_cursor=gdk.CursorType.XTERM)
        self.fact_labels.add_child(self.activity_label)

        self.category_label = graphics.Label("", color="#333", size=9, y=7, interactive=True,
                                             mouse_cursor=gdk.CursorType.XTERM)
        self.category_label.text = stuff.escape_pango(" - %s" % fact.category)
        self.category_label.x = self.activity_label.x + self.activity_label.width
        self.fact_labels.add_child(self.category_label)


        self.duration_label = graphics.Label(stuff.format_duration(fact.delta), size=11, color="#333", interactive=True,
                                             mouse_cursor=gdk.CursorType.XTERM)
        self.duration_label.x = self.width - self.duration_label.width - 5
        self.duration_label.y = 5
        self.fact_labels.add_child(self.duration_label)

        self.add_child(self.fact_labels)

        self.edit_links = graphics.Sprite(x=10, y = 110, opacity=0)

        self.delete_link = graphics.Label("Delete", size=11, color="#555", interactive=True)
        self.save_link = graphics.Label("Save", size=11, x=390, color="#555", interactive=True)
        self.cancel_link = graphics.Label("Cancel", size=11, x=440, color="#555", interactive=True)
        self.edit_links.add_child(self.delete_link, self.save_link, self.cancel_link)

        self.add_child(self.edit_links)

        for sprite in self.fact_labels.sprites:
            sprite.connect("on-click", self.on_sprite_click)

        self.connect("on-render", self.on_render)
        self.connect("on-click", self.on_click)
Exemple #14
0
    def update_text(self):
        today = self.storage.get_todays_facts()

        if today and today[-1].end_time is None:
            fact = today[-1]

            self.set_tooltip("%s - %s" % (fact.activity, fact.category))
            self.set_badge(stuff.format_duration(fact.delta, human=False))
        else:
            self.set_tooltip(_("No activity"))
            self.reset_badge()
    def update_text(self):
        today = self.storage.get_todays_facts()

        if today and today[-1].end_time is None:
            fact = today[-1]

            self.iface.SetText("%s - %s" % (fact.activity, fact.category))
            self.iface.SetBadgeText(stuff.format_duration(fact.delta, human=False))
        else:
            self.iface.SetText(_("No activity"))
            self.iface.ResetBadgeText()
Exemple #16
0
    def show(self, g, colors, fact=None, is_selected=False):
        """Display the fact row.

        If fact is given, the fact attribute is updated.
        """
        g.save_context()

        if fact is not None:
            # before the selection highlight, to get the correct height
            self.set_fact(fact)

        color, bg = colors["normal"], colors["normal_bg"]
        if is_selected:
            color, bg = colors["selected"], colors["selected_bg"]
            g.fill_area(0, 0, self.width, self.height, bg)

        g.translate(self.row_margin_H, self.row_margin_V)

        g.set_color(color)
        self.time_label.show(g)
        self.activity_label.show(g)

        if self.fact.category:
            g.save_context()
            category_color = graphics.ColorUtils.mix(bg, color, 0.57)
            g.set_color(category_color)
            x = self.activity_label.x + self.activity_label.layout.get_pixel_size(
            )[0]
            self.category_label.show(g, x=x, y=self.category_offset_V)
            g.restore_context()

        if self.fact.description or self.fact.tags:
            g.save_context()
            g.translate(self.activity_label.x, self.activity_label.height + 3)

            if self.fact.tags:
                self._show_tags(g, color, bg)
                tag_height = (self.tag_label.height +
                              self.tag_inner_margin_V * 2 +
                              self.tag_row_margin_V * 2)
                g.translate(0, tag_height)

            if self.fact.description:
                self.description_label.show(g)

            g.restore_context()

        self.duration_label.show(g,
                                 stuff.format_duration(self.fact.delta),
                                 x=self.width - 105)

        g.restore_context()
Exemple #17
0
    def show(self, g, colors, fact, current=False):
        g.save_context()

        color, bg = colors["normal"], colors["normal_bg"]
        if current:
            color, bg = colors["selected"], colors["selected_bg"]
            g.fill_area(0, 0, self.width, self.height(fact), bg)

        g.translate(5, 2)

        time_label = fact.start_time.strftime("%H:%M -")
        if fact.end_time:
            time_label += fact.end_time.strftime(" %H:%M")

        g.set_color(color)
        self.time_label.show(g, time_label)

        self.activity_label.show(g, stuff.escape_pango(fact.activity))
        if fact.category:
            g.save_context()
            g.set_color(color if current else "#999")
            x = self.activity_label.x + self.activity_label.layout.get_pixel_size(
            )[0]
            self.category_label.show(g,
                                     "  - %s" %
                                     stuff.escape_pango(fact.category),
                                     x=x,
                                     y=2)
            g.restore_context()

        if fact.description or fact.tags:
            g.save_context()
            g.translate(self.activity_label.x, self.activity_label.height + 3)

            if fact.tags:
                self._show_tags(g, fact.tags, color, bg)
                g.translate(0, self.tag_label.height + 5)

            if fact.description:
                self.description_label.show(
                    g,
                    "<small>%s</small>" % stuff.escape_pango(fact.description))
            g.restore_context()

        self.duration_label.show(g,
                                 stuff.format_duration(fact.delta),
                                 x=self.width - 105)

        g.restore_context()
Exemple #18
0
    def show(self, g, colors, fact, current=False):
        g.save_context()

        color, bg = colors["normal"], colors["normal_bg"]
        if current:
            color, bg = colors["selected"], colors["selected_bg"]
            g.fill_area(0, 0, self.width, self.height(fact), bg)

        g.translate(5, 2)

        time_label = fact.start_time.strftime("%H:%M -")
        if fact.end_time:
            time_label += fact.end_time.strftime(" %H:%M")

        g.set_color(color)
        self.time_label.show(g, time_label)

        self.activity_label.show(g, stuff.escape_pango(fact.activity))
        if fact.category:
            g.save_context()
            g.set_color(color if current else "#999")
            x = self.activity_label.x + self.activity_label.layout.get_pixel_size()[0]
            self.category_label.show(g, "  - %s" % stuff.escape_pango(fact.category), x=x, y=2)
            g.restore_context()

        if fact.description or fact.tags:
            g.save_context()
            g.translate(self.activity_label.x, self.activity_label.height + 3)

            if fact.tags:
                self._show_tags(g, fact.tags, color, bg)
                g.translate(0, self.tag_label.height + 5)

            if fact.description:
                self.description_label.show(g, "<small>%s</small>" % stuff.escape_pango(fact.description))
            g.restore_context()

        self.duration_label.show(g, stuff.format_duration(fact.delta), x=self.width - 105)

        g.restore_context()
Exemple #19
0
    def set_facts(self, facts):
        totals = defaultdict(lambda: defaultdict(dt.timedelta))
        for fact in facts:
            for key in ('category', 'activity'):
                totals[key][getattr(fact, key)] += fact.delta

            for tag in fact.tags:
                totals["tag"][tag] += fact.delta


        for key, group in totals.items():
            totals[key] = sorted(group.items(), key=lambda x: x[1], reverse=True)
        self.totals = totals

        self.activities_chart.set_values(totals['activity'])
        self.categories_chart.set_values(totals['category'])
        self.tag_chart.set_values(totals['tag'])

        self.stacked_bar.set_items([(cat, delta.total_seconds() / 60.0) for cat, delta in totals['category']])
        self.category_totals.markup = ", ".join("<b>%s:</b> %s" % (
            stuff.escape_pango(cat), stuff.format_duration(hours)) \
            for cat, hours in totals['category'])
Exemple #20
0
    def show_popup(self):
        if not self._parent_click_watcher:
            self._parent_click_watcher = self.get_toplevel().connect(
                "button-press-event", self._on_focus_out_event)

        # we will be adding things, need datetime
        i_time_0 = dt.datetime.combine(self.start_date or dt.date.today(),
                                       self.start_time or dt.time())

        if self.start_time is None:
            # full 24 hours
            i_time = i_time_0
            interval = dt.timedelta(minutes=15)
            end_time = i_time_0 + dt.timedelta(days=1)
        else:
            # from start time to start time + 12 hours
            interval = dt.timedelta(minutes=15)
            i_time = i_time_0 + interval
            end_time = i_time_0 + dt.timedelta(hours=12)

        time = self.figure_time(self.get_text())
        focus_time = dt.datetime.combine(dt.date.today(),
                                         time) if time else None
        hours = gtk.ListStore(str)

        i, focus_row = 0, None
        while i_time < end_time:
            row_text = self._format_time(i_time)
            if self.start_time is not None:
                delta_text = format_duration(i_time - i_time_0)

                row_text += " (%s)" % delta_text

            hours.append([row_text])

            if focus_time and i_time <= focus_time < i_time + interval:
                focus_row = i

            i_time += interval

            i += 1

        self.time_tree.set_model(hours)

        #focus on row
        if focus_row != None:
            selection = self.time_tree.get_selection()
            selection.select_path(focus_row)
            self.time_tree.scroll_to_cell(focus_row,
                                          use_align=True,
                                          row_align=0.4)

        #move popup under the widget
        alloc = self.get_allocation()
        w = alloc.width
        self.time_tree.set_size_request(w, alloc.height * 5)

        window = self.get_parent_window()
        dmmy, x, y = window.get_origin()

        self.popup.move(x + alloc.x, y + alloc.y + alloc.height)
        self.popup.resize(*self.time_tree.get_size_request())
        self.popup.show_all()
Exemple #21
0
    def update_suggestions(self, text=""):
        """
            * from previous activity | set time | minutes ago | start now
            * to ongoing | set time

            * activity
            * [@category]
            * #tags, #tags, #tags

            * we will leave description for later

            all our magic is space separated, strictly, start-end can be just dash

            phases:

            [start_time] | [-end_time] | activity | [@category] | [#tag]
        """
        now = dt.datetime.now()

        text = text.lstrip()

        time_re = re.compile("^([0-1]?[0-9]|[2][0-3]):([0-5][0-9])$")
        time_range_re = re.compile("^([0-1]?[0-9]|[2][0-3]):([0-5][0-9])-([0-1]?[0-9]|[2][0-3]):([0-5][0-9])$")
        delta_re = re.compile("^-[0-9]{1,3}$")

        # when the time is filled, we need to make sure that the chunks parse correctly



        delta_fragment_re = re.compile("^-[0-9]{0,3}$")


        templates = {
            "start_time": "",
            "start_delta": ("start activity -n minutes ago", "-"),
        }

        # need to set the start_time template before
        prev_fact = self.todays_facts[-1] if self.todays_facts else None
        if prev_fact and prev_fact.end_time:
            templates["start_time"] = ("from previous activity %s ago" % stuff.format_duration(now - prev_fact.end_time),
                                       prev_fact.end_time.strftime("%H:%M "))

        variants = []

        fact = Fact(text)

        # figure out what we are looking for
        # time -> activity[@category] -> tags -> description
        # presence of each next attribute means that we are not looking for the previous one
        # we still might be looking for the current one though
        looking_for = "start_time"
        fields = ["start_time", "end_time", "activity", "category", "tags", "description", "done"]
        for field in reversed(fields):
            if getattr(fact, field, None):
                looking_for = field
                if text[-1] == " ":
                    looking_for = fields[fields.index(field)+1]
                break


        fragments = [f for f in re.split("[\s|#]", text)]
        current_fragment = fragments[-1] if fragments else ""

        if not text.strip():
            variants = [templates[name] for name in ("start_time",
                                                     "start_delta") if templates[name]]
        elif looking_for == "start_time" and text == "-":
            if len(current_fragment) > 1: # avoid blank "-"
                templates["start_delta"] = ("%s minutes ago" % (-int(current_fragment)), current_fragment)
            variants.append(templates["start_delta"])


        res = []
        for (description, variant) in variants:
            res.append(DataRow(variant, description=description))

        # regular activity
        if (looking_for in ("start_time", "end_time") and not looks_like_time(text.split(" ")[-1])) or \
            looking_for in ("activity", "category"):

            search = extract_search(text)

            matches = []
            for match, score in self.suggestions:
                if search in match:
                    if match.startswith(search):
                        score += 10**8 # boost beginnings
                    matches.append((match, score))

            matches = sorted(matches, key=lambda x: x[1], reverse=True)[:7] # need to limit these guys, sorry

            for match, score in matches:
                label = (fact.start_time or now).strftime("%H:%M")
                if fact.end_time:
                    label += fact.end_time.strftime("-%H:%M")

                markup_label = label + " " + (stuff.escape_pango(match).replace(search, "<b>%s</b>" % search) if search else match)
                label += " " + match

                res.append(DataRow(markup_label, match, label))

        if not res:
            # in case of nothing to show, add preview so that the user doesn't
            # think they are lost
            label = (fact.start_time or now).strftime("%H:%M")
            if fact.end_time:
                label += fact.end_time.strftime("-%H:%M")

            if fact.activity:
                label += " " + fact.activity
            if fact.category:
                label += "@" + fact.category

            if fact.tags:
                label += " #" + " #".join(fact.tags)

            res.append(DataRow(stuff.escape_pango(label), description="Start tracking"))

        self.complete_tree.set_rows(res)
Exemple #22
0
    def __init__(self, width, fact, color, **kwargs):
        graphics.Sprite.__init__(self, **kwargs)
        self.width = width
        self.height = 27
        self.natural_height = 27
        self.fact = fact
        self.color = color

        self.interactive = True
        self.mouse_cursor = gdk.CursorType.XTERM

        self.fact_labels = graphics.Sprite()

        self.start_label = graphics.Label("",
                                          color="#333",
                                          size=11,
                                          x=10,
                                          y=5,
                                          interactive=True,
                                          mouse_cursor=gdk.CursorType.XTERM)
        self.start_label.text = "%s - " % fact.start_time.strftime("%H:%M")
        self.fact_labels.add_child(self.start_label)

        self.end_label = graphics.Label("",
                                        color="#333",
                                        size=11,
                                        x=65,
                                        y=5,
                                        interactive=True,
                                        mouse_cursor=gdk.CursorType.XTERM)
        if fact.end_time:
            self.end_label.text = fact.end_time.strftime("%H:%M")
        self.fact_labels.add_child(self.end_label)

        self.activity_label = graphics.Label(fact.activity,
                                             color="#333",
                                             size=11,
                                             x=120,
                                             y=5,
                                             interactive=True,
                                             mouse_cursor=gdk.CursorType.XTERM)
        self.fact_labels.add_child(self.activity_label)

        self.category_label = graphics.Label("",
                                             color="#333",
                                             size=9,
                                             y=7,
                                             interactive=True,
                                             mouse_cursor=gdk.CursorType.XTERM)
        self.category_label.text = stuff.escape_pango(" - %s" % fact.category)
        self.category_label.x = self.activity_label.x + self.activity_label.width
        self.fact_labels.add_child(self.category_label)

        self.duration_label = graphics.Label(stuff.format_duration(fact.delta),
                                             size=11,
                                             color="#333",
                                             interactive=True,
                                             mouse_cursor=gdk.CursorType.XTERM)
        self.duration_label.x = self.width - self.duration_label.width - 5
        self.duration_label.y = 5
        self.fact_labels.add_child(self.duration_label)

        self.add_child(self.fact_labels)

        self.edit_links = graphics.Sprite(x=10, y=110, opacity=0)

        self.delete_link = graphics.Label("Delete",
                                          size=11,
                                          color="#555",
                                          interactive=True)
        self.save_link = graphics.Label("Save",
                                        size=11,
                                        x=390,
                                        color="#555",
                                        interactive=True)
        self.cancel_link = graphics.Label("Cancel",
                                          size=11,
                                          x=440,
                                          color="#555",
                                          interactive=True)
        self.edit_links.add_child(self.delete_link, self.save_link,
                                  self.cancel_link)

        self.add_child(self.edit_links)

        for sprite in self.fact_labels.sprites:
            sprite.connect("on-click", self.on_sprite_click)

        self.connect("on-render", self.on_render)
        self.connect("on-click", self.on_click)
Exemple #23
0
    def set_last_activity(self):
        activity = self.last_activity
        #sets all the labels and everything as necessary
        self.get_widget("stop_tracking").set_sensitive(activity != None)
        arbitrary_issue_id = self.get_widget("arbitrary_issue_id_entry").get_text()
        active_activity = self.get_widget("time_activity_combo").get_active()


        if activity:
            self.get_widget("switch_activity").show()
            self.get_widget("start_tracking").hide()
            
            # If the Redmine integration is enabled, show the Redmine frame and set insensitivity of combos
            if conf.get("redmine_integration_enabled"):
              self.get_widget("redmine_frame").show()
              self.get_widget("issue_combo").set_sensitive(False)
              self.get_widget("time_activity_combo").set_sensitive(False)
              self.get_widget("arbitrary_issue_id_entry").set_sensitive(False)
              if arbitrary_issue_id != None:
                self.get_widget("arbitrary_issue_id_entry").set_text(arbitrary_issue_id)
                self.get_widget("time_activity_combo").set_active(active_activity)

            delta = dt.datetime.now() - activity.start_time
            duration = delta.seconds //  60

            if activity.category != _("Unsorted"):
                if isinstance(activity, RedmineFact):
                    self.get_widget("last_activity_name").set_text("%s %s - %s" %(activity.activity, activity.redmine_tag(), activity.category))
                else:
                    self.get_widget("last_activity_name").set_text("%s - %s" %(activity.activity, activity.category))
            else:
                if isinstance(activity, RedmineFact):
                    self.get_widget("last_activity_name").set_text("%s %s"%(activity.activity, activity.redmine_tag()))
                else:
                    self.get_widget("last_activity_name").set_text(activity.activity)

            self.get_widget("last_activity_duration").set_text(stuff.format_duration(duration) or _("Just started"))
            self.get_widget("last_activity_description").set_text(activity.description or "")
            self.get_widget("activity_info_box").show()

            self.tag_box.draw(activity.tags)
        else:
            self.get_widget("switch_activity").hide()
            self.get_widget("start_tracking").show()

            self.get_widget("last_activity_name").set_text(_("No activity"))

            self.get_widget("activity_info_box").hide()

            self.tag_box.draw([])
            
            # If the Redmine integration is enabled, show the Redmine frame and set up the combos (if there is no selection), making sure they are sensitive
            if conf.get("redmine_integration_enabled"):
              self.get_widget("redmine_frame").show()
              self.get_widget("issue_combo").set_sensitive(True)
              self.get_widget("time_activity_combo").set_sensitive(True)
              if self.get_widget("issue_combo").get_active() == -1 or self.get_widget("issue_combo").get_active() == 0:
                self.fill_issues_combo()
                self.get_widget("arbitrary_issue_id_entry").set_sensitive(True)
              if self.get_widget("time_activity_combo").get_active() == -1 or self.get_widget("time_activity_combo").get_active() == 0:
                self.fill_time_activities_combo()
              if arbitrary_issue_id != None:
                self.get_widget("arbitrary_issue_id_entry").set_text(arbitrary_issue_id)
                self.get_widget("time_activity_combo").set_active(active_activity)
Exemple #24
0
    def update_suggestions(self, text=""):
        """
            * from previous activity | set time | minutes ago | start now
            * to ongoing | set time

            * activity
            * [@category]
            * #tags, #tags, #tags

            * we will leave description for later

            all our magic is space separated, strictly, start-end can be just dash

            phases:

            [start_time] | [-end_time] | activity | [@category] | [#tag]
        """
        now = dt.datetime.now()

        text = text.lstrip()

        time_re = re.compile("^([0-1]?[0-9]|[2][0-3]):([0-5][0-9])$")
        time_range_re = re.compile("^([0-1]?[0-9]|[2][0-3]):([0-5][0-9])-([0-1]?[0-9]|[2][0-3]):([0-5][0-9])$")
        delta_re = re.compile("^-[0-9]{1,3}$")

        # when the time is filled, we need to make sure that the chunks parse correctly



        delta_fragment_re = re.compile("^-[0-9]{0,3}$")


        templates = {
            "start_time": "",
            "start_delta": ("start activity -n minutes ago", "-"),
        }

        # need to set the start_time template before
        prev_fact = self.todays_facts[-1] if self.todays_facts else None
        if prev_fact and prev_fact.end_time:
            templates["start_time"] = ("from previous activity %s ago" % stuff.format_duration(now - prev_fact.end_time),
                                       prev_fact.end_time.strftime("%H:%M "))

        variants = []

        fact = Fact(text)

        # figure out what we are looking for
        # time -> activity[@category] -> tags -> description
        # presence of each next attribute means that we are not looking for the previous one
        # we still might be looking for the current one though
        looking_for = "start_time"
        fields = ["start_time", "end_time", "activity", "category", "tags", "description", "done"]
        for field in reversed(fields):
            if getattr(fact, field, None):
                looking_for = field
                if text[-1] == " ":
                    looking_for = fields[fields.index(field)+1]
                break


        fragments = [f for f in re.split("[\s|#]", text)]
        current_fragment = fragments[-1] if fragments else ""

        if not text.strip():
            variants = [templates[name] for name in ("start_time",
                                                     "start_delta") if templates[name]]
        elif looking_for == "start_time" and text == "-":
            if len(current_fragment) > 1: # avoid blank "-"
                templates["start_delta"] = ("%s minutes ago" % (-int(current_fragment)), current_fragment)
            variants.append(templates["start_delta"])


        res = []
        for (description, variant) in variants:
            res.append(DataRow(variant, description=description))

        # regular activity
        if (looking_for in ("start_time", "end_time") and not looks_like_time(text.split(" ")[-1])) or \
            looking_for in ("activity", "category"):

            search = extract_search(text)

            matches = []
            for match, score in self.suggestions:
                if search in match:
                    if match.startswith(search):
                        score += 10**8 # boost beginnings
                    matches.append((match, score))

            matches = sorted(matches, key=lambda x: x[1], reverse=True)[:7] # need to limit these guys, sorry

            for match, score in matches:
                label = (fact.start_time or now).strftime("%H:%M")
                if fact.end_time:
                    label += fact.end_time.strftime("-%H:%M")

                markup_label = label + " " + (stuff.escape_pango(match).replace(search, "<b>%s</b>" % search) if search else match)
                label += " " + match

                res.append(DataRow(markup_label, match, label))

        if not res:
            # in case of nothing to show, add preview so that the user doesn't
            # think they are lost
            label = (fact.start_time or now).strftime("%H:%M")
            if fact.end_time:
                label += fact.end_time.strftime("-%H:%M")

            if fact.activity:
                label += " " + fact.activity
            if fact.category:
                label += "@" + fact.category

            if fact.tags:
                label += " #" + " #".join(fact.tags)

            res.append(DataRow(stuff.escape_pango(label), description="Start tracking"))

        self.complete_tree.set_rows(res)
Exemple #25
0
    def show_popup(self):
        if not self._parent_click_watcher:
            self._parent_click_watcher = self.get_toplevel().connect(
                "button-press-event", self._on_focus_out_event)

        # will be going either 24 hours or from start time to start time + 12 hours
        start_time = dt.datetime.combine(
            dt.date.today(), self.start_time)  # we will be adding things
        i_time = start_time  # we will be adding things

        if self.start_time:
            end_time = i_time + dt.timedelta(hours=12)
            i_time += dt.timedelta(minutes=15)
        else:
            end_time = i_time + dt.timedelta(days=1)

        focus_time = dt.datetime.combine(dt.date.today(),
                                         self.figure_time(self.get_text()))
        hours = gtk.ListStore(gobject.TYPE_STRING)

        i, focus_row = 0, None
        while i_time < end_time:
            row_text = self._format_time(i_time)
            if self.start_time:
                delta = (i_time - start_time).seconds / 60
                delta_text = format_duration(delta)

                row_text += " (%s)" % delta_text

            hours.append([row_text])


            if focus_time and i_time <= focus_time <= i_time + \
                                                     dt.timedelta(minutes = 30):
                focus_row = i

            if self.start_time:
                i_time += dt.timedelta(minutes=15)
            else:
                i_time += dt.timedelta(minutes=30)

            i += 1

        self.time_tree.set_model(hours)

        #focus on row
        if focus_row != None:
            selection = self.time_tree.get_selection()
            selection.select_path(focus_row)
            self.time_tree.scroll_to_cell(focus_row,
                                          use_align=True,
                                          row_align=0.4)

        #move popup under the widget
        alloc = self.get_allocation()
        w = alloc.width
        if self.start_time:
            w = w * 2
        self.time_tree.set_size_request(w, alloc.height * 5)

        window = self.get_parent_window()
        dmmy, x, y = window.get_origin()

        self.popup.move(x + alloc.x, y + alloc.y + alloc.height)
        self.popup.resize(*self.time_tree.get_size_request())
        self.popup.show_all()
Exemple #26
0
    def update_suggestions(self, text=""):
        """
            * from previous activity | set time | minutes ago | start now
            * to ongoing | set time

            * activity
            * [@category]
            * #tags, #tags, #tags

            * we will leave description for later

            all our magic is space separated, strictly, start-end can be just dash

            phases:

            [start_time] | [-end_time] | activity | [@category] | [#tag]
        """

        res = []

        fact = Fact(text)
        now = dt.datetime.now()

        # figure out what we are looking for
        # time -> activity[@category] -> tags -> description
        # presence of an attribute means that we are not looking for the previous one
        # we still might be looking for the current one though
        looking_for = "start_time"
        fields = ["start_time", "end_time", "activity", "category", "tags", "description", "done"]
        for field in reversed(fields):
            if getattr(fact, field, None):
                looking_for = field
                if text[-1] == " ":
                    looking_for = fields[fields.index(field)+1]
                break


        fragments = [f for f in re.split("[\s|#]", text)]
        current_fragment = fragments[-1] if fragments else ""


        search = extract_search(text)

        matches = []
        for match, score in self.suggestions:
            if search in match:
                if match.startswith(search):
                    score += 10**8 # boost beginnings
                matches.append((match, score))

        # need to limit these guys, sorry
        matches = sorted(matches, key=lambda x: x[1], reverse=True)[:7]

        for match, score in matches:
            label = (fact.start_time or now).strftime("%H:%M")
            if fact.end_time:
                label += fact.end_time.strftime("-%H:%M")

            markup_label = label + " " + (stuff.escape_pango(match).replace(search, "<b>%s</b>" % search) if search else match)
            label += " " + match

            res.append(DataRow(markup_label, match, label))

        # list of tuples (description, variant)
        variants = []

        if self.original_fact:
            # editing an existing fact

            variant_fact = None
            if self.original_fact.end_time is None:
                description = "stop now"
                variant_fact = self.original_fact.copy()
                variant_fact.end_time = now
            elif self.original_fact == self.todays_facts[-1]:
                # that one is too dangerous, except for the last entry
                description = "keep up"
                # Do not use Fact(..., end_time=None): it would be a no-op
                variant_fact = self.original_fact.copy()
                variant_fact.end_time = None

            if variant_fact:
                variant = variant_fact.serialized(prepend_date=False)
                variants.append((description, variant))

        else:
            # brand new fact
            description = "start now"
            variant = now.strftime("%H:%M ")
            variants.append((description, variant))

            prev_fact = self.todays_facts[-1] if self.todays_facts else None
            if prev_fact and prev_fact.end_time:
                since = stuff.format_duration(now - prev_fact.end_time)
                description = "from previous activity, %s ago" % since
                variant = prev_fact.end_time.strftime("%H:%M ")
                variants.append((description, variant))

            description = "start activity -n minutes ago (1 or 3 digits allowed)"
            variant = "-"
            variants.append((description, variant))

        text = text.strip()
        if text:
            description = "clear"
            variant = ""
            variants.append((description, variant))

        for (description, variant) in variants:
            res.append(DataRow(variant, description=description))

        self.complete_tree.set_rows(res)
Exemple #27
0
    def set_last_activity(self):
        activity = self.last_activity
        #sets all the labels and everything as necessary
        self.get_widget("stop_tracking").set_sensitive(activity != None)
        arbitrary_issue_id = self.get_widget(
            "arbitrary_issue_id_entry").get_text()
        active_activity = self.get_widget("time_activity_combo").get_active()

        if activity:
            self.get_widget("switch_activity").show()
            self.get_widget("start_tracking").hide()

            # If the Redmine integration is enabled, show the Redmine frame and set insensitivity of combos
            if conf.get("redmine_integration_enabled"):
                self.get_widget("redmine_frame").show()
                self.get_widget("issue_combo").set_sensitive(False)
                self.get_widget("time_activity_combo").set_sensitive(False)
                self.get_widget("arbitrary_issue_id_entry").set_sensitive(
                    False)
                if arbitrary_issue_id != None:
                    self.get_widget("arbitrary_issue_id_entry").set_text(
                        arbitrary_issue_id)
                    self.get_widget("time_activity_combo").set_active(
                        active_activity)

            delta = dt.datetime.now() - activity.start_time
            duration = delta.seconds // 60

            if activity.category != _("Unsorted"):
                if isinstance(activity, RedmineFact):
                    self.get_widget("last_activity_name").set_text(
                        "%s %s - %s" %
                        (activity.activity, activity.redmine_tag(),
                         activity.category))
                else:
                    self.get_widget("last_activity_name").set_text(
                        "%s - %s" % (activity.activity, activity.category))
            else:
                if isinstance(activity, RedmineFact):
                    self.get_widget("last_activity_name").set_text(
                        "%s %s" % (activity.activity, activity.redmine_tag()))
                else:
                    self.get_widget("last_activity_name").set_text(
                        activity.activity)

            self.get_widget("last_activity_duration").set_text(
                stuff.format_duration(duration) or _("Just started"))
            self.get_widget("last_activity_description").set_text(
                activity.description or "")
            self.get_widget("activity_info_box").show()

            self.tag_box.draw(activity.tags)
        else:
            self.get_widget("switch_activity").hide()
            self.get_widget("start_tracking").show()

            self.get_widget("last_activity_name").set_text(_("No activity"))

            self.get_widget("activity_info_box").hide()

            self.tag_box.draw([])

            # If the Redmine integration is enabled, show the Redmine frame and set up the combos (if there is no selection), making sure they are sensitive
            if conf.get("redmine_integration_enabled"):
                self.get_widget("redmine_frame").show()
                self.get_widget("issue_combo").set_sensitive(True)
                self.get_widget("time_activity_combo").set_sensitive(True)
                if self.get_widget("issue_combo").get_active(
                ) == -1 or self.get_widget("issue_combo").get_active() == 0:
                    self.fill_issues_combo()
                    self.get_widget("arbitrary_issue_id_entry").set_sensitive(
                        True)
                if self.get_widget("time_activity_combo").get_active(
                ) == -1 or self.get_widget(
                        "time_activity_combo").get_active() == 0:
                    self.fill_time_activities_combo()
                if arbitrary_issue_id != None:
                    self.get_widget("arbitrary_issue_id_entry").set_text(
                        arbitrary_issue_id)
                    self.get_widget("time_activity_combo").set_active(
                        active_activity)
Exemple #28
0
    def show_popup(self):
        if not self._parent_click_watcher:
            self._parent_click_watcher = self.get_toplevel().connect("button-press-event", self._on_focus_out_event)

        # will be going either 24 hours or from start time to start time + 12 hours
        start_time = dt.datetime.combine(dt.date.today(), self.start_time) # we will be adding things
        i_time = start_time # we will be adding things

        if self.start_time:
            end_time = i_time + dt.timedelta(hours = 12)
            i_time += dt.timedelta(minutes = 15)
        else:
            end_time = i_time + dt.timedelta(days = 1)


        focus_time = dt.datetime.combine(dt.date.today(), self.figure_time(self.get_text()))
        hours = gtk.ListStore(gobject.TYPE_STRING)


        i, focus_row = 0, None
        while i_time < end_time:
            row_text = self._format_time(i_time)
            if self.start_time:
                delta = (i_time - start_time).seconds / 60
                delta_text = format_duration(delta)

                row_text += " (%s)" % delta_text

            hours.append([row_text])


            if focus_time and i_time <= focus_time <= i_time + \
                                                     dt.timedelta(minutes = 30):
                focus_row = i

            if self.start_time:
                i_time += dt.timedelta(minutes = 15)
            else:
                i_time += dt.timedelta(minutes = 30)

            i += 1

        self.time_tree.set_model(hours)

        #focus on row
        if focus_row != None:
            selection = self.time_tree.get_selection()
            selection.select_path(focus_row)
            self.time_tree.scroll_to_cell(focus_row, use_align = True, row_align = 0.4)


        #move popup under the widget
        alloc = self.get_allocation()
        w = alloc.width
        if self.start_time:
            w = w * 2
        self.time_tree.set_size_request(w, alloc.height * 5)

        window = self.get_parent_window()
        dmmy, x, y= window.get_origin()

        self.popup.move(x + alloc.x,y + alloc.y + alloc.height)
        self.popup.resize(*self.time_tree.get_size_request())
        self.popup.show_all()
Exemple #29
0
    def _list(self, start_time, end_time, search=""):
        """Print a listing of activities"""
        facts = self.storage.get_facts(start_time, end_time, search)

        headers = {
            'activity': _("Activity"),
            'category': _("Category"),
            'tags': _("Tags"),
            'description': _("Description"),
            'start': _("Start"),
            'end': _("End"),
            'duration': _("Duration")
        }

        # print date if it is not the same day
        print_with_date = start_time.date() != end_time.date()

        cols = 'start', 'end', 'duration', 'activity', 'category'

        widths = dict([(col, len(headers[col])) for col in cols])
        for fact in facts:
            fact = fact_dict(fact, print_with_date)
            for col in cols:
                widths[col] = max(widths[col], len(fact[col]))

        cols = [
            "{{{col}: <{len}}}".format(col=col, len=widths[col])
            for col in cols
        ]
        fact_line = " | ".join(cols)

        row_width = sum(val + 3 for val in list(widths.values()))

        print()
        print(fact_line.format(**headers))
        print("-" * min(row_width, 80))

        by_cat = {}
        for fact in facts:
            cat = fact.category or _("Unsorted")
            by_cat.setdefault(cat, dt.timedelta(0))
            by_cat[cat] += fact.delta

            pretty_fact = fact_dict(fact, print_with_date)
            print(fact_line.format(**pretty_fact))

            if pretty_fact['description']:
                for line in word_wrap(pretty_fact['description'], 76):
                    print("    {}".format(line))

            if pretty_fact['tags']:
                for line in word_wrap(pretty_fact['tags'], 76):
                    print("    {}".format(line))

        print("-" * min(row_width, 80))

        cats = []
        total_duration = dt.timedelta()
        for cat, duration in sorted(by_cat.items(),
                                    key=lambda x: x[1],
                                    reverse=True):
            cats.append("{}: {}".format(cat, stuff.format_duration(duration)))
            total_duration += duration

        for line in word_wrap(", ".join(cats), 80):
            print(line)
        print("Total: ", stuff.format_duration(total_duration))

        print()