Skip to content

Commit

Permalink
Update agency api to MDS 0.3.0 (#51)
Browse files Browse the repository at this point in the history
* Update agency_api vehicle to MDS 0.3.0

* Update service_areas serializer

* Update device statuses

* fix locales

* Various review fixes + migration

* poll_provider updates device status

* update changelog

* Missing translations and source
  • Loading branch information
hnico authored Mar 1, 2019
1 parent 8796fb3 commit f17d972
Show file tree
Hide file tree
Showing 13 changed files with 283 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Changelog
-------------------

- Added indexes to polygon and area models, also added alphabetical ordering for polygon and area lists
- Update agency_api to MDS 0.3.0 specs.


0.4.15 (2019-02-22)
Expand Down
20 changes: 18 additions & 2 deletions mds/apis/agency_api/v0_x/service_areas.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from rest_framework import serializers
from rest_framework import viewsets

from mds import enums
from mds import models
from mds.access_control.permissions import require_scopes
from mds.access_control.scopes import SCOPE_AGENCY_API
from mds.apis import utils


class MultiPolygonField(serializers.Field):
Expand All @@ -26,11 +28,25 @@ class AreaSerializer(serializers.ModelSerializer):
service_area_id = serializers.UUIDField(
source="id", help_text="Unique Area identifier (UUID)"
)
service_area = MultiPolygonField(source="polygons.all")
start_date = utils.UnixTimestampMilliseconds(
source="creation_date",
help_text="Date at which this service area became effective",
)
end_date = utils.UnixTimestampMilliseconds(
source="deletion_date",
required=False,
help_text="If exists, Date at which this service area was replaced.",
)
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", "service_area")
fields = ("service_area_id", "start_date", "end_date", "area", "type")


class AreaViewSet(viewsets.ReadOnlyModelViewSet):
Expand Down
50 changes: 39 additions & 11 deletions mds/apis/agency_api/v0_x/vehicles.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from rest_framework.response import Response

from django.contrib.gis.geos import Point
from django.db.utils import IntegrityError

from mds import enums
from mds import models
Expand Down Expand Up @@ -85,16 +86,32 @@ class DeviceRegisterSerializer(serializers.Serializer):

def create(self, validated_data):
provider_id = self.context["request"].user.provider_id
return models.Device.objects.create(provider_id=provider_id, **validated_data)
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 utils.AlreadyRegisteredError({"already_registered": detail})


class GPSSerializer(serializers.Serializer):
lat = serializers.FloatField()
lng = serializers.FloatField()
altitude = serializers.FloatField(help_text="in meters")
heading = serializers.FloatField(help_text="degrees, starting at 0 at true North")
speed = serializers.FloatField(help_text="in meters/second")
accuracy = serializers.FloatField(help_text="in meters")
altitude = serializers.FloatField(required=False, help_text="in meters")
heading = serializers.FloatField(
required=False, min_value=0, help_text="degrees, starting at 0 at true North"
)
speed = serializers.FloatField(required=False, help_text="in meters/second")
hdop = serializers.FloatField(
required=False,
min_value=1,
source="accuracy",
help_text="Horizontal GPS accuracy",
)
satellites = serializers.IntegerField(
required=False, min_value=0, help_text="Number of GPS satellites"
)


def gps_to_gis_point(gps_data):
Expand All @@ -110,12 +127,22 @@ class DeviceTelemetrySerializer(serializers.Serializer):
timestamp = utils.UnixTimestampMilliseconds(
help_text="Unix timestamp in milliseconds"
)
charge = serializers.FloatField(
required=False,
source="dn_battery_pct",
min_value=0,
max_value=1,
help_text="Percent battery charge of vehicle, expressed between 0 and 1",
)


class DeviceEventSerializer(serializers.Serializer):
event_type = serializers.ChoiceField(
choices=enums.choices(enums.EVENT_TYPE), help_text="Vehicle event."
)
timestamp = utils.UnixTimestampMilliseconds(
help_text="Timestamp of the last event update"
)
telemetry = DeviceTelemetrySerializer(
write_only=True, help_text="Single point of telemetry."
)
Expand All @@ -131,7 +158,7 @@ class DeviceEventSerializer(serializers.Serializer):
def create(self, validated_data):
device = self.context["device"]
return models.EventRecord.objects.create(
timestamp=validated_data["telemetry"]["timestamp"],
timestamp=validated_data["timestamp"],
point=gps_to_gis_point(validated_data["telemetry"].get("gps", {})),
device_id=device.id,
event_type=validated_data["event_type"],
Expand All @@ -142,10 +169,11 @@ def create(self, validated_data):
)


# TODO: these are in the spec but I don't see what it adds.
# class DeviceEventResponseSerializer(serializers.Serializer):
# device_id = serializers.UUIDField()
# status = serializers.ChoiceField(choices=enums.DEVICE_STATUS_CHOICES)
class DeviceEventResponseSerializer(serializers.Serializer):
device_id = serializers.UUIDField()
status = serializers.ChoiceField(
source="updated_status", choices=enums.choices(enums.DEVICE_STATUS)
)


class DeviceTelemetryInputSerializer(serializers.Serializer):
Expand Down Expand Up @@ -205,7 +233,7 @@ class DeviceViewSet(
},
"event": {
"request": DeviceEventSerializer,
"response": utils.EmptyResponseSerializer,
"response": DeviceEventResponseSerializer,
},
"telemetry": {
"request": DeviceTelemetryInputSerializer,
Expand Down
12 changes: 12 additions & 0 deletions mds/apis/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@
from rest_framework import serializers
from rest_framework.response import Response

from rest_framework import status
from rest_framework.exceptions import APIException
from django.utils.translation import pgettext_lazy

# Errors #######################################################


class AlreadyRegisteredError(APIException):
status_code = status.HTTP_409_CONFLICT
default_detail = pgettext_lazy("API Error", "Already registered")
default_code = "already_registered"


# Pagination ###################################################

Expand Down
33 changes: 24 additions & 9 deletions mds/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ def choices(enum):
("reserved", pgettext_lazy("Device status", "Reserved")),
("unavailable", pgettext_lazy("Device status", "Unavailable")),
("removed", pgettext_lazy("Device status", "Removed")),
("trip", pgettext_lazy("Device status", "Trip")),
("elsewhere", pgettext_lazy("Device status", "Elsewhere")),
("inactive", pgettext_lazy("Device status", "Inactive")),
("unknown", pgettext_lazy("Device status", "Unknown")),
],
)
Expand All @@ -31,6 +34,8 @@ def choices(enum):
DEVICE_PROPULSION = enum.Enum(
"Device propulsion",
[
("human", pgettext_lazy("Device propulsion", "Human")),
("electric_assist", pgettext_lazy("Device propulsion", "Electric Assist")),
("electric", pgettext_lazy("Device propulsion", "Electric")),
("combustion", pgettext_lazy("Device propulsion", "Combustion")),
],
Expand All @@ -41,7 +46,7 @@ def choices(enum):
[
("service_start", pgettext_lazy("Event type", "Service start")),
("trip_end", pgettext_lazy("Event type", "Trip end")),
("rebalance_drop_off", pgettext_lazy("Event type", "Rebalance drop_off")),
("rebalance_drop_off", pgettext_lazy("Event type", "Rebalance drop off")),
("maintenance_drop_off", pgettext_lazy("Event type", "Maintenance drop off")),
("cancel_reservation", pgettext_lazy("Event type", "Cancel reservation")),
("reserve", pgettext_lazy("Event type", "Reserve")),
Expand Down Expand Up @@ -70,16 +75,16 @@ def choices(enum):
EVENT_TYPE.maintenance_drop_off.name: DEVICE_STATUS.available.name,
EVENT_TYPE.cancel_reservation.name: DEVICE_STATUS.available.name,
EVENT_TYPE.reserve.name: DEVICE_STATUS.reserved.name,
EVENT_TYPE.trip_start.name: DEVICE_STATUS.reserved.name,
EVENT_TYPE.trip_enter.name: DEVICE_STATUS.reserved.name,
EVENT_TYPE.trip_leave.name: DEVICE_STATUS.reserved.name,
EVENT_TYPE.register.name: DEVICE_STATUS.unknown.name,
EVENT_TYPE.trip_start.name: DEVICE_STATUS.trip.name,
EVENT_TYPE.trip_enter.name: DEVICE_STATUS.trip.name,
EVENT_TYPE.trip_leave.name: DEVICE_STATUS.elsewhere.name,
EVENT_TYPE.register.name: DEVICE_STATUS.unavailable.name,
EVENT_TYPE.low_battery.name: DEVICE_STATUS.unavailable.name,
EVENT_TYPE.maintenance.name: DEVICE_STATUS.unavailable.name,
EVENT_TYPE.service_end.name: DEVICE_STATUS.unavailable.name,
EVENT_TYPE.rebalance_pick_up.name: DEVICE_STATUS.reserved.name,
EVENT_TYPE.maintenance_pick_up.name: DEVICE_STATUS.reserved.name,
EVENT_TYPE.deregister.name: DEVICE_STATUS.unknown.name,
EVENT_TYPE.service_end.name: DEVICE_STATUS.removed.name,
EVENT_TYPE.rebalance_pick_up.name: DEVICE_STATUS.removed.name,
EVENT_TYPE.maintenance_pick_up.name: DEVICE_STATUS.removed.name,
EVENT_TYPE.deregister.name: DEVICE_STATUS.inactive.name,
EVENT_TYPE.telemetry.name: DEVICE_STATUS.unknown.name,
EVENT_TYPE.battery_ok.name: DEVICE_STATUS.available.name,
}
Expand Down Expand Up @@ -111,3 +116,13 @@ def choices(enum):
("pull", pgettext_lazy("Event source", "pull")),
],
)

