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

Merge branch '38-facility-address' into 'master'

Resolve "Serve physical address of restaurants"

Closes #38

See merge request !18
parents 5bcf2aa2 da1fc5f8
Pipeline #1370 passed with stage
in 1 minute and 6 seconds
......@@ -13,4 +13,5 @@ whats_open/secret_key.py
whats_open/assets/
static/admin/
data
whats_open/website/migrations
\ No newline at end of file
whats_open/website/migrations
.vscode
\ No newline at end of file
"""
Django admin interface configuration.
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
......@@ -6,45 +11,50 @@ from __future__ import (absolute_import, division, print_function,
from django.contrib import admin
# App Imports
from .models import Facility, Schedule, OpenTime, Category
class OpenTimeInline(admin.TabularInline):
model = OpenTime
fk_name = 'schedule'
max_num = 7
class OpenTimeAdmin(admin.ModelAdmin):
pass
from .models import Facility, Schedule, OpenTime, Category, Location
class FacilityAdmin(admin.ModelAdmin):
"""
"""
model = Facility
list_display = ['name', 'location' ]
list_filter = ['facility_category', ]
list_display = ['name', ]
list_filter = ['facility_category', 'facility_location']
fieldsets = (
(None, {
'fields': ('name', 'facility_category',
('location', 'on_campus'), 'address',
'main_schedule', 'special_schedules', ),
}),
('Advanced', {
'fields': ('owners', ),
'classes': ('collapse', ),
'fields': ('name', 'facility_category', 'facility_location',
'main_schedule', 'special_schedules', 'owners'),
}),
)
class OpenTimeInline(admin.TabularInline):
"""
A table of time periods that represent an "open time" for a Facility.
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.TabularInline
"""
# Columns correspond to each attribute in the OpenTime table
model = OpenTime
# 7 days of the week, so only have 7 rows
max_num = 7
class ScheduleAdmin(admin.ModelAdmin):
"""
"""
list_display = ['name', 'modified']
inlines = [OpenTimeInline, ]
fieldsets = (
(None, {
'fields': ('name',
('valid_start', 'valid_end'),)
}),
'fields': ('name',
# Pair valid_start and valid_end together on the same
# line
('valid_start', 'valid_end'))
}),
)
class CategoryAdmin(admin.ModelAdmin):
pass
# Register the administration panels
# https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#modeladmin-objects
admin.site.register(Facility, FacilityAdmin)
admin.site.register(Schedule, ScheduleAdmin)
admin.site.register(Category, CategoryAdmin)
# Use the default ModelAdmin interface for these
admin.site.register(Category)
admin.site.register(Location)
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
# Python stdlib Imports
import re
# App Imports
from .models import Facility
def export_data():
facilities = list()
# Sort the facilities by alphabetical order ignoring "the" and "a"
alphalist = sorted(Facility.objects.all(),
key=lambda r: re.sub('^(the|a) ', '', r.name, count=1,
flags=re.IGNORECASE))
for facility in alphalist:
facility_data = {
'name': facility.name,
'location': facility.location,
'address': facility.address,
'id': facility.id
}
open_times = list()
# Sort open times by their start day and time
sorted_times = sorted(facility.main_schedule.open_times.all(),
key=lambda t: (t.start_day, t.start_time, t.end_time))
for time in sorted_times:
open_times.append({
'start_day': time.start_day,
'start_time': time.start_time.isoformat(),
'end_day': time.end_day,
'end_time': time.end_time.isoformat()
})
facility_data['main_schedule'] = {
'name': facility.main_schedule.name,
'open_times': open_times
}
special_schedules = list()
for schedule in facility.special_schedules.all():
open_times = list()
sorted_times = sorted(schedule.open_times.all(),
key=lambda t: (t.start_day, t.start_time, t.end_time))
for time in sorted_times:
open_times.append({
'start_day': time.start_day,
'start_time': time.start_time.isoformat(),
'end_day': time.end_day,
'end_time': time.end_time.isoformat()
})
special_schedules.append({
'name': schedule.name,
'start': schedule.valid_start.isoformat(),
'end': schedule.valid_end.isoformat(),
'open_times': open_times
})
facility_data['special_schedules'] = special_schedules
facilities.append(facility_data)
return facilities
......@@ -12,34 +12,75 @@ from model_utils.models import TimeStampedModel
from autoslug import AutoSlugField
class Category(TimeStampedModel):
"""
"""
name = models.CharField(max_length=100)
class Meta:
verbose_name = "category"
verbose_name_plural = "categories"
# Sort by name in admin view
# Sort by name in admin view.
ordering = ['name']
def __str__(self):
return '%s' % self.name
class Facility(TimeStampedModel):
"""Represents a facility location on campus."""
name = models.CharField(max_length=100)
slug = AutoSlugField(populate_from='name',unique=True) # instead of id
facility_category = models.ForeignKey('Category', related_name="facilities", null=True, blank=True)
class Location(TimeStampedModel):
"""
Represents a specific location that a Facility can be found.
"""
# The building that the facility is located in (on campus).
building = models.CharField(max_length=100)
# The physical address of the facility.
address = models.CharField(max_length=100)
# Boolean for whether or not the location is "on campus" or not.
on_campus = models.BooleanField(default=True)
location = models.CharField(max_length=100, null=True, blank=True)
address = models.CharField(max_length=100, null=True, blank=True)
class Meta:
verbose_name = "location"
verbose_name_plural = "locations"
def __str__(self):
"""
String representation of a Location object.
"""
return 'Found in %s at %s | On Campus: %s' % (self.building,
self.address,
self.on_campus)
class Facility(TimeStampedModel):
"""
Represents a specific facility location.
"""
# The name of the Facility
name = models.CharField(max_length=100)
# Instead of id
slug = AutoSlugField(populate_from='name', unique=True)
# The category that this facility falls under
facility_category = models.ForeignKey('Category',
related_name="facilities",
null=True, blank=True)
# The location object that relates to this facility
facility_location = models.ForeignKey('Location',
related_name="facilities")
# The User(s) that claim ownership over this facility
owners = models.ManyToManyField(User)
# The schedule that is defaulted to if no special schedule is in effect
main_schedule = models.ForeignKey('Schedule',
related_name='facility_main')
related_name='facility_main')
# A schedule that has a specific start and end date
special_schedules = models.ManyToManyField('Schedule',
related_name='facility_special', blank=True,
help_text='This schedule will come into effect only for its specified duration.')
related_name='facility_special',
blank=True,
help_text="""This schedule will
come into effect
only for its
specified duration.
""")
class Meta:
verbose_name = "facility"
verbose_name_plural = "facilities"
......@@ -52,7 +93,6 @@ class Facility(TimeStampedModel):
First checks any valid special schedules and then checks the
main default schedule.
"""
today = datetime.datetime.today().date()
# Check special schedules first
......@@ -77,20 +117,23 @@ class Schedule(TimeStampedModel):
For special (temporary) schedules, start and end dates for
when this schedule will be valid can also be set.
"""
name = models.CharField(max_length=100)
# inclusive:
# (inclusive)
valid_start = models.DateField('Start Date', null=True, blank=True,
help_text='Date that this schedule goes into effect')
help_text="""Date that this schedule goes
into effect""")
valid_end = models.DateField('End Date', null=True, blank=True,
help_text='Last day that this schedule is in effect')
help_text="""Last day that this schedule is
in effect""")
class Meta:
ordering = ['name']
def isOpenNow(self):
"""Return true if this schedule is open right now."""
"""
Return true if this schedule is open right now.
"""
for time in OpenTime.objects.filter(schedule=self):
if time.isOpenNow():
return True
......@@ -101,8 +144,9 @@ class Schedule(TimeStampedModel):
class OpenTime(TimeStampedModel):
"""Represents a period time when a Facility is open"""
"""
Represents a period time when a Facility is open.
"""
MONDAY = 0
TUESDAY = 1
WEDNESDAY = 2
......@@ -122,13 +166,16 @@ class OpenTime(TimeStampedModel):
)
schedule = models.ForeignKey('Schedule', related_name='open_times')
start_day = models.IntegerField(default=0, choices=DAY_CHOICES) # 0-6, Monday == 0
# 0-6, Monday == 0
start_day = models.IntegerField(default=0, choices=DAY_CHOICES)
start_time = models.TimeField()
end_day = models.IntegerField(default=0, choices=DAY_CHOICES)
end_time = models.TimeField()
def isOpenNow(self):
"""Return true if the current time is this OpenTime's range"""
"""
Return true if the current time is this OpenTime's range
"""
today = datetime.datetime.today()
if self.start_day <= self.end_day:
if self.start_day == today.weekday():
......@@ -154,7 +201,9 @@ class OpenTime(TimeStampedModel):
def __str__(self):
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
'Saturday', 'Sunday']
'Saturday', 'Sunday']
return '%s %s to %s %s' % (weekdays[self.start_day],
self.start_time.strftime("%H:%M:%S"), weekdays[self.end_day],
self.end_time.strftime("%H:%M:%S"))
self.start_time.strftime("%H:%M:%S"),
# to
weekdays[self.end_day],
self.end_time.strftime("%H:%M:%S"))
"""
Rest Framework Serializers
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
# App Imports
from .models import Category, Facility, Schedule, OpenTime
from .models import Category, Facility, Schedule, OpenTime, Location
# Other Imports
from rest_framework import serializers
class OpenTimeSerializer(serializers.ModelSerializer):
"""
"""
class Meta:
model = OpenTime
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
"""
"""
class Meta:
model = Category
fields = '__all__'
class OpenTimeSerializer(serializers.ModelSerializer):
class LocationSerializer(serializers.ModelSerializer):
"""
"""
class Meta:
model = OpenTime
model = Location
fields = '__all__'
class ScheduleSerializer(serializers.ModelSerializer):
"""
"""
open_times = OpenTimeSerializer(many=True, read_only=True)
class Meta:
model = Schedule
fields = ( 'id', 'open_times', 'modified', 'name', 'valid_start', 'valid_end' )
fields = ('id', 'open_times', 'modified', 'name', 'valid_start',
'valid_end')
class FacilitySerializer(serializers.HyperlinkedModelSerializer):
category = CategorySerializer(many=False, read_only=True)
"""
"""
facility_category = CategorySerializer(many=False, read_only=True)
facility_location = LocationSerializer(many=False, read_only=True)
main_schedule = ScheduleSerializer(many=False, read_only=True)
special_schedules = ScheduleSerializer(many=True, read_only=True)
class Meta:
model = Facility
fields = ('id', 'category', 'main_schedule', 'special_schedules',
'location', 'address', 'modified', 'name')
fields = ('id', 'facility_category', 'facility_location',
'main_schedule', 'special_schedules', 'modified', 'name')
......@@ -4,22 +4,28 @@ from __future__ import (absolute_import, division, print_function,
# Django Imports
from django.conf.urls import include, url
from django.views.generic.base import RedirectView
# App Imports
from .views import CategoryViewSet, FacilityViewSet, ScheduleViewSet
from .views import (CategoryViewSet, FacilityViewSet, ScheduleViewSet,
LocationViewSet)
# Other Imports
from rest_framework.routers import DefaultRouter
# Instiantiate our DefaultRouter
# Instantiate our DefaultRouter
ROUTER = DefaultRouter()
# Register views to the API router
ROUTER.register(r'categories', CategoryViewSet)
ROUTER.register(r'facilities', FacilityViewSet)
ROUTER.register(r'schedules', ScheduleViewSet)
ROUTER.register(r'locations', LocationViewSet)
urlpatterns = [
# / - Default route
# We redirect to /api since this is in reality the default page for the API
url(r'^$', RedirectView.as_view(url='/api')),
# /api - Root API URL
url(r'^api/', include(ROUTER.urls)),
]
"""
Rest Framework Class Views
"""
# Future Imports
from __future__ import (absolute_import, division, print_function,
unicode_literals)
# Python stdlib Imports
import hashlib
import json
# Django Imports
from django.template import RequestContext
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.views.decorators.http import condition
from django.views.generic import ListView, DetailView
from model_utils.models import TimeStampedModel
# App Imports
from .models import Facility, OpenTime, Category, Schedule
from .api import export_data
from .serializers import CategorySerializer, FacilitySerializer, ScheduleSerializer, OpenTimeSerializer
from .models import Facility, OpenTime, Category, Schedule, Location
from .serializers import (CategorySerializer, FacilitySerializer,
ScheduleSerializer, OpenTimeSerializer,
LocationSerializer)
# Other Imports
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import viewsets
# Rest Framework Class Views
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
"""
"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
class LocationViewSet(viewsets.ReadOnlyModelViewSet):
"""
"""
queryset = Location.objects.all()
serializer_class = LocationSerializer
class FacilityViewSet(viewsets.ReadOnlyModelViewSet):
"""
"""
queryset = Facility.objects.all()
serializer_class = FacilitySerializer
def get_queryset(self):
"""
"""
queryset = Facility.objects.all()
open_now = self.request.query_params.get('open', None)
if open_now is not None:
results = []
for fac in queryset:
print(fac)
if fac.isOpen():
print(True)
results.append(fac)
return results
else:
return queryset
class ScheduleViewSet(viewsets.ModelViewSet):
"""
"""
queryset = Schedule.objects.all()
serializer_class = ScheduleSerializer
class OpenTimeViewSet(viewsets.ModelViewSet):
"""
"""
queryset = OpenTime.objects.all()
serializer_class = OpenTimeSerializer
def gen_etag(request):
return hashlib.sha1(str(OpenTime.objects.all())).hexdigest()
def gen_last_modified(request):
return TimeStampedModel.objects.all().order_by('-last_modified')[0].last_modified
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