views.py 15 KB
Newer Older
1
"""
2
3
api/views.py

4
Rest Framework Class Views
David Haynes's avatar
David Haynes committed
5
6
7

Each ViewSet determines what data is returned when an API endpoint is hit. In
addition, we define filtering and documentation for each of these endpoints. 
8
"""
9
10
11
12
# Future Imports
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

13
14
15
# Python std. lib. imports
import datetime

16
# App Imports
David Haynes's avatar
David Haynes committed
17
from .models import Facility, OpenTime, Category, Schedule, Location, Alert
18
from .serializers import (CategorySerializer, FacilitySerializer,
David Haynes's avatar
David Haynes committed
19
                          ScheduleSerializer, OpenTimeSerializer,
David Haynes's avatar
David Haynes committed
20
                          LocationSerializer, AlertSerializer)
21

22
# Other Imports
David Haynes's avatar
David Haynes committed
23
24
from rest_framework import viewsets, filters
from django_filters.rest_framework import DjangoFilterBackend
25

David Haynes's avatar
David Haynes committed
26
27
class AlertViewSet(viewsets.ReadOnlyModelViewSet):
    """
28
29
30
    Some type of notification that is displayed to clients that conveys a message.

    Past examples include:
David Haynes's avatar
David Haynes committed
31
32
33
34
35

    - Random closings
    - Modified schedules being in effect
    - Election reminder
    - Advertising for other SRCT projects
36
37
38

    Alerts last for a period of time until the information is no longer dank.

David Haynes's avatar
David Haynes committed
39
40
41
42
43
44
    ---

    ## Default behavior

    [GET /api/alerts/](/api/alerts/?format=json)

David Haynes's avatar
David Haynes committed
45
    Return all Alert objects.
46

David Haynes's avatar
David Haynes committed
47
48
49
50
51
    ## Built-in query parameters

    ### **Search**

    [GET /api/alerts/?search=](/api/alerts/?search=&format=json)
52
53
54

    Query parameter that returns objects that match a keyword provided in the search.

David Haynes's avatar
David Haynes committed
55
56
57
58
59
60
61
62
63
64
    **Example Usage**

    [GET /api/alerts/?search=srct](/api/alerts/?search=srct&format=json)

    Return any Alert objects that have the string "srct" located in one of its fields.

    ### **Ordering**

    [GET /api/alerts/?ordering=](/api/alerts/?ordering=&format=json)

65
66
    Query parameter that orders the returned objects based on the provided field to order by.

David Haynes's avatar
David Haynes committed
67
68
69
    **Example Usage**

    [GET /api/alerts/?ordering=urgency_tag](/api/alerts/?ordering=urgency_tag&format=json)
70

David Haynes's avatar
David Haynes committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
    Return all Alert objects ordered by urgency_tag ascending.

    [GET /api/alerts/?ordering=-urgency_tag](/api/alerts/?ordering=-urgency_tag&format=json)

    Return all Alert objects ordered by urgency_tag descending.

    ### **Field filtering**

    You can query directly against any field.

    **Example Usage**

    [GET /api/alerts/?urgency_tag=major](/api/alerts/?urgency_tag=major&format=json)

    Return all Alert objects that are tagged as "major" urgency.
David Haynes's avatar
David Haynes committed
86
    """
87
88
89
90
91
92
93
94
95
96
    # All model fields that are available for filtering
    FILTER_FIELDS = (
        # Alert fields
        'urgency_tag',
        'message',
        'start_datetime',
        'end_datetime'
    )

    # Associate a serializer with the ViewSet
David Haynes's avatar
David Haynes committed
97
98
    serializer_class = AlertSerializer

99
100
101
102
103
104
105
    # Setup filtering
    filter_backends = (filters.SearchFilter, DjangoFilterBackend,
                       filters.OrderingFilter, )
    search_fields = FILTER_FIELDS
    ordering_fields = FILTER_FIELDS
    filter_fields = FILTER_FIELDS

David Haynes's avatar
David Haynes committed
106
    def get_queryset(self):
107
108
109
110
        """
        Handle incoming GET requests and enumerate objects that get returned by
        the API.
        """
David Haynes's avatar
David Haynes committed
111
112
        return Alert.objects.all()

