karlicoss / orgparse

Python module for reading Emacs org-mode files
https://orgparse.readthedocs.org
BSD 2-Clause "Simplified" License
372 stars 43 forks source link

Support parsing timestamp cookies (i.e., repeated tasks, and warnings) #50

Closed j3soon closed 2 years ago

j3soon commented 2 years ago

This pull request solves issue #15

Format Definition in the OrgMode manual

Description

Unsupported format examples

These edge cases isn't supported by OrgMode, so we don't need to support them:

<2022-03-07 Mon 02:00 +1w>--<2022-03-07 Mon 04:00 +1w>
<2022-03-07 Mon 02:00 +1w>--<2022-03-07 Mon 04:00>
<2022-03-07 Mon 02:00>--<2022-03-07 Mon 04:00 +1w>

[Further Improvements] Use OrgDate as an iterator?

Here are some test cases:

def test_date_iterator() -> None:
    testcases = [
        ('<2022-03-07 Mon 02:00 +1h>', '<2022-03-07 Mon 03:00 +1h>'),
        ('<2022-03-07 Mon +1d>', '<2022-03-08 Tue +1d>'),
        ('<2022-03-07 Mon +1w>', '<2022-03-14 Mon +1w>'),
        ('<2022-03-07 Mon +1m>', '<2022-04-07 Thu +1m>'),
        ('<2022-03-31 Thu +1m>', '<2022-05-01 Sun +1m>'),
        ('<2022-03-07 Mon +1y>', '<2023-03-07 Tue +1y>'),
        ('<2024-02-29 Thu +1y>', '<2025-03-01 Sat +1y>'),
    ]
    for (input, expected) in testcases:
        root = loads(input)
        output = root[0].datelist[0]
        assert str(output) == input
        assert str(next(output)) == expected

We may add something like this in OrgDate:

def __iter__(self):
    return self

def __next__(self):
    if self._repeater is not None:
        odate = OrgDate(self._start, self._end, self._active,
                        self._repeater, self._warning)
        num = self._repeater[1]
        interval = self._repeater[2]
        d = {'h': 'hours', 'd': 'days', 'w': 'weeks', 'm': 'months', 'y': 'years'}
        timedelta = datetime.timedelta(**{d[interval]: num})
        odate._start += timedelta
        if odate._end:
            odate._end += timedelta
        return odate
    else:
        raise StopIteration

However, datetime.timedelta does not support months and years. We may use python-dateutil for this, but it'll introduce an extra external dependency. Implement the behavior by hand is error-prone, so we may discuss how to implement this in the future.

j3soon commented 2 years ago

Oh, I didn't think of using helper functions to extract datetime and timedelta from OrgDate. Using helper functions are much more straightforward and cleaner than the iterator solution.

To make the OrgDate class more accessible, maybe we can add the following features in the future:

  1. Export OrgDate in __init__.py (users can import the class instead of copying the code as mentioned in #14)
  2. Add member (helper) functions to extract datetime and timedelta from OrgDate. (improve the user experience of #15)