def test_parse_fuzzy_dates(self): """ Parse fuzzy dates like now, soon, later, someday """ self.assertEqual(Date.parse("now"), Date.now()) self.assertEqual(Date.parse("soon"), Date.soon()) self.assertEqual(Date.parse("later"), Date.someday()) self.assertEqual(Date.parse("someday"), Date.someday()) self.assertEqual(Date.parse(""), Date.no_date())
def test_parse_local_fuzzy_dates(self): """ Parse fuzzy dates in their localized version """ self.assertEqual(Date.parse(_("now")), Date.now()) self.assertEqual(Date.parse(_("soon")), Date.soon()) self.assertEqual(Date.parse(_("later")), Date.someday()) self.assertEqual(Date.parse(_("someday")), Date.someday()) self.assertEqual(Date.parse(""), Date.no_date())
def workview(self, task, parameters=None): wv = (self.active(task) and self.is_started(task) and self.is_workable(task) and self.no_disabled_tag(task) and task.get_due_date() != Date.someday()) return wv
def check_commands(commands_list): """ Execute search commands This method is recursive for !or and !and """ def fulltext_search(task, word): """ check if task contains the word """ word = word.lower() text = task.get_excerpt(strip_tags=False).lower() title = task.get_title().lower() return word in text or word in title value_checks = { 'after': lambda t, v: task.get_due_date() > v, 'before': lambda t, v: task.get_due_date() < v, 'tag': lambda t, v: v in task.get_tags_name(), 'word': fulltext_search, 'today': lambda task, v: task.get_due_date() == Date.today(), 'tomorrow': lambda task, v: task.get_due_date() == Date.tomorrow(), 'nodate': lambda task, v: task.get_due_date() == Date.no_date(), 'now': lambda task, v: task.get_due_date() == Date.now(), 'soon': lambda task, v: task.get_due_date() == Date.soon(), 'someday': lambda task, v: task.get_due_date() == Date.someday(), 'notag': lambda task, v: task.get_tags() == [], } for command in commands_list: cmd, positive, args = command[0], command[1], command[2:] result = False if cmd == 'or': for sub_cmd in args[0]: if check_commands([sub_cmd]): result = True break elif value_checks.get(cmd, None): if len(args) > 0: args = args[0] result = value_checks[cmd](task, args) if (positive and not result) or (not positive and result): return False return True
def get_node_bgcolor(self, node): """ This method checks the urgency of a node (task) and returns its urgency background color""" sdate = node.get_start_date() ddate = node.get_due_date() daysleft = ddate.days_left() # Dates undefined (Fix to bug #1039655) if ddate == Date.today(): return self._get_color(2) # High urgency elif daysleft < 0 and ddate != Date.no_date(): return self._get_color(3) # Overdue elif ( sdate == Date.no_date() # Has no start date and ddate != Date.no_date() # and a due date and not ddate.is_fuzzy() ): # which is not fuzzy, is fixed return self._get_color(1) # Normal # Fuzzy dates (now, soon, someday) # These can ignore the start date if ddate == Date.now(): return self._get_color(2) elif ddate == Date.soon(): return self._get_color(1) elif ddate == Date.someday(): return self._get_color(0) # Dates fully defined. Calculate gradient color elif sdate != Date.no_date() != ddate: dayspan = (ddate - sdate).days redf = self._pref_data["reddays"] reddays = int(ceil(redf / 100.0 * dayspan)) # Gradient variables grad_dayspan = dayspan - reddays grad_half_dayspan = grad_dayspan / 2.0 # Default to low urgency color color = self._get_color(0) # CL : low urgency color # CN : normal urgency color # CH : high urgency color # CO : overdue color # To understand this section, it is easier to draw out a # timeline divided into 3 sections: CL to CN, CN to CH and # the reddays section. Then point out the spans of the # different variables (dayspan, daysleft, reddays, # grad_dayspan, grad_half_dayspan) if daysleft < 0: # CO color = self._get_color(3) elif daysleft <= reddays: # CH color = self._get_color(2) elif daysleft <= (dayspan - grad_half_dayspan): # Gradient CN to CH # Has to be float so division by it is non-zero steps = float(grad_half_dayspan) step = grad_half_dayspan - (daysleft - reddays) color = self._get_gradient_color(self._get_color(1), self._get_color(2), step / steps) elif daysleft <= dayspan: # Gradient CL to CN steps = float(grad_half_dayspan) step = grad_half_dayspan - (daysleft - reddays - grad_half_dayspan) color = self._get_gradient_color(self._get_color(0), self._get_color(1), step / steps) return color # Insufficient data to determine urgency else: return None
def get_node_bgcolor(self, node): """ This method checks the urgency of a node (task) and returns its urgency background color""" sdate = node.get_start_date() ddate = node.get_due_date() daysleft = ddate.days_left() # Dates undefined (Fix to bug #1039655) if (ddate == Date.today()): return self._get_color(2) # High urgency elif (daysleft < 0 and ddate != Date.no_date()): return self._get_color(3) # Overdue elif (sdate == Date.no_date() # Has no start date and ddate != Date.no_date() # and a due date and not ddate.is_fuzzy()): # which is not fuzzy, is fixed return self._get_color(1) # Normal # Fuzzy dates (now, soon, someday) # These can ignore the start date if (ddate == Date.now()): return self._get_color(2) elif (ddate == Date.soon()): return self._get_color(1) elif (ddate == Date.someday()): return self._get_color(0) # Dates fully defined. Calculate gradient color elif (sdate != Date.no_date() != ddate): dayspan = (ddate - sdate).days redf = self._pref_data['reddays'] reddays = int(ceil(redf / 100.0 * dayspan)) # Gradient variables grad_dayspan = dayspan - reddays grad_half_dayspan = grad_dayspan / 2.0 # Default to low urgency color color = self._get_color(0) # CL : low urgency color # CN : normal urgency color # CH : high urgency color # CO : overdue color # To understand this section, it is easier to draw out a # timeline divided into 3 sections: CL to CN, CN to CH and # the reddays section. Then point out the spans of the # different variables (dayspan, daysleft, reddays, # grad_dayspan, grad_half_dayspan) if daysleft < 0: # CO color = self._get_color(3) elif daysleft <= reddays: # CH color = self._get_color(2) elif daysleft <= (dayspan - grad_half_dayspan): # Gradient CN to CH # Has to be float so division by it is non-zero steps = float(grad_half_dayspan) step = grad_half_dayspan - (daysleft - reddays) color = self._get_gradient_color(self._get_color(1), self._get_color(2), step / steps) elif daysleft <= dayspan: # Gradient CL to CN steps = float(grad_half_dayspan) step = grad_half_dayspan - (daysleft - reddays - grad_half_dayspan) color = self._get_gradient_color(self._get_color(0), self._get_color(1), step / steps) return color # Insufficient data to determine urgency else: return None
def __init__(self, requester, vmanager, task, thisisnew=False, clipboard=None): """ req is the requester vmanager is the view manager thisisnew is True when a new task is created and opened """ self.req = requester self.vmanager = vmanager self.browser_config = self.req.get_config('browser') self.config = self.req.get_task_config(task.get_id()) self.time = None self.clipboard = clipboard self.builder = Gtk.Builder() self.builder.add_from_file(self.EDITOR_UI_FILE) self.donebutton = self.builder.get_object("mark_as_done") self.undonebutton = self.builder.get_object("mark_as_undone") self.dismissbutton = self.builder.get_object("dismiss") self.undismissbutton = self.builder.get_object("undismiss") self.add_subtask = self.builder.get_object("add_subtask") self.tag_store = self.builder.get_object("tag_store") self.parent_button = self.builder.get_object("parent") # Closed date self.closed_popover = self.builder.get_object("closed_popover") self.closed_entry = self.builder.get_object("closeddate_entry") self.closed_calendar = self.builder.get_object("calendar_closed") # Start date self.start_popover = self.builder.get_object("start_popover") self.start_entry = self.builder.get_object("startdate_entry") self.start_calendar = self.builder.get_object("calendar_start") # Due date self.due_popover = self.builder.get_object("due_popover") self.due_entry = self.builder.get_object("duedate_entry") self.due_calendar = self.builder.get_object("calendar_due") # Create our dictionary and connect it dic = { "on_mark_as_done": self.change_status, "on_dismiss": self.dismiss, "delete_clicked": self.delete_task, "on_tags_popover": self.open_tags_popover, "on_tag_toggled": self.on_tag_toggled, "on_insert_subtask_clicked": self.insert_subtask, "on_parent_select": self.on_parent_select, "on_move": self.on_move, "show_popover_start": self.show_popover_start, "startdate_selected": lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_START), "startingdate_changed": lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_START), "startdate_cleared": lambda w: self.on_date_cleared(w, GTGCalendar.DATE_KIND_START), "startdate_focus_out": lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_START ), "show_popover_due": self.show_popover_due, "duedate_selected": lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_DUE), "duedate_changed": lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_DUE), "duedate_now_selected": lambda w: self.on_duedate_fuzzy(w, Date.now()), "duedate_soon_selected": lambda w: self.on_duedate_fuzzy(w, Date.soon()), "duedate_someday_selected": lambda w: self.on_duedate_fuzzy(w, Date.someday()), "duedate_cleared": lambda w: self.on_date_cleared(w, GTGCalendar.DATE_KIND_DUE), "duedate_focus_out": lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_DUE), "show_popover_closed": self.show_popover_closed, "closeddate_changed": lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_CLOSED), "closeddate_selected": lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_CLOSED), "closeddate_focus_out": lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_CLOSED ), } self.builder.connect_signals(dic) self.window = self.builder.get_object("TaskEditor") # Removing the Normal textview to replace it by our own # So don't try to change anything with glade, this is a home-made # widget textview = self.builder.get_object("textview") scrolled = self.builder.get_object("scrolledtask") scrolled.remove(textview) self.textview = TaskView(self.req, self.clipboard) self.textview.show() self.textview.set_subtask_callback(self.new_subtask) self.textview.open_task_callback(self.vmanager.open_task) self.textview.set_left_margin(7) self.textview.set_right_margin(5) scrolled.add(self.textview) conf_font_value = self.browser_config.get("font_name") if conf_font_value != "": self.textview.override_font(Pango.FontDescription(conf_font_value)) # Voila! it's done """ TODO(jakubbrindza): Once all the functionality in editor is back and working, bring back also the accelerators! Dayleft_label needs to be brought back, however its position is unsure. """ # self.dayleft_label = self.builder.get_object("dayleft") # Define accelerator keys self.init_accelerators() self.task = task tags = task.get_tags() self.textview.subtasks_callback(task.get_children) self.textview.removesubtask_callback(task.remove_child) self.textview.set_get_tagslist_callback(task.get_tags_name) self.textview.set_add_tag_callback(task.add_tag) self.textview.set_remove_tag_callback(task.remove_tag) self.textview.save_task_callback(self.light_save) texte = self.task.get_text() title = self.task.get_title() # the first line is the title self.textview.set_text("%s\n" % title) # we insert the rest of the task if texte: self.textview.insert("%s" % texte) else: # If not text, we insert tags if tags: for t in tags: self.textview.insert_text("%s, " % t.get_name()) self.textview.insert_text("\n") # If we don't have text, we still need to insert subtasks if any subtasks = task.get_children() if subtasks: self.textview.insert_subtasks(subtasks) # We select the title if it's a new task if thisisnew: self.textview.select_title() else: self.task.set_to_keep() self.textview.modified(full=True) self.window.connect("destroy", self.destruction) # Connect search field to tags popup self.tags_entry = self.builder.get_object("tags_entry") self.tags_tree = self.builder.get_object("tags_tree") self.tags_tree.set_search_entry(self.tags_entry) self.tags_tree.set_search_equal_func(self.search_function, None) # plugins self.pengine = PluginEngine() self.plugin_api = PluginAPI(self.req, self.vmanager, self) self.pengine.register_api(self.plugin_api) self.pengine.onTaskLoad(self.plugin_api) # Putting the refresh callback at the end make the start a lot faster self.textview.refresh_callback(self.refresh_editor) self.refresh_editor() self.textview.grab_focus() self.init_dimensions() self.textview.set_editable(True) self.window.show()