示例#1
0
    def set_fact(self, fact):
        """Set current fact."""

        self.fact = fact

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

        self.activity_label.set_text(stuff.escape_pango(fact.activity))

        category_text = "  - {}".format(stuff.escape_pango(
            fact.category)) if fact.category else ""
        self.category_label.set_text(category_text)

        text = stuff.escape_pango(fact.description)
        description_text = "<small><i>{}</i></small>".format(
            text) if fact.description else ""
        self.description_label.set_text(description_text)

        if fact.tags:
            # for now, tags are on a single line.
            # The first one is enough to determine the height.
            self.tag_label.set_text(stuff.escape_pango(fact.tags[0]))
示例#2
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()
示例#3
0
    def set_text(self, activity, duration):
        label = stuff.escape_pango(activity)

        if len(activity) > 25:  #ellipsize at some random length
            label = "%s%s" % (stuff.escape_pango(activity[:25]), "&#8230;")

        self.activity = label
        self.duration = duration
        self.reformat_label()
示例#4
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()
示例#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()
示例#6
0
    def plot(self, keys, data):
        self.data = data

        bars = dict([(bar.key, bar.normalized) for bar in self.bars])

        max_val = float(max(data or [0]))

        new_bars, new_labels = [], []
        for key, value in zip(keys, data):
            if max_val:
                normalized = value / max_val
            else:
                normalized = 0
            bar = Bar(key, locale.format(self.value_format, value), normalized, self.label_color)
            bar.interactive = self.graph_interactive

            if key in bars:
                bar.normalized = bars[key]
                self.tweener.add_tween(bar, normalized=normalized)
            new_bars.append(bar)

            label = graphics.Label(stuff.escape_pango(key), size = 8, alignment = pango.Alignment.RIGHT)
            new_labels.append(label)


        self.plot_area.remove_child(*self.bars)
        self.remove_child(*self.labels)

        self.bars, self.labels = new_bars, new_labels
        self.add_child(*self.labels)
        self.plot_area.add_child(*self.bars)

        self.show()
        self.redraw()
示例#7
0
    def _draw(self, context, opacity, matrix):
        g = graphics.Graphics(context)
        g.save_context()
        g.translate(self.x, self.y)

        hours = [(value.days * 24.0 + value.seconds / 3600.0)
                 for _, value in self.values]
        total = self._total.days * 24.0 + self._total.seconds / 3600.0

        for i, (label, value) in enumerate(self.values):
            percent = 100.0 * hours[i] / total
            label += " (%3.2fh; %3.1f%%)" % (hours[i], percent)
            g.set_color("#333")
            self.layout.set_markup(stuff.escape_pango(label))
            label_w, label_h = self.layout.get_pixel_size()

            y = int(i * label_h * 1.5)
            g.move_to(180 - label_w, y)
            pangocairo.show_layout(context, self.layout)

            w = (self.alloc_w -
                 200) * value.total_seconds() / self._max.total_seconds()
            w = max(1, int(round(w)))
            g.rectangle(190, y, int(w), int(label_h))
            g.fill("#999")

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

            for tag in fact.tags:
                totals["tag"][tag.strip()] += fact.delta
                total_sums["tag"] += fact.delta

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

        self.activities_chart.set_values(totals['activity'])
        self.activities_chart.set_total(total_sums['activity'])

        self.categories_chart.set_values(totals['category'])
        self.categories_chart.set_total(total_sums['category'])

        self.tag_chart.set_values(totals['tag'])
        self.tag_chart.set_total(total_sums['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'])
示例#9
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)
示例#10
0
    def add_action(self, name, text):
        """Add an action to the suggestions.

        name (str): unique label, use to retrieve the action index.
        text (str): text used to display the action.
        """
        markup = "<i>{}</i>".format(stuff.escape_pango(text))
        idx = len(self._action_list)
        self.completion.insert_action_markup(idx, markup)
        self._action_list.append(name)
示例#11
0
文件: tray.py 项目: Americas/hamster
 def _clamp_text(self, text, length=25, with_ellipsis=True, is_indicator=False):
     text = stuff.escape_pango(text)
     if len(text) > length:  #ellipsize at some random length
         if with_ellipsis:
             if is_indicator:
                 text = "%s%s" % (text[:length], "...")
             else:
                 text = "%s%s" % (text[:length], "…")
         else:
             text = "%s" % (text[:length])
     return text
示例#12
0
文件: facttree.py 项目: RoSk0/hamster
    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()
示例#13
0
文件: tray.py 项目: Americas/hamster
 def _clamp_text(self,
                 text,
                 length=25,
                 with_ellipsis=True,
                 is_indicator=False):
     text = stuff.escape_pango(text)
     if len(text) > length:  #ellipsize at some random length
         if with_ellipsis:
             if is_indicator:
                 text = "%s%s" % (text[:length], "...")
             else:
                 text = "%s%s" % (text[:length], "…")
         else:
             text = "%s" % (text[:length])
     return text
