Coverage for events/models.py: 78%
127 statements
« prev ^ index » next coverage.py v6.4.4, created at 2022-11-02 14:35 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2022-11-02 14:35 -0600
1import uuid
2from django.db import models
3from app.models import RandomSlugModel, TimeStampedModel
4from app.utils import create_qr_code
5from .managers import StationCheckInManager
8class Event(RandomSlugModel, TimeStampedModel):
9 """
10 Event Model
11 """
13 owner = models.ForeignKey(
14 "users.User", on_delete=models.PROTECT, related_name="events", null=True
15 )
16 name = models.CharField(max_length=64)
17 date = models.DateField(null=True)
18 image = models.ImageField(upload_to="events/", null=True, blank=True)
21class Challenge(RandomSlugModel, TimeStampedModel):
22 """
23 Event's challenge Model
24 """
26 event = models.ForeignKey(
27 "events.Event", on_delete=models.PROTECT, related_name="challenges"
28 )
29 name = models.CharField(max_length=64)
30 order = models.IntegerField(null=True)
31 objective = models.TextField(null=True, blank=True)
33 start_date = models.DateTimeField(null=True, blank=True)
34 end_date = models.DateTimeField(null=True, blank=True)
36 class Status(models.TextChoices):
37 PAST = "P"
38 CURRENT = "C"
39 FUTURE = "F"
41 status = models.CharField(
42 max_length=1, choices=Status.choices, default=Status.FUTURE
43 )
45 class Type(models.TextChoices):
46 DEFAULT = "D"
47 SCAVENGER_HUNT = "SH"
49 type_of = models.CharField(max_length=2, choices=Type.choices, default=Type.DEFAULT)
51 class StationsOrder(models.TextChoices):
52 DEFAULT = "D"
53 STAGGERED = "S"
54 RANDOM = "R"
55 UNORDERED = "U"
57 scavenger_order = models.CharField(
58 max_length=1, choices=StationsOrder.choices, null=True, blank=True
59 )
61 @property
62 def next_challenge(self):
63 try:
64 next_challenge = self.event.challenges.get(order=self.order + 1)
65 except Challenge.DoesNotExist:
66 next_challenge = None
67 return next_challenge
69 class Meta:
70 ordering = ["order"]
72 def save(self, *args, **kwargs):
73 if self.order is None:
74 self.order = self.event.challenges.count() + 1
75 super(Challenge, self).save(*args, **kwargs)
78class Station(TimeStampedModel):
79 """
80 Scavenger Hunt Challenge's Station Model
81 """
83 uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
84 challenge = models.ForeignKey(
85 "events.Challenge",
86 on_delete=models.PROTECT,
87 related_name="stations",
88 limit_choices_to={"type_of": "SH"},
89 )
91 name = models.CharField(max_length=64, blank=True, null=True)
92 hint = models.CharField(max_length=256)
94 class StationCheckInType(models.TextChoices):
95 QR = "Q"
96 MEDIA = "M"
98 type_of = models.CharField(
99 max_length=1, choices=StationCheckInType.choices, default=StationCheckInType.QR
100 )
102 order = models.IntegerField(null=True, editable=False)
103 qr_code = models.ImageField(null=True, blank=True, editable=False)
105 def save(self, *args, **kwargs):
106 if self.order is None:
107 self.order = self.challenge.stations.count() + 1
108 if self.name is None:
109 self.name = f"Station {self.order}"
110 super(Station, self).save(*args, **kwargs)
111 if not self.qr_code and self.type_of == self.StationCheckInType.QR:
112 create_qr_code(self, "qr_code", self.uuid, "order-{}.png".format(self.pk))
114 class Meta:
115 ordering = ["order"]
117 def __str__(self):
118 return self.name
120 @property
121 def previous_station(self):
122 try:
123 previous_station = self.challenge.stations.get(order=self.order - 1)
124 except Station.DoesNotExist:
125 previous_station = None
126 return previous_station
128 @property
129 def next_station(self):
130 try:
131 next_station = self.challenge.stations.get(order=self.order + 1)
132 except Station.DoesNotExist:
133 next_station = None
134 return next_station
137class StationCheckIn(RandomSlugModel, TimeStampedModel):
138 """
139 SH Station CheckIn Model to handle access
140 """
142 station = models.ForeignKey(
143 "events.Station", on_delete=models.PROTECT, related_name="check_ins"
144 )
145 team = models.ForeignKey(
146 "events.Team", on_delete=models.PROTECT, related_name="check_ins"
147 )
148 participant = models.ForeignKey(
149 "events.Participant",
150 on_delete=models.PROTECT,
151 null=True,
152 blank=True,
153 related_name="check_ins",
154 )
155 checkin_media = models.FileField(null=True, blank=True, upload_to="checkins/")
157 has_checkin = models.BooleanField(default=False)
158 checkin_timestamp = models.DateTimeField(null=True, blank=True)
159 approve_timestamp = models.DateTimeField(null=True, editable=False)
160 is_approved = models.BooleanField(default=False)
161 approved_by = models.ForeignKey(
162 "events.Participant", on_delete=models.PROTECT, null=True, editable=False
163 )
165 objects = StationCheckInManager()
167 class Meta:
168 unique_together = ["station", "team"]
170 def __str__(self):
171 return f"{self.team.name}-{self.station.name}"
174class Team(RandomSlugModel):
175 """
176 Team Model
177 """
179 event = models.ForeignKey(
180 "events.Event", on_delete=models.PROTECT, related_name="teams"
181 )
182 name = models.CharField(max_length=64)
184 @property
185 def total_score(self):
186 total = self.scores.aggregate(models.Sum("score")).get("score__sum")
187 return total if total else 0
190class Score(RandomSlugModel):
191 """
192 Team Challenge Score Model
193 """
195 team = models.ForeignKey(
196 "events.Team", on_delete=models.PROTECT, related_name="scores"
197 )
198 challenge = models.ForeignKey(
199 "events.Challenge", on_delete=models.PROTECT, related_name="scores"
200 )
202 position = models.IntegerField(null=True)
203 score = models.DecimalField(max_digits=12, decimal_places=2, null=True)
205 class Meta:
206 ordering = ["challenge", "score"]
207 unique_together = ["team", "challenge"]
210class Participant(RandomSlugModel, TimeStampedModel):
211 """
212 Event's Participant Model
213 """
215 event = models.ForeignKey(
216 "events.Event", on_delete=models.PROTECT, related_name="participants"
217 )
218 user = models.ForeignKey(
219 "users.User", on_delete=models.PROTECT, related_name="participants"
220 )
221 invited_by = models.ForeignKey(
222 "users.User",
223 on_delete=models.PROTECT,
224 related_name="invites",
225 null=True,
226 blank=True,
227 )
229 team = models.ForeignKey(
230 "events.Team",
231 on_delete=models.PROTECT,
232 related_name="members",
233 null=True,
234 blank=True,
235 )
236 is_master_of_ceremony = models.BooleanField(default=False)
238 class RSVP(models.TextChoices):
239 PENDING = "P"
240 ACCEPTED = "A"
241 REJECTED = "R"
243 rsvp = models.CharField(max_length=1, choices=RSVP.choices, default=RSVP.PENDING)
245 class Meta:
246 unique_together = ["event", "user"]
248 def __str__(self):
249 return f"{self.user.__str__()} - {self.event.name}"