Coverage for events/models.py: 78%

127 statements  

« 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 

6 

7 

8class Event(RandomSlugModel, TimeStampedModel): 

9 """ 

10 Event Model 

11 """ 

12 

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) 

19 

20 

21class Challenge(RandomSlugModel, TimeStampedModel): 

22 """ 

23 Event's challenge Model 

24 """ 

25 

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) 

32 

33 start_date = models.DateTimeField(null=True, blank=True) 

34 end_date = models.DateTimeField(null=True, blank=True) 

35 

36 class Status(models.TextChoices): 

37 PAST = "P" 

38 CURRENT = "C" 

39 FUTURE = "F" 

40 

41 status = models.CharField( 

42 max_length=1, choices=Status.choices, default=Status.FUTURE 

43 ) 

44 

45 class Type(models.TextChoices): 

46 DEFAULT = "D" 

47 SCAVENGER_HUNT = "SH" 

48 

49 type_of = models.CharField(max_length=2, choices=Type.choices, default=Type.DEFAULT) 

50 

51 class StationsOrder(models.TextChoices): 

52 DEFAULT = "D" 

53 STAGGERED = "S" 

54 RANDOM = "R" 

55 UNORDERED = "U" 

56 

57 scavenger_order = models.CharField( 

58 max_length=1, choices=StationsOrder.choices, null=True, blank=True 

59 ) 

60 

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 

68 

69 class Meta: 

70 ordering = ["order"] 

71 

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) 

76 

77 

78class Station(TimeStampedModel): 

79 """ 

80 Scavenger Hunt Challenge's Station Model 

81 """ 

82 

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 ) 

90 

91 name = models.CharField(max_length=64, blank=True, null=True) 

92 hint = models.CharField(max_length=256) 

93 

94 class StationCheckInType(models.TextChoices): 

95 QR = "Q" 

96 MEDIA = "M" 

97 

98 type_of = models.CharField( 

99 max_length=1, choices=StationCheckInType.choices, default=StationCheckInType.QR 

100 ) 

101 

102 order = models.IntegerField(null=True, editable=False) 

103 qr_code = models.ImageField(null=True, blank=True, editable=False) 

104 

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)) 

113 

114 class Meta: 

115 ordering = ["order"] 

116 

117 def __str__(self): 

118 return self.name 

119 

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 

127 

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 

135 

136 

137class StationCheckIn(RandomSlugModel, TimeStampedModel): 

138 """ 

139 SH Station CheckIn Model to handle access 

140 """ 

141 

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/") 

156 

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 ) 

164 

165 objects = StationCheckInManager() 

166 

167 class Meta: 

168 unique_together = ["station", "team"] 

169 

170 def __str__(self): 

171 return f"{self.team.name}-{self.station.name}" 

172 

173 

174class Team(RandomSlugModel): 

175 """ 

176 Team Model 

177 """ 

178 

179 event = models.ForeignKey( 

180 "events.Event", on_delete=models.PROTECT, related_name="teams" 

181 ) 

182 name = models.CharField(max_length=64) 

183 

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 

188 

189 

190class Score(RandomSlugModel): 

191 """ 

192 Team Challenge Score Model 

193 """ 

194 

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 ) 

201 

202 position = models.IntegerField(null=True) 

203 score = models.DecimalField(max_digits=12, decimal_places=2, null=True) 

204 

205 class Meta: 

206 ordering = ["challenge", "score"] 

207 unique_together = ["team", "challenge"] 

208 

209 

210class Participant(RandomSlugModel, TimeStampedModel): 

211 """ 

212 Event's Participant Model 

213 """ 

214 

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 ) 

228 

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) 

237 

238 class RSVP(models.TextChoices): 

239 PENDING = "P" 

240 ACCEPTED = "A" 

241 REJECTED = "R" 

242 

243 rsvp = models.CharField(max_length=1, choices=RSVP.choices, default=RSVP.PENDING) 

244 

245 class Meta: 

246 unique_together = ["event", "user"] 

247 

248 def __str__(self): 

249 return f"{self.user.__str__()} - {self.event.name}"