AREA_TYPE = enum.Enum(
"Area type",
[
("unrestricted", pgettext_lazy("Area type", "Unrestricted")),
("restricted", pgettext_lazy("Area type", "Restricted")),
("preferred_pick_up", pgettext_lazy("Area type", "Preferred pick up")),
("preferred_drop_off", pgettext_lazy("Area type", "Preferred drop off")),
],
)
59 changes: 57 additions & 2 deletions mds/locale/en_US/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-01-22 13:22+0000\n"
"POT-Creation-Date: 2019-03-01 11:31+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
"Language: en_US\n"
Expand All @@ -22,10 +22,25 @@ msgstr ""
msgid "Error decoding signature."
msgstr ""

msgid "Expired or revoked token"
msgstr ""

#, python-format
msgid "Invalid payload, missing fields: %(fields)s"
msgstr ""

#, python-format
msgid "No application known for owner %s"
msgstr ""

#, python-format
msgid "No access token known for %s"
msgstr ""

msgctxt "API Error"
msgid "Already registered"
msgstr ""

msgctxt "Device status"
msgid "Available"
msgstr ""
Expand All @@ -42,6 +57,18 @@ msgctxt "Device status"
msgid "Removed"
msgstr ""

msgctxt "Device status"
msgid "Trip"
msgstr ""