113
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
114
    """
115
116
    A Category is a grouping of Facilities that serve a common/similar purpose.

David Haynes's avatar
David Haynes committed
117
118
119
120
121
122
123
124
125
126
127
    Examples:

    - Dining
    - Gyms
    - Study areas (Libraries, The Ridge, JC, etc)

    ---

    ## Default behavior

    [GET /api/categories/](/api/categories/)
128

David Haynes's avatar
David Haynes committed
129
    Return all Category objects.
130

David Haynes's avatar
David Haynes committed
131
132
133
134
135
    ## Built-in query parameters

    ### **Search**

    [GET /api/categories/?search=](/api/categories/?search=&format=json)
136
137
138

    Query parameter that returns objects that match a keyword provided in the search.

David Haynes's avatar
David Haynes committed
139
140
141
142
143
144
145
146
147
148
    **Example Usage**

    [GET /api/categories/?search=din](/api/categories/?search=din&format=json)

    Return all Category objects that have a field that matches the string "din".

    ### **Ordering**

    [GET /api/categories/?ordering=](/api/categories/?ordering=&format=json)

149
150
    Query parameter that orders the returned objects based on the provided field to order by.

David Haynes's avatar
David Haynes committed
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
    **Example Usage**

    [GET /api/categories/?ordering=name](/api/categories/?ordering=name&format=json)

    Return all Category objects filtered by name ascending.

    [GET /api/categories/?ordering=-name](/api/categories/?ordering=-name&format=json)

    Return all Category objects filtered by name descending.

    ### **Field filtering**

    You can query directly against any field.

    **Example Usage**
166

David Haynes's avatar
David Haynes committed
167
168
169
    [GET /api/categories/?name=dining](/api/categories/?name=dining&format=json)

    Return the Category object that is named "dining".
170
    """
171
172
173
174
175
176
177
    # All model fields that are available for filtering
    FILTER_FIELDS = (
        # Category fields
        'name',
    )

    # Associate a serializer with the ViewSet
178
179
    serializer_class = CategorySerializer

180
181
182
183
184
185
186
    # Setup filtering
    filter_backends = (filters.SearchFilter, DjangoFilterBackend,
                       filters.OrderingFilter, )
    search_fields = FILTER_FIELDS
    ordering_fields = FILTER_FIELDS
    filter_fields = FILTER_FIELDS

David Haynes's avatar
David Haynes committed
187
    def get_queryset(self):
188
189
190
191
        """
        Handle incoming GET requests and enumerate objects that get returned by
        the API.
        """
David Haynes's avatar
David Haynes committed
192
193
        return Category.objects.all()

David Haynes's avatar
David Haynes committed
194
195
class LocationViewSet(viewsets.ReadOnlyModelViewSet):
    """
196
197
    Represents a specific location that a Facility can be found.

David Haynes's avatar
David Haynes committed
198
199
200
201
202
203
    ---

    ## Default behavior

    [GET /api/locations/](/api/locations/?format=json)

David Haynes's avatar
David Haynes committed
204
    Return all Location objects.
205

David Haynes's avatar
David Haynes committed
206
207
208
209
210
    ## Built-in query parameters

    ### **Search**

    [GET /api/locations/?search=](/api/locations/?search=&format=json)
211
212
213

    Query parameter that returns objects that match a keyword provided in the search.

David Haynes's avatar
David Haynes committed
214
215
216
217
218
219
220
221
222
223
224
    **Example Usage**

    [GET /api/locations/?search=johnson](/api/locations/?search=johnson&format=json)

    Return all Location objects that have a field that matches the "johnson" string.

    ### **Ordering**

    Order the returned objects based on the provided field.

    [GET /api/locations/?ordering=](/api/locations/?ordering=&format=json)
225

David Haynes's avatar
David Haynes committed
226
    **Example Usage**
227

David Haynes's avatar
David Haynes committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
    [GET /api/locations/?ordering=building](/api/locations/?ordering=building&format=json)

    Return all Location objects ordered by building name ascending.

    [GET /api/locations/?ordering=-building](/api/locations/?ordering=-building&format=json)

    Return all Location objects ordered by building name descending.

    ### **Field filtering**

    You can query directly against any field.

    **Example Usage**

    [GET /api/locations/?building=Johnson+Center](/api/locations/?building=Johnson+Center&format=json)

    Return all Location objects located in the "Johnson Center" building.
David Haynes's avatar
David Haynes committed
245
    """
246
247
248
249
250
    # All model fields that are available for filtering
    FILTER_FIELDS = (
        # Location fields
        'building',
        'address',
251
252
        'on_campus',
        'campus_region'
253
254
255
    )

    # Associate a serializer with the ViewSet
David Haynes's avatar
David Haynes committed
256
257
    serializer_class = LocationSerializer

258
259
260
261
262
263
264
    # Setup filtering
    filter_backends = (filters.SearchFilter, DjangoFilterBackend,
                       filters.OrderingFilter, )
    search_fields = FILTER_FIELDS
    ordering_fields = FILTER_FIELDS
    filter_fields = FILTER_FIELDS

David Haynes's avatar
David Haynes committed
265
    def get_queryset(self):
