Sith/election/models.py

250 lines
7.1 KiB
Python
Raw Normal View History

2023-04-06 11:08:42 +00:00
# -*- coding:utf-8 -*-
#
# Copyright 2023 © AE UTBM
# ae@utbm.fr / ae.info@utbm.fr
# All contributors are listed in the CONTRIBUTORS file.
#
# This file is part of the website of the UTBM Student Association (AE UTBM),
# https://ae.utbm.fr.
#
# You can find the whole source code at https://github.com/ae-utbm/sith3
#
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
# OR WITHIN THE LOCAL FILE "LICENSE"
#
# PREVIOUSLY LICENSED UNDER THE MIT LICENSE,
# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE.old
# OR WITHIN THE LOCAL FILE "LICENSE.old"
#
2016-12-05 19:18:03 +00:00
from django.db import models
from django.utils import timezone
2024-06-24 11:07:36 +00:00
from django.utils.translation import gettext_lazy as _
from ordered_model.models import OrderedModel
2016-12-05 19:18:03 +00:00
2024-06-24 11:07:36 +00:00
from core.models import Group, User
2016-12-05 19:18:03 +00:00
class Election(models.Model):
"""
2016-12-07 22:55:05 +00:00
This class allows to create a new election
2016-12-05 19:18:03 +00:00
"""
2018-10-04 19:29:19 +00:00
title = models.CharField(_("title"), max_length=255)
description = models.TextField(_("description"), null=True, blank=True)
start_candidature = models.DateTimeField(_("start candidature"), blank=False)
end_candidature = models.DateTimeField(_("end candidature"), blank=False)
start_date = models.DateTimeField(_("start date"), blank=False)
end_date = models.DateTimeField(_("end date"), blank=False)
2016-12-05 19:18:03 +00:00
2017-06-07 14:22:04 +00:00
edit_groups = models.ManyToManyField(
2018-10-04 19:29:19 +00:00
Group,
related_name="editable_elections",
verbose_name=_("edit groups"),
blank=True,
)
2017-06-07 17:16:55 +00:00
2017-06-07 14:22:04 +00:00
view_groups = models.ManyToManyField(
2018-10-04 19:29:19 +00:00
Group,
related_name="viewable_elections",
verbose_name=_("view groups"),
blank=True,
)
2017-06-07 17:16:55 +00:00
2017-06-07 14:22:04 +00:00
vote_groups = models.ManyToManyField(
2018-10-04 19:29:19 +00:00
Group,
related_name="votable_elections",
verbose_name=_("vote groups"),
blank=True,
)
2017-06-07 17:16:55 +00:00
2017-06-07 14:22:04 +00:00
candidature_groups = models.ManyToManyField(
2018-10-04 19:29:19 +00:00
Group,
related_name="candidate_elections",
verbose_name=_("candidature groups"),
blank=True,
)
voters = models.ManyToManyField(
User, verbose_name=("voters"), related_name="voted_elections"
)
archived = models.BooleanField(_("archived"), default=False)
2016-12-19 19:30:19 +00:00
2016-12-05 19:18:03 +00:00
def __str__(self):
return self.title
@property
def is_vote_active(self):
2016-12-05 19:18:03 +00:00
now = timezone.now()
return bool(now <= self.end_date and now >= self.start_date)
@property
def is_vote_finished(self):
return bool(timezone.now() > self.end_date)
2016-12-07 22:55:05 +00:00
@property
2016-12-13 21:11:06 +00:00
def is_candidature_active(self):
2016-12-07 22:55:05 +00:00
now = timezone.now()
2017-06-07 17:16:55 +00:00
return bool(now <= self.end_candidature and now >= self.start_candidature)
2016-12-07 22:55:05 +00:00
2016-12-24 17:29:26 +00:00
@property
def is_vote_editable(self):
return bool(timezone.now() <= self.end_candidature)
def can_candidate(self, user):
for group_id in self.candidature_groups.values_list("pk", flat=True):
if user.is_in_group(pk=group_id):
return True
return False
2016-12-22 23:34:30 +00:00
def can_vote(self, user):
if not self.is_vote_active or self.has_voted(user):
return False
for group_id in self.vote_groups.values_list("pk", flat=True):
if user.is_in_group(pk=group_id):
2016-12-22 23:34:30 +00:00
return True
return False
2016-12-23 22:49:00 +00:00
def has_voted(self, user):
return self.voters.filter(id=user.id).exists()
2016-12-23 16:13:46 +00:00
@property
def results(self):
2016-12-23 16:13:46 +00:00
results = {}
2016-12-23 22:49:00 +00:00
total_vote = self.voters.count()
2016-12-23 21:58:54 +00:00
for role in self.roles.all():
2016-12-23 22:49:00 +00:00
results[role.title] = role.results(total_vote)
2016-12-23 16:13:46 +00:00
return results
def delete(self, *args, **kwargs):
self.election_lists.all().delete()
super(Election, self).delete(*args, **kwargs)
2016-12-19 19:30:19 +00:00
# Permissions
2016-12-19 15:45:38 +00:00
2016-12-05 19:18:03 +00:00
class Role(OrderedModel):
2016-12-05 19:18:03 +00:00
"""
2016-12-13 21:11:06 +00:00
This class allows to create a new role avaliable for a candidature
2016-12-05 19:18:03 +00:00
"""
2018-10-04 19:29:19 +00:00
election = models.ForeignKey(
Election,
related_name="roles",
verbose_name=_("election"),
on_delete=models.CASCADE,
2018-10-04 19:29:19 +00:00
)
title = models.CharField(_("title"), max_length=255)
description = models.TextField(_("description"), null=True, blank=True)
max_choice = models.IntegerField(_("max choice"), default=1)
2016-12-05 19:18:03 +00:00
2016-12-23 22:49:00 +00:00
def results(self, total_vote):
2016-12-23 16:13:46 +00:00
results = {}
2016-12-23 22:49:00 +00:00
total_vote *= self.max_choice
2016-12-23 16:13:46 +00:00
non_blank = 0
2016-12-23 21:58:54 +00:00
for candidature in self.candidatures.all():
2016-12-23 16:13:46 +00:00
cand_results = {}
2018-10-04 19:29:19 +00:00
cand_results["vote"] = self.votes.filter(candidature=candidature).count()
2017-01-10 18:06:34 +00:00
if total_vote == 0:
2018-10-04 19:29:19 +00:00
cand_results["percent"] = 0
2017-01-10 18:06:34 +00:00
else:
2018-10-04 19:29:19 +00:00
cand_results["percent"] = cand_results["vote"] * 100 / total_vote
non_blank += cand_results["vote"]
2016-12-23 16:13:46 +00:00
results[candidature.user.username] = cand_results
2018-10-04 19:29:19 +00:00
results["total vote"] = total_vote
2017-01-10 18:06:34 +00:00
if total_vote == 0:
2018-10-04 19:29:19 +00:00
results["blank vote"] = {"vote": 0, "percent": 0}
2017-01-10 18:06:34 +00:00
else:
2018-10-04 19:29:19 +00:00
results["blank vote"] = {
"vote": total_vote - non_blank,
"percent": (total_vote - non_blank) * 100 / total_vote,
}
2016-12-23 16:13:46 +00:00
return results
2016-12-24 17:29:26 +00:00
@property
def edit_groups(self):
return self.election.edit_groups
2016-12-05 19:18:03 +00:00
def __str__(self):
return ("%s : %s") % (self.election.title, self.title)
2016-12-19 15:45:38 +00:00
class ElectionList(models.Model):
2016-12-14 17:10:15 +00:00
"""
To allow per list vote
"""
2018-10-04 19:29:19 +00:00
title = models.CharField(_("title"), max_length=255)
election = models.ForeignKey(
Election,
related_name="election_lists",
verbose_name=_("election"),
on_delete=models.CASCADE,
2018-10-04 19:29:19 +00:00
)
2016-12-14 17:10:15 +00:00
def can_be_edited_by(self, user):
return user.can_edit(self.election)
def delete(self):
for candidature in self.candidatures.all():
candidature.delete()
super(ElectionList, self).delete()
def __str__(self):
return self.title
2016-12-14 17:10:15 +00:00
2016-12-13 21:11:06 +00:00
class Candidature(models.Model):
2016-12-05 19:18:03 +00:00
"""
2016-12-07 22:55:05 +00:00
This class is a component of responsability
2016-12-05 19:18:03 +00:00
"""
2018-10-04 19:29:19 +00:00
role = models.ForeignKey(
Role,
related_name="candidatures",
verbose_name=_("role"),
on_delete=models.CASCADE,
)
2018-10-04 19:29:19 +00:00
user = models.ForeignKey(
User,
verbose_name=_("user"),
related_name="candidates",
blank=True,
on_delete=models.CASCADE,
2018-10-04 19:29:19 +00:00
)
program = models.TextField(_("description"), null=True, blank=True)
election_list = models.ForeignKey(
ElectionList,
related_name="candidatures",
verbose_name=_("election list"),
on_delete=models.CASCADE,
2018-10-04 19:29:19 +00:00
)
2016-12-07 22:55:05 +00:00
def delete(self):
for vote in self.votes.all():
vote.delete()
super(Candidature, self).delete()
2016-12-24 17:29:26 +00:00
def can_be_edited_by(self, user):
return (user == self.user) or user.can_edit(self.role.election)
2016-12-24 17:29:26 +00:00
def __str__(self):
return "%s : %s" % (self.role.title, self.user.username)
2016-12-07 22:55:05 +00:00
class Vote(models.Model):
"""
This class allows to vote for candidates
"""
2018-10-04 19:29:19 +00:00
role = models.ForeignKey(
Role, related_name="votes", verbose_name=_("role"), on_delete=models.CASCADE
)
2018-10-04 19:29:19 +00:00
candidature = models.ManyToManyField(
Candidature, related_name="votes", verbose_name=_("candidature")
)
2016-12-07 22:55:05 +00:00
def __str__(self):
2017-06-07 14:22:04 +00:00
return "Vote"