From 70dc2d61840ea35f34d91ae36a85e3fdaeee6a6f Mon Sep 17 00:00:00 2001 From: Julian Minder Date: Tue, 22 Feb 2022 12:41:39 +0100 Subject: [PATCH] updated DATE/DATETIME to also handle datetime inputs and strip away tz info, added mission tests --- docs/documentation.md | 4 +- rel2graph/common_modules/util.py | 6 +- tests/unit/common_modules/test_util.py | 65 +++++++++++++++++++ tests/unit/core/resources/typing.yaml | 8 +++ .../core/resources/typing_exceptions1.yaml | 1 + .../core/resources/typing_exceptions2.yaml | 1 + 6 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 tests/unit/common_modules/test_util.py create mode 100644 tests/unit/core/resources/typing.yaml create mode 100644 tests/unit/core/resources/typing_exceptions1.yaml create mode 100644 tests/unit/core/resources/typing_exceptions2.yaml diff --git a/docs/documentation.md b/docs/documentation.md index f497c6a..b856c09 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -504,9 +504,9 @@ The rel2graph library comes with some predefined wrappers. To use any of them yo import rel2graph.common_modules ``` - **DATETIME**: -The DATETIME attribute wrapper allows to convert strings into datetime objects. It uses the datetime strptime function to convert the strings to datetime based on some formatting. The default formating string is `"%Y-%m-%dT%H:%M:%S"`. You can provide your own formating string as static argument in the conversion schema: `- datetime = DATETIME(entity.datetime_as_str, "%Y/%m/%d %H:%M:%S")`. Check the [datetime documentation](https://docs.python.org/3/library/datetime.html) for details about how strptime works. +The DATETIME attribute wrapper allows to convert strings into datetime objects. It uses the datetime strptime function to convert the strings to datetime based on some formatting. The default formating string is `"%Y-%m-%dT%H:%M:%S"`. You can provide your own formating string as static argument in the conversion schema: `- datetime = DATETIME(entity.datetime_as_str, "%Y/%m/%d %H:%M:%S")`. If the provided argument is a datetime instead of a string, it will just remove any timezone information. Check the [datetime documentation](https://docs.python.org/3/library/datetime.html) for details about how strptime works. - **DATE**: -The DATE attribute wrapper allows to convert strings into date objects. It uses the datetime strptime function to convert the strings to datetime based on some formatting and from there into just a date. The default formating string is `"%Y-%m-%d"`. You can provide your own formating string as static argument in the conversion schema: `- date = DATETIME(entity.date_as_str, "%Y/%m/%d %H:%M:%S")`. Check the [datetime documentation](https://docs.python.org/3/library/datetime.html) for details about how strptime works. If the attribute passed to DATE contains also time information, this is simply stripped away (the format string still must fit the exact format of your attribute). +The DATE attribute wrapper allows to convert strings into date objects. It uses the datetime strptime function to convert the strings to datetime based on some formatting and from there into just a date. The default formating string is `"%Y-%m-%d"`. You can provide your own formating string as static argument in the conversion schema: `- date = DATETIME(entity.date_as_str, "%Y/%m/%d %H:%M:%S")`. Check the [datetime documentation](https://docs.python.org/3/library/datetime.html) for details about how strptime works. If the attribute passed to DATE contains also time information, this is simply stripped away (the format string still must fit the exact format of your attribute). If the provided argument is a datetime instead of a string, it will just remove any timezone information and convert it to date. **Note**: If you encounter the exception `TypeError: Neo4j does not support JSON parameters of type datetime` for the DATE/DATETIME wrappers, make sure that you use the bolt/neo4j scheme with your graph. Dates won't work over *http*: ```g = Graph(scheme="bolt", host="localhost", port=7687, auth=('neo4j', 'password'))``` diff --git a/rel2graph/common_modules/util.py b/rel2graph/common_modules/util.py index e4f18a3..018b72d 100644 --- a/rel2graph/common_modules/util.py +++ b/rel2graph/common_modules/util.py @@ -7,12 +7,16 @@ """ from .. import register_attribute_postprocessor from .. import Attribute -from datetime import datetime, date +from datetime import datetime @register_attribute_postprocessor def DATETIME(attribute, format_string="%Y-%m-%dT%H:%M:%S"): + if isinstance(attribute.value, datetime): + return Attribute(attribute.key, attribute.value.replace(tzinfo=None)) return Attribute(attribute.key, datetime.strptime(attribute.value, format_string)) @register_attribute_postprocessor def DATE(attribute, format_string="%Y-%m-%dT%H:%M:%S"): + if isinstance(attribute.value, datetime): + return Attribute(attribute.key, attribute.value.replace(tzinfo=None).date()) return Attribute(attribute.key, datetime.strptime(attribute.value, format_string).date()) \ No newline at end of file diff --git a/tests/unit/common_modules/test_util.py b/tests/unit/common_modules/test_util.py new file mode 100644 index 0000000..0970f8b --- /dev/null +++ b/tests/unit/common_modules/test_util.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Tests for OData relational module + +TODO: tests heavily access protected members of the library -> improve? how? + +authors: Julian Minder +""" + +import pytest + +from rel2graph.common_modules import util +from rel2graph import Attribute +from datetime import datetime + +@pytest.fixture +def default_format(): + return Attribute("key", "2015-05-17T21:18:19") + +@pytest.fixture +def other_format(): + return Attribute("key", "2015/05/17 21h 18min 19s") + +@pytest.fixture +def as_datetime(): + return Attribute("key", datetime.strptime("2015-05-17T21:18:19", "%Y-%m-%dT%H:%M:%S")) + +def test_date(default_format, other_format, as_datetime): + result = util.DATE(default_format).value + assert(result.year == 2015) + assert(result.month == 5) + assert(result.day == 17) + result = util.DATE(other_format, "%Y/%m/%d %Hh %Mmin %Ss").value + assert(result.year == 2015) + assert(result.month == 5) + assert(result.day == 17) + result = util.DATE(as_datetime).value + assert(result.year == 2015) + assert(result.month == 5) + assert(result.day == 17) + +def test_datetime(default_format, other_format, as_datetime): + result = util.DATETIME(default_format).value + assert(result.year == 2015) + assert(result.month == 5) + assert(result.day == 17) + assert(result.hour == 21) + assert(result.minute == 18) + assert(result.second == 19) + result = util.DATETIME(other_format, "%Y/%m/%d %Hh %Mmin %Ss").value + assert(result.year == 2015) + assert(result.month == 5) + assert(result.day == 17) + assert(result.hour == 21) + assert(result.minute == 18) + assert(result.second == 19) + result = util.DATETIME(as_datetime).value + assert(result.year == 2015) + assert(result.month == 5) + assert(result.day == 17) + assert(result.hour == 21) + assert(result.minute == 18) + assert(result.second == 19) \ No newline at end of file diff --git a/tests/unit/core/resources/typing.yaml b/tests/unit/core/resources/typing.yaml new file mode 100644 index 0000000..165b6b8 --- /dev/null +++ b/tests/unit/core/resources/typing.yaml @@ -0,0 +1,8 @@ +ENTITY("entity"): + NODE("node") test: + - mystr = "1" #str + - myfloat = 1.1 #float + - myint = 1 #int + - myTrue = True # bool + - myFalse = False # bool + RELATION(test, "to", MATCH("node", mystr = "1", myint = 1, myfloat = 1.1, myTrue = True, myFalse = False)): \ No newline at end of file diff --git a/tests/unit/core/resources/typing_exceptions1.yaml b/tests/unit/core/resources/typing_exceptions1.yaml new file mode 100644 index 0000000..39179ec --- /dev/null +++ b/tests/unit/core/resources/typing_exceptions1.yaml @@ -0,0 +1 @@ +ENTITY("TEST") \ No newline at end of file diff --git a/tests/unit/core/resources/typing_exceptions2.yaml b/tests/unit/core/resources/typing_exceptions2.yaml new file mode 100644 index 0000000..39179ec --- /dev/null +++ b/tests/unit/core/resources/typing_exceptions2.yaml @@ -0,0 +1 @@ +ENTITY("TEST") \ No newline at end of file