266
267
268
269
        """
        Handle incoming GET requests and enumerate objects that get returned by
        the API.
        """
David Haynes's avatar
David Haynes committed
270
271
        return Location.objects.all()

272
class FacilityViewSet(viewsets.ReadOnlyModelViewSet):
273
    """
274
275
    A Facility is some type of establishment that has a schedule of open hours and a location that serves a specific purpose that can be categorized.

David Haynes's avatar
David Haynes committed
276
277
278
279
280
281
282
283
284
285
286
    ---

    ## Default behavior

    [GET /api/facilities/](/api/facilities/?format=json)

    Return all Facility objects. Additionally, we filter out stale special_schedules to reduce client side calculations.

    ## Built-in query parameters

    ### **Search**
287

David Haynes's avatar
David Haynes committed
288
    [GET /api/facilities/?search=](/api/facilities/?search=&format=json)
David Haynes's avatar
David Haynes committed
289
290
291

    Query parameter that returns objects that match a keyword provided in the search.

David Haynes's avatar
David Haynes committed
292
293
294
295
296
297
298
299
300
301
    **Example Usage**

    [GET /api/facilities/?search=south](/api/facilities/?search=south&format=json)

    Return all Facility objects that have a field that matches the string "south".

    ### **Ordering**

    [GET /api/facilities/?ordering=](/api/facilities/?ordering=&format=json)

David Haynes's avatar
David Haynes committed
302
303
    Query parameter that orders the returned objects based on the provided field to order by.

David Haynes's avatar
David Haynes committed
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
    **Example Usage**

    [GET /api/facilities/?ordering=facility_name](/api/facilities/?ordering=facility_name&format=json)

    Return all Facility objects ordered by facility_name ascending.

    [GET /api/facilities/?ordering=-facility_name](/api/facilities/?ordering=-facility_name&format=json)

    Return all Facility objects ordered by facility_name descending.

    ### **Field filtering**

    You can query directly against any field.

    **Example Usage**
David Haynes's avatar
David Haynes committed
319

David Haynes's avatar
David Haynes committed
320
    [GET /api/facilities/?facility_name=Southside](/api/facilities/?facility_name=Southside&format=json)
David Haynes's avatar
David Haynes committed
321

David Haynes's avatar
David Haynes committed
322
    Return the Facility object that has "Southside" as its name.
David Haynes's avatar
David Haynes committed
323

David Haynes's avatar
David Haynes committed
324
    ## Custom query parameters
David Haynes's avatar
David Haynes committed
325

David Haynes's avatar
David Haynes committed
326
327
328
329
330
331
332
333
334
335
336
    ### **open_now**

    [GET /api/facilities/?open_now](/api/facilities/?open_now&format=json)

    Only return open Facility objects.

    ### **closed_now**

    [GET /api/facilities/?closed_now](/api/facilities/?closed_now&format=json)

    Only return closed Facility objects.
337
    """
338
    # All model fields that are available for filtering
339
340
341
342
343
344
345
346
347
348
349
    FILTER_FIELDS = (
        # Facility fields
        'facility_name',
        'tapingo_url',
        'facility_product_tags__name',
        # Category fields
        'facility_category__name',
        # Location fields
        'facility_location__building',
        'facility_location__address',
        'facility_location__on_campus',
350
        'facility_location__campus_region',
351
352
353
354
355
356
357
358
359
        # Schedule fields
        'main_schedule__name',
        'main_schedule__valid_start',
        'main_schedule__valid_end',
        'special_schedules__name',
        'special_schedules__valid_start',
        'special_schedules__valid_end',
    )

David Haynes's avatar
David Haynes committed
360
    # Associate a serializer with the ViewSet
361
    serializer_class = FacilitySerializer
362
363

    # Setup filtering
364
365
366
367
368
    filter_backends = (filters.SearchFilter, DjangoFilterBackend,
                       filters.OrderingFilter, )
    search_fields = FILTER_FIELDS
    ordering_fields = FILTER_FIELDS
    filter_fields = FILTER_FIELDS
369

370
    def get_queryset(self):
371
        """
372
373
        Handle incoming GET requests and enumerate objects that get returned by
        the API.
374
        """
David Haynes's avatar
David Haynes committed
375
        # Define ?open_now
David Haynes's avatar
David Haynes committed
376
        open_now = self.request.query_params.get('open_now', None)
David Haynes's avatar
David Haynes committed
377
378
379
        # Define ?closed_now
        closed_now = self.request.query_params.get('closed_now', None)
        if open_now is not None or closed_now is not None:
