Example #1
0
class PolicySerializer(serializers.Serializer):
    name = serializers.CharField(help_text="Name of policy")
    policy_id = serializers.UUIDField(source="id", help_text="Unique ID of policy")
    provider_ids = serializers.SerializerMethodField(
        help_text=(
            "Providers for whom this policy is applicable "
            "(null or absent implies all Providers)"
        )
    )
    description = serializers.CharField(help_text="Description of policy")
    start_date = apis_utils.UnixTimestampMilliseconds(
        help_text="Beginning date/time of policy enforcement"
    )
    end_date = apis_utils.UnixTimestampMilliseconds(
        help_text="End date/time of policy enforcement"
    )
    published_date = apis_utils.UnixTimestampMilliseconds(
        help_text="Timestamp that the policy was published"
    )
    prev_policies = serializers.SerializerMethodField(
        help_text="Unique IDs of prior policies replaced by this one"
    )
    rules = RuleSerializer(many=True, help_text="List of applicable rule elements")

    def get_provider_ids(self, policy):
        # Why we need a prefetch_related()!
        return [str(provider.id) for provider in policy.providers.all()]

    def get_prev_policies(self, policy):
        return [str(policy.id) for policy in policy.prev_policies.all()]
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]
Example #3
0
class DeviceTelemetrySerializer(serializers.Serializer):
    """Telemetry frame from event and telemetry endpoints."""

    device_id = serializers.UUIDField()
    gps = GPSSerializer()
    timestamp = apis_utils.UnixTimestampMilliseconds(
        help_text="Unix timestamp in milliseconds")
    charge = serializers.FloatField(
        required=False,
        source="battery_pct",
        min_value=0,
        max_value=1,
        help_text=
        "Percent battery charge of vehicle, expressed between 0 and 1",
    )
Example #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,
    )
Example #5
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"])
def test_unix_timestamp_milliseconds():
    f = utils.UnixTimestampMilliseconds()

    epoch = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
    assert f.to_representation(epoch) == 0
    dt = epoch + datetime.timedelta(microseconds=1)
    assert f.to_representation(dt) == 0
    dt = epoch + datetime.timedelta(microseconds=999)
    assert f.to_representation(dt) == 1
    dt = epoch + datetime.timedelta(microseconds=1001)
    assert f.to_representation(dt) == 1
    dt = epoch + datetime.timedelta(microseconds=1000001)
    assert f.to_representation(dt) == 1000
    dt = epoch + datetime.timedelta(seconds=1, microseconds=1)
    assert f.to_representation(dt) == 1000
    dt = epoch + datetime.timedelta(seconds=1, microseconds=999)
    assert f.to_representation(dt) == 1001

    assert f.to_internal_value(0) == epoch
    assert f.to_internal_value(1) == epoch + datetime.timedelta(
        microseconds=1000)
    assert f.to_internal_value(1000) == epoch + datetime.timedelta(seconds=1)
    assert f.to_internal_value(1001) == epoch + datetime.timedelta(
        seconds=1, microseconds=1000)
Example #7
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"])