-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodels.py
More file actions
209 lines (154 loc) · 7.07 KB
/
models.py
File metadata and controls
209 lines (154 loc) · 7.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
import secrets
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.db import models
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from django.utils import timezone
User = get_user_model()
class GroupMembership(models.Model):
# INVARIANT: either user or username should always be set. if user is not None, then the
# username should the be username of the associated user.
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="memberships", blank=True, null=True
)
group = models.ForeignKey("Group", on_delete=models.CASCADE, related_name="memberships")
# When accepted is False, this is a request, otherwise this is an active membership.
accepted = models.BooleanField(default=False)
ADMIN = "A"
MEMBER = "M"
type = models.CharField(max_length=10, choices=[(ADMIN, "Admin"), (MEMBER, "Member")])
pennkey_allow = models.BooleanField(default=False)
notifications = models.BooleanField(default=True)
is_wharton = models.BooleanField(blank=True, null=True, default=None)
is_seas = models.BooleanField(blank=True, null=True, default=None)
@property
def is_invite(self):
return not self.accepted
def __str__(self):
return f"{self.user}<->{self.group}"
def save(self, *args, **kwargs):
# Determines whether user is wharton or not
if self.is_wharton is None:
self.is_wharton = self.check_wharton()
# Determines whether user is seas or not
if self.is_seas is None:
self.is_seas = self.check_seas()
super().save(*args, **kwargs)
def check_wharton(self):
return WhartonGSRBooker.is_wharton(self.user)
def check_seas(self):
return PennGroupsGSRBooker.is_seas(self.user)
class Meta:
verbose_name = "Group Membership"
class Group(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
members = models.ManyToManyField(User, through=GroupMembership, related_name="booking_groups")
name = models.CharField(max_length=255)
color = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
ADMIN = "A"
MEMBER = "M"
def __str__(self):
return f"{self.name}-{self.pk}"
def has_member(self, user):
memberships = GroupMembership.objects.filter(group=self, user=user)
return memberships.all().exists()
def has_admin(self, user):
memberships = GroupMembership.objects.filter(group=self, accepted=True)
return memberships.all().filter(type="A").filter(user=user).exists()
def get_pennkey_active_members(self):
memberships = GroupMembership.objects.filter(group=self, accepted=True)
pennkey_active_members_list = memberships.all().filter(pennkey_allow=True).all()
return [member for member in pennkey_active_members_list]
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
GroupMembership.objects.get_or_create(
group=self, user=self.owner, type=GroupMembership.ADMIN, accepted=True
)
class GSRManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(in_use=True)
class GSR(models.Model):
KIND_WHARTON = "WHARTON"
KIND_LIBCAL = "LIBCAL"
KIND_PENNGROUPS = "PENNGRP"
KIND_OPTIONS = (
(KIND_WHARTON, "Wharton"),
(KIND_LIBCAL, "Libcal"),
(KIND_PENNGROUPS, "Penngrp"),
)
kind = models.CharField(max_length=7, choices=KIND_OPTIONS, default=KIND_LIBCAL)
lid = models.CharField(max_length=255)
gid = models.IntegerField(null=True)
name = models.CharField(max_length=255)
bookable_days = models.IntegerField(default=7)
image_url = models.URLField()
in_use = models.BooleanField(default=True)
objects = GSRManager()
all_objects = models.Manager() # for admin page
def __str__(self):
return f"{self.name}: {self.lid}-{self.gid}"
class Reservation(models.Model):
start = models.DateTimeField(default=timezone.now)
end = models.DateTimeField(default=timezone.now)
creator = models.ForeignKey(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE, null=True, blank=True)
is_cancelled = models.BooleanField(default=False)
reminder_sent = models.BooleanField(default=False)
class GSRBooking(models.Model):
# TODO: change to non-null after reservations are created for current bookings
reservation = models.ForeignKey(Reservation, on_delete=models.CASCADE, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
booking_id = models.CharField(max_length=255, null=True, blank=True)
gsr = models.ForeignKey(GSR, on_delete=models.CASCADE)
room_id = models.IntegerField()
room_name = models.CharField(max_length=255)
start = models.DateTimeField(default=timezone.now)
end = models.DateTimeField(default=timezone.now)
is_cancelled = models.BooleanField(default=False)
def __str__(self):
return f"{self.user} - {self.gsr.name} - {self.start} - {self.end}"
class GSRShareCode(models.Model):
code = models.CharField(max_length=8, unique=True, db_index=True)
booking = models.OneToOneField(GSRBooking, on_delete=models.CASCADE, related_name="share_code")
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name="gsr_share_codes")
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.code} - {self.booking}"
@classmethod
def generate_code(cls):
while True:
# Creates unique 8 character code
code = secrets.token_urlsafe(6)[:8]
if not cls.objects.filter(code=code).exists():
return code
def is_valid(self):
"""Check if the share code is still valid (not revoked and not expired)"""
now = timezone.now()
if self.booking.end and self.booking.end <= now:
return False
return True
# import at end to prevent circular dependency
from gsr_booking.api_wrapper import PennGroupsGSRBooker, WhartonGSRBooker # noqa: E402
# Signal handlers to clear location caches when GSR data changes
def clear_gsr_location_caches():
"""Clear all GSR location caches for all permission combinations"""
cache_keys = [
"gsr_locations:penn_labs",
"gsr_locations:wharton_True_seas_True",
"gsr_locations:wharton_True_seas_False",
"gsr_locations:wharton_False_seas_True",
"gsr_locations:wharton_False_seas_False",
]
for key in cache_keys:
cache.delete(key)
@receiver(post_save, sender=GSR)
def clear_cache_on_gsr_save(sender, instance, **kwargs):
"""Clear location caches when a GSR is saved (via admin or any other method)"""
clear_gsr_location_caches()
@receiver(post_delete, sender=GSR)
def clear_cache_on_gsr_delete(sender, instance, **kwargs):
"""Clear location caches when a GSR is deleted (via admin or any other method)"""
clear_gsr_location_caches()