class DeviceSerializer(serializers.ModelSerializer): id = serializers.UUIDField(help_text="Unique device identifier (UUID)") model = serializers.CharField(required=False, help_text="Vehicle model") identification_number = serializers.CharField( help_text="VIN (Vehicle Identification Number)") category = serializers.ChoiceField(enums.choices(enums.DEVICE_CATEGORY), help_text="Device type") propulsion = serializers.ListField( child=serializers.ChoiceField(enums.choices(enums.DEVICE_PROPULSION)), help_text="Propulsion type(s)", ) provider_id = serializers.UUIDField( source="provider.id", help_text="ID of the service provider of the device") provider_name = serializers.CharField( source="provider.name", help_text="Name of the service provider of the device") registration_date = serializers.DateTimeField( help_text="Device registration date") last_telemetry_date = serializers.DateTimeField( source="dn_gps_timestamp", help_text="Latest GPS timestamp", allow_null=True) position = utils.PointSerializer(source="dn_gps_point", help_text="Latest GPS position") status = serializers.ChoiceField( enums.choices(enums.DEVICE_STATUS), source="dn_status", help_text="Latest status", allow_null=True, ) battery = serializers.FloatField(source="dn_battery_pct", help_text="Percentage between 0 and 1", allow_null=True) class Meta: model = models.Device fields = ( "id", "provider_id", "provider_name", "model", "identification_number", "category", "propulsion", "status", "position", "registration_date", "last_telemetry_date", "battery", )
class DeviceRegisterSerializer(serializers.Serializer): """Receive a new device to create from a provider.""" device_id = serializers.UUIDField( source="id", help_text="Provided by Operator to uniquely identify a vehicle.") provider_id = serializers.UUIDField( help_text="Provider id issued by the city.", required=False) vehicle_id = serializers.CharField( source="identification_number", help_text= "Vehicle Identification Number (vehicle_id) visible on vehicle.", ) type = serializers.ChoiceField( source="category", choices=enums.choices(enums.DEVICE_CATEGORY), help_text="Vehicle type.", ) propulsion = serializers.ListSerializer( child=serializers.ChoiceField( choices=enums.choices(enums.DEVICE_PROPULSION)), help_text="Array of Propulsion Type.", ) year = serializers.IntegerField(required=False, source="year_manufactured", help_text="Year Manufactured.") mfgr = serializers.CharField(required=False, source="manufacturer", help_text="Vehicle Manufacturer.") model = serializers.CharField(required=False, help_text="Vehicle Model.") def create(self, validated_data): user = self.context["request"].user provider_id = str(validated_data.pop("provider_id", user.provider_id)) if not provider_id: logger.warning("Trying to register a device without provider_id") if provider_id not in user.aggregator_for: logger.warning( "%s is trying to push an event with the provider id %s" % (user.provider_id, provider_id)) try: return models.Device.objects.create(provider_id=provider_id, **validated_data) except IntegrityError: detail = f"A vehicle with id={validated_data['id']} is already registered" raise apis_utils.AlreadyRegisteredError( {"already_registered": detail})
class DeviceEventResponseSerializer(serializers.Serializer): """Response format for the event endpoint.""" device_id = serializers.UUIDField() status = serializers.ChoiceField(source="updated_status", choices=enums.choices( enums.DEVICE_STATUS))
class DeviceSerializer(serializers.Serializer): """Expose devices as described in the Agency spec.""" device_id = serializers.UUIDField( source="id", help_text="Provided by Operator to uniquely identify a vehicle.") provider_id = serializers.UUIDField( help_text="Issued by City and tracked.") vehicle_id = serializers.CharField( source="identification_number", help_text= "Vehicle Identification Number (vehicle_id) visible on vehicle.", ) type = serializers.ChoiceField( source="category", choices=enums.choices(enums.DEVICE_CATEGORY), help_text="Vehicle type.", ) propulsion = serializers.ListSerializer( child=serializers.ChoiceField( choices=enums.choices(enums.DEVICE_PROPULSION)), help_text="Array of Propulsion Type.", ) year = serializers.IntegerField(source="year_manufactured", help_text="Year Manufactured.") mfgr = serializers.CharField(source="manufacturer", help_text="Vehicle Manufacturer.") model = serializers.CharField(help_text="Vehicle Model.") status = serializers.ChoiceField( source="dn_status", choices=enums.choices(enums.DEVICE_STATUS), help_text="Current vehicle status.", ) prev_event = serializers.ChoiceField( source="latest_event.event_type", choices=enums.choices(enums.EVENT_TYPE), help_text="Last Vehicle Event.", allow_null=True, ) updated = apis_utils.UnixTimestampMilliseconds( source="latest_event.saved_at", help_text="Date of last event update as Unix Timestamp (milliseconds).", allow_null=True, )
class DeviceFilter(filters.FilterSet): id = filters.CharFilter(lookup_expr="icontains") category = utils.ChoicesInFilter( choices=enums.choices(enums.DEVICE_CATEGORY)) provider = utils.UUIDInFilter() status = utils.ChoicesInFilter("dn_status", choices=enums.choices(enums.DEVICE_STATUS)) registrationDateFrom = filters.IsoDateTimeFilter("registration_date", lookup_expr="gte") registrationDateTo = filters.IsoDateTimeFilter("registration_date", lookup_expr="lte") class Meta: model = models.Device fields = [ "id", "category", "provider", "status", "registrationDateFrom", "registrationDateTo", ]
class DeviceStatusChangesSerializer(serializers.ModelSerializer): associated_trip = serializers.CharField(source="properties.trip_id") device_id = serializers.CharField(source="device.id") event_location = serializers.SerializerMethodField() event_time = apis_utils.UnixTimestampMilliseconds(source="timestamp") event_type = serializers.SerializerMethodField() event_type_reason = serializers.CharField(source="event_type") propulsion_type = serializers.ListSerializer( source="device.propulsion", child=serializers.ChoiceField( choices=enums.choices(enums.DEVICE_PROPULSION)), ) provider_id = serializers.CharField(source="device.provider.id") provider_name = serializers.CharField(source="device.provider.name") vehicle_id = serializers.CharField(source="device.identification_number") vehicle_type = serializers.CharField(source="device.category") class Meta: model = models.EventRecord fields = ( "associated_trip", "device_id", "event_location", "event_time", "event_type", "event_type_reason", "propulsion_type", "provider_name", "provider_id", "vehicle_id", "vehicle_type", ) def create(self, data): raise NotImplementedError() def update(self, instance, data): raise NotImplementedError() def get_event_location(self, obj): telemetry = obj.properties.get("telemetry", {}) return { "type": "Feature", "properties": { "timestamp": telemetry.get("timestamp") }, "geometry": obj.point_as_geojson, } def get_event_type(self, obj): return enums.EVENT_TYPE_TO_DEVICE_STATUS[obj.event_type]
class AreaSerializer(serializers.ModelSerializer): """A service area """ service_area_id = serializers.UUIDField( source="id", help_text="Unique Area identifier (UUID)") area = MultiPolygonField(source="polygons.all") type = serializers.ChoiceField( source="area_type", choices=enums.choices(enums.AREA_TYPE), default="unrestricted", ) class Meta: model = models.Area fields = ("service_area_id", "area", "type")
class DeviceEventSerializer(serializers.Serializer): """Receive a new event pushed by a provider.""" event_type = serializers.ChoiceField(choices=enums.choices( enums.EVENT_TYPE), help_text="Vehicle event.") timestamp = apis_utils.UnixTimestampMilliseconds( help_text="Timestamp of the last event update") telemetry = DeviceTelemetrySerializer( write_only=True, help_text="Single point of telemetry.") trip_id = serializers.UUIDField( required=False, allow_null=True, help_text=("UUID provided by Operator to uniquely identify the trip. " "Required for trip_* events."), ) def create(self, validated_data): device = self.context["device"] event_record = models.EventRecord( timestamp=validated_data["timestamp"], point=gps_to_gis_point(validated_data["telemetry"].get("gps", {})), device_id=device.id, event_type=validated_data["event_type"], properties={ "telemetry": validated_data["telemetry"], "trip_id": validated_data.get("trip_id"), }, ) db_helpers.upsert_event_records([event_record], enums.EVENT_SOURCE.agency_api.name, on_conflict_update=True) # We don't get the created event record but we need to return it return models.EventRecord.objects.get( device=device, timestamp=validated_data["timestamp"])
class DeviceEventSerializer(serializers.Serializer): """Receive a new event pushed by a provider.""" event_type = serializers.ChoiceField(choices=enums.choices( enums.EVENT_TYPE), help_text="Vehicle event.") event_type_reason = serializers.ChoiceField( choices=enums.choices(enums.EVENT_TYPE_REASON), help_text="Vehicle event type reason.", required=False, allow_null=True, ) timestamp = apis_utils.UnixTimestampMilliseconds( help_text="Timestamp of the last event update") telemetry = DeviceTelemetrySerializer( write_only=True, help_text="Single point of telemetry.") trip_id = serializers.UUIDField( required=False, allow_null=True, help_text=("UUID provided by Operator to uniquely identify the trip. " "Required for trip_* events."), ) def get_event(self, validated_data): # We first check whether the provider uses the old or the new agency # event_type(s) and event_type_reason(s). # A provider uses the old version if agency_api_version == "draft" in its # api_configuration. provider_id = self.context["request"].user.provider_id provider = models.Provider.objects.get(id=provider_id) # TODO(hcauwelier) make it mandatory, see SMP-1673 api_version = provider.agency_api_configuration.get("api_version") if not api_version: # TODO(hcauwelier) clean up, "api_configuration" was for the provider API api_version = provider.api_configuration.get("agency_api_version") if not api_version: api_version = enums.MDS_VERSIONS[ enums.DEFAULT_AGENCY_API_VERSION].value else: # We store the enum key, which cannot be a numeric identifier # It doubles as a validity check api_version = enums.MDS_VERSIONS[api_version].value event_type = validated_data.get("event_type") event_type_reason = validated_data.get("event_type_reason") # TODO(hcauwelier) remove "draft" (pre 0.2) support if api_version == "draft": return (event_type, None) else: event = ((event_type, event_type_reason) if event_type_reason else (event_type, )) if event not in provider_mapping.AGENCY_EVENT_TO_PROVIDER_REASON: # This should be avoided if possible msg = f"The event ({event[0]}, {event[1]}) is not in the mapping." logger.warning(msg) raise ValidationError(msg) return (event_type, event_type_reason) def create(self, validated_data): event_type, event_type_reason = self.get_event(validated_data) device = self.context["device"] event_record = models.EventRecord( timestamp=validated_data["timestamp"], point=gps_to_gis_point(validated_data["telemetry"].get("gps", {})), device_id=device.id, event_type=event_type, event_type_reason=event_type_reason, properties={ "telemetry": validated_data["telemetry"], "trip_id": validated_data.get("trip_id"), }, ) db_helpers.upsert_event_records([event_record], enums.EVENT_SOURCE.agency_api.name, on_conflict_update=True) # We don't get the created event record but we need to return it return models.EventRecord.objects.get( device=device, timestamp=validated_data["timestamp"])