Commit d8371cc6 authored by David Haynes's avatar David Haynes 🙆
Browse files

Merge branch '39-next-level-filtering' into 'master'

Resolve "Sorting of facilities"

Closes #39

See merge request !25
parents 88f9728d 38e2c37c
......@@ -4,8 +4,12 @@ django-cas-client==1.3.0
djangorestframework==3.6.3
django-model-utils==3.0.0
mysqlclient==1.3.10
setuptools==36.0.1
setuptools==36.2.0
django-taggit==0.22.1
django-taggit-serializer==0.1.5
six==1.10.0
djangorestframework-gis==0.11.2
\ No newline at end of file
djangorestframework-gis==0.11.2
django-filter==1.0.4
django-crispy-forms==1.6.1
markdown==2.6.8
coreapi==2.3.1
......@@ -27,7 +27,7 @@ class FacilityAdmin(admin.ModelAdmin):
# We are basically reordering things to look nicer to the user here
fieldsets = (
(None, {
'fields': ('name', 'facility_category', 'facility_location',
'fields': ('facility_name', 'facility_category', 'facility_location',
'main_schedule', 'special_schedules',
'facility_product_tags', 'tapingo_url', 'owners'),
}),
......
......@@ -81,9 +81,9 @@ class Facility(TimeStampedModel):
a specific purpose that can be categorized.
"""
# The name of the Facility
name = models.CharField(max_length=100)
facility_name = models.CharField(max_length=100)
# Instead of id
slug = AutoSlugField(populate_from='name', unique=True)
slug = AutoSlugField(populate_from='facility_name', unique=True)
# The category that this facility can be grouped with
facility_category = models.ForeignKey('Category',
......@@ -162,13 +162,13 @@ class Facility(TimeStampedModel):
verbose_name = "facility"
verbose_name_plural = "facilities"
# Sort by name in admin view
ordering = ['name']
ordering = ['facility_name']
def __str__(self):
"""
String representation of a Facility object.
"""
return self.name
return self.facility_name
class Schedule(TimeStampedModel):
"""
......
......@@ -95,4 +95,4 @@ class FacilitySerializer(serializers.HyperlinkedModelSerializer):
# List the fields that we are serializing
fields = ('id', 'facility_category', 'facility_location',
'facility_product_tags', 'tapingo_url', 'main_schedule',
'special_schedules', 'modified', 'name')
'special_schedules', 'modified', 'facility_name')
......@@ -24,11 +24,11 @@ from rest_framework.routers import DefaultRouter
ROUTER = DefaultRouter()
# Register views to the API router
ROUTER.register(r'alerts', AlertViewSet, 'alert')
ROUTER.register(r'categories', CategoryViewSet, 'category')
ROUTER.register(r'facilities', FacilityViewSet, 'facility')
ROUTER.register(r'schedules', ScheduleViewSet, 'schedule')
ROUTER.register(r'locations', LocationViewSet, 'location')
ROUTER.register(r'alerts', AlertViewSet, 'alert')
ROUTER.register(r'schedules', ScheduleViewSet, 'schedule')
urlpatterns = [
# / - Default route
......
......@@ -2,6 +2,9 @@
api/views.py
Rest Framework Class Views
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.
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
......@@ -17,14 +20,89 @@ from .serializers import (CategorySerializer, FacilitySerializer,
LocationSerializer, AlertSerializer)
# Other Imports
from rest_framework import viewsets
from rest_framework import viewsets, filters
from django_filters.rest_framework import DjangoFilterBackend
class AlertViewSet(viewsets.ReadOnlyModelViewSet):
"""
Some type of notification that is displayed to clients that conveys a message.
Past examples include:
- Random closings
- Modified schedules being in effect
- Election reminder
- Advertising for other SRCT projects
Alerts last for a period of time until the information is no longer dank.
---
## Default behavior
[GET /api/alerts/](/api/alerts/?format=json)
Return all Alert objects.
## Built-in query parameters
### **Search**
[GET /api/alerts/?search=](/api/alerts/?search=&format=json)
Query parameter that returns objects that match a keyword provided in the search.
**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)
Query parameter that orders the returned objects based on the provided field to order by.
**Example Usage**
[GET /api/alerts/?ordering=urgency_tag](/api/alerts/?ordering=urgency_tag&format=json)
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.
"""
# 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
serializer_class = AlertSerializer
# Setup filtering
filter_backends = (filters.SearchFilter, DjangoFilterBackend,
filters.OrderingFilter, )
search_fields = FILTER_FIELDS
ordering_fields = FILTER_FIELDS
filter_fields = FILTER_FIELDS
def get_queryset(self):
"""
Handle incoming GET requests and enumerate objects that get returned by
......@@ -34,10 +112,78 @@ class AlertViewSet(viewsets.ReadOnlyModelViewSet):
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
"""
A Category is a grouping of Facilities that serve a common/similar purpose.
Examples:
- Dining
- Gyms
- Study areas (Libraries, The Ridge, JC, etc)
---
## Default behavior
[GET /api/categories/](/api/categories/)
Return all Category objects.
## Built-in query parameters
### **Search**
[GET /api/categories/?search=](/api/categories/?search=&format=json)
Query parameter that returns objects that match a keyword provided in the search.
**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)
Query parameter that orders the returned objects based on the provided field to order by.
**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**
[GET /api/categories/?name=dining](/api/categories/?name=dining&format=json)
Return the Category object that is named "dining".
"""
# All model fields that are available for filtering
FILTER_FIELDS = (
# Category fields
'name',
)
# Associate a serializer with the ViewSet
serializer_class = CategorySerializer
# Setup filtering
filter_backends = (filters.SearchFilter, DjangoFilterBackend,
filters.OrderingFilter, )
search_fields = FILTER_FIELDS
ordering_fields = FILTER_FIELDS
filter_fields = FILTER_FIELDS
def get_queryset(self):
"""
Handle incoming GET requests and enumerate objects that get returned by
......@@ -47,10 +193,74 @@ class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
class LocationViewSet(viewsets.ReadOnlyModelViewSet):
"""
Represents a specific location that a Facility can be found.
---
## Default behavior
[GET /api/locations/](/api/locations/?format=json)
Return all Location objects.
## Built-in query parameters
### **Search**
[GET /api/locations/?search=](/api/locations/?search=&format=json)
Query parameter that returns objects that match a keyword provided in the search.
**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)
**Example Usage**
[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.
"""
# All model fields that are available for filtering
FILTER_FIELDS = (
# Location fields
'building',
'address',
'on_campus'
)
# Associate a serializer with the ViewSet
serializer_class = LocationSerializer
# Setup filtering
filter_backends = (filters.SearchFilter, DjangoFilterBackend,
filters.OrderingFilter, )
search_fields = FILTER_FIELDS
ordering_fields = FILTER_FIELDS
filter_fields = FILTER_FIELDS
def get_queryset(self):
"""
Handle incoming GET requests and enumerate objects that get returned by
......@@ -62,22 +272,109 @@ class FacilityViewSet(viewsets.ReadOnlyModelViewSet):
"""
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.
GET /api/facilities/
Return all Facility objects. We additionally filter out stale special_schedules to reduce client side calculations.
---
## 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**
[GET /api/facilities/?search=](/api/facilities/?search=&format=json)
Query parameter that returns objects that match a keyword provided in the search.
**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)
Query parameter that orders the returned objects based on the provided field to order by.
**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**
[GET /api/facilities/?facility_name=Southside](/api/facilities/?facility_name=Southside&format=json)
Return the Facility object that has "Southside" as its name.
## Custom query parameters
GET /api/facilities/?open_now
Query parameter that only returns open Facility objects.
### **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.
"""
# All model fields that are available for filtering
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',
# Schedule fields
'main_schedule__name',
'main_schedule__valid_start',
'main_schedule__valid_end',
'special_schedules__name',
'special_schedules__valid_start',
'special_schedules__valid_end',
)
# Associate a serializer with the ViewSet
serializer_class = FacilitySerializer
# Setup filtering
filter_backends = (filters.SearchFilter, DjangoFilterBackend,
filters.OrderingFilter, )
search_fields = FILTER_FIELDS
ordering_fields = FILTER_FIELDS
filter_fields = FILTER_FIELDS
def get_queryset(self):
"""
Handle incoming GET requests and enumerate objects that get returned by
the API.
"""
# Define and handle ?open_now
# Define ?open_now
open_now = self.request.query_params.get('open_now', None)
if open_now is not None:
# 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:
# List of all open facilities
open_facilities = []
for facility in Facility.objects.all():
......@@ -86,7 +383,12 @@ class FacilityViewSet(viewsets.ReadOnlyModelViewSet):
open_facilities.append(facility.pk)
# Return all Facility objects with the primary keys located in the
# open_facilities list
return Facility.objects.filter(pk__in=open_facilities)
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)
# Default behavior
else:
for facility in Facility.objects.all():
......@@ -98,11 +400,75 @@ class ScheduleViewSet(viewsets.ModelViewSet):
"""
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.
GET /api/schedules
---
## Default behavior
[GET /api/schedules/](/api/schedules/?format=json)
Return all Schedule objects that have not expired. (ie. end_date is before today)
## Built-in query parameters
### **Search**
[GET /api/schedules/?search=](/api/schedules/?search=&format=json)
Query parameter that returns objects that match a keyword provided in the search.
**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)
Query parameter that orders the returned objects based on the provided field to order by.
**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)
Return the Schedule object that has "southside_main" as its name.
"""
# All model fields that are available for filtering
FILTER_FIELDS = (
# Schedule fields
'name',
'valid_start',
'valid_end',
)
# Associate a serializer with the ViewSet
serializer_class = ScheduleSerializer
# Setup filtering
filter_backends = (filters.SearchFilter, DjangoFilterBackend,
filters.OrderingFilter, )
search_fields = FILTER_FIELDS
ordering_fields = FILTER_FIELDS
filter_fields = FILTER_FIELDS
def get_queryset(self):
"""
Handle incoming GET requests and enumerate objects that get returned by
......@@ -121,8 +487,14 @@ class ScheduleViewSet(viewsets.ModelViewSet):
class OpenTimeViewSet(viewsets.ModelViewSet):
"""
Return all OpenTime objects.
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.
"""
# Associate a serializer with the ViewSet
serializer_class = OpenTimeSerializer
def get_queryset(self):
......@@ -130,4 +502,4 @@ class OpenTimeViewSet(viewsets.ModelViewSet):
Handle incoming GET requests and enumerate objects that get returned by
the API.
"""
return OpenTime.objects.all()
\ No newline at end of file
return OpenTime.objects.all()
......@@ -244,6 +244,8 @@ INSTALLED_APPS = (
'taggit_serializer',
'rest_framework',
'rest_framework_gis',
'django_filters',
'crispy_forms',
)
"""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment