def on_clipboard_text(self, clipboard, text, data): # first check that we have a date selected fact = self.fact_tree.get_selected_fact() if not fact: return if isinstance(fact, dt.date): selected_date = fact else: selected_date = fact.date fact = stuff.Fact(text.decode("utf-8")) if not all((fact.activity, fact.start_time, fact.end_time)): return fact.start_time = fact.start_time.replace(year = selected_date.year, month = selected_date.month, day = selected_date.day) fact.end_time = fact.end_time.replace(year = selected_date.year, month = selected_date.month, day = selected_date.day) new_id = runtime.storage.add_fact(fact) # You can do that?! - copy/pasted an activity trophies.unlock("can_do_that") if new_id: self.fact_tree.select_fact(new_id)
def on_today_row_activated(self, tree, path, column): fact = tree.get_selected_fact() fact = stuff.Fact(fact.activity, tags = ", ".join(fact.tags), category = fact.category, description = fact.description) if fact.activity: runtime.storage.add_fact(fact) self.__show_toggle(False)
def on_switch_activity_clicked(self, widget): activity, temporary = self.new_name.get_value() fact = stuff.Fact(activity, tags = self.new_tags.get_text().decode("utf8", "replace")) if not fact.activity: return runtime.storage.add_fact(fact, temporary) self.new_name.set_text("") self.new_tags.set_text("") self.__show_toggle(False)
def from_dbus_fact(fact): """unpack the struct into a proper dict""" return stuff.Fact( fact[4], start_time=dt.datetime.utcfromtimestamp(fact[1]), end_time=dt.datetime.utcfromtimestamp(fact[2]) if fact[2] else None, description=fact[3], activity_id=fact[5], category=fact[6], tags=fact[7], date=dt.datetime.utcfromtimestamp(fact[8]).date(), delta=dt.timedelta(days=fact[9] // (24 * 60 * 60), seconds=fact[9] % (24 * 60 * 60)), id=fact[0])
def on_save_button_clicked(self, button): activity_name, temporary = self.new_name.get_value() if self.get_widget("in_progress").get_active(): end_time = None else: end_time = self._get_datetime("end") fact = stuff.Fact(activity_name, description=self.figure_description(), tags=self.new_tags.get_text().decode('utf8'), start_time=self._get_datetime("start"), end_time=end_time) if not fact.activity: return False if self.fact_id: runtime.storage.update_fact(self.fact_id, fact, temporary) else: runtime.storage.add_fact(fact, temporary) self.close_window()
def on_workspace_changed(self, screen, previous_workspace): if not previous_workspace: # wnck has a slight hiccup on init and after that calls # workspace changed event with blank previous state that should be # ignored return if not self.workspace_tracking: return # default to not doing anything current_workspace = screen.get_active_workspace() # rely on workspace numbers as names change prev = previous_workspace.get_number() new = current_workspace.get_number() # on switch, update our mapping between spaces and activities self.workspace_activities[prev] = self.last_activity activity = None if "name" in self.workspace_tracking: # first try to look up activity by desktop name mapping = conf.get("workspace_mapping") fact = None if new < len(mapping): fact = stuff.Fact(mapping[new]) if fact.activity: category_id = None if fact.category: category_id = runtime.storage.get_category_id(fact.category) activity = runtime.storage.get_activity_by_name(fact.activity, category_id, ressurect = False) if activity: # we need dict below activity = dict(name = activity.activity, category = activity.category, description = fact.description, tags = fact.tags) if not activity and "memory" in self.workspace_tracking: # now see if maybe we have any memory of the new workspace # (as in - user was here and tracking Y) # if the new workspace is in our dict, switch to the specified activity if new in self.workspace_activities and self.workspace_activities[new]: activity = self.workspace_activities[new] if not activity: return # check if maybe there is no need to switch, as field match: if self.last_activity and \ self.last_activity.activity.lower() == activity.activity.lower() and \ (self.last_activity.category or "").lower() == (activity.category or "").lower() and \ ", ".join(self.last_activity.tags).lower() == ", ".join(activity.tags).lower(): return # ok, switch fact = stuff.Fact(activity.activity, tags = ", ".join(activity.tags), category = activity.category, description = activity.description); runtime.storage.add_fact(fact) if self.notification: self.notification.update(_("Changed activity"), _("Switched to '%s'") % activity.activity, "hamster-applet") self.notification.show()
def __add_fact(self, serialized_fact, start_time, end_time = None, temporary = False): fact = stuff.Fact(serialized_fact, start_time = start_time, end_time = end_time) if not fact.activity or start_time is None: # sanity check return 0 # get tags from database - this will create any missing tags too tags = [dict(zip(('id', 'name', 'autocomplete'), row)) for row in self.GetTagIds(fact.tags)] now = datetime.datetime.now() # if in future - roll back to past if start_time > datetime.datetime.now(): start_time = dt.datetime.combine(now.date(), start_time.time()) if start_time > now: start_time -= dt.timedelta(days = 1) if end_time and end_time > now: end_time = dt.datetime.combine(now.date(), end_time.time()) if end_time > now: end_time -= dt.timedelta(days = 1) # now check if maybe there is also a category category_id = None if fact.category: category_id = self.__get_category_id(fact.category) if not category_id: category_id = self.__add_category(fact.category) trophies.unlock("no_hands") # try to find activity, resurrect if not temporary activity_id = self.__get_activity_by_name(fact.activity, category_id, resurrect = not temporary) if not activity_id: activity_id = self.__add_activity(fact.activity, category_id, temporary) else: activity_id = activity_id['id'] # if we are working on +/- current day - check the last_activity if (dt.datetime.now() - start_time <= dt.timedelta(days=1)): # pull in previous facts facts = self.__get_todays_facts() previous = None if facts and facts[-1]["end_time"] == None: previous = facts[-1] if previous and previous['start_time'] < start_time: # check if maybe that is the same one, in that case no need to restart if previous["activity_id"] == activity_id \ and set(previous["tags"]) == set([tag["name"] for tag in tags]) \ and (previous["description"] or "") == (fact.description or ""): return None # if no description is added # see if maybe previous was too short to qualify as an activity if not previous["description"] \ and 60 >= (start_time - previous['start_time']).seconds >= 0: self.__remove_fact(previous['id']) # now that we removed the previous one, see if maybe the one # before that is actually same as the one we want to start # (glueing) if len(facts) > 1 and 60 >= (start_time - facts[-2]['end_time']).seconds >= 0: before = facts[-2] if before["activity_id"] == activity_id \ and set(before["tags"]) == set([tag["name"] for tag in tags]): # resume and return update = """ UPDATE facts SET end_time = null WHERE id = ? """ self.execute(update, (before["id"],)) return before["id"] else: # otherwise stop update = """ UPDATE facts SET end_time = ? WHERE id = ? """ self.execute(update, (start_time, previous["id"])) # done with the current activity, now we can solve overlaps if not end_time: end_time = self.__squeeze_in(start_time) else: self.__solve_overlaps(start_time, end_time) # finally add the new entry insert = """ INSERT INTO facts (activity_id, start_time, end_time, description) VALUES (?, ?, ?, ?) """ self.execute(insert, (activity_id, start_time, end_time, fact.description)) fact_id = self.__last_insert_rowid() #now link tags insert = ["insert into fact_tags(fact_id, tag_id) values(?, ?)"] * len(tags) params = [(fact_id, tag["id"]) for tag in tags] self.execute(insert, params) self.__remove_index([fact_id]) return fact_id
def __solve_overlaps(self, start_time, end_time): """finds facts that happen in given interval and shifts them to make room for new fact """ if end_time is None or start_time is None: return # possible combinations and the OR clauses that catch them # (the side of the number marks if it catches the end or start time) # |----------------- NEW -----------------| # |--- old --- 1| |2 --- old --- 1| |2 --- old ---| # |3 ----------------------- big old ------------------------ 3| query = """ SELECT a.*, b.name, c.name as category FROM facts a LEFT JOIN activities b on b.id = a.activity_id LEFT JOIN categories c on b.category_id = c.id WHERE (end_time > ? and end_time < ?) OR (start_time > ? and start_time < ?) OR (start_time < ? and end_time > ?) ORDER BY start_time """ conflicts = self.fetchall(query, (start_time, end_time, start_time, end_time, start_time, end_time)) for fact in conflicts: # won't eliminate as it is better to have overlapping entries than loosing data if start_time < fact["start_time"] and end_time > fact["end_time"]: continue # split - truncate until beginning of new entry and create new activity for end if fact["start_time"] < start_time < fact["end_time"] and \ fact["start_time"] < end_time < fact["end_time"]: logging.info("splitting %s" % fact["name"]) # truncate until beginning of the new entry self.execute("""UPDATE facts SET end_time = ? WHERE id = ?""", (start_time, fact["id"])) fact_name = fact["name"] # create new fact for the end new_fact = stuff.Fact(fact["name"], category = fact["category"], description = fact["description"], ) new_fact_id = self.__add_fact(new_fact.serialized_name(), end_time, fact["end_time"]) # copy tags tag_update = """INSERT INTO fact_tags(fact_id, tag_id) SELECT ?, tag_id FROM fact_tags WHERE fact_id = ?""" self.execute(tag_update, (new_fact_id, fact["id"])) #clone tags trophies.unlock("split") # overlap start elif start_time < fact["start_time"] < end_time: logging.info("Overlapping start of %s" % fact["name"]) self.execute("UPDATE facts SET start_time=? WHERE id=?", (end_time, fact["id"])) # overlap end elif start_time < fact["end_time"] < end_time: logging.info("Overlapping end of %s" % fact["name"]) self.execute("UPDATE facts SET end_time=? WHERE id=?", (start_time, fact["id"]))