Esempio n. 1
0
 def set_gtg(self, todo: iCalendar, task: Task,
             namespace: str = None) -> None:
     remote_tags = [self.to_tag(categ) for categ in self.get_dav(todo)]
     local_tags = set(tag_name for tag_name in super().get_gtg(task))
     for to_add in set(remote_tags).difference(local_tags):
         task.add_tag(to_add)
     for to_delete in local_tags.difference(remote_tags):
         task.remove_tag(to_delete)
     task.tags.sort(key=remote_tags.index)
Esempio n. 2
0
    def test_due_date_caldav_restriction(self):
        task = Task('uid', Mock())
        later = datetime(2021, 11, 24, 21, 52, 45)
        before = later - timedelta(days=1)
        task.set_start_date(later)
        task.set_due_date(before)
        field = DueDateField('due', 'get_due_date_constraint', 'set_due_date')
        self.assertEqual(later, field.get_gtg(task, '').dt_value)

        task.set_start_date(before)
        task.set_due_date(later)
        self.assertEqual(later, field.get_gtg(task, '').dt_value)
Esempio n. 3
0
 def get_gtg(self, task: Task, namespace: str = None):
     parents = task.get_parents()
     if not parents or not parents[0]:
         return
     parent = task.req.get_task(parents[0])
     uid = UID_FIELD.get_gtg(task, namespace)
     return parent.get_child_index(uid)
Esempio n. 4
0
 def _set_task(self, task: Task) -> None:
     logger.debug('set_task todo for %r', task.get_uuid())
     with DisabledSyncCtx(task, sync_on_exit=False):
         seq_value = SEQUENCE.get_gtg(task, self.namespace)
         SEQUENCE.write_gtg(task, seq_value + 1, self.namespace)
     todo, calendar = self._get_todo_and_calendar(task)
     if not calendar:
         logger.info("%r has no calendar to be synced with", task)
         return
     if todo and todo.parent.url != calendar.url:  # switch calendar
         self._remove_todo(UID_FIELD.get_dav(todo), todo)
         self._create_todo(task, calendar)
     elif todo:  # found one, saving it
         if not Translator.should_sync(task, self.namespace, todo):
             logger.debug('insufficient change, ignoring set_task call')
             return
         # updating vtodo content
         Translator.fill_vtodo(task, calendar.name, self.namespace,
                               todo.instance.vtodo)
         logger.info('SYNCING updating todo %r', todo)
         try:
             todo.save()
         except caldav.lib.error.DAVError:
             logger.exception(
                 'Something went wrong while updating '
                 '%r => %r', task, todo)
     else:  # creating from task
         self._create_todo(task, calendar)
Esempio n. 5
0
    def task_factory(self, tid, newtask=False):
        """
        Instantiates the given task id as a Task object.

        @param tid: a task id. Must be unique
        @param newtask: True if the task has never been seen before
        @return Task: a Task instance
        """
        return Task(tid, self.requester, newtask)
Esempio n. 6
0
 def fill_task(cls, todo: iCalendar, task: Task, namespace: str):
     nmspc = {'namespace': namespace}
     with DisabledSyncCtx(task):
         for field in cls.fields:
             field.set_gtg(todo, task, **nmspc)
         task.set_attribute("url", str(todo.url), **nmspc)
         task.set_attribute("calendar_url", str(todo.parent.url), **nmspc)
         task.set_attribute("calendar_name", todo.parent.name, **nmspc)
         if not CATEGORIES.has_calendar_tag(task, todo.parent):
             task.add_tag(CATEGORIES.get_calendar_tag(todo.parent))
     return task
Esempio n. 7
0
 def _get_todo_and_calendar(self, task: Task):
     """For a given task, try to get the todo out of the cache and figures
     out its calendar if one is linked to it"""
     todo, calendar = self._cache.get_todo(UID_FIELD.get_gtg(task)), None
     # lookup by task
     for __, calendar in self._cache.calendars:
         if CATEGORIES.has_calendar_tag(task, calendar):
             logger.debug('Found from task tag %r and %r', todo, calendar)
             return todo, calendar
     cname = task.get_attribute('calendar_name', namespace=self.namespace)
     curl = task.get_attribute("calendar_url", namespace=self.namespace)
     if curl or cname:
         calendar = self._cache.get_calendar(name=cname, url=curl)
         if calendar:
             logger.debug('Found from task attr %r and %r', todo, calendar)
             return todo, calendar
     if todo and getattr(todo, 'parent', None):
         logger.debug('Found from todo %r and %r', todo, todo.parent)
         return todo, todo.parent
     return None, None
