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
Ben Waters's avatar
Ben Waters committed
19
from model_utils.models import TimeStampedModel
20
from autoslug import AutoSlugField
Tyler Hallada's avatar
Tyler Hallada committed
21

Ben Waters's avatar
Ben Waters committed
22
class Category(TimeStampedModel):
23
    """
David Haynes's avatar
David Haynes committed
24
25
26
27
28
29
30
    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)
31
    """
David Haynes's avatar
David Haynes committed
32
    # The name of the category
33
34
35
36
37
    name = models.CharField(max_length=100)

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

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

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

58
59
60
61
62
63
64
65
66
67
68
69
    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
70
class Facility(TimeStampedModel):
David Haynes's avatar
David Haynes committed
71
    """
David Haynes's avatar
David Haynes committed
72
73
74
    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
75
76
    """
    # The name of the Facility
Tyler Hallada's avatar
Tyler Hallada committed
77
    name = models.CharField(max_length=100)
David Haynes's avatar
David Haynes committed
78
79
    # Instead of id
    slug = AutoSlugField(populate_from='name', unique=True)
80

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

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

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

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

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

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

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

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

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

David Haynes's avatar
David Haynes committed
167
    def is_open_now(self):
168
169
170
        """
        Return true if this schedule is open right now.
        """
David Haynes's avatar
David Haynes committed
171
172
173
174
175
176
        # 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
177
                return True
David Haynes's avatar
David Haynes committed
178
        # Closed (all open times are not open)
179
180
        return False

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

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


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

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

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

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

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

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

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

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