Skip to content

Commit

Permalink
Merge branch 'disc-parking'
Browse files Browse the repository at this point in the history
* disc-parking:
  Make it possible to override empty end times in valid parkings
  Add disc parking info to enforcement parking validity
  docs/enforcement: Add is_disc_parking field
  docs/enforcement: Add forgotten Parking.operator field
  Add tests for the disc parking
  Remove the is_disc_parking field for normal parking's response
  Change the required fields based on the type of parking
  Add disc parking changes to Parking model
  docs/operator: Specify Parking Info Queries
  docs/operator: Specify parking disc parking API
  • Loading branch information
suutari-ai committed Jan 21, 2020
2 parents bdabb5d + 1e9ec9b commit 5c0c2d9
Show file tree
Hide file tree
Showing 13 changed files with 430 additions and 49 deletions.
12 changes: 12 additions & 0 deletions docs/api/enforcement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,18 @@ components:
zone:
description: Parking zone id
type: integer
is_disc_parking:
description: >-
Specify whether this is a parking disc parking.
Note: For compatibility reasons this field is present in the
result only for parking disc parkings, i.e. when the value
is true.
type: boolean
operator:
description: Id of the operator
type: string
format: uuid
operator_name:
description: Name of the operator
type: string
Expand Down
269 changes: 232 additions & 37 deletions docs/api/operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ tags:
- name: Parkings
description: >-
Endpoints for creating and updating parkings
- name: Parking Information Queries
description: >-
Endpoint for performing parking spot information queries
paths:
/parking/:
post:
Expand All @@ -32,43 +35,95 @@ paths:
content:
application/json:
schema:
type: object
example:
location:
type: Point
coordinates: [24.938466, 60.170014]
registration_number: LOL-007
time_start: "2016-12-24T21:00:00Z"
time_end: "2016-12-24T22:00:00Z"
zone: 2
properties:
location:
$ref: '#/components/schemas/Location'
terminal_number:
description: >-
Payment terminal number, if the parking was bought
from a payment terminal.
type: string
default: ''
registration_number:
description: Registration number for the parking
type: string
time_start:
description: Start time for the parking
type: string
format: dateTime
time_end:
description: End time for the parking
type: string
format: dateTime
zone:
description: Payment zone
type: integer
enum: [1, 2, 3]
required:
- registration_number
- time_start
- zone
anyOf:
- title: Paid parking
example:
location:
type: Point
coordinates: [24.938466, 60.170014]
registration_number: LOL-007
time_start: "2016-12-24T21:00:00Z"
time_end: "2016-12-24T22:00:00Z"
zone: 2
type: object
properties:
location:
$ref: '#/components/schemas/Location'
terminal_number:
description: >-
Payment terminal number, if the parking was bought
from a payment terminal.
type: string
default: ''
registration_number:
description: Registration number for the parking
type: string
time_start:
description: Start time for the parking
type: string
format: dateTime
time_end:
description: End time for the parking
type: string
format: dateTime
zone:
description: Payment zone
type: integer
enum: [1, 2, 3]
is_disc_parking:
description: >-
Specify whether this is a parking disc parking.
Note: This field can be left out from the
request and will then default to false. This
way the API for regular paid parkings is
compatible with the previous version.
type: boolean
enum: [false]
default: false
required:
- registration_number
- time_start
- zone
- title: Parking disc parking
example:
location:
type: Point
coordinates: [24.938466, 60.170014]
registration_number: LOL-007
time_start: "2016-12-24T21:00:00Z"
is_disc_parking: true
type: object
properties:
location:
$ref: '#/components/schemas/Location'
terminal_number:
description: >-
Payment terminal number, if the parking was bought
from a payment terminal.
type: string
default: ''
registration_number:
description: Registration number for the parking
type: string
time_start:
description: Start time for the parking
type: string
format: dateTime
time_end:
description: End time for the parking
type: string
format: dateTime
is_disc_parking:
description: >-
Specify whether this is a parking disc parking.
type: boolean
enum: [true]
required:
- registration_number
- time_start
- location
- is_disc_parking
responses:
'201':
description: The parking was created successfully
Expand Down Expand Up @@ -223,6 +278,53 @@ paths:
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/parking_info_query/:
post:
tags: ['Parking Information Queries']
summary: Query parking spot information
operationId: queryParkingInfo
security: [{ApiKey: []}]
requestBody:
required: true
description: Location of the parking spot to query
content:
application/json:
schema:
anyOf:
- title: By GPS coordinates
example:
location:
type: Point
coordinates: [24.938466, 60.170014]
type: object
properties:
location:
$ref: '#/components/schemas/Location'
required:
- location
- title: By payment terminal number
example:
terminal_number: "34B"
type: object
properties:
terminal_number:
description: Payment terminal number
type: string
required:
- terminal_number
responses:
'200':
description: Parking spot information query succeeded
content:
application/json:
schema:
$ref: '#/components/schemas/ParkingInfo'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
components:
securitySchemes:
ApiKey:
Expand Down Expand Up @@ -288,6 +390,14 @@ components:
description: Payment zone
type: integer
enum: [1, 2, 3]
is_disc_parking:
description: >-
Specify whether this is a parking disc parking.
Note: For compatibility reasons this field is present in the
result only for parking disc parkings, i.e. when the value
is true.
type: boolean
required:
- registration_number
- time_start
Expand All @@ -313,6 +423,91 @@ components:
items:
type: number
format: float
ParkingInfo:
description: Parking spot information
type: object
example:
location:
type: "Point"
coordinates: [24.938466, 60.170014]
terminal_number: "34B"
zone: 2
rules:
- after: "2019-11-15T06:00:00Z"
policy: "disc"
maximum_duration: 120
- after: "2019-11-15T16:00:00Z"
policy: "free"
- after: "2019-11-16T07:00:00Z"
policy: "disc"
maximum_duration: 120
- after: "2019-11-16T13:00:00Z"
policy: "free"
- after: "2019-11-17T02:00:00Z"
policy: "denied"
- after: "2019-11-18T02:00:00Z"
policy: "free"
- after: "2019-11-18T06:00:00Z"
policy: "disc"
maximum_duration: 120
- after: "2019-11-18T16:00:00Z"
policy: "free"
- after: "2019-11-19T02:00:00Z"
policy: "unknown"
required:
- location
- zone
- rules
properties:
location:
$ref: '#/components/schemas/Location'
terminal_number:
description: >-
Number of the closest payment terminal, if there is any near
the given location.
type: string
zone:
description: Payment zone of the parking spot
type: integer
rules:
description: >-
List of rules that determine if parking is allowed and on
what conditions. Each rule is valid for a time period
starting from the "after" timestamp specified in the rule
and ending to "after" timestamp of the next rule.
type: array
maxItems: 100
items:
type: object
required:
- after
- policy
properties:
after:
description: Start time of this rule
type: string
format: dateTime
policy:
description: >-
What kind of parking is allowed during this period.
Options are:
* `paid`: Must pay the parking fee
* `disc`: Must use a parking disc (regular or digital)
* `free`: Parking is allowed for free without a disc
* `denied`: Parking is not allowed
* `unknown`: There is no information available
type: string
enum: ["paid", "disc", "free", "denied", "unknown"]
maximum_duration:
description: >-
Maximum duration of parking during this period, in
minutes.
Note: Start times of parking disc parkings are rounded
to next half hour, which might allow almost 30 minutes
more parking time in practice.
type: integer
responses:
BadRequest: # 400
description: Bad request, details in request body
Expand Down
14 changes: 14 additions & 0 deletions parkings/api/enforcement/valid_parking.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,22 @@ class Meta:
'zone',
'operator',
'operator_name',
'is_disc_parking',
]