Esempio n. 8
0
 def test_translate_from_vtodo(self):
     DESCRIPTION = Translator.fields[1]
     self.assertEqual(DESCRIPTION.dav_name, 'description')
     todo = self._get_todo(VTODO_GRAND_CHILD)
     self.assertEqual(todo.instance.vtodo.serialize(), VTODO_GRAND_CHILD)
     self.assertEqual(date(2020, 12, 24),
                      todo.instance.vtodo.contents['due'][0].value)
     uid = UID_FIELD.get_dav(todo)
     self.assertTrue(isinstance(uid, str), f"should be str is {uid!r}")
     self.assertEqual(uid, UID_FIELD.get_dav(vtodo=todo.instance.vtodo))
     task = Task(uid, Mock())
     Translator.fill_task(todo, task, NAMESPACE)
     self.assertEqual('date', task.get_due_date().accuracy.value)
     vtodo = Translator.fill_vtodo(task, todo.parent.name, NAMESPACE)
     for field in Translator.fields:
         if field.dav_name in DAV_IGNORE:
             continue
         self.assertTrue(field.is_equal(task, NAMESPACE, todo))
         self.assertTrue(field.is_equal(task, NAMESPACE, vtodo=vtodo.vtodo))
     vtodo.vtodo.contents['description'][0].value = 'changed value'
     self.assertTrue(
         DESCRIPTION.is_equal(task, NAMESPACE, todo), 'same '
         'hashes should prevent changes on vTodo to be noticed')
     task.set_text(task.get_text() + 'more content')
     self.assertFalse(DESCRIPTION.is_equal(task, NAMESPACE, todo))
Esempio n. 9
0
    def _extract_plain_text(self, task: Task) -> str:
        """Will extract plain text from task content, replacing subtask
        referenced in the text by their proper titles"""
        result, content = '', task.get_text()
        for line_no, line in enumerate(content.splitlines()):
            for tag in self.XML_TAGS:
                while tag in line:
                    line = line.replace(tag, '')

            if line_no == 0:  # is first line, striping all tags on first line
                new_line = self.__clean_first_line(line)
                if new_line:
                    result += new_line + '\n'
            elif line.startswith('{!') and line.endswith('!}'):
                subtask = task.req.get_task(line[2:-2].strip())
                if not subtask:
                    continue
                if subtask.get_status() == Task.STA_DONE:
                    result += f"[x] {subtask.get_title()}\n"
                else:
                    result += f"[ ] {subtask.get_title()}\n"
            else:
                result += line.strip() + '\n'
        return result.strip()
Esempio n. 10
0
 def test_translate_from_task(self):
     now, today = datetime.now(), date.today()
     task = Task('uuid', Mock())
     task.set_title('holy graal')
     task.set_text('the knights who says ni')
     task.set_recurring(True, 'other-day')
     task.set_start_date(today)
     task.set_due_date('soon')
     task.set_closed_date(now)
     vtodo = Translator.fill_vtodo(task, 'My Calendar Name', NAMESPACE)
     for field in Translator.fields:
         self.assertTrue(field.is_equal(task, NAMESPACE, vtodo=vtodo.vtodo),
                         f'{field!r} has differing values')
     serialized = vtodo.serialize()
     self.assertTrue(
         f"DTSTART;VALUE=DATE:{today.strftime('%Y%m%d')}" in serialized,
         f"missing from {serialized}")
     self.assertTrue(re.search(r"COMPLETED:[0-9]{8}T[0-9]{6}Z", serialized),
                     f"missing from {serialized}")
     self.assertTrue("DUE;GTGFUZZY=soon" in serialized,
                     f"missing from {serialized}")
     # trying to fill utc only with fuzzy
     task.set_closed_date('someday')
     vtodo = Translator.fill_vtodo(task, 'My Calendar Name', NAMESPACE)
     serialized = vtodo.serialize()
     self.assertTrue("COMPLETED;GTGFUZZY=someday:" in serialized,
                     f"missing from {serialized}")
     # trying to fill utc only with date
     task.set_closed_date(today)
     vtodo = Translator.fill_vtodo(task, 'My Calendar Name', NAMESPACE)
     serialized = vtodo.serialize()
     today_in_utc = now.replace(hour=0, minute=0, second=0)\
         .replace(tzinfo=LOCAL_TIMEZONE).astimezone(UTC)\
         .strftime('%Y%m%dT%H%M%SZ')
     self.assertTrue(f"COMPLETED:{today_in_utc}" in serialized,
                     f"missing {today_in_utc} from {serialized}")
     # emptying date by setting None or no_date
     task.set_closed_date(Date.no_date())
     task.set_due_date(None)
     task.set_start_date('')
     vtodo = Translator.fill_vtodo(task, 'My Calendar Name', NAMESPACE)
     serialized = vtodo.serialize()
     self.assertTrue("CATEGORIES:" not in serialized)
     self.assertTrue("COMPLETED:" not in serialized)
     self.assertTrue("DUE:" not in serialized)
     self.assertTrue("DTSTART:" not in serialized)
Esempio n. 11
0
 def get_gtg(self, task: Task, namespace: str = None) -> tuple:
     return task.get_recurring(), task.get_recurring_term()
Esempio n. 12
0
 def write_gtg(self, task: Task, value, namespace: str = None):
     hash_, text = value
     if hash_ and hash_ == self._get_content_hash(task.get_text()):
         logger.debug('not writing %r from vtodo, hash matches', task)
         return
     return super().write_gtg(task, text)
Esempio n. 13
0
 def write_gtg(self, task: Task, value, namespace: str = None):
     task.set_attribute(self.dav_name, value, namespace=namespace)
Esempio n. 14
0
 def get_gtg(self, task: Task, namespace: str = None) -> str:
     return task.get_attribute(self.dav_name, namespace=namespace)
Esempio n. 15
0
 def has_calendar_tag(self, task: Task, calendar: iCalendar) -> bool:
     return self.get_calendar_tag(calendar) in task.get_tags_name()
Esempio n. 16
0
 def _browse_subtasks(cls, task: Task):
     yield task
     for subtask in task.get_subtasks():
         yield from cls._browse_subtasks(subtask)