sensors.py 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. from flask import abort
  2. from marshmallow import fields, ValidationError
  3. from sqlalchemy import select
  4. from flexmeasures.data import db
  5. from flexmeasures.api import FMValidationError
  6. from flexmeasures.utils.entity_address_utils import (
  7. parse_entity_address,
  8. EntityAddressException,
  9. )
  10. from flexmeasures.data.models.time_series import Sensor
  11. from flexmeasures.utils.unit_utils import is_valid_unit
  12. class EntityAddressValidationError(FMValidationError):
  13. status = "INVALID_DOMAIN" # USEF error status
  14. class SensorIdField(fields.Integer):
  15. """
  16. Field that represents a sensor ID. It de-serializes from the sensor id to a sensor instance.
  17. """
  18. def _deserialize(self, sensor_id: int, attr, obj, **kwargs) -> Sensor:
  19. sensor: Sensor = db.session.execute(
  20. select(Sensor).filter_by(id=int(sensor_id))
  21. ).scalar_one_or_none()
  22. if sensor is None:
  23. raise abort(404, f"Sensor {sensor_id} not found")
  24. return sensor
  25. def _serialize(self, sensor: Sensor, attr, data, **kwargs) -> int:
  26. return sensor.id
  27. class SensorField(fields.Str):
  28. """Field that de-serializes to a Sensor,
  29. and serializes a Sensor into an entity address (string).
  30. """
  31. # todo: when Actuators also get an entity address, refactor this class to EntityField,
  32. # where an Entity represents anything with an entity address: we currently foresee Sensors and Actuators
  33. def __init__(
  34. self,
  35. entity_type: str = "sensor",
  36. fm_scheme: str = "fm1",
  37. *args,
  38. **kwargs,
  39. ):
  40. """
  41. :param entity_type: "sensor" (in the future, possibly also another type of resource that is assigned an entity address)
  42. :param fm_scheme: "fm0" or "fm1"
  43. """
  44. self.entity_type = entity_type
  45. self.fm_scheme = fm_scheme
  46. super().__init__(*args, **kwargs)
  47. def _deserialize(self, value, attr, obj, **kwargs) -> Sensor:
  48. """De-serialize to a Sensor."""
  49. try:
  50. ea = parse_entity_address(value, self.entity_type, self.fm_scheme)
  51. if self.fm_scheme == "fm0":
  52. raise EntityAddressException("The fm0 scheme is no longer supported.")
  53. else:
  54. sensor = db.session.execute(
  55. select(Sensor).filter_by(id=ea["sensor_id"])
  56. ).scalar_one_or_none()
  57. if sensor is not None:
  58. return sensor
  59. else:
  60. raise EntityAddressValidationError(
  61. f"{self.entity_type} with entity address {value} doesn't exist."
  62. )
  63. except EntityAddressException as eae:
  64. raise EntityAddressValidationError(str(eae))
  65. def _serialize(self, value: Sensor, attr, data, **kwargs):
  66. """Serialize to an entity address."""
  67. if self.fm_scheme == "fm0":
  68. return value.entity_address_fm0
  69. else:
  70. return value.entity_address
  71. class UnitField(fields.Str):
  72. """Field that represents a unit."""
  73. def _deserialize(self, value, attr, data, **kwargs) -> str:
  74. if not is_valid_unit(value):
  75. raise ValidationError(f"Invalid unit: {value}")
  76. return value
  77. def _serialize(self, value: str, attr, obj, **kwargs) -> str:
  78. return value