diff --git a/management/forms.py b/management/forms.py index 5f97ceb4c190d12525b85f1754ea2ec7bfb9967f..d0a716fdaea4c864e63f92cda80b7c2458be8bbb 100644 --- a/management/forms.py +++ b/management/forms.py @@ -152,7 +152,6 @@ class AddElectionForm(TemplateStringForm): def clean_remind_text(self): test_data = ["name", "title", "url", "end_time", "end_date", "end_date_en", "end_time_en"] - print(self.cleaned_data['remind_text']) return self.clean_email_text(test_data, 'remind_text') def clean(self): @@ -164,8 +163,12 @@ class AddElectionForm(TemplateStringForm): if self.request.POST.get("submit_type") == "test" and not self.cleaned_data['email']: self.add_error('email', "Email must be set for sending the test mail.") if self.request.POST.get("submit_type") == "test" and not self.cleaned_data['remind_text']: - print(self.cleaned_data['remind_text']) self.add_error('remind_text', "The test email can only be send when the remind text is filled.") + if self.cleaned_data['end_date'] and self.cleaned_data['end_date'] < timezone.now(): + self.add_error('end_date', "End date cannot be in the past.") + if self.cleaned_data.get('end_date') and self.cleaned_data.get('start_date') and \ + self.cleaned_data['start_date'] > self.cleaned_data['end_date']: + raise forms.ValidationError("Start date needs to be before end date") return self.cleaned_data def save(self, commit=True): diff --git a/management/management/commands/process_reminders.py b/management/management/commands/process_reminders.py new file mode 100644 index 0000000000000000000000000000000000000000..66289615bfbe524ff373cee11844b6bbdcd8e77b --- /dev/null +++ b/management/management/commands/process_reminders.py @@ -0,0 +1,21 @@ +from django.core.management.base import BaseCommand +from django.utils import timezone + +from vote.models import Election + + +class Command(BaseCommand): + help = 'Process all elections and send remind emails to the voters when the elction has started' + + def add_arguments(self, parser): + pass + + def handle(self, *args, **options): + for election in Election.objects.all(): + if election.start_date is None or election.remind_text_sent or not election.send_emails_on_start or \ + timezone.now() < election.start_date: + continue + election.remind_text_sent = True + election.save() + for voter in election.session.participants.all(): + voter.send_reminder(election.session.managers.all().first().stusta_email, election) diff --git a/management/templates/management/add_election.html b/management/templates/management/add_election.html index 55e06926c5c055ea4d29d0f75d567d8cd222d78f..6a426e6e44bf03ce9ae6190d903e8bf633a81165 100644 --- a/management/templates/management/add_election.html +++ b/management/templates/management/add_election.html @@ -51,9 +51,10 @@ Here is an example:<br><br> <p class="monospace">Dear,<br><br>This is a reminder that the {title} election has just began. - So you can now start voting for your favourite candidates on {url}. The access code can be found - in the invitation email. Please be reminded that you can only vote until {end_time_en} on the - {end_date_en}. + So you can now start voting for your favourite candidates on + <a href="{url}">{url}</a>. + The access code can be found in the invitation email. Please be reminded that you can only vote until + {end_time_en} on the {end_date_en}. <br><br> Best regards,<br> Your awesome Organizers diff --git a/management/templates/management/add_session.html b/management/templates/management/add_session.html index ae1039bb664c1157a26990093cdef03b55840af5..1127ff7e93ea75893bb2178f238705a9f8df3925 100644 --- a/management/templates/management/add_session.html +++ b/management/templates/management/add_session.html @@ -50,7 +50,7 @@ <p class="monospace">Dear,<br><br>You have been invited to our awesome meeting {title}. We are meeting on {meeting_link}. It takes place on the {start_date_en} at {start_time_en}. You can login with the following link: - {login_url}. + <a href="{login_url}">{login_url}</a>. You can also use the following access code on {base_url}: {access_code}<br><br> Best regards,<br> Your awesome Organizers diff --git a/management/templates/management/election.html b/management/templates/management/election.html index fed03b0f37d8089a33fadc67b1c86f7b1e827526..9feb492e54c37f18a2a38f3c94fd62dc851f036f 100644 --- a/management/templates/management/election.html +++ b/management/templates/management/election.html @@ -13,6 +13,16 @@ <div class="card-body"> <div>Election not started yet.</div> + <div> + {% if election.end_date and election.start_date %} + Election period: {{ election.start_date|date:"l Y-m-d H:i:s" }} - + {{ election.end_date|date:"l Y-m-d H:i:s" }} + {% elif election.start_date %} + Election starts {{ election.start_date|date:"l Y-m-d H:i:s" }} + {% else %} + This election needs to be started manually. + {% endif %} + </div> <hr> <form class="user" action="{% url 'management:election' election.pk %}" method="post"> {% csrf_token %} @@ -30,7 +40,7 @@ <div class="col-6 my-auto">minutes</div> </div> </div> - <button type="submit" id="id_btn_start" class="btn btn-success">Start election + <button type="submit" id="id_btn_start" class="btn btn-success">Start election now </button> </form> </div> @@ -38,8 +48,12 @@ <div class="card-body"> <div>Election still open.</div> <div> - Election period: {{ election.start_date|date:"l Y-m-d H:i:s" }} - - {{ election.end_date|date:"l Y-m-d H:i:s" }} + {% if election.end_date %} + Election period: {{ election.start_date|date:"l Y-m-d H:i:s" }} - + {{ election.end_date|date:"l Y-m-d H:i:s" }} + {% else %} + This election needs to be closed manually. + {% endif %} </div> <hr> <form class="user" action="{% url 'management:election' election.pk %}" method="post"> diff --git a/management/templates/management/session.html b/management/templates/management/session.html index c05f34fd7c1012cc033108d96972a80954fc332d..7ac6af726727a70488e5e64ab1c9ffb08b90dcf1 100644 --- a/management/templates/management/session.html +++ b/management/templates/management/session.html @@ -30,14 +30,18 @@ </button> <small class="float-right"> - {% if not election.start_date %} - <span class="right-margin">Not started yet</span> - {% elif election.is_open %} + {% if not election.started and election.start_date %} + <span class="right-margin">Starts at {{ election.start_date|date:"Y-m-d H:i:s" }}</span> + {% elif not election.started %} + <span class="right-margin">Needs to be started manually</span> + {% elif election.is_open and election.end_date %} <span class="right-margin"> - Open {{ election.start_date|date:"H:i" }} - {{ election.end_date|date:"H:i" }} + Open until {{ election.end_date|date:"Y-m-d H:i:s" }} </span> {% elif election.closed %} <span class="right-margin">Closed</span> + {% else %} + <span class="right-margin">Open, needs to be closed manually</span> {% endif %} </small> </div> diff --git a/management/views.py b/management/views.py index f46783d7bcca3ba03f87cd3428c15b773ba19b5a..a98ae0098bf7079c96dbb35278ea1bdf2f73752d 100644 --- a/management/views.py +++ b/management/views.py @@ -71,7 +71,7 @@ def index(request): }) test_voter.email_user = partial(Voter.email_user, test_voter) - Voter.send_invitation(test_voter, "mock-up-access-token", manager.email) + Voter.send_invitation(test_voter, "mock-up-access-token", manager.stusta_email) variables = { "{name}": "Voter's name if set", @@ -143,7 +143,7 @@ def add_election(request, pk=None): 'start_date'] else datetime.now(), }) - Voter.send_reminder(test_voter, manager.email, test_election) + Voter.send_reminder(test_voter, manager.stusta_email, test_election) else: form.save() return redirect('management:session', pk=session.pk) diff --git a/vote/management/commands/reset_voter.py b/vote/management/commands/reset_voter.py index a849e2b79dba1aef90892db4db3cea9b1ea2f2de..6a74a46ebaeb6d8ae5779affbcab456dc9893547 100644 --- a/vote/management/commands/reset_voter.py +++ b/vote/management/commands/reset_voter.py @@ -26,5 +26,5 @@ class Command(BaseCommand): access_code = Voter.get_access_code(voter_id, password) if options['send_invitation']: - voter.send_invitation(access_code) + voter.send_invitation(access_code, voter.session.managers.all().first().stusta_email) self.stdout.write(self.style.SUCCESS('Successfully reset voter "%s"\nAccess Code: %s' % (voter, access_code))) diff --git a/vote/models.py b/vote/models.py index cc07d08b739a30496d89dce6172d6a2e7bf175a6..4668abab2482f79ca8394d11470a864c00c7e718 100644 --- a/vote/models.py +++ b/vote/models.py @@ -67,7 +67,7 @@ class Enc32: class Session(models.Model): title = models.CharField(max_length=256) meeting_link = models.CharField(max_length=512, blank=True, null=True) - start_date = models.DateTimeField(blank=True, null=True, default=timezone.now) + start_date = models.DateTimeField(blank=True, null=True) invite_text = models.TextField(max_length=1000, blank=True, null=True) @@ -82,10 +82,11 @@ class Election(models.Model): voters_self_apply = models.BooleanField(default=False) send_emails_on_start = models.BooleanField(default=False) remind_text = models.TextField(max_length=1000, blank=True, null=True) + remind_text_sent = models.BooleanField(default=False) @property def started(self): - if self.start_date is not None and self.end_date is not None: + if self.start_date is not None: return timezone.now() > self.start_date else: return False @@ -99,8 +100,10 @@ class Election(models.Model): @property def is_open(self): - if self.end_date: + if self.start_date and self.end_date: return self.start_date < timezone.now() < self.end_date + elif self.start_date: + return self.start_date < timezone.now() else: return False @@ -262,6 +265,8 @@ class Voter(models.Model): return str(self) def send_invitation(self, access_code: str, from_email: str): + if not self.email: + return subject = f'Invitation for {self.session.title}' if self.session.invite_text: context = { @@ -270,10 +275,10 @@ class Voter(models.Model): 'access_code': access_code, 'login_url': 'https://vote.stustanet.de' + reverse('vote:link_login', kwargs={'access_code': access_code}), - 'start_date': self.session.start_date.strftime("%d.%m.%Y"), - 'start_time': self.session.start_date.strftime("%H:%M"), - 'start_date_en': self.session.start_date.strftime("%Y/%m/%d"), - 'start_time_en': self.session.start_date.strftime("%I:%M %p"), + 'start_date': self.session.start_date.strftime("%d.%m.%Y") if self.session.start_date else "", + 'start_time': self.session.start_date.strftime("%H:%M") if self.session.start_date else "", + 'start_date_en': self.session.start_date.strftime("%Y/%m/%d") if self.session.start_date else "", + 'start_time_en': self.session.start_date.strftime("%I:%M %p") if self.session.start_date else "", 'base_url': 'https://vote.stustanet.de', 'meeting_link': self.session.meeting_link } @@ -298,16 +303,18 @@ class Voter(models.Model): ) def send_reminder(self, from_email: str, election): + if not self.email: + return subject = f'{election.title} is now open' if election.remind_text: context = { 'name': self.name, 'title': election.title, 'url': 'https://vote.stustanet.de' + reverse('vote:vote', kwargs={'election_id': election.pk}), - 'end_date': election.start_date.strftime("%d.%m.%y"), - 'end_time': election.start_date.strftime("%H:%M"), - 'end_date_en': election.start_date.strftime("%Y/%m/%d"), - 'end_time_en': election.start_date.strftime("%I:%M %p"), + 'end_date': election.start_date.strftime("%d.%m.%y") if election.start_date else "", + 'end_time': election.start_date.strftime("%H:%M") if election.start_date else "", + 'end_date_en': election.start_date.strftime("%Y/%m/%d") if election.start_date else "", + 'end_time_en': election.start_date.strftime("%I:%M %p") if election.start_date else "", } body_html = election.remind_text.format(**context) else: diff --git a/vote/templates/vote/mails/start.j2 b/vote/templates/vote/mails/start.j2 index d3bd53df8c4250341296b07e6854d4427aaa964d..cab3134e11a5747462f2977a20e241eca4b13992 100644 --- a/vote/templates/vote/mails/start.j2 +++ b/vote/templates/vote/mails/start.j2 @@ -10,7 +10,6 @@ die Abstimmungsphase von "{{ election.title }}" hat nun begonnen und ihr könnt Die Abstimmung ist möglich bis zum {{ election.end_date|date:"d.m.Y" }} um {{ election.end_date|date:"H:i" }} unter: <a href="{{ url }}">{{ url }}</a> (Persönlicher Zugangscode befindet sich in der ersten E-Mail). - --- English version --- @@ -23,11 +22,3 @@ The election phase of "{{ election.title }}" has begun and you can now vote. The election is possible until {{ election.end_date|date:"Y/m/d" }} at {{ election.end_date|date:"h:i A" }} via the following link: <a href="{{ url }}">{{ url }}</a> (personal access code can be found in the first email). - -The application phase is now closed. - -And remember: On Friday night, the formal part of the election will be held via a video conference. Here is the link for your house: -https://meet.stusta.de/vrhQPWeUzx6EGA - -Regards -Your StuStaNet Executive Board \ No newline at end of file