class SensorDataDescriptionSchema(ma.Schema): """ Schema describing sensor data (specifically, the sensor and the timing of the data). """ sensor = SensorField(required=True, entity_type="sensor", fm_scheme="fm1") start = AwareDateTimeField(required=True, format="iso") duration = DurationField(required=True) horizon = DurationField(required=False) prior = AwareDateTimeField(required=False, format="iso") unit = fields.Str(required=True) @validates_schema def check_schema_unit_against_sensor_unit(self, data, **kwargs): """Allows units compatible with that of the sensor. For example, a sensor with W units allows data to be posted with units: - W, kW, MW, etc. (i.e. units with different prefixes) - J/s, Nm/s, etc. (i.e. units that can be converted using some multiplier) - Wh, kWh, etc. (i.e. units that represent a stock delta, which knowing the duration can be converted to a flow) For compatible units, the SensorDataSchema converts values to the sensor's unit. """ posted_unit = data["unit"] required_unit = data["sensor"].unit if posted_unit != required_unit and not units_are_convertible( posted_unit, required_unit ): raise ValidationError( f"Required unit for this sensor is {data['sensor'].unit}, got incompatible unit: {data['unit']}" )
class ChartRequestSchema(Schema): """ This schema describes the request for a chart. """ resource = fields.Str(required=True) start_time = fields.DateTime(required=True) end_time = fields.DateTime(required=True) resolution = DurationField(required=True) show_consumption_as_positive = fields.Bool(missing=True) show_individual_traces_for = fields.Str( missing="none", validate=validate.OneOf(["none", "schedules", "power"])) forecast_horizon = DurationField(missing="PT6H")
def test_duration_field_nominal_grounded( duration_input, exp_deserialization, grounded_timedelta ): """Nominal durations are tricky: https://en.wikipedia.org/wiki/Talk:ISO_8601/Archive_2#Definition_of_Duration_is_incorrect We want to test if we can ground them as expected. We use a particular datetime to ground, in a leap year February. For the Europe/Amsterdam timezone, daylight saving time started on March 29th 2020. # todo: the commented out tests would work if isodate.parse_duration would have the option to stop coercing ISO 8601 days into datetime.timedelta days """ df = DurationField() deser = df.deserialize(duration_input, None, None) assert deser == exp_deserialization dummy_time = pytz.timezone("Europe/Amsterdam").localize( datetime(2020, 2, 22, 18, 7) ) grounded = DurationField.ground_from(deser, dummy_time) assert grounded == grounded_timedelta
def decorated_service(*args, **kwargs): duration_arg = parser.parse( {"duration": DurationField()}, request, location="args_and_json", unknown=marshmallow.EXCLUDE, ) if "duration" in duration_arg: duration = duration_arg["duration"] duration = DurationField.ground_from( duration, kwargs.get("start", kwargs.get("datetime", None)), ) if not duration: # TODO: deprecate extra_info = "Cannot parse 'duration' value." current_app.logger.warning(extra_info) return invalid_period(extra_info) kwargs["duration"] = duration else: kwargs["duration"] = default_duration return fn(*args, **kwargs)
required=True, multiple=True, type=SensorIdField(), help="ID of sensor(s). This argument can be given multiple times.", ) @click.option( "--start", "start", type=AwareDateTimeField(), required=True, help="Plot starting at this datetime. Follow up with a timezone-aware datetime in ISO 6801 format.", ) @click.option( "--duration", "duration", type=DurationField(), required=True, help="Duration of the plot, after --start. Follow up with a duration in ISO 6801 format, e.g. PT1H (1 hour) or PT45M (45 minutes).", ) @click.option( "--belief-time-before", "belief_time_before", type=AwareDateTimeField(), required=False, help="Time at which beliefs had been known. Follow up with a timezone-aware datetime in ISO 6801 format.", ) @click.option( "--source-id", "source", required=False, type=DataSourceIdField(),
def test_duration_field_invalid(duration_input, error_msg): df = DurationField() with pytest.raises(DurationValidationError) as ve: df.deserialize(duration_input, None, None) assert error_msg in str(ve)
def test_duration_field_straightforward(duration_input, exp_deserialization): """Testing straightforward cases""" df = DurationField() deser = df.deserialize(duration_input, None, None) assert deser == exp_deserialization assert df.serialize("duration", {"duration": deser}) == duration_input