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)
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()
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([])
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))
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()
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)
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()
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()
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'])
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)
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()
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)