def to_representation(self, instance):
representation = super().to_representation(instance)

if not instance.is_disc_parking:
representation.pop('is_disc_parking')

if instance.time_end is None:
replacement_value = getattr(
settings, 'PARKKIHUBI_NONE_END_TIME_REPLACEMENT', None)
representation['time_end'] = replacement_value or None

return representation


class ValidParkingFilter(django_filters.rest_framework.FilterSet):
reg_num = django_filters.CharFilter(
Expand Down
18 changes: 17 additions & 1 deletion parkings/api/operator/parking.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Meta:
'registration_number',
'time_start', 'time_end',
'zone',
'status',
'status', 'is_disc_parking',
)

# these are needed because by default a PUT request that does not contain some optional field
Expand All @@ -30,12 +30,22 @@ class Meta:
'location': {'default': None},
'terminal_number': {'default': ''},
'time_end': {'default': None},
'is_disc_parking': {'default': False},
}

def __init__(self, *args, **kwargs):
super(OperatorAPIParkingSerializer, self).__init__(*args, **kwargs)
self.fields['time_start'].timezone = pytz.utc
self.fields['time_end'].timezone = pytz.utc
self.fields['zone'].required = True

self._set_required_extra_fields()

def _set_required_extra_fields(self):
initial_data = getattr(self, 'initial_data', None)
if initial_data and self.initial_data.get('is_disc_parking', False):
self.fields['location'].required = True
self.fields['zone'].required = False

def validate(self, data):
if self.instance and (now() - self.instance.created_at) > settings.PARKKIHUBI_TIME_PARKINGS_EDITABLE:
Expand All @@ -58,6 +68,12 @@ def validate(self, data):

return data

def to_representation(self, instance):
representation = super().to_representation(instance)
if not instance.is_disc_parking:
representation.pop('is_disc_parking')
return representation


class OperatorAPIParkingPermission(permissions.BasePermission):
def has_permission(self, request, view):
Expand Down
Loading

0 comments on commit 5c0c2d9

Please sign in to comment.