def __date_comp(self, task1, task2, para, order): '''This is a quite complex method to sort tasks by date, handling fuzzy date and complex situation. Return -1 if nid1 is before nid2, return 1 otherwise ''' if task1 and task2: if para == 'start': t1 = task1.get_start_date() t2 = task2.get_start_date() elif para == 'due': #BUG: get_urgent_date() compares children, and the constraint one compares parents, and we are blindly blending the two together?!?! t1 = task1.get_urgent_date() t2 = task2.get_urgent_date() if t1 == Date.no_date(): t1 = task1.get_due_date_constraint() if t2 == Date.no_date(): t2 = task2.get_due_date_constraint() elif para == 'closed': t1 = task1.get_closed_date() t2 = task2.get_closed_date() else: raise ValueError( 'invalid date comparison parameter: %s') % para #return reverse_if_descending(order, cmp(t2, t1)) # Tricky! swapped is correct!? return cmp(t2, t1); else: return 0;
def __init__(self, ze_id, requester, newtask=False): TreeNode.__init__(self, ze_id) # the id of this task in the project should be set # tid is a string ! (we have to choose a type and stick to it) assert(isinstance(ze_id, str) or isinstance(ze_id, str)) self.tid = str(ze_id) self.set_uuid(uuid.uuid4()) self.remote_ids = {} self.content = "" self.title = _("My new task") # available status are: Active - Done - Dismiss - Note self.status = self.STA_ACTIVE self.closed_date = Date.no_date() self.due_date = Date.no_date() self.start_date = Date.no_date() self.can_be_deleted = newtask # tags self.tags = [] self.req = requester self.__main_treeview = requester.get_main_view() # If we don't have a newtask, we will have to load it. self.loaded = newtask # Should not be necessary with the new backends # if self.loaded: # self.req._task_loaded(self.tid) self.attributes = {} self._modified_update()
def __init__(self, ze_id, requester, newtask=False): TreeNode.__init__(self, ze_id) # the id of this task in the project should be set # tid is a string ! (we have to choose a type and stick to it) assert(isinstance(ze_id, str) or isinstance(ze_id, unicode)) self.tid = str(ze_id) self.set_uuid(uuid.uuid4()) self.remote_ids = {} self.content = "" self.title = _("My new task") # available status are: Active - Done - Dismiss - Note self.status = self.STA_ACTIVE self.closed_date = Date.no_date() self.due_date = Date.no_date() self.start_date = Date.no_date() self.can_be_deleted = newtask # tags self.tags = [] self.req = requester self.__main_treeview = requester.get_main_view() # If we don't have a newtask, we will have to load it. self.loaded = newtask # Should not be necessary with the new backends # if self.loaded: # self.req._task_loaded(self.tid) self.attributes = {} self._modified_update()
def on_date_cleared(self, widget, kind): """ Callback when a date is cleared through the popups. """ if kind == GTGCalendar.DATE_KIND_START: self.task.set_start_date(Date.no_date()) self.start_entry.set_text('') elif kind == GTGCalendar.DATE_KIND_DUE: self.task.set_due_date(Date.no_date()) self.due_entry.set_text('')
def __date_comp(self, task1, task2, para, order): '''This is a quite complex method to sort tasks by date, handling fuzzy date and complex situation. Return -1 if nid1 is before nid2, return 1 otherwise ''' if task1 and task2: if para == 'start': t1 = task1.get_start_date() t2 = task2.get_start_date() elif para == 'due': t1 = task1.get_urgent_date() t2 = task2.get_urgent_date() if t1 == Date.no_date(): t1 = task1.get_due_date_constraint() if t2 == Date.no_date(): t2 = task2.get_due_date_constraint() elif para == 'closed': t1 = task1.get_closed_date() t2 = task2.get_closed_date() else: raise ValueError( 'invalid date comparison parameter: %s') % para sort = (t2 > t1) - (t2 < t1) else: sort = 0 # local function def reverse_if_descending(s): """Make a cmpare result relative to the top instead of following user-specified sort direction""" if order == Gtk.SortType.ASCENDING: return s else: return -1 * s if sort == 0: # Group tasks with the same tag together for visual cleanness t1_tags = task1.get_tags_name() t1_tags.sort() t2_tags = task2.get_tags_name() t2_tags.sort() cmp_tags = (t1_tags > t2_tags) - (t1_tags < t2_tags) sort = reverse_if_descending(cmp_tags) if sort == 0: # Break ties by sorting by title t1_title = task1.get_title() t2_title = task2.get_title() t1_title = locale.strxfrm(t1_title) t2_title = locale.strxfrm(t2_title) cmp_title = (t1_title > t2_title) - (t1_title < t2_title) sort = reverse_if_descending(cmp_title) return sort
def __date_comp(self, task1, task2, para, order): '''This is a quite complex method to sort tasks by date, handling fuzzy date and complex situation. Return -1 if nid1 is before nid2, return 1 otherwise ''' if task1 and task2: if para == 'start': t1 = task1.get_start_date() t2 = task2.get_start_date() elif para == 'due': t1 = task1.get_urgent_date() t2 = task2.get_urgent_date() if t1 == Date.no_date(): t1 = task1.get_due_date_constraint() if t2 == Date.no_date(): t2 = task2.get_due_date_constraint() elif para == 'closed': t1 = task1.get_closed_date() t2 = task2.get_closed_date() else: raise ValueError( 'invalid date comparison parameter: %s') % para sort = (t2 > t1) - (t2 < t1) else: sort = 0 # local function def reverse_if_descending(s): """Make a cmpare result relative to the top instead of following user-specified sort direction""" if order == Gtk.SortType.ASCENDING: return s else: return -1 * s if sort == 0: # Group tasks with the same tag together for visual cleanness t1_tags = task1.get_tags_name() t1_tags.sort() t2_tags = task2.get_tags_name() t2_tags.sort() cmp_tags = (t1_tags > t2_tags) - (t1_tags < t2_tags) sort = reverse_if_descending(cmp_tags) if sort == 0: # Break ties by sorting by title t1_title = task1.get_title() t2_title = task2.get_title() t1_title = locale.strxfrm(t1_title) t2_title = locale.strxfrm(t2_title) cmp_title = (t1_title > t2_title) - (t1_title < t2_title) sort = reverse_if_descending(cmp_title) return sort
def set_complex_title(self, text, tags=[]): if tags: assert (isinstance(tags[0], str)) due_date = Date.no_date() defer_date = Date.no_date() if text: # Get tags in the title for match in extract_tags_from_text(text): tags.append(match) # Get attributes regexp = r'([\s]*)([\w-]+):\s*([^\s]+)' matches = re.findall(regexp, text, re.UNICODE) for spaces, attribute, args in matches: valid_attribute = True if attribute.lower() in ["tags", _("tags"), "tag", _("tag")]: for tag in args.split(","): if not tag.strip() == "@" and not tag.strip() == "": if not tag.startswith("@"): tag = "@" + tag tags.append(tag) elif attribute.lower() in [ "defer", _("defer"), "start", _("start") ]: try: defer_date = Date.parse(args) except ValueError: valid_attribute = False elif attribute.lower() == "due" or \ attribute.lower() == _("due"): try: due_date = Date.parse(args) except: valid_attribute = False else: # attribute is unknown valid_attribute = False if valid_attribute: # remove valid attribute from the task title text = \ text.replace("%s%s:%s" % (spaces, attribute, args), "") for t in tags: self.add_tag(t) if text != "": self.set_title(text.strip()) self.set_to_keep() self.set_due_date(due_date) self.set_start_date(defer_date)
def set_complex_title(self, text, tags=[]): if tags: assert(isinstance(tags[0], str)) due_date = Date.no_date() defer_date = Date.no_date() if text: # Get tags in the title for match in extract_tags_from_text(text): tags.append(match) # Get attributes regexp = r'([\s]*)([\w-]+):\s*([^\s]+)' matches = re.findall(regexp, text, re.UNICODE) for spaces, attribute, args in matches: valid_attribute = True if attribute.lower() in ["tags", _("tags"), "tag", _("tag")]: for tag in args.split(","): if not tag.strip() == "@" and not tag.strip() == "": if not tag.startswith("@"): tag = "@" + tag tags.append(tag) elif attribute.lower() in ["defer", _("defer"), "start", _("start")]: try: defer_date = Date.parse(args) except ValueError: valid_attribute = False elif attribute.lower() == "due" or \ attribute.lower() == _("due"): try: due_date = Date.parse(args) except: valid_attribute = False else: # attribute is unknown valid_attribute = False if valid_attribute: # remove valid attribute from the task title text = \ text.replace("%s%s:%s" % (spaces, attribute, args), "") for t in tags: self.add_tag(t) if text != "": self.set_title(text.strip()) self.set_to_keep() self.set_due_date(due_date) self.set_start_date(defer_date)
def __init__(self): super().__init__() self.__builder = Gtk.Builder() self.__builder.add_from_file(GnomeConfig.CALENDAR_UI_FILE) self.__date_kind = None self.__date = Date.no_date() self.__init_gtk__()
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 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_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 bgcolor(self, node, standard_color): color = self.get_node_bgcolor(node) def __get_active_child_list(node): """ This function recursively fetches a list of all the children of a task which are active (i.e - the subtasks which are not marked as 'Done' or 'Dismissed' """ child_list = [] for child_id in node.children: child = node.req.get_task(child_id) child_list += __get_active_child_list(child) if child.get_status() in [child.STA_ACTIVE]: child_list.append(child_id) return child_list child_list = __get_active_child_list(node) daysleft = None for child_id in child_list: child = self.req.get_task(child_id) if child.get_due_date() == Date.no_date(): continue daysleft_of_child = child.get_due_date().days_left() if daysleft is None: daysleft = daysleft_of_child color = self.get_node_bgcolor(child) elif daysleft_of_child < daysleft: daysleft = daysleft_of_child color = self.get_node_bgcolor(child) return color
def __init__(self): super(GTGCalendar, self).__init__() self.__builder = Gtk.Builder() self.__builder.add_from_file(GnomeConfig.CALENDAR_UI_FILE) self.__date_kind = None self.__date = Date.no_date() self.__init_gtk__()
def task_duedate_column(self, node): # We show the most constraining due date for task with no due dates. if node.get_due_date() == Date.no_date(): return node.get_due_date_constraint().to_readable_string() else: # Other tasks show their due date (which *can* be fuzzy) return node.get_due_date().to_readable_string()
def task_duedate_column(self, node): # We show the most constraining due date for task with no due dates. if node.get_due_date() == Date.no_date(): return node.get_due_date_constraint().to_readable_string() else: # Other tasks show their due date (which *can* be fuzzy) return node.get_due_date().to_readable_string()
def bgcolor(self, node, standard_color): color = self.get_node_bgcolor(node) def __get_active_child_list(node): """ This function recursively fetches a list of all the children of a task which are active (i.e - the subtasks which are not marked as 'Done' or 'Dismissed' """ child_list = [] for child_id in node.children: child = node.req.get_task(child_id) child_list += __get_active_child_list(child) if child.get_status() in [child.STA_ACTIVE]: child_list.append(child_id) return child_list child_list = __get_active_child_list(node) daysleft = None for child_id in child_list: child = self.req.get_task(child_id) if child.get_due_date() == Date.no_date(): continue daysleft_of_child = child.get_due_date().days_left() if daysleft is None: daysleft = daysleft_of_child color = self.get_node_bgcolor(child) elif daysleft_of_child < daysleft: daysleft = daysleft_of_child color = self.get_node_bgcolor(child) return color
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 get_due_date(self): ''' Gets the task due date ''' due = self.rtm_task.due if due == "": return Date.no_date() date = self.__time_rtm_to_datetime(due).date() return Date(date)
def get_due_date(self): ''' Gets the task due date ''' due = self.rtm_task.due if due == "": return Date.no_date() date = self.__time_rtm_to_datetime(due).date() return Date(date)
def _due_within(task, danger_zone): """ Determine if a task is the danger zone. Convention: a danger zone of 1 day includes tasks due today. """ ddate = task.get_due_date() if (ddate != Date.no_date()): if ddate.days_left() < danger_zone: return True return False
def _due_within(task, danger_zone): """ Determine if a task is the danger zone. Convention: a danger zone of 1 day includes tasks due today. """ ddate = task.get_due_date() if (ddate != Date.no_date()): if ddate.days_left() < danger_zone: return True return False
def _populate_evo_task(self, task, evo_task): evo_task.set_summary(task.get_title()) text = task.get_excerpt(strip_tags=True, strip_subtasks=True) if evo_task.get_description() != text: evo_task.set_description(text) due_date = task.get_due_date() if due_date == Date.no_date(): evo_task.set_due(None) else: evo_task.set_due(self.__date_from_gtg_to_evo(due_date)) status = task.get_status() if _EVOLUTION_TO_GTG_STATUS[evo_task.get_status()] != status: evo_task.set_status(_GTG_TO_EVOLUTION_STATUS[status]) # this calls are sometime ignored by evolution. Doing it twice # is a hackish way to solve the problem. (TODO: send bug report) self._evolution_tasks.update_object(evo_task) self._evolution_tasks.update_object(evo_task)
def _populate_evo_task(self, task, evo_task): evo_task.set_summary(task.get_title()) text = task.get_excerpt(strip_tags=True, strip_subtasks=True) if evo_task.get_description() != text: evo_task.set_description(text) due_date = task.get_due_date() if due_date == Date.no_date(): evo_task.set_due(None) else: evo_task.set_due(self.__date_from_gtg_to_evo(due_date)) status = task.get_status() if _EVOLUTION_TO_GTG_STATUS[evo_task.get_status()] != status: evo_task.set_status(_GTG_TO_EVOLUTION_STATUS[status]) # this calls are sometime ignored by evolution. Doing it twice # is a hackish way to solve the problem. (TODO: send bug report) self._evolution_tasks.update_object(evo_task) self._evolution_tasks.update_object(evo_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 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 _populate_task(self, task, evo_task): ''' Updates the attributes of a GTG task copying the ones of an Evolution task ''' task.set_title(evo_task.get_summary()) text = evo_task.get_description() if text is None: text = "" task.set_text(text) due_date_timestamp = evo_task.get_due() if isinstance(due_date_timestamp, (int, float)): due_date = self.__date_from_evo_to_gtg(due_date_timestamp) else: due_date = Date.no_date() task.set_due_date(due_date) status = evo_task.get_status() if task.get_status() != _EVOLUTION_TO_GTG_STATUS[status]: task.set_status(_EVOLUTION_TO_GTG_STATUS[status]) task.set_only_these_tags(extract_tags_from_text(text))
def _populate_task(self, task, evo_task): ''' Updates the attributes of a GTG task copying the ones of an Evolution task ''' task.set_title(evo_task.get_summary()) text = evo_task.get_description() if text is None: text = "" task.set_text(text) due_date_timestamp = evo_task.get_due() if isinstance(due_date_timestamp, (int, float)): due_date = self.__date_from_evo_to_gtg(due_date_timestamp) else: due_date = Date.no_date() task.set_due_date(due_date) status = evo_task.get_status() if task.get_status() != _EVOLUTION_TO_GTG_STATUS[status]: task.set_status(_EVOLUTION_TO_GTG_STATUS[status]) task.set_only_these_tags(extract_tags_from_text(text))
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, gtk_builder): super(GTGCalendar, self).__init__() self.__builder = gtk_builder self.__date_kind = None self.__date = Date.no_date() self.__init_gtk__()
def __init__(self, Gtk_builder): super(GTGCalendar, self).__init__() self.__builder = Gtk_builder self.__date_kind = None self.__date = Date.no_date() self.__init_gtk__()
def populate_from_single_line_of_text(self, text, tags=[]): if tags: assert (isinstance(tags[0], str)) # TODO: We expect one line, without tabs, so... a bit of paranoia for pasted text? # Reduce multiple spaces text = string.replace(text, ' ', ' ').strip() text = string.replace(text, ' ', ' ') if not text: print("populate_from_single_line_of_text: empty/whitespace") return due_date = Date.no_date() defer_date = Date.no_date() # Extract/remove tags from the title, but only if doing so would not make it empty. for match in extract_tags_from_text(text): tags.append(match) without_tag = string.replace(text, match, '') without_tag = string.replace(without_tag, ' ', ' ').strip() if without_tag: text = without_tag # Get key-value attributes of the form "key:value" regexp = r'([\s]*)([\w-]+):\s*([^\s]+)' matches = re.findall(regexp, text, re.UNICODE) for spaces, attribute, args in matches: valid_attribute = True if attribute.lower() in ["tags", _("tags"), "tag", _("tag")]: for tag in args.split(","): if not tag.strip() == "@" and not tag.strip() == "": if not tag.startswith("@"): tag = "@" + tag tags.append(tag) elif attribute.lower() in [ "defer", _("defer"), "start", _("start") ]: try: defer_date = Date.parse(args) except ValueError: valid_attribute = False elif attribute.lower() == "due" or \ attribute.lower() == _("due"): try: due_date = Date.parse(args) except: valid_attribute = False else: # attribute is unknown valid_attribute = False if valid_attribute: # remove valid attribute from the task title text = \ text.replace("%s%s:%s" % (spaces, attribute, args), "") for t in tags: self.add_tag(t) if text != "": self.set_title(text.strip()) self.set_to_keep() self.set_due_date(due_date) self.set_start_date(defer_date) self._has_been_modified()
def get_days_late(self): due_date = self.get_due_date() if due_date == Date.no_date(): return None closed_date = self.get_closed_date() return (closed_date - due_date).days
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_days_late(self): due_date = self.get_due_date() if due_date == Date.no_date(): return None closed_date = self.get_closed_date() return (closed_date - due_date).days