def test_parse_local_fuzzy_dates(self): """ Parse fuzzy dates in their localized version """ self.assertEqual(Date(_("now")), Date.now()) self.assertEqual(Date(_("soon")), Date.soon()) self.assertEqual(Date(_("later")), Date.someday()) self.assertEqual(Date(_("someday")), Date.someday()) self.assertEqual(Date(""), Date.no_date())
def test_parse_fuzzy_dates(self): """ Parse fuzzy dates like now, soon, later, someday """ self.assertEqual(Date("now"), Date.now()) self.assertEqual(Date("soon"), Date.soon()) self.assertEqual(Date("later"), Date.someday()) self.assertEqual(Date("someday"), Date.someday()) self.assertEqual(Date(""), Date.no_date())
def new(self, title: str = None, parent: UUID = None) -> Task2: """Create a new task and add it to the store.""" tid = uuid4() title = title or _('New Task') task = Task2(id=tid, title=title) task.date_added = Date.now() if parent: self.add(task, parent) else: self.data.append(task) self.lookup[tid] = task self.emit('added', task) return task
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 __init__(self, requester, app, task, thisisnew=False, clipboard=None): """ req is the requester app is the view manager thisisnew is True when a new task is created and opened """ self.req = requester self.app = app 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") # Recurrence self.recurring_menu = RecurringMenu(self.req, task.tid, self.builder) # Create our dictionary and connect it dic = { "on_tags_popover": self.open_tags_popover, "on_tag_toggled": self.on_tag_toggled, "on_move": self.on_move, "set_recurring_term_every_day": self.set_recurring_term_every_day, "set_recurring_term_every_otherday": self.set_recurring_term_every_otherday, "set_recurring_term_every_week": self.set_recurring_term_every_week, "set_recurring_term_every_month": self.set_recurring_term_every_month, "set_recurring_term_every_year": self.set_recurring_term_every_year, "set_recurring_term_week_day": self.set_recurring_term_week_day, "set_recurring_term_calender_month": self.set_recurring_term_month, "set_recurring_term_calender_year": self.set_recurring_term_year, "toggle_recurring_status": self.toggle_recurring_status, "on_repeat_icon_toggled": self.on_repeat_icon_toggled, "show_popover_start": self.show_popover_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_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_focus_out": lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_CLOSED ), } self.window = self.builder.get_object("TaskEditor") self.builder.connect_signals(dic) self.window.set_application(app) if task.has_parent(): self.parent_button.set_label(_('Open Parent')) else: self.parent_button.set_label(_('Add Parent')) # Connect signals for the calendar self.start_handle = self.start_calendar.connect( 'day-selected', lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_START)) self.due_handle = self.due_calendar.connect( 'day-selected', lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_DUE)) self.closed_handle = self.closed_calendar.connect( 'day-selected', lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_CLOSED)) # 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() 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)) self.textview.browse_tag_cb = app.select_tag self.textview.new_subtask_cb = self.new_subtask self.textview.get_subtasks_cb = task.get_children self.textview.delete_subtask_cb = self.remove_subtask self.textview.rename_subtask_cb = self.rename_subtask self.textview.open_subtask_cb = self.open_subtask self.textview.save_cb = self.light_save self.textview.add_tasktag_cb = task.tag_added self.textview.remove_tasktag_cb = task.remove_tag self.textview.refresh_cb = self.refresh_editor self.textview.get_tagslist_cb = task.get_tags_name self.textview.tid = task.tid # Voila! it's done self.textview.connect('focus-in-event', self.on_textview_focus_in) self.textview.connect('focus-out-event', self.on_textview_focus_out) """ 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") self.task = task tags = task.get_tags() text = self.task.get_text() title = self.task.get_title() self.textview.buffer.set_text(f"{title}\n") if text: self.textview.insert(text) # Insert any remaining tags if tags: tag_names = [t.get_name() for t in tags] self.textview.insert_tags(tag_names) else: # If not text, we insert tags if tags: tag_names = [t.get_name() for t in tags] self.textview.insert_tags(tag_names) start = self.textview.buffer.get_end_iter() self.textview.buffer.insert(start, '\n') # Insert subtasks if they weren't inserted in the text subtasks = task.get_children() for sub in subtasks: if sub not in self.textview.subtasks['tags']: self.textview.insert_existing_subtask(sub) if thisisnew: self.textview.select_title() else: self.task.set_to_keep() 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.app, 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.refresh_editor() self.textview.grab_focus() self.init_dimensions() self.window.insert_action_group('app', app) self.window.insert_action_group('win', app.browser) self.textview.set_editable(True) self.window.set_transient_for(self.app.browser) self.window.show()
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 if (ddate == Date.today()): return self._get_color(2) # High urgency elif ddate != Date.no_date(): # Has a due date if daysleft < 0: return self._get_color(3) # Overdue elif (sdate == Date.no_date() and # Has no start date not ddate.is_fuzzy()): # Due date is not fuzzy 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 test_due_date(self): task1 = Task2(id=uuid4(), title='A Parent Task') task2 = Task2(id=uuid4(), title='A Child Task') task3 = Task2(id=uuid4(), title='Another Child Task') task4 = Task2(id=uuid4(), title='Yet Another Child Task') task5 = Task2(id=uuid4(), title='So many Child Tasks') task6 = Task2(id=uuid4(), title='More childs') task1.children.append(task2) task1.children.append(task3) task3.children.append(task4) task4.children.append(task5) task4.children.append(task6) # Test changing parent's due random_date = Date('1996-2-3') random_date2 = Date('2010-7-10') task1.date_due = random_date self.assertEqual(task1.date_due, random_date) task2.date_due = random_date task3.date_due = random_date task4.date_due = random_date task5.date_due = random_date task6.date_due = random_date # Test changes in the parent - Fuzzy task1.date_due = Date.now() self.assertEqual(task1.date_due, Date.now()) self.assertEqual(task2.date_due, random_date) self.assertEqual(task3.date_due, random_date) self.assertEqual(task4.date_due, random_date) self.assertEqual(task5.date_due, random_date) self.assertEqual(task6.date_due, random_date) # Test changing child's due (after parent's) task3.date_due = random_date2 self.assertEqual(task3.date_due, random_date2) self.assertEqual(task4.date_due, random_date) self.assertEqual(task5.date_due, random_date) self.assertEqual(task6.date_due, random_date) # Test changing child's due (before parent's) task4.date_due = random_date2 task3.date_due = random_date self.assertEqual(task4.date_due, random_date) self.assertEqual(task5.date_due, random_date) self.assertEqual(task6.date_due, random_date) # Test changing parent's due (before child's) task4.date_due = random_date task3.date_due = random_date2 self.assertEqual(task3.date_due, random_date2) self.assertEqual(task4.date_due, random_date) # Test changing parent's due (None or nodate) task2.date_due = random_date task1.date_due = None self.assertEqual(task2.date_due, random_date)
def __init__(self, requester, app, task, thisisnew=False, clipboard=None): """ req is the requester app is the view manager thisisnew is True when a new task is created and opened """ self.req = requester self.app = app 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_tags_popover": self.open_tags_popover, "on_tag_toggled": self.on_tag_toggled, "on_move": self.on_move, "show_popover_start": self.show_popover_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_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_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") self.window.set_application(app) # Connect signals for the calendar self.start_handle = self.start_calendar.connect( 'day-selected', lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_START)) self.due_handle = self.due_calendar.connect( 'day-selected', lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_DUE)) self.closed_handle = self.closed_calendar.connect( 'day-selected', lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_CLOSED)) # 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.app.open_task) self.textview.set_left_margin(20) self.textview.set_right_margin(20) 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") 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(f"{title}\n") # we insert the rest of the task if texte: self.textview.insert(f"{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.app, 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.window.insert_action_group('app', app) self.window.insert_action_group('win', app.browser) self._set_actions() self.textview.set_editable(True) self.window.set_transient_for(self.app.browser) self.window.show()