Changeset - 7335282e5a64
[Not reviewed]
0 3 0
Brett Smith - 3 years ago 2021-03-11 18:52:31
brettcsmith@brettcsmith.org
rtutil: Add RTDateTime class.

See comments for rationale.
3 files changed with 58 insertions and 0 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/rtutil.py
Show inline comments
...
 
@@ -2,28 +2,31 @@
 
# Copyright © 2020  Brett Smith
 
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
 
#
 
# Full copyright and licensing details can be found at toplevel file
 
# LICENSE.txt in the repository.
 

	
 
import datetime
 
import functools
 
import logging
 
import mimetypes
 
import os
 
import re
 
import sqlite3
 
import urllib.parse as urlparse
 

	
 
import dateutil.parser
 
import rt
 

	
 
from pathlib import Path
 

	
 
from . import data
 
from beancount.core import data as bc_data
 

	
 
from typing import (
 
    cast,
 
    overload,
 
    Callable,
 
    Iterable,
 
    Iterator,
 
    MutableMapping,
 
    Optional,
...
 
@@ -37,12 +40,34 @@ from .beancount_types import (
 

	
 
RTId = Union[int, str]
 
TicketAttachmentIds = Tuple[str, Optional[str]]
 
_LinkCache = MutableMapping[TicketAttachmentIds, Optional[str]]
 
_URLLookup = Callable[..., Optional[str]]
 

	
 
class RTDateTime(datetime.datetime):
 
    """Construct datetime objects from strings returned by RT
 

	
 
    Typical usage looks like::
 

	
 
        ticket = rt_client.get_ticket(...)
 
        created = RTDateTime(ticket.get('Created'))
 
    """
 
    # Normally I'd just write a function to do this, but having a dedicated
 
    # class helps support query-report: the class can pull double duty to both
 
    # parse the data from RT, and determine proper output formatting.
 
    # The RT REST API returns datetimes in the user's configured timezone, and
 
    # there doesn't seem to be any API call that tells you what that is. You
 
    # have to live with the object being timezone-naive.
 
    def __new__(cls, source: str) -> 'RTDateTime':
 
        if not source or source == 'Not set':
 
            retval = datetime.datetime.min
 
        else:
 
            retval = dateutil.parser.parse(source)
 
        return cast(RTDateTime, retval)
 

	
 

	
 
class RTLinkCache(_LinkCache):
 
    """Cache RT links to disk
 

	
 
    This class provides a dict-like interface to a cache of RT links.
 
    Once an object is in RT, a link to it should never change.
 
    The only exception is when objects get shredded, and those objects
setup.py
Show inline comments
...
 
@@ -14,12 +14,13 @@ setup(
 
        'babel>=2.6',  # Debian:python3-babel
 
        'beancount>=2.2',  # Debian:beancount
 
        'GitPython>=2.0',  # Debian:python3-git
 
        # 1.4.1 crashes when trying to save some documents.
 
        'odfpy>=1.4.0,!=1.4.1',  # Debian:python3-odf
 
        'pdfminer.six>=20200101',
 
        'python-dateutil>=2.7',  # Debian:python3-dateutil
 
        'PyYAML>=3.0',  # Debian:python3-yaml
 
        'regex',  # Debian:python3-regex
 
        'rt>=2.0',
 
    ],
 
    setup_requires=[
 
        'pytest-mypy',
tests/test_rtutil.py
Show inline comments
...
 
@@ -3,12 +3,13 @@
 
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
 
#
 
# Full copyright and licensing details can be found at toplevel file
 
# LICENSE.txt in the repository.
 

	
 
import contextlib
 
import datetime
 
import itertools
 
import logging
 
import re
 

	
 
import pytest
 

	
...
 
@@ -284,6 +285,37 @@ def test_txn_with_urls_with_fmts(rt):
 
    statement_path = EXPECTED_URLS_MAP[(1, 4)]
 
    assert actual.meta['statement'] == ' '.join([
 
        '(rt:1/99)',
 
        f'<{DEFAULT_RT_URL}{statement_path}>',
 
        '[stmt.txt]',
 
    ])
 

	
 
@pytest.mark.parametrize('arg,exp_num,exp_offset', [
 
    # These correspond to the different datetime formats available through
 
    # RT's user settings.
 
    ('Mon Mar 1 01:01:01 2021', 1, None),
 
    ('2021-03-02 02:02:02', 2, None),
 
    ('2021-03-03T03:03:03-0500', 3, -18000),
 
    ('Thu, 4 Mar 2021 04:04:04 -0600', 4, -21600),
 
    ('Fri, 5 Mar 2021 05:05:05 GMT', 5, 0),
 
    ('20210306T060606Z', 6, 0),
 
    ('Sun, Mar 7, 2021 07:07:07 AM', 7, None),
 
    ('Sun, Mar 14, 2021 02:14:14 PM', 14, None),
 
])
 
def test_rt_datetime(arg, exp_num, exp_offset):
 
    actual = rtutil.RTDateTime(arg)
 
    assert actual.year == 2021
 
    assert actual.month == 3
 
    assert actual.day == exp_num
 
    assert actual.hour == exp_num
 
    assert actual.minute == exp_num
 
    assert actual.second == exp_num
 
    if exp_offset is None:
 
        assert actual.tzinfo is None
 
    else:
 
        assert actual.tzinfo.utcoffset(None).total_seconds() == exp_offset
 

	
 
@pytest.mark.parametrize('arg', ['Not set', '', None])
 
def test_rt_datetime_empty(arg):
 
    actual = rtutil.RTDateTime(arg)
 
    assert actual == datetime.datetime.min
 
    assert actual.tzinfo is None
0 comments (0 inline, 0 general)