@@ -158,6 +158,13 @@ class Status(models.TextChoices):
158158
159159 objects = BillManager ()
160160
161+ # Annotations from default manager query
162+ total_votes = 0
163+ yes_count = 0
164+ yes_percent = 0
165+ no_count = 0
166+ no_percent = 0
167+
161168 class Meta :
162169 constraints = [
163170 models .UniqueConstraint (
@@ -184,6 +191,29 @@ def log(self, msg, *args, level=logging.INFO):
184191 )
185192 # f-string necessary to let string interpolation work in msg
186193
194+ def save (self , * args , ** kwargs ):
195+ created = self ._state .adding
196+ super ().save (* args , ** kwargs )
197+ if created and self .status != self .Status .DRAFT :
198+ self ._schedule_submit_task ()
199+
200+ def _schedule_submit_task (self ) -> None :
201+ """Create a periodic task to submit this bill after the voting period."""
202+
203+ voting_ends , __ = IntervalSchedule .objects .get_or_create (
204+ every = settings .WEBISCITE_VOTING_PERIOD , period = IntervalSchedule .DAYS
205+ )
206+ self ._submit_task = PeriodicTask .objects .create (
207+ interval = voting_ends ,
208+ name = f"bill_submit:{ self .id } " ,
209+ task = "democrasite.webiscite.tasks.submit_bill" ,
210+ args = json .dumps ([self .id ]),
211+ one_off = True ,
212+ last_run_at = timezone .now (),
213+ )
214+ super ().save ()
215+ self .log ("Scheduled %s" , self ._submit_task .name )
216+
187217 def get_absolute_url (self ) -> str :
188218 """Returns URL to view this Bill instance"""
189219 return reverse ("webiscite:bill-detail" , kwargs = {"pk" : self .id })
@@ -196,20 +226,12 @@ def get_vote_url(self) -> str:
196226 """Returns URL for the current user to vote on this Bill instance"""
197227 return reverse ("webiscite:bill-vote" , kwargs = {"pk" : self .id })
198228
199- @property
200- def yes_votes (self ) -> models .QuerySet [User ]:
201- return self .votes .filter (vote__support = True )
202-
203- @property
204- def no_votes (self ) -> models .QuerySet [User ]:
205- return self .votes .filter (vote__support = False )
206-
207229 def vote (self , user : User , * , support : bool ) -> None :
208230 """Sets the given user's vote based on the support parameter
209231
210232 If the user already voted the way the method would set, their vote is
211- removed from the bill (i.e. if `` user`` is in ``bill.yes_votes`` and support is
212- ``True``, ``user`` is removed from ``bill.yes_votes`` )
233+ removed from the bill (i.e. if the user previously voted yes and support is
234+ ``True``, their vote is removed)
213235
214236 Args:
215237 user (User): The user voting on the bill
@@ -255,34 +277,11 @@ def user_supports(self, user: User) -> bool | None:
255277 else :
256278 return vote .support
257279
258- def save (self , * args , ** kwargs ):
259- created = self ._state .adding
260- super ().save (* args , ** kwargs )
261- if created and self .status != self .Status .DRAFT :
262- self ._schedule_submit_task ()
263-
264- def _schedule_submit_task (self ) -> None :
265- """Create a periodic task to submit this bill after the voting period."""
266-
267- voting_ends , __ = IntervalSchedule .objects .get_or_create (
268- every = settings .WEBISCITE_VOTING_PERIOD , period = IntervalSchedule .DAYS
269- )
270- self ._submit_task = PeriodicTask .objects .create (
271- interval = voting_ends ,
272- name = f"bill_submit:{ self .id } " ,
273- task = "democrasite.webiscite.tasks.submit_bill" ,
274- args = json .dumps ([self .id ]),
275- one_off = True ,
276- last_run_at = timezone .now (),
277- )
278- super ().save ()
279- self .log ("Scheduled %s" , self ._submit_task .name )
280-
281280 def close (self , status : "Bill.Status" = Status .CLOSED ) -> None :
282281 """Close the bill and disable its submit task"""
283282 self .status = status
284283 self .save ()
285- self .log ("Closed" )
284+ self .log (status )
286285
287286 if self ._submit_task is not None :
288287 self ._submit_task .enabled = False
@@ -310,20 +309,18 @@ def submit(self) -> None:
310309 self .save ()
311310
312311 def _check_approval (self ) -> "Bill.Status" :
313- total_votes = self .votes .count ()
314- if total_votes < settings .WEBISCITE_MINIMUM_QUORUM :
312+ if self .total_votes < settings .WEBISCITE_MINIMUM_QUORUM :
315313 self .log ("Rejected due to insufficient votes" )
316314 return self .Status .FAILED
317315
318- approval = self .yes_votes .count () / total_votes
319316 if self .constitutional :
320- approved = approval > settings .WEBISCITE_SUPERMAJORITY
317+ approved = self . yes_percent / 100 > settings .WEBISCITE_SUPERMAJORITY
321318 else :
322- approved = approval > settings .WEBISCITE_NORMAL_MAJORITY
319+ approved = self . yes_percent / 100 > settings .WEBISCITE_NORMAL_MAJORITY
323320
324321 if not approved :
325- self .log ("Rejected with %s%% approval" , approval * 100 )
322+ self .log ("Rejected with %s%% approval" , self . yes_percent )
326323 return self .Status .REJECTED
327324
328- self .log ("Approved with %s%% approval" , approval * 100 )
325+ self .log ("Approved with %s%% approval" , self . yes_percent )
329326 return self .Status .APPROVED
0 commit comments