Exemplo n.º 1
0
 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())
Exemplo n.º 2
0
 def test_parse_fuzzy_dates_str(self):
     """ Print fuzzy dates in localized version """
     self.assertEqual(str(Date.parse("now")), _("now"))
     self.assertEqual(str(Date.parse("soon")), _("soon"))
     self.assertEqual(str(Date.parse("later")), _("someday"))
     self.assertEqual(str(Date.parse("someday")), _("someday"))
     self.assertEqual(str(Date.parse("")), "")
Exemplo n.º 3
0
 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())
Exemplo n.º 4
0
    def test_due(self):
        expected1 = {
            'title': 'Do a thing',
            'tags': set(),
            'start': None,
            'due': Date.parse('monday'),
            'recurring': None
        }

        expected2 = {
            'title': 'Do a thing',
            'tags': set(),
            'start': None,
            'due': Date.parse('someday'),
            'recurring': None
        }

        expected3 = {
            'title': 'Do a thing',
            'tags': set(),
            'start': None,
            'due': Date.parse('2099/02/12'),
            'recurring': None
        }

        text1 = 'Do a thing due:monday'
        text2 = 'Do a thing due:monday'
        text3 = 'Do a thing due: monday'
        text4 = 'Do a thing due: someday'
        text5 = 'Do a thing due: 2099/02/12'

        self.assertEqual(expected1, parse(text1))
        self.assertEqual(expected1, parse(text2))
        self.assertEqual(expected1, parse(text3))
        self.assertEqual(expected3, parse(text5))
Exemplo n.º 5
0
def parse(text: str) -> Dict:
    """Parse contents of the quick add input."""

    result = {
        'title': '',
        'tags': set(),
        'start': None,
        'due': None,
        'recurring': None
    }

    for match in re.finditer(TAG_REGEX, text):
        data = match.group(0)
        result['tags'].add(data[1:])

    for match in re.finditer(TOKEN_REGEX, text):
        token = match.group(2)
        data = match.group(3)
        matched = False

        if token in TAGS_TOKEN:
            for tag in data.split(','):
                if tag:
                    # Strip @
                    if tag.startswith('@'):
                        tag = tag[1:]

                    result['tags'].add(tag)

            matched = True

        elif token in START_TOKEN:
            try:
                result['start'] = Date.parse(data)
                matched = True
            except ValueError:
                pass

        elif token in DUE_TOKEN:
            try:
                result['due'] = Date.parse(data)
                matched = True
            except ValueError:
                pass

        elif token in REPEAT_TOKEN:
            try:
                Date.today().parse_from_date(data)
                result['recurring'] = data
                matched = True
            except ValueError:
                pass

        # Remove this part from the title
        if matched:
            text = text.replace(match[0], '')

    result['title'] = text
    return result