示例#14
0
文件: tags.py 项目: Br3nda/hamster
    def __init__(self, text, interactive = True, color = "#F1EAAA"):
        graphics.Sprite.__init__(self, interactive = interactive)

        self.width, self.height = 0,0

        font = gtk.Style().font_desc
        font_size = int(font.get_size() * 0.8 / pango.SCALE) # 80% of default

        self.label = graphics.Label(text, size = font_size, color = (30, 30, 30), y = 1)
        self.color = color
        self.add_child(self.label)

        self.corner = int((self.label.height + 3) / 3) + 0.5
        self.label.x = self.corner + 6

        self.text = stuff.escape_pango(text)
        self.connect("on-render", self.on_render)
示例#15
0
    def _show_tags(self, g, color, bg):
        label = self.tag_label
        label.color = bg

        g.save_context()
        g.translate(self.tag_row_margin_H, self.tag_row_margin_V)
        for tag in self.fact.tags:
            label.set_text(stuff.escape_pango(tag))
            w, h = label.layout.get_pixel_size()
            rw = w + self.tag_inner_margin_H * 2
            rh = h + self.tag_inner_margin_V * 2
            g.rectangle(0, 0, rw, rh, 2)
            g.fill(color, 0.5)
            label.show(g, x=self.tag_inner_margin_H, y=self.tag_inner_margin_V)

            g.translate(rw + self.inter_tag_margin, 0)

        g.restore_context()
示例#16
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")
            self.layout.set_markup(stuff.escape_pango(label))
            label_w, label_h = self.layout.get_pixel_size()

            y = int(i * label_h * 1.5)
            g.move_to(100 - label_w, y)
            pangocairo.show_layout(context, self.layout)

            w = (self.alloc_w - 110) * value.total_seconds() / self._max.total_seconds()
            w = max(1, int(round(w)))
            g.rectangle(110, y, int(w), int(label_h))
            g.fill("#999")

        g.restore_context()
示例#17
0
文件: overview.py 项目: IPZS/hamster
    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.iteritems():
            totals[key] = sorted(group.iteritems(), 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'])
示例#18
0
文件: overview.py 项目: IPZS/hamster
    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")
            self.layout.set_markup(stuff.escape_pango(label))
            label_w, label_h = self.layout.get_pixel_size()

            y = int(i * label_h * 1.5)
            g.move_to(100 - label_w, y)
            pangocairo.show_layout(context, self.layout)

            w = (self.alloc_w - 110) * value.total_seconds() / self._max.total_seconds()
            w = max(1, int(round(w)))
            g.rectangle(110, y, int(w), int(label_h))
            g.fill("#999")

        g.restore_context()
示例#19
0
文件: tags.py 项目: webnard/hamster
    def __init__(self, text, interactive=True, color="#F1EAAA"):
        graphics.Sprite.__init__(self, interactive=interactive)

        self.width, self.height = 0, 0

        font = gtk.Style().font_desc
        font_size = int(font.get_size() * 0.8 / pango.SCALE)  # 80% of default

        self.label = graphics.Label(text,
                                    size=font_size,
                                    color=(30, 30, 30),
                                    y=1)
        self.color = color
        self.add_child(self.label)

        self.corner = int((self.label.height + 3) / 3) + 0.5
        self.label.x = self.corner + 6

        self.text = stuff.escape_pango(text)
        self.connect("on-render", self.on_render)
示例#20
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)
示例#21
0
    def validate_fields(self):
        """Check fields information.

        Update gui status about entry and description validity.
        Try to merge date, activity and description informations.

        Return the consolidated fact if successful, or None.
        """
        fact = self.localized_fact()

        now = hamster_now()
        self.get_widget("button-next-day").set_sensitive(
            self.date < now.date())

        if self.date == now.date():
            default_dt = now
        else:
            default_dt = dt.datetime.combine(self.date, now.time())

        self.draw_preview(fact.start_time or default_dt, fact.end_time
                          or default_dt)

        if fact.start_time is None:
            self.update_status(status="wrong", markup="Missing start time")
            return None

        if not fact.activity:
            self.update_status(status="wrong", markup="Missing activity")
            return None

        description_box_content = self.figure_description()
        if fact.description and description_box_content:
            escaped_cmd = escape_pango(fact.description)
            escaped_box = escape_pango(description_box_content)
            markup = dedent("""\
                             <b>Duplicate description</b>
                             <i>command line</i>:
                             '{}'
                             <i>description box</i>:
                             '''{}'''
                             """).format(escaped_cmd, escaped_box)
            self.update_status(status="wrong", markup=markup)
            return None

        # Good to go, no description ambiguity
        if description_box_content:
            fact.description = description_box_content

        if (fact.delta < dt.timedelta(0)) and fact.end_time:
            fact.end_time += dt.timedelta(days=1)
            markup = dedent("""\
                            <b>Working late ?</b>
                            Duration would be negative.
                            This happens when the activity crosses the
                            hamster day start time ({:%H:%M} from tracking settings).

                            Changing the end time date to the next day.
                            Pressing the button would save
                            an actvity going from
                            {}
                            to
                            {}
                            (in civil local time)
                            """.format(conf.day_start, fact.start_time,
                                       fact.end_time))
            self.update_status(status="warning", markup=markup)
            return fact

        # nothing unusual
        self.update_status(status="looks good", markup="")
        return fact
示例#22
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)
示例#23
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)
示例#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]
        """

        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)