예제 #1
0
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",
        )
예제 #2
0
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})
예제 #3
0
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))
예제 #4
0
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,
    )
예제 #5
0
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",
        ]
예제 #6
0
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]
예제 #7
0
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")
예제 #8
0
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"])
예제 #9
0
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"])