diff --git a/README.md b/README.md index a50f7119bb0323be304f19b1e4a4ffec603f5d1a..6705e4b23cb90fcbc8b304d965eafc94fa99adad 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [](https://git-ce.rwth-aachen.de/wzl-mq-ms/forschung-lehre/lava/unified-device-interface/python/commits/master) # Python Unified Device Interface -Current stable version: 10.0.0 +Current stable version: 10.0.1 ## Installation 1. Install the WZL-UDI package via pip @@ -56,8 +56,22 @@ Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) under Project-ID 432233186 -- AIMS. +## License of used third-party libraries + +| Library | License | +|--------------|---------------------------------------| +| aiohttp | Apache License, Version 2.0 | +| Deprecated | MIT License | +| nest-asyncio | BSD License | +| pytz | MIT License | +| rdflib | BSD License | +| wzl-mqtt | MIT License | + ## Recent changes +**10.0.1** - 2024-03-21 + - removed strict-rfc3339 dependency to avoid licensing issues + **10.0.0** - 2024-03-21 - the getter method of a measurement must always return a tuple of value and uncertainty quantification, if the uncertainty is not applicable None must be returned for the uncertainty - semantic path for Observation and MeasurementResults can now be resolved diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000000000000000000000000000000000000..1e3781b3a3ed69d6e1f522b840ae26070cba2409 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,8 @@ +aiohttp==3.8.4 +Deprecated==1.2.13 +nest-asyncio==1.5.6 +pytest==7.1.1 +rdflib==7.0.0 +sphinx==3.5.2 +sphinx-rtd-theme==1.0.0 +wzl-mqtt~=2.6.1 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 9d3aff5b04e6c396aa63f9f4d69cedbf7f7ad70c..fd38f944734b9a092354a42fddb39a7f59d9b914 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,6 @@ -aiohttp==3.8.4 +aiohttp==3.9.1 Deprecated==1.2.13 -jinja2==3.0.3 nest-asyncio==1.5.6 -pytest==7.1.1 +pytz==2024.1 rdflib==7.0.0 -sphinx==3.5.2 -sphinx-rtd-theme==1.0.0 -strict-rfc3339==0.7 wzl-mqtt~=2.6.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 2ad2d8163f5678be1ec035657c783ef179202450..71b071615217855987ba3ca5e736e519bdafb289 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() setup(name='wzl-udi', - version='10.0.0', + version='10.0.1', url='https://git-ce.rwth-aachen.de/wzl-mq-public/soil/python', project_urls={ "Bug Tracker": "https://git-ce.rwth-aachen.de/wzl-mq-public/soil/python/-/issues", @@ -24,7 +24,7 @@ setup(name='wzl-udi', install_requires=['aiohttp~=3.9.1', 'Deprecated~=1.2.13', 'nest-asyncio~=1.5.6', - 'strict-rfc3339==0.7', + 'pytz==2024.1', 'wzl-mqtt~=2.6.1', 'rdflib~=7.0.0' ], diff --git a/src/soil/measurement.py b/src/soil/measurement.py index a414e77b08a4b7e32289b2fe097d567b8411c9c9..f62a96ce6b6cdcc8a93ad9581cfc80b5a43a9fdc 100644 --- a/src/soil/measurement.py +++ b/src/soil/measurement.py @@ -152,7 +152,10 @@ class Measurement(Variable): for key in keys: value = self.__getitem__(key, method) # in case of timestamp convert into RFC3339 string - if key == 'timestamp' or (key == 'value' and self._datatype == 'time'): + if key == 'timestamp': + value = value.isoformat() + 'Z' if value is not None else datetime.datetime.now( + datetime.timezone.utc).isoformat().replace('+00:00', 'Z') + if key == 'value' and self._datatype == 'time': value = value.isoformat() + 'Z' if value is not None else "" if key == "datatype": dictionary[key] = value.to_string(legacy_mode) diff --git a/src/soil/variable.py b/src/soil/variable.py index 5495dc393e47b631fbccf7145e9ccb9ff1d0e178..3be402e3ada7586f9f5e35f3af9468d5e6066264 100644 --- a/src/soil/variable.py +++ b/src/soil/variable.py @@ -6,6 +6,7 @@ from abc import ABC from typing import Any, List, Callable, Union import nest_asyncio +import pytz import rdflib import strict_rfc3339 as rfc3339 @@ -30,14 +31,29 @@ def parse_time(time_rfc3339: Union[str, List]): else: if time_rfc3339 is None or time_rfc3339 == "": return None - timestamp = rfc3339.rfc3339_to_timestamp(time_rfc3339) - date = list(time.gmtime(int(timestamp)))[:6] - return datetime.datetime(*date, int((timestamp - int(timestamp)) * 1e6)) + + formats = ["%Y-%m-%dT%H:%M:%S.%f%z", "%Y-%m-%dT%H:%M:%S%z", "%Y-%m-%dT%H:%M:%S.%fZ", "%Y-%m-%dT%H:%M:%SZ"] + + for fmt in formats: + try: + # Attempt to parse the timestamp with the current format + if fmt.endswith('Z'): + # Strip the 'Z' and replace it after parsing if format specifies 'Z' at the end + dt = datetime.strptime(time_rfc3339.rstrip('Z'), fmt.rstrip('Z')) + dt = dt.replace(tzinfo=pytz.UTC) + else: + dt = datetime.strptime(time_rfc3339, fmt) + return dt + + except ValueError: + # If parsing fails, try the next format + continue + + return None def serialize_time(time): - timestamp = datetime.datetime.timestamp(time) - return rfc3339.timestamp_to_rfc3339_utcoffset(timestamp) + return time.isoformat() class Variable(Element, ABC):