msgctxt "Device status"
msgid "Elsewhere"
msgstr ""

msgctxt "Device status"
msgid "Inactive"
msgstr ""

msgctxt "Device status"
msgid "Unknown"
msgstr ""
Expand All @@ -58,6 +85,14 @@ msgctxt "Device category"
msgid "Car"
msgstr ""

msgctxt "Device propulsion"
msgid "Human"
msgstr ""

msgctxt "Device propulsion"
msgid "Electric Assist"
msgstr ""

msgctxt "Device propulsion"
msgid "Electric"
msgstr ""
Expand All @@ -75,7 +110,7 @@ msgid "Trip end"
msgstr ""

msgctxt "Event type"
msgid "Rebalance drop_off"
msgid "Rebalance drop off"
msgstr ""

msgctxt "Event type"
Expand Down Expand Up @@ -134,10 +169,30 @@ msgctxt "Event type"
msgid "Received telemetry"
msgstr ""

msgctxt "Event type"
msgid "Battery OK"
msgstr ""

msgctxt "Event source"
msgid "push"
msgstr ""

msgctxt "Event source"
msgid "pull"
msgstr ""

msgctxt "Area type"
msgid "Unrestricted"
msgstr ""

msgctxt "Area type"
msgid "Restricted"
msgstr ""

msgctxt "Area type"
msgid "Preferred pick up"
msgstr ""

msgctxt "Area type"
msgid "Preferred drop off"
msgstr ""
Loading

0 comments on commit f17d972

Please sign in to comment.