Exemplo n.º 6
0
    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(f"{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)
Exemplo n.º 7
0
def parse(text: str) -> Dict:
    """Parse contents of the quick add input."""

    result = {
        'title': '',
        'tags': set(),
        'start': None,
        'due': None,
        'recurring': None
    }

    for match in re.finditer(TAG_REGEX, text):
        data = match.group(0)
        result['tags'].add(data[1:])

    for match in re.finditer(TOKEN_REGEX, text):
        token = match.group(2)
        data = match.group(3)

        if token in TAGS_TOKEN:
            for t in data.split(','):
                # Strip @
                if t.startswith('@'):
                    t = t[1:]

                result['tags'].add(t)

        elif token in START_TOKEN:
            try:
                result['start'] = Date.parse(data)
            except ValueError:
                continue

        elif token in DUE_TOKEN:
            try:
                result['due'] = Date.parse(data)
            except ValueError:
                continue

        elif token in REPEAT_TOKEN:
            try:
                Date.today().parse_from_date(data)
                result['recurring'] = data
            except ValueError:
                continue

        # Remove this part from the title
        text = text[:match.start()] + text[match.end():]

    result['title'] = text
    return result
Exemplo n.º 8
0
    def date_focus_out(self, widget, event, date_kind):
        try:
            datetoset = Date.parse(widget.get_text())
        except ValueError:
            datetoset = None

        if datetoset is not None:
            # TODO: New Core
            t = self.app.ds.tasks.get(self.task.tid)

            if date_kind == GTGCalendar.DATE_KIND_START:
                self.task.set_start_date(datetoset)
                t.date_start = datetoset
                self.start_popover.popdown()

            elif date_kind == GTGCalendar.DATE_KIND_DUE:
                self.task.set_due_date(datetoset)
                t.date_due = datetoset
                self.due_popover.popdown()

            elif date_kind == GTGCalendar.DATE_KIND_CLOSED:
                self.task.set_closed_date(datetoset)
                t.date_closed = datetoset
                self.closed_popover.popdown()

            self.refresh_editor()
Exemplo n.º 9
0
    def test_missing_year_next_year(self):
        """ Parsing %m%d have to find correct date:
        we enter a day the next year """
        aday = date.today()
        if aday.day == 1 and aday.month == 1:
            # not possible to add a day next year
            return

        aday = aday.replace(year=aday.year + 1, month=1, day=1)
        self.assertEqual(Date.parse("0101"), aday)
Exemplo n.º 10
0
    def test_on_certain_day(self):
        """ Parse due:3 as 3rd day this month or next month
        if it is already more or already 3rd day """
        for i in range(28):
            i += 1
            aday = date.today()
            if i <= aday.day:
                aday = next_month(aday, i)
            else:
                aday = aday.replace(day=i)

            self.assertEqual(Date.parse(str(i)), aday)
Exemplo n.º 11
0
    def date_changed(self, widget, data):
        try:
            Date.parse(widget.get_text())
            valid = True
        except ValueError:
            valid = False

        if valid:
            # If the date is valid, we write with default color in the widget
            # "none" will set the default color.
            widget.override_color(Gtk.StateType.NORMAL, None)
            widget.override_background_color(Gtk.StateType.NORMAL, None)
        else:
            # We should write in red in the entry if the date is not valid
            text_color = Gdk.RGBA()
            text_color.parse("#F00")
            widget.override_color(Gtk.StateType.NORMAL, text_color)

            bg_color = Gdk.RGBA()
            bg_color.parse("#F88")
            widget.override_background_color(Gtk.StateType.NORMAL, bg_color)
Exemplo n.º 12
0
    def ModifyTask(self, tid, task_data):
        """
        Updates the task with ID tid using the provided information
        in the task_data structure.  Note that any fields left blank
        or undefined in task_data will clear the value in the task,
        so the best way to update a task is to first retrieve it via
        get_task(tid), modify entries as desired, and send it back
        via this function.
        """
        task = self.req.get_task(tid)
        task.set_status(task_data["status"],
                        donedate=Date.parse(task_data["donedate"]))
        task.set_title(task_data["title"])
        task.set_due_date(Date.parse(task_data["duedate"]))
        task.set_start_date(Date.parse(task_data["startdate"]))
        task.set_text(task_data["text"])

        for tag in task_data["tags"]:
            task.add_tag(tag)
        for sub in task_data["subtask"]:
            task.add_child(sub)
        return task_to_dict(task)
Exemplo n.º 13
0
 def NewTask(self, status, title, duedate, startdate, donedate, tags,
             text, subtasks):
     """
     Generate a new task object and return the task data as a dict
     @param status:     One of 'Active', 'Dismiss', or 'Done'
     @param title:      String name of the task
     @param duedate:    Date the task is due, such as "2010-05-01".
      also supports 'now', 'soon', 'someday'
     @param startdate:  Date the task will be started
     @param donedate:   Date the task was finished
     @param tags:       List of strings for tags to apply for this task
     @param text:       String description
     @param subtasks:   A list of task ids of tasks to add as children
     @return: A dictionary with the data of the newly created task
     """
     nt = self.req.new_task(tags=tags)
     for sub in subtasks:
         nt.add_child(sub)
     nt.set_status(status, donedate=Date.parse(donedate))
     nt.set_title(title)
     nt.set_due_date(Date.parse(duedate))
     nt.set_start_date(Date.parse(startdate))
     nt.set_text(text)
     return task_to_dict(nt)
Exemplo n.º 14
0
    def test_parse_week_days(self):
        """ Parse name of week days and don't care about case-sensitivity """
        weekday = date.today().weekday()
        for i, day in enumerate(['Monday', 'Tuesday', 'Wednesday',
                                 'Thursday', 'Friday', 'Saturday', 'Sunday']):
            if i <= weekday:
                expected = date.today() + timedelta(7 + i - weekday)
            else:
                expected = date.today() + timedelta(i - weekday)

            self.assertEqual(Date.parse(day), expected)
            self.assertEqual(Date.parse(day.lower()), expected)
            self.assertEqual(Date.parse(day.upper()), expected)

            # Test localized version
            day = _(day)
            self.assertEqual(Date.parse(day), expected)
            self.assertEqual(Date.parse(day.lower()), expected)
            self.assertEqual(Date.parse(day.upper()), expected)
Exemplo n.º 15
0
def task_from_xml(task, xmlnode):
    # print "********************************"
    # print xmlnode.toprettyxml()

    task.set_uuid(xmlnode.getAttribute("uuid"))
    task.set_title(read_node(xmlnode, "title"))

    status = xmlnode.getAttribute("status")
    donedate = Date.parse(read_node(xmlnode, "donedate"))
    task.set_status(status, donedate=donedate)


    duedate = Date(read_node(xmlnode, "duedate"))
    task.set_due_date(duedate)

    startdate = Date(read_node(xmlnode, "startdate"))
    task.set_start_date(startdate)

    modified = read_node(xmlnode, "modified")
    if modified != "":
        modified = datetime.strptime(modified, "%Y-%m-%dT%H:%M:%S")
        task.set_modified(modified)

    added = read_node(xmlnode, "addeddate")
    if added:
        added = datetime.strptime(added, "%Y-%m-%dT%H:%M:%S")
        task.set_added_date(added)

    tags = xmlnode.getAttribute("tags").replace(' ', '')
    tags = (tag for tag in tags.split(',') if tag.strip() != "")
    for tag in tags:
        # FIXME why unescape????
        task.tag_added(saxutils.unescape(tag))

    # FIXME why we need to convert that through an XML?
    content = read_node(xmlnode, "content")
    if content != "":
        content = f"<content>{content}</content>"
        content = minidom.parseString(content).firstChild.toxml()
        task.set_text(content)

    for subtask in xmlnode.getElementsByTagName("subtask"):
        task.add_child(get_text(subtask))

    for attr in xmlnode.getElementsByTagName("attribute"):
        if len(attr.childNodes) > 0:
            value = get_text(attr)
        else:
            value = ""
        key = attr.getAttribute("key")
        namespace = attr.getAttribute("namespace")
        task.set_attribute(key, value, namespace=namespace)

    # FIXME do we need remote task ids? I don't think so
    # FIXME if so => rework them into a more usable structure!!!
    #                (like attributes)
    # REMOTE TASK IDS
    """
    remote_ids_list = xmlnode.getElementsByTagName("task-remote-ids")
    for remote_id in remote_ids_list:
        if remote_id.childNodes:
            node = remote_id.childNodes[0]
            backend_id = node.firstChild.nodeValue
            remote_task_id = node.childNodes[1].firstChild.nodeValue
            task.add_remote_id(backend_id, remote_task_id)
            """

    return task
Exemplo n.º 16
0
def task_from_element(task, element: etree.Element):
    """Populate task from XML element."""

    task.set_title(element.find('title').text)
    task.set_uuid(element.get('id'))

    dates = element.find('dates')

    modified = dates.find('modified').text
    task.set_modified(datetime.fromisoformat(modified))

    added = dates.find('added').text
    task.set_added_date(datetime.fromisoformat(added))

    # Dates
    try:
        done_date = Date.parse(dates.find('done').text)
        task.set_status(element.attrib['status'], donedate=done_date)
    except AttributeError:
        pass

    try:
        due_date = Date.parse(dates.find('due').text)
        task.set_due_date(due_date)
    except AttributeError:
        pass

    try:
        start = dates.find('start').text
        task.set_start_date(start)
    except (AttributeError, TypeError):
        pass

    # Recurring tasks
    recurring = element.find('recurring')
    recurring_enabled = recurring.get('enabled')

    try:
        recurring_term = element.find('term').text
        task.set_recurring(recurring_enabled == 'true',
                           None if recurring_term == 'None'
                           else recurring_term)

    except AttributeError:
        pass

    taglist = element.find('tags')

    if taglist is not None:
        [task.tag_added_by_id(t.text) for t in taglist.iter('tag')]

    # Content
    content = element.find('content').text or ''

    content = content.replace(']]&gt;', ']]>')
    task.set_text(content)

    # Subtasks
    subtasks = element.find('subtasks')

    for sub in subtasks.findall('sub'):
        task.add_child(sub.text)

    return task
Exemplo n.º 17
0
    def refresh_editor(self, title=None, refreshtext=False):
        if self.window is None:
            return
        to_save = False
        # title of the window
        if title:
            self.window.set_title(title)
            to_save = True
        else:
            self.window.set_title(self.task.get_title())

        status = self.task.get_status()
        if status == Task.STA_DISMISSED:
            self.donebutton.show()
            self.undonebutton.hide()
            self.dismissbutton.hide()
            self.undismissbutton.show()
        elif status == Task.STA_DONE:
            self.donebutton.hide()
            self.undonebutton.show()
            self.dismissbutton.show()
            self.undismissbutton.hide
        else:
            self.donebutton.show()
            self.undonebutton.hide()
            self.dismissbutton.show()
            self.undismissbutton.hide()

        # Refreshing the parent button
        if self.task.has_parent():
            # Translators: Button label to open the parent task
            self.parent_button.set_label(_('Open Parent'))
        else:
            # Translators: Button label to add an new parent task
            self.parent_button.set_label(_('Add Parent'))

        # Refreshing the status bar labels and date boxes
        if status in [Task.STA_DISMISSED, Task.STA_DONE]:
            self.builder.get_object("start_box").hide()
            self.builder.get_object("closed_box").show()
        else:
            self.builder.get_object("closed_box").hide()
            self.builder.get_object("start_box").show()

        # refreshing the start date field
        startdate = self.task.get_start_date()
        try:
            prevdate = Date.parse(self.start_entry.get_text())
            update_date = startdate != prevdate
        except ValueError:
            update_date = True

        if update_date:
            self.start_entry.set_text(str(startdate))

        # refreshing the due date field
        duedate = self.task.get_due_date()
        try:
            prevdate = Date.parse(self.due_entry.get_text())
            update_date = duedate != prevdate
        except ValueError:
            update_date = True

        if update_date:
            self.due_entry.set_text(str(duedate))

        # refreshing the closed date field
        closeddate = self.task.get_closed_date()
        prevcldate = Date.parse(self.closed_entry.get_text())
        if closeddate != prevcldate:
            self.closed_entry.set_text(str(closeddate))

        # refreshing the day left label
        """
        TODO(jakubbrindza): re-enable refreshing the day left.
        We need to come up how and where this information is viewed
        in the editor window.
        """
        # self.refresh_day_left()

        if refreshtext:
            self.textview.modified(refresheditor=False)
        if to_save:
            self.light_save()
Exemplo n.º 18
0
 def test_missing_year_this_year(self):
     """ Parsing %m%d have to find correct date:
     we enter a day this year """
     aday = next_month(date.today(), day=1)
     parse_string = "%02d%02d" % (aday.month, aday.day)
     self.assertEqual(Date.parse(parse_string), aday)
Exemplo n.º 19
0
 def test_parses_todays_month_day_format(self):
     today = date.today()
     parse_string = "%02d%02d" % (today.month, today.day)
     self.assertEqual(Date.parse(parse_string), today)
Exemplo n.º 20
0
 def test_parses_common_formats(self):
     self.assertEqual(str(Date.parse("1985-03-29")), "1985-03-29")
     self.assertEqual(str(Date.parse("19850329")), "1985-03-29")
     self.assertEqual(str(Date.parse("1985/03/29")), "1985-03-29")
Exemplo n.º 21
0
def parse_search_query(query):
    """ Parse query into parameters for search filter

    If query is not correct, exception InvalidQuery is raised.
    """

    if len(query.strip()) == 0:
        raise InvalidQuery("Query is empty")

    if query.count('"') % 2 != 0:
        raise InvalidQuery("Query has odd number of quotes")

    commands = []

    not_count, after_or = 0, False
    require_date = None
    for token, value in _tokenize_query(query):
        cmd = None

        if require_date:
            if token not in ['date', 'word', 'literal']:
                raise InvalidQuery(
                    f"Unexpected token '{token}' after '{require_date}'")

            value = value.strip('"')
            try:
                date = Date.parse(value)
            except ValueError:
                raise InvalidQuery(f"Date '{value}' in wrong format")

            cmd = (require_date, not_count % 2 == 0, date)
            require_date = None

        elif token == 'command':
            value = value.lower()[1:]

            found = False
            for keyword in KEYWORDS:
                if value not in KEYWORDS[keyword]:
                    continue

                if keyword == 'not':
                    not_count += 1
                elif keyword == 'or':
                    if not_count > 0:
                        raise InvalidQuery("!or cann't follow !not")

                    if commands == []:
                        raise InvalidQuery(
                            "Or is not allowed at the beginning of query")

                    if commands[-1][0] != "or":
                        commands.append(("or", True, [commands.pop()]))

                    after_or = True
                elif keyword in ['after', 'before']:
                    require_date = keyword
                else:
                    cmd = (keyword, not_count % 2 == 0)
                found = True
                break
            if not found:
                raise InvalidQuery(f"Unknown command !{value}")

        elif token == 'tag':
            cmd = (token, not_count % 2 == 0, value)
        elif token in ['literal', 'word']:
            cmd = ('word', not_count % 2 == 0, value.strip('"').lower())

        if cmd is not None:
            if after_or:
                commands[-1][2].append(cmd)
            else:
                commands.append(cmd)

            not_count, after_or = 0, False

    if not_count > 0:
        raise InvalidQuery("Query cannot end with !not (Forgot something?)")

    if after_or:
        raise InvalidQuery("Or is not allowed at the end of query")

    if require_date:
        raise InvalidQuery(f"Required date after '{require_date}'")

    return {'q': commands}
Exemplo n.º 22
0
    def from_xml(self, xml: Element, tag_store: TagStore) -> None:
        """Load up tasks from a lxml object."""

        elements = list(xml.iter(self.XML_TAG))

        for element in elements:
            tid = element.get('id')
            title = element.find('title').text
            status = element.get('status')

            task = Task2(id=tid, title=title)

            dates = element.find('dates')

            modified = dates.find('modified').text
            task.date_modified = Date(
                datetime.datetime.fromisoformat(modified))

            added = dates.find('added').text
            task.date_added = Date(datetime.datetime.fromisoformat(added))

            if status == 'Done':
                task.status = Status.DONE
            elif status == 'Dismissed':
                task.status = Status.DISMISSED

            # Dates
            try:
                closed = Date.parse(dates.find('done').text)
                task.date_closed = closed
            except AttributeError:
                pass

            fuzzy_due_date = Date.parse(dates.findtext('fuzzyDue'))
            due_date = Date.parse(dates.findtext('due'))

            if fuzzy_due_date:
                task._date_due = fuzzy_due_date
            elif due_date:
                task._date_due = due_date

            fuzzy_start = dates.findtext('fuzzyStart')
            start = dates.findtext('start')

            if fuzzy_start:
                task.date_start = Date(fuzzy_start)
            elif start:
                task.date_start = Date(start)

            taglist = element.find('tags')

            if taglist is not None:
                for t in taglist.iter('tag'):
                    try:
                        tag = tag_store.get(t.text)
                        task.tags.append(tag)
                    except KeyError:
                        pass

            # Content
            content = element.find('content').text or ''
            content = content.replace(']]&gt;', ']]>')
            task.content = content

            self.add(task)

            log.debug('Added %s', task)

        # All tasks have been added, now we parent them
        for element in elements:
            parent_tid = element.get('id')
            subtasks = element.find('subtasks')

            for sub in subtasks.findall('sub'):
                self.parent(sub.text, parent_tid)
Exemplo n.º 23
0
def task_from_element(task, element: etree.Element):
    """Populate task from XML element."""

    task.set_uuid(element.get('uuid'))
    task.set_title(element.find('title').text)

    # Dates
    try:
        done_date = Date.parse(element.find('donedate').text)
        task.set_status(element.attrib['status'], donedate=done_date)
    except AttributeError:
        pass

    try:
        due_date = Date.parse(element.find('duedate').text)
        task.set_due_date(due_date)
    except AttributeError:
        pass

    try:
        modified = element.find('modified').text
        modified = datetime.strptime(modified, "%Y-%m-%dT%H:%M:%S")
        task.set_modified(modified)
    except AttributeError:
        pass

    try:
        added = element.find('addeddate').text
        added = datetime.strptime(added, "%Y-%m-%dT%H:%M:%S")
        task.set_added_date(added)
    except AttributeError:
        pass

    try:
        start = element.find('startdate').text
        task.set_start_date(start)
    except (AttributeError, TypeError):
        pass

    # Task Tags
    tags = element.get('tags')
    tags = [t for t in tags.split(',') if t.strip() != '']

    [task.tag_added(t) for t in tags]

    try:
        content = element.find('content').text

        # Content includes serialized xml, so it needs to be re-serialized
        # and then deserialized again.
        if content:
            content = f"<content>{content}</content>"
            task.set_text(content)

    except AttributeError:
        # Some tasks might not have a content tag
        pass

    # Subtasks
    [task.add_child(sub.text) for sub in element.findall('subtask')]

    return task