Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/enforcement api check parking add details parameter #26

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
54 changes: 53 additions & 1 deletion docs/api/enforcement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,16 @@ paths:
given, defaults to current time.
type: string
format: date-time
details:
description: >-
Optional additional details. Takes as argument a list of preferred details.
The details are: operator, time_start and permissions.
type: array
items:
type: string
default: []
example: ["operator", "time_start", "permissions"]

responses:
'200':
description: OK. Validity check succeeded.
Expand Down Expand Up @@ -454,11 +464,53 @@ paths:
nullable: true
time:
description: >-
Time used in the check. It will either be equal
Time used in the check. It will either be equal
to the time value provided in the request or the
current time if none was provided.
type: string
format: date-time
operator:
description: >-
If 'operator' in details, returns the operator
of the allowed parking.
type: string
nullable: true
time_start:
description: >-
If 'time_start' in details, returns the time_start
of the allowed parking.
type: string
format: date-time
nullable: true
permissions:
description: >-
If 'permissions' in details, returns the active permissions
for the registration number.
type: object
properties:
event_areas:
description: >-
List of event area IDs to which the registration number has permissions.
type: array
items:
type: string
zones:
description: List of zones to which the registration number has permissions.
type: array
items:
type: integer
permits:
description: List of active permits for the registration number.
type: array
items:
type: object
properties:
external_id:
type: string
subjects: &permitSubjects
$ref: '#/components/schemas/PermitSubjects'
targets: &permitAreas
$ref: '#/components/schemas/PermitAreas'
'400':
$ref: '#/components/responses/BadRequest'
'401':
Expand Down
127 changes: 95 additions & 32 deletions parkings/api/enforcement/check_parking.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,22 @@
from rest_framework.response import Response

from ...models import (
EventArea, EventParking, Parking, ParkingCheck, PaymentZone, PermitArea,
PermitLookupItem)
EventArea, EventParking, Parking, ParkingCheck, PaymentZone, Permit,
PermitArea, PermitLookupItem)
from ...models.constants import GK25FIN_SRID, WGS84_SRID
from .permissions import IsEnforcer


class PermitPermissionsSerializer(serializers.ModelSerializer):
class Meta:
model = Permit
fields = [
"external_id",
"subjects",
"areas"
]


class AwareDateTimeField(serializers.DateTimeField):
default_error_messages = dict(
serializers.DateField.default_error_messages,
Expand All @@ -35,6 +45,7 @@ class CheckParkingSerializer(serializers.Serializer):
registration_number = serializers.CharField(max_length=20)
location = LocationSerializer()
time = AwareDateTimeField(required=False)
details = serializers.ListSerializer(child=serializers.CharField(), required=False)


class CheckParking(generics.GenericAPIView):
Expand Down Expand Up @@ -62,7 +73,7 @@ def post(self, request):
area = get_permit_area(gk25_location, domain)
event_area = get_event_area(gk25_location, domain)

(allowed_by, parking, end_time) = check_parking(
(allowed_by, parking, end_time, active_parkings, permit_lookup_items, active_event_parkings) = check_parking(
registration_number, zone, area, time, domain, event_area)
allowed = bool(allowed_by)

Expand All @@ -71,8 +82,20 @@ def post(self, request):
# one that has just expired, i.e. was valid a few minutes
# ago (where "a few minutes" is the grace duration)
past_time = time - get_grace_duration()
(_allowed_by, parking, end_time) = check_parking(
registration_number, zone, area, past_time, domain, event_area)
(
_allowed_by,
parking,
end_time,
active_parkings,
permit_lookup_items,
active_event_parkings
) = check_parking(
registration_number,
zone, area,
past_time,
domain,
event_area
)

result = {
"allowed": allowed,
Expand All @@ -84,6 +107,24 @@ def post(self, request):
},
"time": time,
}

operator_detail, time_start_detail, permissions_detail = get_details(params)

if operator_detail:
result["operator"] = parking.operator.name if allowed and parking and parking.operator else None

if time_start_detail:
result["time_start"] = parking.time_start if allowed and parking and parking.time_start else None

if permissions_detail:
result["permissions"] = {
"zones": list({p.zone.number for p in active_parkings}),
"permits": [
PermitPermissionsSerializer(item.permit).data for item in permit_lookup_items
],
"event_areas": list({e_p.event_area.id for e_p in active_event_parkings})
}

filter = {
"performer": request.user,
"time": time,
Expand All @@ -93,6 +134,7 @@ def post(self, request):
"result": result,
"allowed": allowed
}

if isinstance(parking, Parking):
filter["found_parking"] = parking
if isinstance(parking, EventParking):
Expand All @@ -102,6 +144,22 @@ def post(self, request):
return Response(result)


def get_details(params):
details = params.get("details", [])
operator = False
time_start = False
permissions = False

for detail in [d.lower() for d in details]:
if detail == "operator":
operator = True
if detail == "time_start":
time_start = True
if detail == "permissions":
permissions = True
return operator, time_start, permissions


def get_location(params):
longitude = params["location"]["longitude"]
latitude = params["location"]["latitude"]
Expand Down Expand Up @@ -151,48 +209,53 @@ def check_parking(registration_number, zone, area, time, domain, event_area):
:type area: str|None
:type domain: parkings.models.EnforcementDomain
:type time: datetime.datetime
:rtype: (str, Parking|None, datetime.datetime)
:rtype: (str, Parking|None, datetime.datetime, ParkingQuerySet, PermitLookupItemQuerySet, EventParkingQuerySet)
"""
permit_lookup_items = (
PermitLookupItem.objects
.active()
.by_time(time)
.by_subject(registration_number)
.filter(permit__domain=domain))

active_parkings = (
Parking.objects
.registration_number_like(registration_number)
.valid_at(time)
.only("id", "zone", "time_end")
.filter(domain=domain))

active_event_parkings = (
EventParking.objects
.registration_number_like(registration_number)
.valid_at(time)
.only("id", "time_end")
.filter(domain=domain))

for parking in active_parkings:
if zone is None or parking.zone.number <= zone:
return ("parking", parking, parking.time_end)
return ("parking",
parking,
parking.time_end,
active_parkings,
permit_lookup_items,
active_event_parkings)

if area:
permit_end_time = (
PermitLookupItem.objects
.active()
.by_time(time)
.by_subject(registration_number)
.by_area(area)
.values_list("end_time", flat=True)
.filter(permit__domain=domain)
.first())

permit_end_time = permit_lookup_items.by_area(area).values_list("end_time", flat=True).first()
if permit_end_time:
return ("permit", None, permit_end_time)
return ("permit", None, permit_end_time, active_parkings, permit_lookup_items, active_event_parkings)

active_event_parking = (
EventParking.objects
.registration_number_like(registration_number)
.valid_at(time)
.only("id", "time_end")
.filter(domain=domain)).first()

if active_event_parking:
for active_event_parking in active_event_parkings:
if active_event_parking.event_area == event_area:
return ("event parking", active_event_parking, active_event_parking.time_end)
else:
# Event parking not parked in the assigned event area, i.e., not allowed.
return (None, active_event_parking, active_event_parking.time_end)

return (None, None, None)
return ("event_parking",
active_event_parking,
active_event_parking.time_end,
active_parkings,
permit_lookup_items,
active_event_parkings)

return (None, None, None, active_parkings, permit_lookup_items, active_event_parkings)


def get_grace_duration(default=datetime.timedelta(minutes=15)):
Expand Down
Loading
Loading