models.py 10.1 KB
Newer Older
David Haynes's avatar
David Haynes committed
1
2
3
4
5
6
7
8
"""
api/models.py

Define the objects that will be stored in the database and later served through
the API.

https://docs.djangoproject.com/en/1.11/topics/db/models/
"""
9
10
11
12
13
14
15
16
# Future Imports
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

# Python stdlib Imports
import datetime

# Django Imports
17
from django.db import models
18
from django.contrib.auth.models import User
David Haynes's avatar
David Haynes committed
19
20

# Other Imports
Ben Waters's avatar
Ben Waters committed
21
from model_utils.models import TimeStampedModel
22
from autoslug import AutoSlugField
Tyler Hallada's avatar
Tyler Hallada committed
23

Ben Waters's avatar
Ben Waters committed
24
class Category(TimeStampedModel):
25
    """
David Haynes's avatar
David Haynes committed
26
27
28
29
30
31
32
    Represents the "category" that a Facility falls under. A Category is a
    grouping of Facilities that serve a common/similar purpose.

    ex.
    - Dining
    - Gyms
    - Study areas (Libraries, The Ridge, JC, etc)
33
    """
David Haynes's avatar
David Haynes committed
34
    # The name of the category
35
36
37
38
39
    name = models.CharField(max_length=100)

    class Meta:
        verbose_name = "category"
        verbose_name_plural = "categories"
40
        # Sort by name in admin view.
41
42
        ordering = ['name']

43
    def __str__(self):
David Haynes's avatar
David Haynes committed
44
45
46
47
        """
        String representation of a Category object.
        """
        return self.name
48

David Haynes's avatar
David Haynes committed
49
50
class Location(TimeStampedModel):
    """
51
    Represents a specific location that a Facility can be found.
David Haynes's avatar
David Haynes committed
52
    """
53
54
55
56
57
    # 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.
David Haynes's avatar
David Haynes committed
58
59
    on_campus = models.BooleanField(default=True)

60
61
62
63
64
65
66
67
68
69
70
71
    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)

Ben Waters's avatar
Ben Waters committed
72
class Facility(TimeStampedModel):
David Haynes's avatar
David Haynes committed
73
    """
David Haynes's avatar
David Haynes committed
74
75
76
    Represents a specific facility location. 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
77
78
    """
    # The name of the Facility
Tyler Hallada's avatar
Tyler Hallada committed
79
    name = models.CharField(max_length=100)
David Haynes's avatar
David Haynes committed
80
81
    # Instead of id
    slug = AutoSlugField(populate_from='name', unique=True)
82

David Haynes's avatar
David Haynes committed
83
    # The category that this facility can be grouped with
84
    facility_category = models.ForeignKey('Category',
David Haynes's avatar
David Haynes committed
85
                                          related_name="categories")
David Haynes's avatar
David Haynes committed
86
    # The location object that relates to this facility
87
88
    facility_location = models.ForeignKey('Location',
                                          related_name="facilities")
89

David Haynes's avatar
David Haynes committed
90
    # The User(s) that claim ownership over this facility
91
    owners = models.ManyToManyField(User)
David Haynes's avatar
David Haynes committed
92
93

    # The schedule that is defaulted to if no special schedule is in effect
94
    main_schedule = models.ForeignKey('Schedule',
David Haynes's avatar
David Haynes committed
95
96
                                      related_name='facility_main')
    # A schedule that has a specific start and end date
97
    special_schedules = models.ManyToManyField('Schedule',
David Haynes's avatar
David Haynes committed
98
99
                                               related_name='facility_special',
                                               blank=True,
100
101
102
103
104
                                               help_text="""This schedule will
                                                            come into effect
                                                            only for its
                                                            specified duration.
                                                            """)
David Haynes's avatar
David Haynes committed
105

David Haynes's avatar
David Haynes committed
106
    def is_open(self):
107
        """
108
        Return true if this facility is currently open.
109

David Haynes's avatar
David Haynes committed
110
111
        First checks any valid special schedules and then checks the main,
        default, schedule.
112
        """
