Skip to content

Commit

Permalink
feat: epoch parser (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
shawnsarwar authored Aug 13, 2020
1 parent eef8972 commit 0b053ac
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 0 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ _usage:_ ```$.my.path.`datetime({strptime_str}, {return_slice})` ```
*_White space after commas is required!_*


### Parse Epoch Timestamp

Attempts to parse the epoch timestamp from a value found at a given jsonpath. Then returns a part or the whole of the parsed datetime as specified by a python array slice. Will operate invidiually on array items in the case the path resolves to an array. The input units must be indicated as one of:

- `seconds`
- `millis`
- `micros`


The array slice notation used is the same as in `datetime`.

_usage:_ ```$.my.path.`epoch({units}, {return_slice})` ```

*_White space after commas is required!_*

### Hash

Returns a 128bit, hex formatted MD5 hash of the value of a resolved jsonpath added to a provided `salt`. This is useful for generating a UUID compatable value from a piece of source information along with some unique salt. This allows for a non-UUID foreign key to be reliably converted to the same UUID compatable value every time. Will operate invidiually on array items in the case the path resolves to an array.
Expand Down
2 changes: 2 additions & 0 deletions eha_jsonpath/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ def p_jsonpath_named_operator(self, p):
p[0] = fn.Match(p[1])
elif p[1].startswith("notmatch("):
p[0] = fn.NotMatch(p[1])
elif p[1].startswith("epoch("):
p[0] = fn.ParseEpochDatetime(p[1])
elif p[1].startswith("datetime("):
p[0] = fn.ParseDatetime(p[1])
elif p[1].startswith("hash("):
Expand Down
29 changes: 29 additions & 0 deletions eha_jsonpath/ext_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,35 @@ def _do(self, obj):
return value


class ParseEpochDatetime(BaseFn):
'''
usage: `epoch({unit}, {return_slice})`
! White space after commas is required!
result is : iso_datetime[return_slice]
'''
METHOD_SIG = re.compile(r'epoch\((.+),\s+(.+)\)')
VALID_FORMATS = {
'second': 1,
'millis': 1_000,
'micros': 1_000_000
}

def __init__(self, method=None):
args = self.get_args(method)
self.time_format = args[0]
self.slice_arg = args[1]
self.method = method

def _do(self, obj):
if self.time_format not in self.VALID_FORMATS:
raise DefintionInvalid(
f'{self.time_format} is invalid: excepted {self.VALID_FORMATS.keys()}')
deno = self.VALID_FORMATS[self.time_format]
value = datetime.fromtimestamp(float(obj) / deno)
return ParseDatetime.args_to_slice(
self.slice_arg, value.isoformat())


class Hash(BaseFn):
'''
usage: `hash({salt})`
Expand Down
41 changes: 41 additions & 0 deletions test/test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
import uuid

from eha_jsonpath import parse
from eha_jsonpath.ext_functions import BaseFn
Expand All @@ -9,6 +10,9 @@
'comma': '1,2,3,4',
'float': '1.04',
'bad_float': '1.04s',
'epoch1': '0',
'epoch2': 1_000_000_000,
'epoch3': 1_000_000_000_000_000,
'int': '1.09',
'str': 1.0,
'str2': 'a',
Expand Down Expand Up @@ -109,6 +113,27 @@
('$.boolean2.`match(1, 0)`',
[True],
False),
('$.epoch1.`epoch(second, 0:4)`',
['1970'],
False),
('$.epoch1.`epoch(millis, 0:4)`',
['1970'],
False),
('$.epoch1.`epoch(micros, 0:4)`',
['1970'],
False),
('$.epoch1.`epoch(other, 0:4)`',
['1970'],
True),
('$.epoch2.`epoch(second, 0:4)`',
['2001'],
False),
('$.epoch2.`epoch(millis, 0:4)`',
['1970'],
False),
('$.epoch3.`epoch(micros, 0:4)`',
['2001'],
False),
('$.dt1.`datetime(%Y-%m-%d, 0:4)`',
['2019'],
False),
Expand Down Expand Up @@ -200,3 +225,19 @@ def test_hashs_equality(cmd1, cmd2):
])
def test_hashs_inequality(cmd1, cmd2):
parse(cmd1).find(src)[0] != parse(cmd2).find(src)[0]


@pytest.mark.parametrize("cmd1", [
('$.hashable1.`hash(a)`'),
('$.hashable2.`hash(b)`'),
('$.hashable3.`hash(a)`'),
('$.hashable4.`hash(b)`')
])
def test_hash_is_uuid(cmd1):
try:
id_ = parse(cmd1).find(src)[0].value
uuid.UUID(id_, version=4)
except (ValueError, AttributeError, TypeError) as err:
assert(False), (id_, err)
else:
assert(True)

0 comments on commit 0b053ac

Please sign in to comment.