380
381
382
383
384
385
386
387
            # List of all open facilities
            open_facilities = []
            for facility in Facility.objects.all():
                if facility.is_open():
                    # Append the primary key
                    open_facilities.append(facility.pk)
            # Return all Facility objects with the primary keys located in the
            # open_facilities list
David Haynes's avatar
David Haynes committed
388
389
390
391
392
393
            if open_now:
                return Facility.objects.filter(pk__in=open_facilities)
            # Return all Facility objects with the primary keys not located in
            # the open_facilities list
            elif closed_now:
                return Facility.objects.exclude(pk__in=open_facilities)
394
        # Default behavior
395
        else:
396
397
398
            for facility in Facility.objects.all():
                # Remove all special_schedules that have expired
                facility.clean_special_schedules()
David Haynes's avatar
David Haynes committed
399
            return Facility.objects.all()
400

401
class ScheduleViewSet(viewsets.ModelViewSet):
402
    """
403
404
    A period of time between two dates that represents the beginning and end of a "schedule" or rather, a collection of open times for a facility.

David Haynes's avatar
David Haynes committed
405
406
407
408
409
410
    ---

    ## Default behavior

    [GET /api/schedules/](/api/schedules/?format=json)

411
    Return all Schedule objects that have not expired. (ie. end_date is before today)
412

David Haynes's avatar
David Haynes committed
413
414
415
416
417
    ## Built-in query parameters

    ### **Search**

    [GET /api/schedules/?search=](/api/schedules/?search=&format=json)
418
419
420

    Query parameter that returns objects that match a keyword provided in the search.

David Haynes's avatar
David Haynes committed
421
422
423
424
425
426
427
428
429
430
    **Example Usage**

    [GET /api/schedules/?search=south](/api/schedules/?search=south&format=json)

    Return all Schedule objects that have a field matching the string "south".

    ### **Ordering**

    [GET /api/schedules/?ordering=](/api/schedules/?ordering=&format=json)

431
432
    Query parameter that orders the returned objects based on the provided field to order by.

David Haynes's avatar
David Haynes committed
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
    **Example Usage**

    [GET /api/schedules/?ordering=name](/api/schedules/?ordering=name&format=json)

    Return all Schedule objects ordered by name ascending.

    [GET /api/schedules/?ordering=-name](/api/schedules/?ordering=-name&format=json)

    [GET /api/schedules/?ordering=name](/api/schedules/?ordering=name&format=json)

    Return all Schedule objects ordered by name ascending.
    Return all Schedule objects ordered by name descending.

    ### **Field filtering**

    You can query directly against any field.

    **Example Usage**

    [GET /api/schedules/?name=southside_main](/api/schedules/?name=southside_main&format=json)
453

David Haynes's avatar
David Haynes committed
454
    Return the Schedule object that has "southside_main" as its name.
455
    """
456
457
458
459
460
461
462
463
464
    # All model fields that are available for filtering
    FILTER_FIELDS = (
        # Schedule fields
        'name',
        'valid_start',
        'valid_end',
    )

    # Associate a serializer with the ViewSet
465
466
    serializer_class = ScheduleSerializer

467
468
469
470
471
472
473
    # Setup filtering
    filter_backends = (filters.SearchFilter, DjangoFilterBackend,
                       filters.OrderingFilter, )
    search_fields = FILTER_FIELDS
    ordering_fields = FILTER_FIELDS
    filter_fields = FILTER_FIELDS

David Haynes's avatar
David Haynes committed
474
    def get_queryset(self):
475
476
477
478
479
480
481
482
483
484
485
486
487
488
        """
        Handle incoming GET requests and enumerate objects that get returned by
        the API.
        """
        # List of all schedules that are outdated
        filter_old_schedules = []
        for schedule in Schedule.objects.all():
            if schedule.valid_end and schedule.valid_start:
                # If the schedule ended before today
                if schedule.valid_end < datetime.date.today():
                    # Add it to the list of objects we are excluding
                    filter_old_schedules.append(schedule.pk)
        # Return all Schedule objects that have not expired
        return Schedule.objects.exclude(pk__in=filter_old_schedules)
David Haynes's avatar
David Haynes committed
489

490
class OpenTimeViewSet(viewsets.ModelViewSet):
491
    """
492
493
494
495
496
497
    Represents a time period when a Facility is open.

    Monday = 0, Sunday = 6.

    These objects are returned within a larger Schedule object and thus are not
    an endpoint that is query-able, so just return everything when requested.
498
    """
499
    # Associate a serializer with the ViewSet
500
    serializer_class = OpenTimeSerializer
David Haynes's avatar
David Haynes committed
501
502

    def get_queryset(self):
503
504
505
506
        """
        Handle incoming GET requests and enumerate objects that get returned by
        the API.
        """
507
        return OpenTime.objects.all()