David Haynes's avatar
David Haynes committed
113
        # Get the current date
114
        today = datetime.datetime.today().date()
David Haynes's avatar
David Haynes committed
115
        # Check special schedules first, loop through all of them
116
117
118
        for schedule in self.special_schedules.all():
            # Special schedules must have valid_start and valid_end set
            if schedule.valid_start and schedule.valid_end:
David Haynes's avatar
David Haynes committed
119
                # If a special schedule in in effect
120
                if schedule.valid_start <= today <= schedule.valid_end:
David Haynes's avatar
David Haynes committed
121
122
123
124
                    # Check if the facility is open or not based on that 
                    # special schedule
                    if schedule.is_open_now():
                        # Open
125
                        return True
126
                    else:
David Haynes's avatar
David Haynes committed
127
                        # Closed
128
                        return False
David Haynes's avatar
David Haynes committed
129
130
131
132
        # If no special schedule is in effect then check if the facility is
        # open using the main_schedule
        if self.main_schedule.is_open_now():
            # Open
133
            return True
David Haynes's avatar
David Haynes committed
134
135
136
137
138
139
140
141
142
        else:
            # Closed
            return False

    class Meta:
        verbose_name = "facility"
        verbose_name_plural = "facilities"
        # Sort by name in admin view
        ordering = ['name']
143

144
    def __str__(self):
David Haynes's avatar
David Haynes committed
145
146
147
        """
        String representation of a Facility object.
        """
148
149
        return self.name

Ben Waters's avatar
Ben Waters committed
150
class Schedule(TimeStampedModel):
151
    """
David Haynes's avatar
David Haynes committed
152
153
    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.
154
    """
David Haynes's avatar
David Haynes committed
155
    # The name of the schedule
Tyler Hallada's avatar
Tyler Hallada committed
156
    name = models.CharField(max_length=100)
David Haynes's avatar
David Haynes committed
157
158

    # The start date of the schedule
159
    # (inclusive)
160
    valid_start = models.DateField('Start Date', null=True, blank=True,
161
162
                                   help_text="""Date that this schedule goes
                                                into effect""")
David Haynes's avatar
David Haynes committed
163
164
    # The end date of the schedule
    # (inclusive)
165
    valid_end = models.DateField('End Date', null=True, blank=True,
166
167
168
                                 help_text="""Last day that this schedule is
                                              in effect""")

David Haynes's avatar
David Haynes committed
169
    def is_open_now(self):
170
171
172
        """
        Return true if this schedule is open right now.
        """
David Haynes's avatar
David Haynes committed
173
174
175
176
177
178
        # Loop through all the open times that correspond to this schedule
        for open_time in OpenTime.objects.filter(schedule=self):
            # If the current time we are looking at is open, then the schedule 
            # will say that the facility is open
            if open_time.is_open_now():
                # Open
179
                return True
David Haynes's avatar
David Haynes committed
180
        # Closed (all open times are not open)
181
182
        return False

David Haynes's avatar
David Haynes committed
183
184
185
186
    class Meta:
        # Sort by name in admin view
        ordering = ['name']

187
    def __str__(self):
David Haynes's avatar
David Haynes committed
188
189
190
        """
        String representation of a Schedule object.
        """
191
192
193
        return self.name


Ben Waters's avatar
Ben Waters committed
194
class OpenTime(TimeStampedModel):
195
    """
David Haynes's avatar
David Haynes committed
196
197
198
    Represents a time period when a Facility is open.

    Monday = 0, Sunday = 6.
199
    """
David Haynes's avatar
David Haynes committed
200
    # Define integer constants to represent days of the week
201
202
203
204
205
206
207
208
    MONDAY = 0
    TUESDAY = 1
    WEDNESDAY = 2
    THURSDAY = 3
    FRIDAY = 4
    SATURDAY = 5
    SUNDAY = 6

David Haynes's avatar
David Haynes committed
209
    # Tuple that ties a day of the week with an integer representation
