From afe39ed8c17ca0129547183a6767e89568b0a3c7 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Wed, 26 Jun 2024 16:28:38 +0200 Subject: [PATCH 01/42] . --- .../time_series/v1alpha/time_series.proto | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index f0845c92..0723aa07 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -76,6 +76,8 @@ service TimeseriesService { // In case mapping is enabled, the root nodes for source and target may be // the same. rpc CopyTimeseriesBetweenObjects(CopyTimeseriesBetweenObjectsRequest) returns (CopyTimeseriesBetweenObjectsResponse) {} + + rpc CreatePhysicalTimeseriesResource(CreatePhysicalTimeseriesResourceRequest) returns (TimeseriesResource) {} } message ReadTimeseriesRequest { @@ -127,7 +129,7 @@ message GetTimeseriesResourceRequest { message TimeseriesResource { // Unique identifier for physical or virtual time series. // Set to 0 if it is a local session time series. - // This is the only field that could be used for updating + // This is the only field that could be used for updating // time series attribute with new time series resource. int64 timeseries_key = 1; @@ -247,7 +249,7 @@ message CopyTimeseriesBetweenObjectsRequest { // If interval is not supplied, the request will do a tentative operation // which logs what time series will be copied if a valid interval is given. volue.mesh.grpc.type.UtcInterval interval = 2; - + // Identify the root object of the Source hierarchy (path or guid) volue.mesh.grpc.type.MeshId source = 3; // If scenario name is specified, the source series must be bound to @@ -282,3 +284,17 @@ message CopyTimeseriesBetweenObjectsResponse { // When this value is > 0 the operations were successful. int64 match_series_count = 3; } + +message CreatePhysicalTimeseriesResourceRequest { + volue.mesh.grpc.type.Guid session_id = 1; + + string path = 2; + + string name = 3; + + volue.mesh.grpc.type.Curve curve_type = 4; + + volue.mesh.grpc.type.Resolution resolution = 5; + + volue.mesh.grpc.model_definition.v1alpha.UnitOfMeasurement unit_of_measurement = 6; +} From d0959b22d2e047920aecaae99b575bc7bb020c52 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 1 Jul 2024 15:40:57 +0200 Subject: [PATCH 02/42] . --- src/volue/mesh/_connection.py | 39 +++++++++++++++++++ .../time_series/v1alpha/time_series.proto | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index d62a0fff..7e2b7537 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -30,6 +30,7 @@ RatingCurveVersion, XySet, _from_proto_guid, + _to_proto_curve_type, _to_proto_guid, _to_proto_timeseries, ) @@ -176,6 +177,44 @@ def update_timeseries_resource_info( ) self.time_series_service.UpdateTimeseriesResource(request) + # FIXME: Should this be called 'create_physical_timeseries_resource' instead? + def create_timeseries( + self, + path: str, + name: str, + curve_type: Timeseries.Curve, + resolution: timedelta, + unit_of_measurement: str, + ) -> TimeseriesResource: + # TODO: Move this to a single function which just takes a string and returns the unit + # of measurement ID. + unit_of_measurement_id = None + + list_response = self.model_definition_service.ListUnitsOfMeasurement( + model_definition_pb2.ListUnitsOfMeasurementRequest( + session_id=_to_proto_guid(self.session_id) + ) + ) + + unit_of_measurement_id = ( + super()._get_unit_of_measurement_id_by_name( + unit_of_measurement, list_response + ) + ) + + request = time_series_pb2.CreatePhysicalTimeseriesResourceRequest( + session_id=_to_proto_guid(self.session_id), + path=path, + name=name, + curve_type=_to_proto_curve_type(curve_type), + resolution=protobuf.duration_pb2.Duration().FromTimedelta(resolution), + unit_of_measurement_id=unit_of_measurement_id + ) + + response = self.time_series_service.CreatePhysicalTimeseriesResource(request) + + return _from_proto_timeseries_resource(response) + def get_attribute( self, target: Union[uuid.UUID, str, AttributeBase], diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index 0723aa07..87a178c2 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -296,5 +296,5 @@ message CreatePhysicalTimeseriesResourceRequest { volue.mesh.grpc.type.Resolution resolution = 5; - volue.mesh.grpc.model_definition.v1alpha.UnitOfMeasurement unit_of_measurement = 6; + volue.mesh.grpc.type.Guid unit_of_measurement_id = 6; } From bc9223058b46ab9edf4298fd0b391990c5a52d5d Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 1 Jul 2024 16:57:02 +0200 Subject: [PATCH 03/42] : --- src/volue/mesh/_connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index 7e2b7537..f911ef98 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -183,8 +183,9 @@ def create_timeseries( path: str, name: str, curve_type: Timeseries.Curve, + # FIXME: Should we take a Timeseries.Resolution instead? resolution: timedelta, - unit_of_measurement: str, + unit_of_measurement: str ) -> TimeseriesResource: # TODO: Move this to a single function which just takes a string and returns the unit # of measurement ID. From 837aa548868b693fcba63113b313eb6e0abf2ea7 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 1 Jul 2024 17:54:11 +0200 Subject: [PATCH 04/42] . --- src/volue/mesh/_common.py | 30 ++++++++++++++++++++++++++++++ src/volue/mesh/_connection.py | 6 +++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/volue/mesh/_common.py b/src/volue/mesh/_common.py index f540eab6..6dbd19ab 100644 --- a/src/volue/mesh/_common.py +++ b/src/volue/mesh/_common.py @@ -397,6 +397,36 @@ def _from_proto_curve_type(proto_curve: type.resources_pb2.Curve) -> Timeseries. return curve +# FIXME: Don't have such duplicated functions. +def _to_proto_resolution(resolution: Timeseries.Resolution) -> type.resources_pb2.Resolution: + """ + Converts from Timeseries.Resolution type to protobuf resolution type. + + Args: + resolution: The resolution to convert. + """ + proto_resolution = type.resources_pb2.Resolution.RESOLUTION_UNSPECIFIED + + if resolution == Timeseries.Resolution.BREAKPOINT: + proto_resolution = type.resources_pb2.Resolution.BREAKPOINT + elif resolution == Timeseries.Resolution.MIN15: + proto_resolution = type.resources_pb2.Resolution.MIN15 + elif resolution == Timeseries.Resolution.MIN30: + proto_resolution = type.resources_pb2.Resolution.MIN30 + elif resolution == Timeseries.Resolution.HOUR: + proto_resolution = type.resources_pb2.Resolution.HOUR + elif resolution == Timeseries.Resolution.DAY: + proto_resolution = type.resources_pb2.Resolution.DAY + elif resolution == Timeseries.Resolution.WEEK: + proto_resolution = type.resources_pb2.Resolution.WEEK + elif resolution == Timeseries.Resolution.MONTH: + proto_resolution = type.resources_pb2.Resolution.MONTH + elif resolution == Timeseries.Resolution.YEAR: + proto_resolution = type.resources_pb2.Resolution.YEAR + + return proto_resolution + + def _from_proto_resolution( proto_resolution: type.resources_pb2.Resolution, ) -> Timeseries.Resolution: diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index f911ef98..6909ef07 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -32,6 +32,7 @@ _from_proto_guid, _to_proto_curve_type, _to_proto_guid, + _to_proto_resolution, _to_proto_timeseries, ) from volue.mesh.calc.forecast import ForecastFunctions @@ -183,8 +184,7 @@ def create_timeseries( path: str, name: str, curve_type: Timeseries.Curve, - # FIXME: Should we take a Timeseries.Resolution instead? - resolution: timedelta, + resolution: Timeseries.Resolution, unit_of_measurement: str ) -> TimeseriesResource: # TODO: Move this to a single function which just takes a string and returns the unit @@ -208,7 +208,7 @@ def create_timeseries( path=path, name=name, curve_type=_to_proto_curve_type(curve_type), - resolution=protobuf.duration_pb2.Duration().FromTimedelta(resolution), + resolution=_to_proto_resolution(resolution), unit_of_measurement_id=unit_of_measurement_id ) From 909c354017d0b3f0ab184f6adc86e47176ce1c8b Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 1 Jul 2024 18:20:21 +0200 Subject: [PATCH 05/42] . --- src/volue/mesh/_common.py | 21 ++++++++++++--------- src/volue/mesh/_connection.py | 4 +++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/volue/mesh/_common.py b/src/volue/mesh/_common.py index 6dbd19ab..27dbfd0d 100644 --- a/src/volue/mesh/_common.py +++ b/src/volue/mesh/_common.py @@ -405,24 +405,27 @@ def _to_proto_resolution(resolution: Timeseries.Resolution) -> type.resources_pb Args: resolution: The resolution to convert. """ - proto_resolution = type.resources_pb2.Resolution.RESOLUTION_UNSPECIFIED + proto_resolution = type.resources_pb2.Resolution() if resolution == Timeseries.Resolution.BREAKPOINT: - proto_resolution = type.resources_pb2.Resolution.BREAKPOINT + proto_resolution.type = type.resources_pb2.Resolution.BREAKPOINT elif resolution == Timeseries.Resolution.MIN15: - proto_resolution = type.resources_pb2.Resolution.MIN15 + proto_resolution.type = type.resources_pb2.Resolution.MIN15 elif resolution == Timeseries.Resolution.MIN30: - proto_resolution = type.resources_pb2.Resolution.MIN30 + proto_resolution.type = type.resources_pb2.Resolution.MIN30 elif resolution == Timeseries.Resolution.HOUR: - proto_resolution = type.resources_pb2.Resolution.HOUR + proto_resolution.type = type.resources_pb2.Resolution.HOUR elif resolution == Timeseries.Resolution.DAY: - proto_resolution = type.resources_pb2.Resolution.DAY + proto_resolution.type = type.resources_pb2.Resolution.DAY elif resolution == Timeseries.Resolution.WEEK: - proto_resolution = type.resources_pb2.Resolution.WEEK + proto_resolution.type = type.resources_pb2.Resolution.WEEK elif resolution == Timeseries.Resolution.MONTH: - proto_resolution = type.resources_pb2.Resolution.MONTH + proto_resolution.type = type.resources_pb2.Resolution.MONTH elif resolution == Timeseries.Resolution.YEAR: - proto_resolution = type.resources_pb2.Resolution.YEAR + proto_resolution.type = type.resources_pb2.Resolution.YEAR + else: + # FIXME: Should we throw instead? + proto_resolution.type = type.resources_pb2.Resolution.RESOLUTION_UNSPECIFIED return proto_resolution diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index 6909ef07..50d69dd5 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -214,7 +214,9 @@ def create_timeseries( response = self.time_series_service.CreatePhysicalTimeseriesResource(request) - return _from_proto_timeseries_resource(response) + print(response) + + return TimeseriesResource._from_proto_timeseries_resource(response) def get_attribute( self, From 5097091d646ad57cdbe5c646bda83fd73d763635 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 1 Jul 2024 18:22:48 +0200 Subject: [PATCH 06/42] . --- src/volue/mesh/examples/quickstart.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index 4f05880c..46f226d2 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -1,6 +1,6 @@ import helpers -from volue.mesh import Connection +from volue.mesh import Connection, Timeseries def main(address, port, root_pem_certificate): @@ -14,12 +14,18 @@ def main(address, port, root_pem_certificate): print(f"Connected to {version_info.name} {version_info.version}") # Create a remote session on the Volue Mesh server - session = connection.create_session() - session.open() - print("You have now an open session and can request time series") - - # Close the remote session - session.close() + with connection.create_session() as session: + print("You have now an open session and can request time series") + + result = session.create_timeseries( + path='Path/To/Test/Timeseries', + name='Test_Timeseries', + curve_type=Timeseries.Curve.PIECEWISELINEAR, + resolution=Timeseries.Resolution.HOUR, + unit_of_measurement="Unit1" + ) + + print(result) if __name__ == "__main__": From 2afab36d8603b526d6f5c1344831d0adef0524b0 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Wed, 3 Jul 2024 13:09:06 +0200 Subject: [PATCH 07/42] . --- src/volue/mesh/examples/quickstart.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index 46f226d2..ca564799 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -18,13 +18,15 @@ def main(address, port, root_pem_certificate): print("You have now an open session and can request time series") result = session.create_timeseries( - path='Path/To/Test/Timeseries', + path='/Path/To/Test/Timeseries/', name='Test_Timeseries', curve_type=Timeseries.Curve.PIECEWISELINEAR, resolution=Timeseries.Resolution.HOUR, unit_of_measurement="Unit1" ) + session.commit() + print(result) From 3592cf2a03554549f1f7f3808e5d5f67b7f1ec12 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Wed, 3 Jul 2024 16:27:17 +0200 Subject: [PATCH 08/42] . --- .../proto/time_series/v1alpha/time_series.proto | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index 87a178c2..5707a4fc 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -77,6 +77,13 @@ service TimeseriesService { // the same. rpc CopyTimeseriesBetweenObjects(CopyTimeseriesBetweenObjectsRequest) returns (CopyTimeseriesBetweenObjectsResponse) {} + // Create a new (empty) physical timeseries resource, including its + // corresponding entry in the TIMESER table. Returns the newly created + // resource. See the 'CreatePhysicalTimeseriesResourceRequest' message + // documentation for more info. + // FIXME: Should we rename this to 'CreateTimeseriesResource' and disallow VTS + // for now? + // FIXME: Should we return just a timeseries key instead? rpc CreatePhysicalTimeseriesResource(CreatePhysicalTimeseriesResourceRequest) returns (TimeseriesResource) {} } @@ -288,6 +295,15 @@ message CopyTimeseriesBetweenObjectsResponse { message CreatePhysicalTimeseriesResourceRequest { volue.mesh.grpc.type.Guid session_id = 1; + // The path of the timeseries resource in the Mesh resource catalog. It is not + // mandatory that this mirrors the topology of the Mesh model. + // This *must* start and end with forward slashes. + // Note that the 'path' field on the returned TimeseriesResource doesn't have + // the same as the value as this field; namely, the returned 'path' will have + // a "Resource/" prefix, and will be suffixed with the contents of the 'name' + // field from this request. That is: + // + // response.path == "Resource/" + request.path + "/" + request.name string path = 2; string name = 3; From dc2b711defb97a55ae3da1de9f4deaf05784f96d Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Thu, 4 Jul 2024 15:56:19 +0200 Subject: [PATCH 09/42] . --- src/volue/mesh/_connection.py | 4 +-- .../time_series/v1alpha/time_series.proto | 31 +++++++++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index ef512bc6..2573c15f 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -203,7 +203,7 @@ def create_timeseries( ) ) - request = time_series_pb2.CreatePhysicalTimeseriesResourceRequest( + request = time_series_pb2.CreatePhysicalTimeseriesRequest( session_id=_to_proto_guid(self.session_id), path=path, name=name, @@ -212,7 +212,7 @@ def create_timeseries( unit_of_measurement_id=unit_of_measurement_id ) - response = self.time_series_service.CreatePhysicalTimeseriesResource(request) + response = self.time_series_service.CreatePhysicalTimeseries(request) print(response) diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index 889cb7a3..8bbcccf4 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -4,8 +4,8 @@ package volue.mesh.grpc.time_series.v1alpha; import "google/protobuf/field_mask.proto"; -import "volue/mesh/proto/model_definition/v1alpha/resources.proto"; -import "volue/mesh/proto/type/resources.proto"; +import "model_definition/v1alpha/resources.proto"; +import "type/resources.proto"; // Mesh time series service @@ -77,14 +77,10 @@ service TimeseriesService { // the same. rpc CopyTimeseriesBetweenObjects(CopyTimeseriesBetweenObjectsRequest) returns (CopyTimeseriesBetweenObjectsResponse) {} - // Create a new (empty) physical timeseries resource, including its - // corresponding entry in the TIMESER table. Returns the newly created - // resource. See the 'CreatePhysicalTimeseriesResourceRequest' message + // Create a new (empty) physical time series resource. Returns the newly + // created resource. See the 'CreatePhysicalTimeseriesRequest' message // documentation for more info. - // FIXME: Should we rename this to 'CreateTimeseriesResource' and disallow VTS - // for now? - // FIXME: Should we return just a timeseries key instead? - rpc CreatePhysicalTimeseriesResource(CreatePhysicalTimeseriesResourceRequest) returns (TimeseriesResource) {} + rpc CreatePhysicalTimeseries(CreatePhysicalTimeseriesRequest) returns (TimeseriesResource) {} } message ReadTimeseriesRequest { @@ -293,18 +289,27 @@ message CopyTimeseriesBetweenObjectsResponse { int64 match_series_count = 3; } -message CreatePhysicalTimeseriesResourceRequest { +message CreatePhysicalTimeseriesRequest { volue.mesh.grpc.type.Guid session_id = 1; - // The path of the timeseries resource in the Mesh resource catalog. It is not - // mandatory that this mirrors the topology of the Mesh model. - // This *must* start and end with forward slashes. + // The path of the time series resource in the Mesh resource catalog. Any + // missing components of the path will be created in the catalog as well. + // For example, if we set 'path' to "/Path/To/Timeseries/" and 'name' to + // "Test_Timeseries", the entire Path/To/Timeseries/Test_Timeseries sequence + // of catalog entries will be created if it doesn't exist already. + // + // It is not mandatory that the path mirrors the topology of the Mesh model. + // The path *must* begin and end with forward slashes. + // // Note that the 'path' field on the returned TimeseriesResource doesn't have // the same as the value as this field; namely, the returned 'path' will have // a "Resource/" prefix, and will be suffixed with the contents of the 'name' // field from this request. That is: // // response.path == "Resource/" + request.path + "/" + request.name + // + // Continuing with the previous example, in this case the 'path' field in the + // response will be "Resource/Path/To/Timeseries/Test_Timeseries". string path = 2; string name = 3; From e5bc084a4f24dca20a8993d6e8c7daefc95be062 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Thu, 4 Jul 2024 16:00:52 +0200 Subject: [PATCH 10/42] . --- src/volue/mesh/_common.py | 4 +++- src/volue/mesh/_connection.py | 10 ++++------ src/volue/mesh/examples/quickstart.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/volue/mesh/_common.py b/src/volue/mesh/_common.py index e9fc37c9..25570912 100644 --- a/src/volue/mesh/_common.py +++ b/src/volue/mesh/_common.py @@ -399,7 +399,9 @@ def _from_proto_curve_type(proto_curve: type.resources_pb2.Curve) -> Timeseries. # FIXME: Don't have such duplicated functions. -def _to_proto_resolution(resolution: Timeseries.Resolution) -> type.resources_pb2.Resolution: +def _to_proto_resolution( + resolution: Timeseries.Resolution, +) -> type.resources_pb2.Resolution: """ Converts from Timeseries.Resolution type to protobuf resolution type. diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index 2573c15f..8b5eb4eb 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -185,7 +185,7 @@ def create_timeseries( name: str, curve_type: Timeseries.Curve, resolution: Timeseries.Resolution, - unit_of_measurement: str + unit_of_measurement: str, ) -> TimeseriesResource: # TODO: Move this to a single function which just takes a string and returns the unit # of measurement ID. @@ -197,10 +197,8 @@ def create_timeseries( ) ) - unit_of_measurement_id = ( - super()._get_unit_of_measurement_id_by_name( - unit_of_measurement, list_response - ) + unit_of_measurement_id = super()._get_unit_of_measurement_id_by_name( + unit_of_measurement, list_response ) request = time_series_pb2.CreatePhysicalTimeseriesRequest( @@ -209,7 +207,7 @@ def create_timeseries( name=name, curve_type=_to_proto_curve_type(curve_type), resolution=_to_proto_resolution(resolution), - unit_of_measurement_id=unit_of_measurement_id + unit_of_measurement_id=unit_of_measurement_id, ) response = self.time_series_service.CreatePhysicalTimeseries(request) diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index ca564799..a5908c1f 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -18,11 +18,11 @@ def main(address, port, root_pem_certificate): print("You have now an open session and can request time series") result = session.create_timeseries( - path='/Path/To/Test/Timeseries/', - name='Test_Timeseries', + path="/Path/To/Test/Timeseries/", + name="Test_Timeseries", curve_type=Timeseries.Curve.PIECEWISELINEAR, resolution=Timeseries.Resolution.HOUR, - unit_of_measurement="Unit1" + unit_of_measurement="Unit1", ) session.commit() From c8e72ff8b3dfdf5cf07da3da7a9862b1717ceba9 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Thu, 4 Jul 2024 16:08:39 +0200 Subject: [PATCH 11/42] . --- src/volue/mesh/proto/time_series/v1alpha/time_series.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index 8bbcccf4..68b5b342 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -4,8 +4,8 @@ package volue.mesh.grpc.time_series.v1alpha; import "google/protobuf/field_mask.proto"; -import "model_definition/v1alpha/resources.proto"; -import "type/resources.proto"; +import "volue/mesh/proto/model_definition/v1alpha/resources.proto"; +import "volue/mesh/proto/type/resources.proto"; // Mesh time series service From 2b73851e2d13c22a9d0c099d22f81001fc684915 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 8 Jul 2024 13:15:39 +0200 Subject: [PATCH 12/42] . --- src/volue/mesh/_connection.py | 5 +- src/volue/mesh/examples/quickstart.py | 2 +- .../mesh/tests/test_timeseries_resource.py | 105 ++++++++++++++++++ 3 files changed, 107 insertions(+), 5 deletions(-) diff --git a/src/volue/mesh/_connection.py b/src/volue/mesh/_connection.py index 8b5eb4eb..03e7982f 100644 --- a/src/volue/mesh/_connection.py +++ b/src/volue/mesh/_connection.py @@ -178,8 +178,7 @@ def update_timeseries_resource_info( ) self.time_series_service.UpdateTimeseriesResource(request) - # FIXME: Should this be called 'create_physical_timeseries_resource' instead? - def create_timeseries( + def create_physical_timeseries( self, path: str, name: str, @@ -212,8 +211,6 @@ def create_timeseries( response = self.time_series_service.CreatePhysicalTimeseries(request) - print(response) - return TimeseriesResource._from_proto_timeseries_resource(response) def get_attribute( diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index a5908c1f..50fb9c87 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -17,7 +17,7 @@ def main(address, port, root_pem_certificate): with connection.create_session() as session: print("You have now an open session and can request time series") - result = session.create_timeseries( + result = session.create_physical_timeseries( path="/Path/To/Test/Timeseries/", name="Test_Timeseries", curve_type=Timeseries.Curve.PIECEWISELINEAR, diff --git a/src/volue/mesh/tests/test_timeseries_resource.py b/src/volue/mesh/tests/test_timeseries_resource.py index fdb8fd28..c7455712 100644 --- a/src/volue/mesh/tests/test_timeseries_resource.py +++ b/src/volue/mesh/tests/test_timeseries_resource.py @@ -124,6 +124,111 @@ def test_update_timeseries_resource_with_non_existing_unit_of_measurement(sessio assert timeseries_resource.unit_of_measurement == original_unit_of_measurement +def test_create_physical_timeseries(session): + """Check that we can create a new physical timeseries.""" + PATH = '/Path/To/Test/Timeseries/', + NAME = 'Test_Timeseries', + CURVE_TYPE = Timeseries.Curve.PIECEWISELINEAR, + RESOLUTION = Timeseries.Resolution.HOUR, + UNIT_OF_MEASUREMENT = 'Unit1' + + timeseries_resource = session.create_physical_timeseries( + path=PATH, + name=NAME, + curve_type=CURVE_TYPE, + resolution=RESOLUTION, + unit_of_measurement=UNIT_OF_MEASUREMENT + ) + + assert timeseries.resource.path == PATH + assert timeseries.resource.name == NAME + assert timeseries.resource.curve_type == CURVE_TYPE + assert timeseries.resource.resolution == RESOLUTION + assert timeseries.resource.unit_of_measurement == UNIT_OF_MEASUREMENT + + # """Check that time series resource can be updated.""" + # session.update_timeseries_resource_info( + # timeseries_key, new_curve_type, new_unit_of_measurement + # ) + # timeseries_info = session.get_timeseries_resource_info(timeseries_key) + + # if new_curve_type is not None: + # assert timeseries_info.curve_type == new_curve_type + # if new_unit_of_measurement is not None: + # assert timeseries_info.unit_of_measurement == new_unit_of_measurement + + +# // --gtest_filter=CreatePhysicalTimeseriesTests.SendRequestAndCommitShouldPass +# TEST_F(CreatePhysicalTimeseriesTests, SendRequestAndCommitShouldPass) { +# const auto& create_ts_request = CreateRequest(); +# const auto& create_ts_response = Api::CreatePhysicalTimeseries(context_, create_ts_request); +# Api::Commit(context_, proto_session_id_); + +# VerifyResponse(create_ts_response); +# } + +# // --gtest_filter=CreatePhysicalTimeseriesTests.CreateExistingTimeseriesShouldThrow +# TEST_F(CreatePhysicalTimeseriesTests, CreateExistingTimeseriesShouldThrow) { +# const auto& create_ts_request = CreateRequest(); + +# const auto& send_request = [&]() -> TimeseriesResource { +# return Api::CreatePhysicalTimeseries(context_, create_ts_request); +# }; + +# // Create the timeseries. +# const auto& create_ts_response = send_request(); + +# Api::Commit(context_, proto_session_id_); + +# VerifyResponse(create_ts_response); + +# const auto& msg = +# fmt::format("An element named {} already exists in the Timeseries resource catalog\n", name_); + +# // Now try to create it again. +# EXPECT_THAT(send_request, ThrowsMessage(StrEq(msg))); +# } + +# // --gtest_filter=CreatePhysicalTimeseriesTests.InvalidPathShouldThrow +# TEST_F(CreatePhysicalTimeseriesTests, InvalidPathShouldThrow) { +# // The path string must begin and end with slashes. +# TestInvalidPath("Path/To/Test/Timeseries"); +# TestInvalidPath("Path/To/Test/Timeseries/"); +# TestInvalidPath("/Path/To/Test/Timeseries"); +# } + +# // --gtest_filter=CreatePhysicalTimeseriesTests.MissingFieldsShouldThrow +# TEST_F(CreatePhysicalTimeseriesTests, MissingFieldsShouldThrow) { +# // Can't use auto here since it's deduced to different lambda types. +# std::function clear_field = [&](CreatePhysicalTimeseriesRequest& create_ts_request) { +# create_ts_request.clear_curve_type(); +# }; + +# TestMissingField(clear_field); + +# clear_field = [&](CreatePhysicalTimeseriesRequest& create_ts_request) { +# create_ts_request.clear_resolution(); +# }; + +# TestMissingField(clear_field); + +# clear_field = [&](CreatePhysicalTimeseriesRequest& create_ts_request) { +# create_ts_request.clear_unit_of_measurement_id(); +# }; + +# TestMissingField(clear_field); +# } + +# // --gtest_filter=CreatePhysicalTimeseriesTests.InvalidUnitOfMeasurementIdShouldThrow +# TEST_F(CreatePhysicalTimeseriesTests, InvalidUnitOfMeasurementIdShouldThrow) { +# unit_of_measurement_id_ = Common::Guid::Empty(); + +# const auto& create_ts_request = CreateRequest(); + +# EXPECT_THROW(Api::CreatePhysicalTimeseries(context_, create_ts_request), std::invalid_argument); +# } + + @pytest.mark.asyncio @pytest.mark.database async def test_timeseries_resource_async(async_session): From e8bec07c0d27fb715851c7aba6cc664a681f2ea3 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 8 Jul 2024 16:10:57 +0200 Subject: [PATCH 13/42] . --- src/volue/mesh/aio/_connection.py | 35 ++++ .../mesh/tests/test_timeseries_resource.py | 153 +++++++----------- 2 files changed, 90 insertions(+), 98 deletions(-) diff --git a/src/volue/mesh/aio/_connection.py b/src/volue/mesh/aio/_connection.py index d0737010..8b887aaf 100644 --- a/src/volue/mesh/aio/_connection.py +++ b/src/volue/mesh/aio/_connection.py @@ -185,6 +185,41 @@ async def update_timeseries_resource_info( ) await self.time_series_service.UpdateTimeseriesResource(request) + async def create_physical_timeseries( + self, + path: str, + name: str, + curve_type: Timeseries.Curve, + resolution: Timeseries.Resolution, + unit_of_measurement: str, + ) -> TimeseriesResource: + # TODO: Move this to a single function which just takes a string and returns the unit + # of measurement ID. + unit_of_measurement_id = None + + list_response = await self.model_definition_service.ListUnitsOfMeasurement( + model_definition_pb2.ListUnitsOfMeasurementRequest( + session_id=_to_proto_guid(self.session_id) + ) + ) + + unit_of_measurement_id = super()._get_unit_of_measurement_id_by_name( + unit_of_measurement, list_response + ) + + request = time_series_pb2.CreatePhysicalTimeseriesRequest( + session_id=_to_proto_guid(self.session_id), + path=path, + name=name, + curve_type=_to_proto_curve_type(curve_type), + resolution=_to_proto_resolution(resolution), + unit_of_measurement_id=unit_of_measurement_id, + ) + + response = await self.time_series_service.CreatePhysicalTimeseries(request) + + return TimeseriesResource._from_proto_timeseries_resource(response) + async def get_attribute( self, target: Union[uuid.UUID, str, AttributeBase], diff --git a/src/volue/mesh/tests/test_timeseries_resource.py b/src/volue/mesh/tests/test_timeseries_resource.py index c7455712..b5cd1bb4 100644 --- a/src/volue/mesh/tests/test_timeseries_resource.py +++ b/src/volue/mesh/tests/test_timeseries_resource.py @@ -10,6 +10,9 @@ from volue.mesh import Timeseries, TimeseriesResource +INVALID_UNIT_OF_MEASUREMENT_NAME = 'no_such_unit' + + def get_physical_timeseries(): """ Physical time series from SimpleThermalModel test model. @@ -109,7 +112,7 @@ def test_update_timeseries_resource_with_non_existing_unit_of_measurement(sessio """ timeseries_key = get_physical_timeseries().timeseries_key - non_existing_unit_of_measurement = "no_such_unit" + non_existing_unit_of_measurement = INVALID_UNIT_OF_MEASUREMENT_NAME original_unit_of_measurement = session.get_timeseries_resource_info( timeseries_key @@ -124,109 +127,63 @@ def test_update_timeseries_resource_with_non_existing_unit_of_measurement(sessio assert timeseries_resource.unit_of_measurement == original_unit_of_measurement -def test_create_physical_timeseries(session): - """Check that we can create a new physical timeseries.""" - PATH = '/Path/To/Test/Timeseries/', - NAME = 'Test_Timeseries', - CURVE_TYPE = Timeseries.Curve.PIECEWISELINEAR, - RESOLUTION = Timeseries.Resolution.HOUR, - UNIT_OF_MEASUREMENT = 'Unit1' - - timeseries_resource = session.create_physical_timeseries( - path=PATH, - name=NAME, - curve_type=CURVE_TYPE, - resolution=RESOLUTION, - unit_of_measurement=UNIT_OF_MEASUREMENT - ) - - assert timeseries.resource.path == PATH - assert timeseries.resource.name == NAME - assert timeseries.resource.curve_type == CURVE_TYPE - assert timeseries.resource.resolution == RESOLUTION - assert timeseries.resource.unit_of_measurement == UNIT_OF_MEASUREMENT - - # """Check that time series resource can be updated.""" - # session.update_timeseries_resource_info( - # timeseries_key, new_curve_type, new_unit_of_measurement - # ) - # timeseries_info = session.get_timeseries_resource_info(timeseries_key) - - # if new_curve_type is not None: - # assert timeseries_info.curve_type == new_curve_type - # if new_unit_of_measurement is not None: - # assert timeseries_info.unit_of_measurement == new_unit_of_measurement - - -# // --gtest_filter=CreatePhysicalTimeseriesTests.SendRequestAndCommitShouldPass -# TEST_F(CreatePhysicalTimeseriesTests, SendRequestAndCommitShouldPass) { -# const auto& create_ts_request = CreateRequest(); -# const auto& create_ts_response = Api::CreatePhysicalTimeseries(context_, create_ts_request); -# Api::Commit(context_, proto_session_id_); - -# VerifyResponse(create_ts_response); -# } - -# // --gtest_filter=CreatePhysicalTimeseriesTests.CreateExistingTimeseriesShouldThrow -# TEST_F(CreatePhysicalTimeseriesTests, CreateExistingTimeseriesShouldThrow) { -# const auto& create_ts_request = CreateRequest(); - -# const auto& send_request = [&]() -> TimeseriesResource { -# return Api::CreatePhysicalTimeseries(context_, create_ts_request); -# }; - -# // Create the timeseries. -# const auto& create_ts_response = send_request(); - -# Api::Commit(context_, proto_session_id_); - -# VerifyResponse(create_ts_response); - -# const auto& msg = -# fmt::format("An element named {} already exists in the Timeseries resource catalog\n", name_); - -# // Now try to create it again. -# EXPECT_THAT(send_request, ThrowsMessage(StrEq(msg))); -# } - -# // --gtest_filter=CreatePhysicalTimeseriesTests.InvalidPathShouldThrow -# TEST_F(CreatePhysicalTimeseriesTests, InvalidPathShouldThrow) { -# // The path string must begin and end with slashes. -# TestInvalidPath("Path/To/Test/Timeseries"); -# TestInvalidPath("Path/To/Test/Timeseries/"); -# TestInvalidPath("/Path/To/Test/Timeseries"); -# } - -# // --gtest_filter=CreatePhysicalTimeseriesTests.MissingFieldsShouldThrow -# TEST_F(CreatePhysicalTimeseriesTests, MissingFieldsShouldThrow) { -# // Can't use auto here since it's deduced to different lambda types. -# std::function clear_field = [&](CreatePhysicalTimeseriesRequest& create_ts_request) { -# create_ts_request.clear_curve_type(); -# }; - -# TestMissingField(clear_field); - -# clear_field = [&](CreatePhysicalTimeseriesRequest& create_ts_request) { -# create_ts_request.clear_resolution(); -# }; +# pytest -k TestCreatePhysicalTimeseries +@pytest.mark.database +class TestCreatePhysicalTimeseries: + class TSInitData: + def __init__(self): + self.path = '/Path/To/Test/Timeseries/' + self.name = 'Test_Timeseries' + self.curve_type = Timeseries.Curve.PIECEWISELINEAR + self.resolution = Timeseries.Resolution.HOUR + self.unit_of_measurement = 'Unit1' + + + @pytest.fixture + def ts_init_data(self): + return TestCreatePhysicalTimeseries.TSInitData() + + + @staticmethod + def _verify_timeseries(timeseries: TimeseriesResource, ts_init_data): + assert timeseries.path == ts_init_data.path + assert timeseries.name == ts_init_data.name + assert timeseries.curve_type == ts_init_data.curve_type + assert timeseries.resolution == ts_init_data.resolution + assert timeseries.unit_of_measurement == ts_init_data.unit_of_measurement + + + def test_create_physical_timeseries(self, session, ts_init_data): + """Check that we can create a new physical timeseries.""" + timeseries = session.create_physical_timeseries( + path=ts_init_data.path, + name=ts_init_data.name, + curve_type=ts_init_data.curve_type, + resolution=ts_init_data.resolution, + unit_of_measurement=ts_init_data.unit_of_measurement + ) -# TestMissingField(clear_field); + session.commit() -# clear_field = [&](CreatePhysicalTimeseriesRequest& create_ts_request) { -# create_ts_request.clear_unit_of_measurement_id(); -# }; + self._verify_timeseries(timeseries, ts_init_data) -# TestMissingField(clear_field); -# } + # Now check that the timeseries actually exists. + stored_timeseries = session.get_timeseries_resource_info( + timeseries_key=timeseries.timeseries_key + ) -# // --gtest_filter=CreatePhysicalTimeseriesTests.InvalidUnitOfMeasurementIdShouldThrow -# TEST_F(CreatePhysicalTimeseriesTests, InvalidUnitOfMeasurementIdShouldThrow) { -# unit_of_measurement_id_ = Common::Guid::Empty(); + self._verify_timeseries(stored_timeseries, ts_init_data) -# const auto& create_ts_request = CreateRequest(); -# EXPECT_THROW(Api::CreatePhysicalTimeseries(context_, create_ts_request), std::invalid_argument); -# } + def test_create_timeseries_with_non_existing_unit_of_measurement(self, session, ts_init_data): + with pytest.raises(ValueError, match="invalid unit of measurement provided"): + timeseries = session.create_physical_timeseries( + path=ts_init_data.path, + name=ts_init_data.name, + curve_type=ts_init_data.curve_type, + resolution=ts_init_data.resolution, + unit_of_measurement=INVALID_UNIT_OF_MEASUREMENT_NAME + ) @pytest.mark.asyncio From 94de2828d153ad1e31171e570549b22371d5ec27 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 8 Jul 2024 16:34:56 +0200 Subject: [PATCH 14/42] . --- src/volue/mesh/tests/test_timeseries_resource.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/volue/mesh/tests/test_timeseries_resource.py b/src/volue/mesh/tests/test_timeseries_resource.py index b5cd1bb4..cd541fb7 100644 --- a/src/volue/mesh/tests/test_timeseries_resource.py +++ b/src/volue/mesh/tests/test_timeseries_resource.py @@ -2,6 +2,8 @@ Tests for volue.mesh.TimeseriesResource """ +import random +import string import sys import grpc @@ -132,8 +134,14 @@ def test_update_timeseries_resource_with_non_existing_unit_of_measurement(sessio class TestCreatePhysicalTimeseries: class TSInitData: def __init__(self): + random_chars = 10 + + # Mesh will throw an exception if we try to create a timeseries with an existing path + # and name. Add a random suffix to the timeseries name to avoid this. + name_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=random_chars)) + self.path = '/Path/To/Test/Timeseries/' - self.name = 'Test_Timeseries' + self.name = 'Test_Timeseries_' + name_suffix self.curve_type = Timeseries.Curve.PIECEWISELINEAR self.resolution = Timeseries.Resolution.HOUR self.unit_of_measurement = 'Unit1' @@ -146,7 +154,11 @@ def ts_init_data(self): @staticmethod def _verify_timeseries(timeseries: TimeseriesResource, ts_init_data): - assert timeseries.path == ts_init_data.path + # Newly created timeseries have "Resource" prepended to their paths, and the timeseries name + # appended to it. + expected_path = 'Resource' + ts_init_data.path + ts_init_data.name + + assert timeseries.path == expected_path assert timeseries.name == ts_init_data.name assert timeseries.curve_type == ts_init_data.curve_type assert timeseries.resolution == ts_init_data.resolution From 4a28b0822afa27f089c6fea5124c77dde2010907 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Mon, 8 Jul 2024 16:50:45 +0200 Subject: [PATCH 15/42] . --- src/volue/mesh/aio/_connection.py | 2 ++ .../mesh/tests/test_timeseries_resource.py | 35 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/volue/mesh/aio/_connection.py b/src/volue/mesh/aio/_connection.py index 8b887aaf..540beb63 100644 --- a/src/volue/mesh/aio/_connection.py +++ b/src/volue/mesh/aio/_connection.py @@ -34,7 +34,9 @@ RatingCurveVersion, XySet, _from_proto_guid, + _to_proto_curve_type, _to_proto_guid, + _to_proto_resolution, _to_proto_timeseries, ) from volue.mesh.calc.forecast import ForecastFunctionsAsync diff --git a/src/volue/mesh/tests/test_timeseries_resource.py b/src/volue/mesh/tests/test_timeseries_resource.py index cd541fb7..938ff773 100644 --- a/src/volue/mesh/tests/test_timeseries_resource.py +++ b/src/volue/mesh/tests/test_timeseries_resource.py @@ -153,16 +153,16 @@ def ts_init_data(self): @staticmethod - def _verify_timeseries(timeseries: TimeseriesResource, ts_init_data): + def _verify_timeseries(timeseries: TimeseriesResource, expected_ts_data): # Newly created timeseries have "Resource" prepended to their paths, and the timeseries name # appended to it. - expected_path = 'Resource' + ts_init_data.path + ts_init_data.name + expected_path = 'Resource' + expected_ts_data.path + expected_ts_data.name assert timeseries.path == expected_path - assert timeseries.name == ts_init_data.name - assert timeseries.curve_type == ts_init_data.curve_type - assert timeseries.resolution == ts_init_data.resolution - assert timeseries.unit_of_measurement == ts_init_data.unit_of_measurement + assert timeseries.name == expected_ts_data.name + assert timeseries.curve_type == expected_ts_data.curve_type + assert timeseries.resolution == expected_ts_data.resolution + assert timeseries.unit_of_measurement == expected_ts_data.unit_of_measurement def test_create_physical_timeseries(self, session, ts_init_data): @@ -187,6 +187,29 @@ def test_create_physical_timeseries(self, session, ts_init_data): self._verify_timeseries(stored_timeseries, ts_init_data) + @pytest.mark.asyncio + async def test_create_physical_timeseries(self, async_session, ts_init_data): + """Check that we can create a new physical timeseries.""" + timeseries = await async_session.create_physical_timeseries( + path=ts_init_data.path, + name=ts_init_data.name, + curve_type=ts_init_data.curve_type, + resolution=ts_init_data.resolution, + unit_of_measurement=ts_init_data.unit_of_measurement + ) + + await async_session.commit() + + self._verify_timeseries(timeseries, ts_init_data) + + # Now check that the timeseries actually exists. + stored_timeseries = await async_session.get_timeseries_resource_info( + timeseries_key=timeseries.timeseries_key + ) + + self._verify_timeseries(stored_timeseries, ts_init_data) + + def test_create_timeseries_with_non_existing_unit_of_measurement(self, session, ts_init_data): with pytest.raises(ValueError, match="invalid unit of measurement provided"): timeseries = session.create_physical_timeseries( From 14c0736a69774c49d340a0985281edce4fd252ed Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 9 Jul 2024 09:42:53 +0200 Subject: [PATCH 16/42] . --- src/volue/mesh/examples/quickstart.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index 50fb9c87..9e50599e 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -27,7 +27,13 @@ def main(address, port, root_pem_certificate): session.commit() - print(result) + print(result.timeseries_key) + + result = session.get_timeseries_resource_info( + timeseries_key=result.timeseries_key + ) + + print(result.path) if __name__ == "__main__": From 2f5849b1e32090bc28745e6cc89f2656853fe574 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 9 Jul 2024 15:18:06 +0200 Subject: [PATCH 17/42] . --- .../mesh/tests/test_timeseries_resource.py | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/volue/mesh/tests/test_timeseries_resource.py b/src/volue/mesh/tests/test_timeseries_resource.py index 938ff773..d1fb4d55 100644 --- a/src/volue/mesh/tests/test_timeseries_resource.py +++ b/src/volue/mesh/tests/test_timeseries_resource.py @@ -179,16 +179,14 @@ def test_create_physical_timeseries(self, session, ts_init_data): self._verify_timeseries(timeseries, ts_init_data) - # Now check that the timeseries actually exists. - stored_timeseries = session.get_timeseries_resource_info( - timeseries_key=timeseries.timeseries_key - ) - - self._verify_timeseries(stored_timeseries, ts_init_data) + # TODO: We should also check that the timeseries actually exists in the database. Normally + # we'd be able to do this by using GetTimeseriesResource; however, that function currently + # requires us to know the timeseries' key, which CreatePhysicalTimeseries cannot return + # since it's generated at the commit stage. @pytest.mark.asyncio - async def test_create_physical_timeseries(self, async_session, ts_init_data): + async def test_create_physical_timeseries_async(self, async_session, ts_init_data): """Check that we can create a new physical timeseries.""" timeseries = await async_session.create_physical_timeseries( path=ts_init_data.path, @@ -202,12 +200,10 @@ async def test_create_physical_timeseries(self, async_session, ts_init_data): self._verify_timeseries(timeseries, ts_init_data) - # Now check that the timeseries actually exists. - stored_timeseries = await async_session.get_timeseries_resource_info( - timeseries_key=timeseries.timeseries_key - ) - - self._verify_timeseries(stored_timeseries, ts_init_data) + # TODO: We should also check that the timeseries actually exists in the database. Normally + # we'd be able to do this by using GetTimeseriesResource; however, that function currently + # requires us to know the timeseries' key, which CreatePhysicalTimeseries cannot return + # since it's generated at the commit stage. def test_create_timeseries_with_non_existing_unit_of_measurement(self, session, ts_init_data): From 3fe6b4d3e415969ee4619a13ff4806765ec7f03c Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 9 Jul 2024 15:53:31 +0200 Subject: [PATCH 18/42] . --- src/volue/mesh/examples/quickstart.py | 7 --- .../time_series/v1alpha/time_series.proto | 48 +++++++++++-------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index 9e50599e..a8c631e3 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -29,13 +29,6 @@ def main(address, port, root_pem_certificate): print(result.timeseries_key) - result = session.get_timeseries_resource_info( - timeseries_key=result.timeseries_key - ) - - print(result.path) - - if __name__ == "__main__": address, port, root_pem_certificate = helpers.get_connection_info() main(address, port, root_pem_certificate) diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index 68b5b342..e2edd8b6 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -78,8 +78,31 @@ service TimeseriesService { rpc CopyTimeseriesBetweenObjects(CopyTimeseriesBetweenObjectsRequest) returns (CopyTimeseriesBetweenObjectsResponse) {} // Create a new (empty) physical time series resource. Returns the newly - // created resource. See the 'CreatePhysicalTimeseriesRequest' message - // documentation for more info. + // created resource. + // + // Any missing components of the requested timeseries path will be created in + // the catalog as well. For example, if we set the CreatePhysicalTimeseriesRequest's + // 'path' field to "/Path/To/Timeseries/", and its 'name' field to + // "Test_Timeseries", the entire Path/To/Timeseries/Test_Timeseries sequence + // of catalog entries will be created if it doesn't exist already. + // + // It is not mandatory that the path mirrors the topology of the Mesh model. + // The path *must* begin and end with forward slashes. + // + // Note that the 'timeseries_key' field on the returned TimeseriesResource + // will *not* have a valid value which can be used in other APIs. This is + // because the real timeseries key values are generated afterwards, at the + // commit stage. + // + // Note also that the 'path' field on the returned TimeseriesResource won't + // have the same as the value as this field; namely, the returned 'path' will + // have a "Resource/" prefix, and will be suffixed with the contents of the + // 'name' field from this request. That is: + // + // response.path == "Resource/" + request.path + "/" + request.name + // + // Continuing with the previous example, in this case the 'path' field in the + // response will be "Resource/Path/To/Timeseries/Test_Timeseries". rpc CreatePhysicalTimeseries(CreatePhysicalTimeseriesRequest) returns (TimeseriesResource) {} } @@ -289,27 +312,12 @@ message CopyTimeseriesBetweenObjectsResponse { int64 match_series_count = 3; } +// Request for creating new physical timeseries resources. All the fields are +// mandatory. +// See the documentation of the CreatePhysicalTimeseries RPC for more info. message CreatePhysicalTimeseriesRequest { volue.mesh.grpc.type.Guid session_id = 1; - // The path of the time series resource in the Mesh resource catalog. Any - // missing components of the path will be created in the catalog as well. - // For example, if we set 'path' to "/Path/To/Timeseries/" and 'name' to - // "Test_Timeseries", the entire Path/To/Timeseries/Test_Timeseries sequence - // of catalog entries will be created if it doesn't exist already. - // - // It is not mandatory that the path mirrors the topology of the Mesh model. - // The path *must* begin and end with forward slashes. - // - // Note that the 'path' field on the returned TimeseriesResource doesn't have - // the same as the value as this field; namely, the returned 'path' will have - // a "Resource/" prefix, and will be suffixed with the contents of the 'name' - // field from this request. That is: - // - // response.path == "Resource/" + request.path + "/" + request.name - // - // Continuing with the previous example, in this case the 'path' field in the - // response will be "Resource/Path/To/Timeseries/Test_Timeseries". string path = 2; string name = 3; From b5b04b1e0d2447d51651e2233d5ea41e2510d3dc Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 9 Jul 2024 16:12:53 +0200 Subject: [PATCH 19/42] . --- src/volue/mesh/proto/time_series/v1alpha/time_series.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index e2edd8b6..ef5d712c 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -95,9 +95,9 @@ service TimeseriesService { // commit stage. // // Note also that the 'path' field on the returned TimeseriesResource won't - // have the same as the value as this field; namely, the returned 'path' will - // have a "Resource/" prefix, and will be suffixed with the contents of the - // 'name' field from this request. That is: + // have the same as the value as CreatePhysicalTimeseriesRequest's 'path'; + // namely, the returned 'path' will have a "Resource/" prefix, and will be + // suffixed with the contents of the 'name' field from this request. That is: // // response.path == "Resource/" + request.path + "/" + request.name // From 19a6418c1b764a2cae82908b3a92b1368a247827 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 9 Jul 2024 16:51:45 +0200 Subject: [PATCH 20/42] . --- src/volue/mesh/examples/quickstart.py | 1 + .../mesh/tests/test_timeseries_resource.py | 29 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index a8c631e3..e56a8504 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -29,6 +29,7 @@ def main(address, port, root_pem_certificate): print(result.timeseries_key) + if __name__ == "__main__": address, port, root_pem_certificate = helpers.get_connection_info() main(address, port, root_pem_certificate) diff --git a/src/volue/mesh/tests/test_timeseries_resource.py b/src/volue/mesh/tests/test_timeseries_resource.py index d1fb4d55..b897e7c6 100644 --- a/src/volue/mesh/tests/test_timeseries_resource.py +++ b/src/volue/mesh/tests/test_timeseries_resource.py @@ -12,7 +12,7 @@ from volue.mesh import Timeseries, TimeseriesResource -INVALID_UNIT_OF_MEASUREMENT_NAME = 'no_such_unit' +INVALID_UNIT_OF_MEASUREMENT_NAME = "no_such_unit" def get_physical_timeseries(): @@ -138,25 +138,25 @@ def __init__(self): # Mesh will throw an exception if we try to create a timeseries with an existing path # and name. Add a random suffix to the timeseries name to avoid this. - name_suffix = ''.join(random.choices(string.ascii_uppercase + string.digits, k=random_chars)) + name_suffix = "".join( + random.choices(string.ascii_uppercase + string.digits, k=random_chars) + ) - self.path = '/Path/To/Test/Timeseries/' - self.name = 'Test_Timeseries_' + name_suffix + self.path = "/Path/To/Test/Timeseries/" + self.name = "Test_Timeseries_" + name_suffix self.curve_type = Timeseries.Curve.PIECEWISELINEAR self.resolution = Timeseries.Resolution.HOUR - self.unit_of_measurement = 'Unit1' - + self.unit_of_measurement = "Unit1" @pytest.fixture def ts_init_data(self): return TestCreatePhysicalTimeseries.TSInitData() - @staticmethod def _verify_timeseries(timeseries: TimeseriesResource, expected_ts_data): # Newly created timeseries have "Resource" prepended to their paths, and the timeseries name # appended to it. - expected_path = 'Resource' + expected_ts_data.path + expected_ts_data.name + expected_path = "Resource" + expected_ts_data.path + expected_ts_data.name assert timeseries.path == expected_path assert timeseries.name == expected_ts_data.name @@ -164,7 +164,6 @@ def _verify_timeseries(timeseries: TimeseriesResource, expected_ts_data): assert timeseries.resolution == expected_ts_data.resolution assert timeseries.unit_of_measurement == expected_ts_data.unit_of_measurement - def test_create_physical_timeseries(self, session, ts_init_data): """Check that we can create a new physical timeseries.""" timeseries = session.create_physical_timeseries( @@ -172,7 +171,7 @@ def test_create_physical_timeseries(self, session, ts_init_data): name=ts_init_data.name, curve_type=ts_init_data.curve_type, resolution=ts_init_data.resolution, - unit_of_measurement=ts_init_data.unit_of_measurement + unit_of_measurement=ts_init_data.unit_of_measurement, ) session.commit() @@ -184,7 +183,6 @@ def test_create_physical_timeseries(self, session, ts_init_data): # requires us to know the timeseries' key, which CreatePhysicalTimeseries cannot return # since it's generated at the commit stage. - @pytest.mark.asyncio async def test_create_physical_timeseries_async(self, async_session, ts_init_data): """Check that we can create a new physical timeseries.""" @@ -193,7 +191,7 @@ async def test_create_physical_timeseries_async(self, async_session, ts_init_dat name=ts_init_data.name, curve_type=ts_init_data.curve_type, resolution=ts_init_data.resolution, - unit_of_measurement=ts_init_data.unit_of_measurement + unit_of_measurement=ts_init_data.unit_of_measurement, ) await async_session.commit() @@ -205,15 +203,16 @@ async def test_create_physical_timeseries_async(self, async_session, ts_init_dat # requires us to know the timeseries' key, which CreatePhysicalTimeseries cannot return # since it's generated at the commit stage. - - def test_create_timeseries_with_non_existing_unit_of_measurement(self, session, ts_init_data): + def test_create_timeseries_with_non_existing_unit_of_measurement( + self, session, ts_init_data + ): with pytest.raises(ValueError, match="invalid unit of measurement provided"): timeseries = session.create_physical_timeseries( path=ts_init_data.path, name=ts_init_data.name, curve_type=ts_init_data.curve_type, resolution=ts_init_data.resolution, - unit_of_measurement=INVALID_UNIT_OF_MEASUREMENT_NAME + unit_of_measurement=INVALID_UNIT_OF_MEASUREMENT_NAME, ) From 59a0377fbe0817a548d4f239ec7e718a6741f1e9 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Wed, 10 Jul 2024 11:13:54 +0200 Subject: [PATCH 21/42] . --- .github/workflows/install_dev_env/action.yml | 2 +- .github/workflows/usage.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/install_dev_env/action.yml b/.github/workflows/install_dev_env/action.yml index cce6cdd7..2cf1e038 100644 --- a/.github/workflows/install_dev_env/action.yml +++ b/.github/workflows/install_dev_env/action.yml @@ -41,4 +41,4 @@ runs: id: download-mesh-server with: GITHUB_TOKEN: ${{ inputs.token }} - MESH_SERVICE_TAG: 'v2.15.0.1960' + MESH_SERVICE_TAG: 'v2.15.0.1975' diff --git a/.github/workflows/usage.yml b/.github/workflows/usage.yml index 02eaa8b3..4d78d4f4 100644 --- a/.github/workflows/usage.yml +++ b/.github/workflows/usage.yml @@ -39,7 +39,7 @@ jobs: id: download-mesh-server with: GITHUB_TOKEN: ${{ secrets.OAUTH_TOKEN }} - MESH_SERVICE_TAG: 'v2.15.0.1960' + MESH_SERVICE_TAG: 'v2.15.0.1975' # run one example before installing pytest packages and pandas # to check if all dependencies are installed together with Mesh Python SDK pip package From e8c2ddb425bb686a4b9cf7f6204e89f8e7f82a1d Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 12 Jul 2024 14:29:14 +0200 Subject: [PATCH 22/42] . --- .../examples/create_physical_timeseries.py | 27 ++++++++++++++++ src/volue/mesh/examples/quickstart.py | 31 +++++++------------ .../time_series/v1alpha/time_series.proto | 4 +-- .../mesh/tests/test_timeseries_resource.py | 20 ++++++------ 4 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 src/volue/mesh/examples/create_physical_timeseries.py diff --git a/src/volue/mesh/examples/create_physical_timeseries.py b/src/volue/mesh/examples/create_physical_timeseries.py new file mode 100644 index 00000000..8b81bfc0 --- /dev/null +++ b/src/volue/mesh/examples/create_physical_timeseries.py @@ -0,0 +1,27 @@ +import helpers + +from volue.mesh import Connection, Timeseries + + +def main(address, port, root_pem_certificate): + """Showing how to create a physical time series resource.""" + + connection = Connection(address, port, root_pem_certificate) + + with connection.create_session() as session: + result = session.create_physical_timeseries( + path="/Path/To/Test/Timeseries/", + name="Test_Timeseries", + curve_type=Timeseries.Curve.PIECEWISELINEAR, + resolution=Timeseries.Resolution.HOUR, + unit_of_measurement="Unit1", + ) + + session.commit() + + print(result) + + +if __name__ == "__main__": + address, port, root_pem_certificate = helpers.get_connection_info() + main(address, port, root_pem_certificate) diff --git a/src/volue/mesh/examples/quickstart.py b/src/volue/mesh/examples/quickstart.py index e56a8504..31aaffe7 100644 --- a/src/volue/mesh/examples/quickstart.py +++ b/src/volue/mesh/examples/quickstart.py @@ -1,35 +1,28 @@ import helpers -from volue.mesh import Connection, Timeseries +from volue.mesh import Connection -def main(address, port, root_pem_certificate): +def main(address, tls_root_pem_cert): """Showing the quickest way to get started.""" - # Configure the connection you want - connection = Connection(address, port, root_pem_certificate) + # For production environments create connection using: with_tls, with_kerberos, or with_external_access_token, e.g.: + # connection = Connection.with_tls(address, tls_root_pem_cert) + connection = Connection.insecure(address) # Which version is the server running version_info = connection.get_version() print(f"Connected to {version_info.name} {version_info.version}") # Create a remote session on the Volue Mesh server - with connection.create_session() as session: - print("You have now an open session and can request time series") + session = connection.create_session() + session.open() + print("You have now an open session and can request time series") - result = session.create_physical_timeseries( - path="/Path/To/Test/Timeseries/", - name="Test_Timeseries", - curve_type=Timeseries.Curve.PIECEWISELINEAR, - resolution=Timeseries.Resolution.HOUR, - unit_of_measurement="Unit1", - ) - - session.commit() - - print(result.timeseries_key) + # Close the remote session + session.close() if __name__ == "__main__": - address, port, root_pem_certificate = helpers.get_connection_info() - main(address, port, root_pem_certificate) + address, tls_root_pem_cert = helpers.get_connection_info() + main(address, tls_root_pem_cert) diff --git a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto index ef5d712c..b86e9135 100644 --- a/src/volue/mesh/proto/time_series/v1alpha/time_series.proto +++ b/src/volue/mesh/proto/time_series/v1alpha/time_series.proto @@ -80,7 +80,7 @@ service TimeseriesService { // Create a new (empty) physical time series resource. Returns the newly // created resource. // - // Any missing components of the requested timeseries path will be created in + // Any missing components of the requested time series path will be created in // the catalog as well. For example, if we set the CreatePhysicalTimeseriesRequest's // 'path' field to "/Path/To/Timeseries/", and its 'name' field to // "Test_Timeseries", the entire Path/To/Timeseries/Test_Timeseries sequence @@ -91,7 +91,7 @@ service TimeseriesService { // // Note that the 'timeseries_key' field on the returned TimeseriesResource // will *not* have a valid value which can be used in other APIs. This is - // because the real timeseries key values are generated afterwards, at the + // because the real time series key values are generated afterwards, at the // commit stage. // // Note also that the 'path' field on the returned TimeseriesResource won't diff --git a/src/volue/mesh/tests/test_timeseries_resource.py b/src/volue/mesh/tests/test_timeseries_resource.py index b897e7c6..5d59060b 100644 --- a/src/volue/mesh/tests/test_timeseries_resource.py +++ b/src/volue/mesh/tests/test_timeseries_resource.py @@ -136,8 +136,8 @@ class TSInitData: def __init__(self): random_chars = 10 - # Mesh will throw an exception if we try to create a timeseries with an existing path - # and name. Add a random suffix to the timeseries name to avoid this. + # Mesh will throw an exception if we try to create a time series with an existing path + # and name. Add a random suffix to the time series name to avoid this. name_suffix = "".join( random.choices(string.ascii_uppercase + string.digits, k=random_chars) ) @@ -154,8 +154,8 @@ def ts_init_data(self): @staticmethod def _verify_timeseries(timeseries: TimeseriesResource, expected_ts_data): - # Newly created timeseries have "Resource" prepended to their paths, and the timeseries name - # appended to it. + # Newly created time series have "Resource" prepended to their paths, and the time series + # name appended to it. expected_path = "Resource" + expected_ts_data.path + expected_ts_data.name assert timeseries.path == expected_path @@ -165,7 +165,7 @@ def _verify_timeseries(timeseries: TimeseriesResource, expected_ts_data): assert timeseries.unit_of_measurement == expected_ts_data.unit_of_measurement def test_create_physical_timeseries(self, session, ts_init_data): - """Check that we can create a new physical timeseries.""" + """Check that we can create a new physical time series.""" timeseries = session.create_physical_timeseries( path=ts_init_data.path, name=ts_init_data.name, @@ -178,14 +178,14 @@ def test_create_physical_timeseries(self, session, ts_init_data): self._verify_timeseries(timeseries, ts_init_data) - # TODO: We should also check that the timeseries actually exists in the database. Normally + # TODO: We should also check that the time series actually exists in the database. Normally # we'd be able to do this by using GetTimeseriesResource; however, that function currently - # requires us to know the timeseries' key, which CreatePhysicalTimeseries cannot return + # requires us to know the time series' key, which CreatePhysicalTimeseries cannot return # since it's generated at the commit stage. @pytest.mark.asyncio async def test_create_physical_timeseries_async(self, async_session, ts_init_data): - """Check that we can create a new physical timeseries.""" + """Check that we can create a new physical time series.""" timeseries = await async_session.create_physical_timeseries( path=ts_init_data.path, name=ts_init_data.name, @@ -198,9 +198,9 @@ async def test_create_physical_timeseries_async(self, async_session, ts_init_dat self._verify_timeseries(timeseries, ts_init_data) - # TODO: We should also check that the timeseries actually exists in the database. Normally + # TODO: We should also check that the time series actually exists in the database. Normally # we'd be able to do this by using GetTimeseriesResource; however, that function currently - # requires us to know the timeseries' key, which CreatePhysicalTimeseries cannot return + # requires us to know the time series' key, which CreatePhysicalTimeseries cannot return # since it's generated at the commit stage. def test_create_timeseries_with_non_existing_unit_of_measurement( From ba5ed275f9cd69159d96aebd96d9a9e092133667 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 12 Jul 2024 14:38:06 +0200 Subject: [PATCH 23/42] . --- docs/source/versions.rst | 4 ++++ src/volue/mesh/examples/create_physical_timeseries.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/source/versions.rst b/docs/source/versions.rst index d66ab97d..56b1d205 100644 --- a/docs/source/versions.rst +++ b/docs/source/versions.rst @@ -23,6 +23,10 @@ New features See :ref:`mesh_client:gRPC communication`. +- We have added a new gRPC function to create physical time series. :issue:`383` + + See `create_physical_timeseries`. + Changes ~~~~~~~~~~~~~~~~~~ diff --git a/src/volue/mesh/examples/create_physical_timeseries.py b/src/volue/mesh/examples/create_physical_timeseries.py index 8b81bfc0..19068007 100644 --- a/src/volue/mesh/examples/create_physical_timeseries.py +++ b/src/volue/mesh/examples/create_physical_timeseries.py @@ -6,7 +6,9 @@ def main(address, port, root_pem_certificate): """Showing how to create a physical time series resource.""" - connection = Connection(address, port, root_pem_certificate) + # For production environments create connection using: with_tls, with_kerberos, or with_external_access_token, e.g.: + # connection = Connection.with_tls(address, tls_root_pem_cert) + connection = Connection.insecure(address) with connection.create_session() as session: result = session.create_physical_timeseries( From 7db0fe65f7e3364ceeb356659883173eb80e384c Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 12 Jul 2024 14:53:20 +0200 Subject: [PATCH 24/42] . --- src/volue/mesh/examples/create_physical_timeseries.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/volue/mesh/examples/create_physical_timeseries.py b/src/volue/mesh/examples/create_physical_timeseries.py index 19068007..4d0eb787 100644 --- a/src/volue/mesh/examples/create_physical_timeseries.py +++ b/src/volue/mesh/examples/create_physical_timeseries.py @@ -3,7 +3,7 @@ from volue.mesh import Connection, Timeseries -def main(address, port, root_pem_certificate): +def main(address, tls_root_pem_cert): """Showing how to create a physical time series resource.""" # For production environments create connection using: with_tls, with_kerberos, or with_external_access_token, e.g.: @@ -25,5 +25,5 @@ def main(address, port, root_pem_certificate): if __name__ == "__main__": - address, port, root_pem_certificate = helpers.get_connection_info() - main(address, port, root_pem_certificate) + address, tls_root_pem_cert = helpers.get_connection_info() + main(address, tls_root_pem_cert) From 47a220521a8a37f841c9f505df39adf9d254feaf Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 12 Jul 2024 15:30:16 +0200 Subject: [PATCH 25/42] . --- .../mesh/examples/create_physical_timeseries.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/volue/mesh/examples/create_physical_timeseries.py b/src/volue/mesh/examples/create_physical_timeseries.py index 4d0eb787..b7498eff 100644 --- a/src/volue/mesh/examples/create_physical_timeseries.py +++ b/src/volue/mesh/examples/create_physical_timeseries.py @@ -11,9 +11,13 @@ def main(address, tls_root_pem_cert): connection = Connection.insecure(address) with connection.create_session() as session: + # Mesh will throw an exception if we try to create a time series with an existing path + # and name. Add a random suffix to the time series name to avoid this. + name_suffix = get_random_suffix() + result = session.create_physical_timeseries( path="/Path/To/Test/Timeseries/", - name="Test_Timeseries", + name="Test_Timeseries_" + name_suffix, curve_type=Timeseries.Curve.PIECEWISELINEAR, resolution=Timeseries.Resolution.HOUR, unit_of_measurement="Unit1", @@ -24,6 +28,14 @@ def main(address, tls_root_pem_cert): print(result) +def get_random_suffix() -> str: + random_chars = 10 + + return "".join( + random.choices(string.ascii_uppercase + string.digits, k=random_chars) + ) + + if __name__ == "__main__": address, tls_root_pem_cert = helpers.get_connection_info() main(address, tls_root_pem_cert) From b146341d42312a8e8b15e8e50da09255327356cc Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 12 Jul 2024 15:44:49 +0200 Subject: [PATCH 26/42] . --- src/volue/mesh/examples/create_physical_timeseries.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/volue/mesh/examples/create_physical_timeseries.py b/src/volue/mesh/examples/create_physical_timeseries.py index b7498eff..50c209ea 100644 --- a/src/volue/mesh/examples/create_physical_timeseries.py +++ b/src/volue/mesh/examples/create_physical_timeseries.py @@ -1,4 +1,5 @@ import helpers +import random from volue.mesh import Connection, Timeseries From 4beff2d96f82543c2fdcc9ab60072d83f9b10229 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 12 Jul 2024 15:52:24 +0200 Subject: [PATCH 27/42] . --- src/volue/mesh/examples/create_physical_timeseries.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/volue/mesh/examples/create_physical_timeseries.py b/src/volue/mesh/examples/create_physical_timeseries.py index 50c209ea..873d5572 100644 --- a/src/volue/mesh/examples/create_physical_timeseries.py +++ b/src/volue/mesh/examples/create_physical_timeseries.py @@ -1,5 +1,6 @@ import helpers import random +import string from volue.mesh import Connection, Timeseries From 10322b120b073462fd4cba9f235e0a2ae2c0c497 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 12:53:40 +0200 Subject: [PATCH 28/42] . --- docs/source/versions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/versions.rst b/docs/source/versions.rst index 56b1d205..78f95106 100644 --- a/docs/source/versions.rst +++ b/docs/source/versions.rst @@ -23,7 +23,7 @@ New features See :ref:`mesh_client:gRPC communication`. -- We have added a new gRPC function to create physical time series. :issue:`383` +- Support for creating physical time series. :issue:`383` See `create_physical_timeseries`. From 6f4f484d44f94e97518f5cf609b1de44f76cac3d Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 15:25:59 +0200 Subject: [PATCH 29/42] . --- pyproject.toml | 1 + src/volue/mesh/_common.py | 102 ++++++++++++++------------------------ 2 files changed, 38 insertions(+), 65 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fdd4b598..0831c3e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ pyarrow = ">=7.0.0" protobuf = ">=3.20.1" winkerberos = { version = ">=0.9.1", markers = "sys_platform == 'win32'" } kerberos = { version = "^1.3.1", markers = "sys_platform == 'linux'" } +bidict = ">=0.23.1" python-dateutil = "^2.8.2" # six is a dependecy of dateutil # version 1.16.0 is required for Python 3.12, see: diff --git a/src/volue/mesh/_common.py b/src/volue/mesh/_common.py index 25570912..908b4275 100644 --- a/src/volue/mesh/_common.py +++ b/src/volue/mesh/_common.py @@ -13,6 +13,8 @@ import pyarrow as pa from google.protobuf import field_mask_pb2, timestamp_pb2 +from bidict import bidict + from volue.mesh import Timeseries from volue.mesh.proto import type from volue.mesh.proto.auth.v1alpha import auth_pb2 @@ -307,16 +309,15 @@ class LogMessage: @classmethod def _from_proto(cls, proto): - if proto.level == type.resources_pb2.LogLevel.TRACE: - level = logging.DEBUG - elif proto.level == type.resources_pb2.LogLevel.DEBUG: - level = logging.DEBUG - elif proto.level == type.resources_pb2.LogLevel.INFO: - level = logging.INFO - elif proto.level == type.resources_pb2.LogLevel.WARN: - level = logging.WARNING - elif proto.level == type.resources_pb2.LogLevel.ERR: - level = logging.ERROR + levels = { + type.resources_pb2.LogLevel.TRACE: logging.DEBUG, + type.resources_pb2.LogLevel.DEBUG: logging.DEBUG, + type.resources_pb2.LogLevel.INFO: logging.INFO, + type.resources_pb2.LogLevel.WARN: logging.WARNING, + type.resources_pb2.LogLevel.ERR: logging.ERROR, + } + + level = levels[proto.level] return cls(level, proto.message) @@ -360,6 +361,17 @@ def _from_proto_guid(guid: Optional[type.resources_pb2.Guid]) -> Optional[uuid.U return uuid.UUID(bytes_le=guid.bytes_le) +# We intentionally leave out Curve.UNKNOWN so that a KeyError will be raised if we receive such +# values. +CURVE_TYPES = bidict( + { + Timeseries.Curve.PIECEWISELINEAR: type.resources_pb2.Curve.PIECEWISELINEAR, + Timeseries.Curve.STAIRCASE: type.resources_pb2.Curve.STAIRCASE, + Timeseries.Curve.STAIRCASESTARTOFSTEP: type.resources_pb2.Curve.STAIRCASESTARTOFSTEP, + } +) + + def _to_proto_curve_type(curve: Timeseries.Curve) -> type.resources_pb2.Curve: """ Converts from Timeseries.Curve type to protobuf curve type. @@ -368,13 +380,8 @@ def _to_proto_curve_type(curve: Timeseries.Curve) -> type.resources_pb2.Curve: curve: The curve to convert. """ proto_curve = type.resources_pb2.Curve() - proto_curve.type = type.resources_pb2.Curve.UNKNOWN - if curve == Timeseries.Curve.PIECEWISELINEAR: - proto_curve.type = type.resources_pb2.Curve.PIECEWISELINEAR - elif curve == Timeseries.Curve.STAIRCASE: - proto_curve.type = type.resources_pb2.Curve.STAIRCASE - elif curve == Timeseries.Curve.STAIRCASESTARTOFSTEP: - proto_curve.type = type.resources_pb2.Curve.STAIRCASESTARTOFSTEP + + proto_curve.type = CURVE_TYPES[curve] return proto_curve @@ -386,19 +393,21 @@ def _from_proto_curve_type(proto_curve: type.resources_pb2.Curve) -> Timeseries. Args: proto_curve: The protobuf curve to convert. """ - curve = Timeseries.Curve.UNKNOWN + return CURVE_TYPES.inverse(proto_curve) - if proto_curve.type == type.resources_pb2.Curve.PIECEWISELINEAR: - curve = Timeseries.Curve.PIECEWISELINEAR - elif proto_curve.type == type.resources_pb2.Curve.STAIRCASE: - curve = Timeseries.Curve.STAIRCASE - elif proto_curve.type == type.resources_pb2.Curve.STAIRCASESTARTOFSTEP: - curve = Timeseries.Curve.STAIRCASESTARTOFSTEP - return curve +RESOLUTIONS = { + Timeseries.Resolution.BREAKPOINT: type.resources_pb2.Resolution.BREAKPOINT, + Timeseries.Resolution.MIN15: type.resources_pb2.Resolution.MIN15, + Timeseries.Resolution.MIN30: type.resources_pb2.Resolution.MIN30, + Timeseries.Resolution.HOUR: type.resources_pb2.Resolution.HOUR, + Timeseries.Resolution.DAY: type.resources_pb2.Resolution.DAY, + Timeseries.Resolution.WEEK: type.resources_pb2.Resolution.WEEK, + Timeseries.Resolution.MONTH: type.resources_pb2.Resolution.MONTH, + Timeseries.Resolution.YEAR: type.resources_pb2.Resolution.YEAR, +} -# FIXME: Don't have such duplicated functions. def _to_proto_resolution( resolution: Timeseries.Resolution, ) -> type.resources_pb2.Resolution: @@ -410,25 +419,7 @@ def _to_proto_resolution( """ proto_resolution = type.resources_pb2.Resolution() - if resolution == Timeseries.Resolution.BREAKPOINT: - proto_resolution.type = type.resources_pb2.Resolution.BREAKPOINT - elif resolution == Timeseries.Resolution.MIN15: - proto_resolution.type = type.resources_pb2.Resolution.MIN15 - elif resolution == Timeseries.Resolution.MIN30: - proto_resolution.type = type.resources_pb2.Resolution.MIN30 - elif resolution == Timeseries.Resolution.HOUR: - proto_resolution.type = type.resources_pb2.Resolution.HOUR - elif resolution == Timeseries.Resolution.DAY: - proto_resolution.type = type.resources_pb2.Resolution.DAY - elif resolution == Timeseries.Resolution.WEEK: - proto_resolution.type = type.resources_pb2.Resolution.WEEK - elif resolution == Timeseries.Resolution.MONTH: - proto_resolution.type = type.resources_pb2.Resolution.MONTH - elif resolution == Timeseries.Resolution.YEAR: - proto_resolution.type = type.resources_pb2.Resolution.YEAR - else: - # FIXME: Should we throw instead? - proto_resolution.type = type.resources_pb2.Resolution.RESOLUTION_UNSPECIFIED + proto_resolution.type = RESOLUTIONS[resolution] return proto_resolution @@ -442,26 +433,7 @@ def _from_proto_resolution( Args: proto_resolution: The protobuf resolution to convert. """ - resolution = Timeseries.Resolution.UNSPECIFIED - - if proto_resolution.type == type.resources_pb2.Resolution.BREAKPOINT: - resolution = Timeseries.Resolution.BREAKPOINT - elif proto_resolution.type == type.resources_pb2.Resolution.MIN15: - resolution = Timeseries.Resolution.MIN15 - elif proto_resolution.type == type.resources_pb2.Resolution.MIN30: - resolution = Timeseries.Resolution.MIN30 - elif proto_resolution.type == type.resources_pb2.Resolution.HOUR: - resolution = Timeseries.Resolution.HOUR - elif proto_resolution.type == type.resources_pb2.Resolution.DAY: - resolution = Timeseries.Resolution.DAY - elif proto_resolution.type == type.resources_pb2.Resolution.WEEK: - resolution = Timeseries.Resolution.WEEK - elif proto_resolution.type == type.resources_pb2.Resolution.MONTH: - resolution = Timeseries.Resolution.MONTH - elif proto_resolution.type == type.resources_pb2.Resolution.YEAR: - resolution = Timeseries.Resolution.YEAR - - return resolution + return RESOLUTIONS.inverse(proto_resolution) def _to_proto_utcinterval( From 8683e293f72f77a182f152b4a45e14372e5af8e5 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 15:51:43 +0200 Subject: [PATCH 30/42] . --- .github/workflows/long_tests.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index 1be2809d..e88ff7c7 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -1,8 +1,12 @@ name: Long tests -on: - schedule: - - cron: '0 3 * * *' +# on: +# schedule: +# - cron: '0 3 * * *' + +push: + branches: + - 2_fix_duplicated_code workflow_dispatch: From eca66464b2d404648e5b9e84d40ee7a1354f0095 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 15:52:21 +0200 Subject: [PATCH 31/42] . --- .github/workflows/long_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index e88ff7c7..bc500d31 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -8,6 +8,7 @@ push: branches: - 2_fix_duplicated_code + workflow_dispatch: jobs: From 9a17452308b3953473df3b953c26bf7395e8c3de Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 15:53:54 +0200 Subject: [PATCH 32/42] . --- .github/workflows/long_tests.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index bc500d31..6cf3d524 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -4,10 +4,10 @@ name: Long tests # schedule: # - cron: '0 3 * * *' -push: - branches: - - 2_fix_duplicated_code - +on: + push: + branches: + - 2_fix_duplicated_code workflow_dispatch: From c6cc8282995d506f6578246c69310ed731eec750 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 16:00:27 +0200 Subject: [PATCH 33/42] . --- .github/workflows/long_tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index 6cf3d524..322b9ee9 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -24,11 +24,12 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v3 + path: 2_fix_duplicated_code - name: Check if branch changed run: | echo "NEW_COMMITS=$(git log --since '1 day ago' --oneline | wc -l)" >> "$env:GITHUB_ENV" - name: Install dev environment - if: ${{ env.NEW_COMMITS > 0 }} + # if: ${{ env.NEW_COMMITS > 0 }} uses: ./.github/workflows/install_dev_env with: python-version: ${{ matrix.python-version }} From 3072b5413ff27780abd4b1d13b2c3c143c56390c Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 16:01:06 +0200 Subject: [PATCH 34/42] . --- .github/workflows/long_tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index 322b9ee9..e7a8d244 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -25,9 +25,9 @@ jobs: - name: Check out repository uses: actions/checkout@v3 path: 2_fix_duplicated_code - - name: Check if branch changed - run: | - echo "NEW_COMMITS=$(git log --since '1 day ago' --oneline | wc -l)" >> "$env:GITHUB_ENV" + # - name: Check if branch changed + # run: | + # echo "NEW_COMMITS=$(git log --since '1 day ago' --oneline | wc -l)" >> "$env:GITHUB_ENV" - name: Install dev environment # if: ${{ env.NEW_COMMITS > 0 }} uses: ./.github/workflows/install_dev_env From 874e249d4f0fc111ab176b5875e0aa7d15bc9f2b Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 16:04:24 +0200 Subject: [PATCH 35/42] . --- .github/workflows/long_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index e7a8d244..c953bf05 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -9,6 +9,7 @@ on: branches: - 2_fix_duplicated_code + workflow_dispatch: jobs: From dd54039a79c9975db0fe5904b3f642e3652d473e Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 16:07:15 +0200 Subject: [PATCH 36/42] . --- .github/workflows/long_tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index c953bf05..965d0914 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -25,7 +25,8 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v3 - path: 2_fix_duplicated_code + with: + path: 2_fix_duplicated_code # - name: Check if branch changed # run: | # echo "NEW_COMMITS=$(git log --since '1 day ago' --oneline | wc -l)" >> "$env:GITHUB_ENV" From 72d63f5bed513d4aea61ed1966b7e72fd2f158ff Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 16:11:52 +0200 Subject: [PATCH 37/42] . --- .github/workflows/long_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index 965d0914..2bd29462 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -26,7 +26,7 @@ jobs: - name: Check out repository uses: actions/checkout@v3 with: - path: 2_fix_duplicated_code + ref: 2_fix_duplicated_code # - name: Check if branch changed # run: | # echo "NEW_COMMITS=$(git log --since '1 day ago' --oneline | wc -l)" >> "$env:GITHUB_ENV" From 5f0dca712b7be5e2ee7a3362d06229e1f79f334b Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Tue, 16 Jul 2024 16:19:48 +0200 Subject: [PATCH 38/42] . --- .github/workflows/long_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index 2bd29462..bb428874 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -37,7 +37,7 @@ jobs: python-version: ${{ matrix.python-version }} token: ${{ secrets.OAUTH_TOKEN }} - name: Run long tests - if: ${{ success() && env.NEW_COMMITS > 0 }} + if: ${{ success() }} #&& env.NEW_COMMITS > 0 }} working-directory: ${{ github.workspace }} run: | poetry run pytest -m "long" From 8678dd8323781a791c1f10029bd06566d87173a0 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Wed, 17 Jul 2024 16:14:25 +0200 Subject: [PATCH 39/42] . --- .github/workflows/long_tests.yml | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/.github/workflows/long_tests.yml b/.github/workflows/long_tests.yml index bb428874..1be2809d 100644 --- a/.github/workflows/long_tests.yml +++ b/.github/workflows/long_tests.yml @@ -1,14 +1,8 @@ name: Long tests -# on: -# schedule: -# - cron: '0 3 * * *' - on: - push: - branches: - - 2_fix_duplicated_code - + schedule: + - cron: '0 3 * * *' workflow_dispatch: @@ -25,19 +19,17 @@ jobs: steps: - name: Check out repository uses: actions/checkout@v3 - with: - ref: 2_fix_duplicated_code - # - name: Check if branch changed - # run: | - # echo "NEW_COMMITS=$(git log --since '1 day ago' --oneline | wc -l)" >> "$env:GITHUB_ENV" + - name: Check if branch changed + run: | + echo "NEW_COMMITS=$(git log --since '1 day ago' --oneline | wc -l)" >> "$env:GITHUB_ENV" - name: Install dev environment - # if: ${{ env.NEW_COMMITS > 0 }} + if: ${{ env.NEW_COMMITS > 0 }} uses: ./.github/workflows/install_dev_env with: python-version: ${{ matrix.python-version }} token: ${{ secrets.OAUTH_TOKEN }} - name: Run long tests - if: ${{ success() }} #&& env.NEW_COMMITS > 0 }} + if: ${{ success() && env.NEW_COMMITS > 0 }} working-directory: ${{ github.workspace }} run: | poetry run pytest -m "long" From 42a3ec810ae8cb7fba3084ae0ba4e03d61ea81f8 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Wed, 17 Jul 2024 16:21:17 +0200 Subject: [PATCH 40/42] . --- src/volue/mesh/_common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/volue/mesh/_common.py b/src/volue/mesh/_common.py index 1780dc9f..7de0718c 100644 --- a/src/volue/mesh/_common.py +++ b/src/volue/mesh/_common.py @@ -393,7 +393,7 @@ def _from_proto_curve_type(proto_curve: type.resources_pb2.Curve) -> Timeseries. Args: proto_curve: The protobuf curve to convert. """ - return CURVE_TYPES.inverse(proto_curve) + return CURVE_TYPES.inverse[proto_curve] RESOLUTIONS = { @@ -468,7 +468,7 @@ def _from_proto_resolution( Args: proto_resolution: The protobuf resolution to convert. """ - return RESOLUTIONS.inverse(proto_resolution) + return RESOLUTIONS.inverse[proto_resolution] def _to_proto_utcinterval( From 009a1863df437c750d4b398332c77d8ad6a009e9 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 19 Jul 2024 10:35:16 +0200 Subject: [PATCH 41/42] . --- src/volue/mesh/_common.py | 43 ++++----------------------------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/src/volue/mesh/_common.py b/src/volue/mesh/_common.py index 7de0718c..3b14cd56 100644 --- a/src/volue/mesh/_common.py +++ b/src/volue/mesh/_common.py @@ -393,10 +393,10 @@ def _from_proto_curve_type(proto_curve: type.resources_pb2.Curve) -> Timeseries. Args: proto_curve: The protobuf curve to convert. """ - return CURVE_TYPES.inverse[proto_curve] + return CURVE_TYPES.inverse[proto_curve.type] -RESOLUTIONS = { +RESOLUTIONS = bidict({ Timeseries.Resolution.BREAKPOINT: type.resources_pb2.Resolution.BREAKPOINT, Timeseries.Resolution.MIN15: type.resources_pb2.Resolution.MIN15, Timeseries.Resolution.MIN30: type.resources_pb2.Resolution.MIN30, @@ -405,7 +405,7 @@ def _from_proto_curve_type(proto_curve: type.resources_pb2.Curve) -> Timeseries. Timeseries.Resolution.WEEK: type.resources_pb2.Resolution.WEEK, Timeseries.Resolution.MONTH: type.resources_pb2.Resolution.MONTH, Timeseries.Resolution.YEAR: type.resources_pb2.Resolution.YEAR, -} +}) def _to_proto_resolution( @@ -424,41 +424,6 @@ def _to_proto_resolution( return proto_resolution -# FIXME: Don't have such duplicated functions. -def _to_proto_resolution( - resolution: Timeseries.Resolution, -) -> type.resources_pb2.Resolution: - """ - Converts from Timeseries.Resolution type to protobuf resolution type. - - Args: - resolution: The resolution to convert. - """ - proto_resolution = type.resources_pb2.Resolution() - - if resolution == Timeseries.Resolution.BREAKPOINT: - proto_resolution.type = type.resources_pb2.Resolution.BREAKPOINT - elif resolution == Timeseries.Resolution.MIN15: - proto_resolution.type = type.resources_pb2.Resolution.MIN15 - elif resolution == Timeseries.Resolution.MIN30: - proto_resolution.type = type.resources_pb2.Resolution.MIN30 - elif resolution == Timeseries.Resolution.HOUR: - proto_resolution.type = type.resources_pb2.Resolution.HOUR - elif resolution == Timeseries.Resolution.DAY: - proto_resolution.type = type.resources_pb2.Resolution.DAY - elif resolution == Timeseries.Resolution.WEEK: - proto_resolution.type = type.resources_pb2.Resolution.WEEK - elif resolution == Timeseries.Resolution.MONTH: - proto_resolution.type = type.resources_pb2.Resolution.MONTH - elif resolution == Timeseries.Resolution.YEAR: - proto_resolution.type = type.resources_pb2.Resolution.YEAR - else: - # FIXME: Should we throw instead? - proto_resolution.type = type.resources_pb2.Resolution.RESOLUTION_UNSPECIFIED - - return proto_resolution - - def _from_proto_resolution( proto_resolution: type.resources_pb2.Resolution, ) -> Timeseries.Resolution: @@ -468,7 +433,7 @@ def _from_proto_resolution( Args: proto_resolution: The protobuf resolution to convert. """ - return RESOLUTIONS.inverse[proto_resolution] + return RESOLUTIONS.inverse[proto_resolution.type] def _to_proto_utcinterval( From e79c80ac8685ef6e2e782a8b2efb4d8c26552071 Mon Sep 17 00:00:00 2001 From: Martin Galvan Date: Fri, 19 Jul 2024 10:45:45 +0200 Subject: [PATCH 42/42] . --- src/volue/mesh/_common.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/volue/mesh/_common.py b/src/volue/mesh/_common.py index 3b14cd56..d0a52b3c 100644 --- a/src/volue/mesh/_common.py +++ b/src/volue/mesh/_common.py @@ -396,16 +396,18 @@ def _from_proto_curve_type(proto_curve: type.resources_pb2.Curve) -> Timeseries. return CURVE_TYPES.inverse[proto_curve.type] -RESOLUTIONS = bidict({ - Timeseries.Resolution.BREAKPOINT: type.resources_pb2.Resolution.BREAKPOINT, - Timeseries.Resolution.MIN15: type.resources_pb2.Resolution.MIN15, - Timeseries.Resolution.MIN30: type.resources_pb2.Resolution.MIN30, - Timeseries.Resolution.HOUR: type.resources_pb2.Resolution.HOUR, - Timeseries.Resolution.DAY: type.resources_pb2.Resolution.DAY, - Timeseries.Resolution.WEEK: type.resources_pb2.Resolution.WEEK, - Timeseries.Resolution.MONTH: type.resources_pb2.Resolution.MONTH, - Timeseries.Resolution.YEAR: type.resources_pb2.Resolution.YEAR, -}) +RESOLUTIONS = bidict( + { + Timeseries.Resolution.BREAKPOINT: type.resources_pb2.Resolution.BREAKPOINT, + Timeseries.Resolution.MIN15: type.resources_pb2.Resolution.MIN15, + Timeseries.Resolution.MIN30: type.resources_pb2.Resolution.MIN30, + Timeseries.Resolution.HOUR: type.resources_pb2.Resolution.HOUR, + Timeseries.Resolution.DAY: type.resources_pb2.Resolution.DAY, + Timeseries.Resolution.WEEK: type.resources_pb2.Resolution.WEEK, + Timeseries.Resolution.MONTH: type.resources_pb2.Resolution.MONTH, + Timeseries.Resolution.YEAR: type.resources_pb2.Resolution.YEAR, + } +) def _to_proto_resolution(