Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: epoch parser #9

Merged
merged 1 commit into from
Aug 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)