210
211
212
213
214
215
216
217
218
219
    DAY_CHOICES = (
        (MONDAY, 'Monday'),
        (TUESDAY, 'Tuesday'),
        (WEDNESDAY, 'Wednesday'),
        (THURSDAY, 'Thursday'),
        (FRIDAY, 'Friday'),
        (SATURDAY, 'Saturday'),
        (SUNDAY, 'Sunday'),
    )

David Haynes's avatar
David Haynes committed
220
    # The schedule that this period of open time is a part of
221
    schedule = models.ForeignKey('Schedule', related_name='open_times')
David Haynes's avatar
David Haynes committed
222
223

    # The day that the open time begins on
224
    start_day = models.IntegerField(default=0, choices=DAY_CHOICES)
David Haynes's avatar
David Haynes committed
225
    # The day that the open time ends on
226
    end_day = models.IntegerField(default=0, choices=DAY_CHOICES)
David Haynes's avatar
David Haynes committed
227
228
229
230

    # The time of day that the open time begins at
    start_time = models.TimeField()
    # The time of day that the open time ends
231
232
    end_time = models.TimeField()

David Haynes's avatar
David Haynes committed
233
    def is_open_now(self):
234
        """
David Haynes's avatar
David Haynes committed
235
        Return true if the current time is this OpenTime's range.
236
        """
David Haynes's avatar
David Haynes committed
237
        # Get the current datetime
238
        today = datetime.datetime.today()
David Haynes's avatar
David Haynes committed
239
        # Check that the start occurs before the end
240
        if self.start_day <= self.end_day:
David Haynes's avatar
David Haynes committed
241
            # If today is the start_day
242
            if self.start_day == today.weekday():
David Haynes's avatar
David Haynes committed
243
                # If the start_time has not occurred
244
                if self.start_time > today.time():
David Haynes's avatar
David Haynes committed
245
                    # Closed
246
                    return False
David Haynes's avatar
David Haynes committed
247
            # If the start_day has not occurred
248
            elif self.start_day > today.weekday():
David Haynes's avatar
David Haynes committed
249
                # Closed
250
                return False
David Haynes's avatar
David Haynes committed
251
            # If the end_day is today
252
            if self.end_day == today.weekday():
David Haynes's avatar
David Haynes committed
253
                # If the end_time has already occurred
254
                if self.end_time < today.time():
David Haynes's avatar
David Haynes committed
255
                    # Closed
256
                    return False
David Haynes's avatar
David Haynes committed
257
            # If the end_day has already occurred
258
            elif self.end_day < today.weekday():
David Haynes's avatar
David Haynes committed
259
                # Closed
260
                return False
David Haynes's avatar
David Haynes committed
261
        # The end_day > start_day
262
        else:
David Haynes's avatar
David Haynes committed
263
            # If today is the start_day
264
            if self.start_day == today.weekday():
David Haynes's avatar
David Haynes committed
265
                # If the start_time has not occurred
266
                if self.start_time > today.time():
David Haynes's avatar
David Haynes committed
267
                    # Closed
268
                    return False
David Haynes's avatar
David Haynes committed
269
            # If the end_day is today
270
            if self.end_day == today.weekday():
David Haynes's avatar
David Haynes committed
271
                # If the end_time has already occurred
272
                if self.end_time < today.time():
David Haynes's avatar
David Haynes committed
273
                    # Closed
274
                    return False
David Haynes's avatar
David Haynes committed
275
276
            # If the current date takes place after the end_date but before
            # start_day
277
            if self.end_day < today.weekday() < self.start_day:
David Haynes's avatar
David Haynes committed
278
                # Closed
279
                return False
David Haynes's avatar
David Haynes committed
280
        # All checks passed, it's Open
281
        return True
282

283
    def __str__(self):
David Haynes's avatar
David Haynes committed
284
285
286
        """
        String representation of a OpenTime object.
        """
287
        weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
288
                    'Saturday', 'Sunday']
289
        return '%s %s to %s %s' % (weekdays[self.start_day],
290
291
292
293
                                   self.start_time.strftime("%H:%M:%S"),
                                   # to
                                   weekdays[self.end_day],
                                   self.end_time.strftime("%H:%M:%S"))