From dca557fb124f96d08e4ee695de2139ccc7574e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bedna=C5=99=C3=ADk?= <jan.bednarik@gmail.com> Date: Mon, 24 May 2021 21:47:40 +0200 Subject: [PATCH] Timezones and localization --- README.md | 2 +- helios/fields.py | 6 +++++- helios/forms.py | 21 +++++++++++++++------ helios/templates/election_not_started.html | 5 +++-- helios/templates/election_tallied.html | 5 +++-- helios/templates/election_view.html | 15 ++++++++------- helios/templates/email/vote_body.txt | 5 +++-- helios/widgets.py | 9 +++++---- settings.py | 6 ++++-- 9 files changed, 47 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 626383f..4523eef 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Helios is an end-to-end verifiable voting system. ## Info -vÄ›tve v gitu: +VÄ›tve v gitu: - `original` je kopie https://github.com/benadida/helios-server - `master` je z `original` odvozená Pirátská verze nasazená v produkci diff --git a/helios/fields.py b/helios/fields.py index 8d8e885..e8eafff 100644 --- a/helios/fields.py +++ b/helios/fields.py @@ -1,6 +1,8 @@ import datetime +import pytz from django.forms import fields +from django.utils.timezone import make_aware, make_naive from .widgets import SplitSelectDateTimeWidget @@ -25,6 +27,8 @@ class SplitDateTimeField(fields.MultiValueField): if data_list: if not (data_list[0] and data_list[1]): return None - return datetime.datetime.combine(*data_list) + value = datetime.datetime.combine(*data_list) + # do formuláře zadáváme Europe/Prague, ale do DB se ukládá naivnĂ UTC + return make_naive(make_aware(value, pytz.timezone("Europe/Prague")), pytz.UTC) return None diff --git a/helios/forms.py b/helios/forms.py index f7540a5..b1d6687 100644 --- a/helios/forms.py +++ b/helios/forms.py @@ -3,8 +3,11 @@ Forms for Helios """ +import pytz + from django import forms from django.conf import settings +from django.utils.timezone import make_aware, make_naive from .fields import SplitDateTimeField from .models import Election @@ -21,18 +24,24 @@ class ElectionForm(forms.Form): randomize_answer_order = forms.BooleanField(required=False, initial=False, help_text=u'zvolte, pokud chcete, aby se kaĹľdĂ©mu voliÄŤi zobrazovaly odpovÄ›di na otázky v náhodnÄ› zvolenĂ©m poĹ™adĂ', label=u"OdpovÄ›di v náhodnĂ©m poĹ™adĂ") private_p = forms.BooleanField(required=False, initial=False, label=u"SoukromĂ©?", help_text=u'SoukromĂ© hlasovánĂ je viditelnĂ© jen pro registrovanĂ© voliÄŤe.') help_email = forms.CharField(required=False, initial="", label=u"E-mail pro nápovÄ›du", help_text=u'e-mailová adresa, na kterou se budou voliÄŤi obracet s žádostmi o pomoc.') - voting_starts_at = SplitDateTimeField(help_text = u'datum a ÄŤas zahájenĂ hlasovánĂ; v UTC, takĹľe oproti ÄŤasovĂ©mu pásmu ÄŚR je menšà o 1 hodinu v zimnĂm, resp. o 2 hodiny v letnĂm ÄŤase', - widget=SplitSelectDateTimeWidget, required=False, label=u"HlasovánĂ zaÄŤĂná v") - voting_ends_at = SplitDateTimeField(help_text = u'datum a ÄŤas ukonÄŤenĂ hlasovánĂ; v UTC, takĹľe oproti ÄŤasovĂ©mu pásmu ÄŚR je menšà o 1 v zimnĂm, resp. o 2 hodiny v letnĂm ÄŤase', - widget=SplitSelectDateTimeWidget, required=False, label=u"HlasovánĂ konÄŤĂ v") + voting_starts_at = SplitDateTimeField(help_text = u'datum a ÄŤas zahájenĂ hlasovánĂ v lokálnĂm ÄŤase!', widget=SplitSelectDateTimeWidget, required=False, label=u"HlasovánĂ zaÄŤĂná v") + voting_ends_at = SplitDateTimeField(help_text = u'datum a ÄŤas ukonÄŤenĂ hlasovánĂ v lokálnĂm ÄŤase!', widget=SplitSelectDateTimeWidget, required=False, label=u"HlasovánĂ konÄŤĂ v") if settings.ALLOW_ELECTION_INFO_URL: election_info_url = forms.CharField(required=False, initial="", label=u"URL pro staĹľenĂ informacĂ o hlasovánĂ", help_text=u"URL dokumentu ve formátu PDF, obsahujĂcĂho doplĹkovĂ© informace k hlasovánĂ, napĹ™. Ĺľivotopisy a profily kandidátĹŻ") - pass + def __init__(self, data=None, *args, **kwargs): + # v DB se ukládá naivnĂ UTC, ale do formuláře potĹ™ebujeme pĹ™evĂ©st zpÄ›t na Europe/Prague + if data: + tz = pytz.timezone("Europe/Prague") + if "voting_starts_at" in data: + data["voting_starts_at"] = make_naive(make_aware(data["voting_starts_at"], pytz.UTC), tz) + if "voting_ends_at" in data: + data["voting_ends_at"] = make_naive(make_aware(data["voting_ends_at"], pytz.UTC), tz) + super().__init__(data, *args, **kwargs) class ElectionTimeExtensionForm(forms.Form): - voting_extended_until = SplitDateTimeField(help_text = u'datum a ÄŤas prodlouĹľenĂ©ho ukonÄŤenĂ hlasovánĂ; v UTC', + voting_extended_until = SplitDateTimeField(help_text = u'datum a ÄŤas prodlouĹľenĂ©ho ukonÄŤenĂ hlasovánĂ; v lokálnĂm ÄŤase!', widget=SplitSelectDateTimeWidget, required=False, label=u"HlasovánĂ prodlouĹľeno do") class EmailVotersForm(forms.Form): diff --git a/helios/templates/election_not_started.html b/helios/templates/election_not_started.html index 3735a12..18cd5c9 100644 --- a/helios/templates/election_not_started.html +++ b/helios/templates/election_not_started.html @@ -1,4 +1,5 @@ {% extends TEMPLATE_BASE %} +{% load tz %} {% block content %} @@ -9,8 +10,8 @@ </p> <p> - {% if election.voting_start_at %}začátek hlasovánĂ: {{election.voting_start_at}}<br />{% endif %} - {% if election.voting_end_at %}konec hlasovánĂ: {{election.voting_end_at}}<br />{% endif %} + {% if election.voting_start_at %}začátek hlasovánĂ: {{election.voting_start_at|timezone:"Europe/Prague"}}<br />{% endif %} + {% if election.voting_end_at %}konec hlasovánĂ: {{election.voting_end_at|timezone:"Europe/Prague"}}<br />{% endif %} </p> <p> diff --git a/helios/templates/election_tallied.html b/helios/templates/election_tallied.html index dd729d2..b46d215 100644 --- a/helios/templates/election_tallied.html +++ b/helios/templates/election_tallied.html @@ -1,4 +1,5 @@ {% extends TEMPLATE_BASE %} +{% load tz %} {% block content %} @@ -9,8 +10,8 @@ </p> <p> - {% if election.voting_start_at %}začátek hlasovánĂ: {{election.voting_start_at}}<br />{% endif %} - {% if election.voting_end_at %}konec hlasovánĂ: {{election.voting_end_at}}<br />{% endif %} + {% if election.voting_start_at %}začátek hlasovánĂ: {{election.voting_start_at|timezone:"Europe/Prague"}}<br />{% endif %} + {% if election.voting_end_at %}konec hlasovánĂ: {{election.voting_end_at|timezone:"Europe/Prague"}}<br />{% endif %} </p> <p> diff --git a/helios/templates/election_view.html b/helios/templates/election_view.html index 0d04aa6..6617500 100644 --- a/helios/templates/election_view.html +++ b/helios/templates/election_view.html @@ -1,4 +1,5 @@ {% extends TEMPLATE_BASE %} +{% load tz %} {% block title %}{{election.name}}{% endblock %} {% block content %} <div style="float: left; margin-right: 50px;"> @@ -48,8 +49,8 @@ toto {{election.election_type}} <u>nenĂ</u> zobrazeno na titulnĂ stránce. <p> {% if election.help_email and admin_p%}Email pro nápovÄ›du: {{election.help_email}}<br />{% endif %} -{% if election.voting_start_at %}HlasovánĂ zaÄŤĂná: {{election.voting_start_at}}<br />{% endif %} -{% if election.voting_end_at %}HlasovánĂ konÄŤĂ: {{election.voting_end_at}}<br />{% endif %} +{% if election.voting_start_at %}HlasovánĂ zaÄŤĂná: {{election.voting_start_at|timezone:"Europe/Prague"}}<br />{% endif %} +{% if election.voting_end_at %}HlasovánĂ konÄŤĂ: {{election.voting_end_at|timezone:"Europe/Prague"}}<br />{% endif %} </p> {% if election.election_info_url %} @@ -97,7 +98,7 @@ toto {{election.election_type}} <u>nenĂ</u> zobrazeno na titulnĂ stránce. <br /> {% if election.voting_starts_at %} jakmile to uÄŤinĂte, hlasovánĂ bude pĹ™ipraveno k odevzdávánĂ hlasovacĂch lĂstkĹŻ a bude automaticky zahájeno<br /> -v {{election.voting_starts_at}}, podle vašeho nastavenĂ. +v {{election.voting_starts_at|timezone:"Europe/Prague"}}, podle vašeho nastavenĂ. {% else %} jakmile to uÄŤinĂte, hlasovánĂ bude ihned zahájeno. {% endif %} @@ -158,7 +159,7 @@ PotĂ© uvidĂte vĂ˝sledek pouze vy jakoĹľto zakladatel hlasovánĂ. {% if show_result %} {% if election.result_released_at %} <span class="highlight-box round"> - Toto hlasovánĂ bylo ukonÄŤeno. VĂ˝sledek byl zveĹ™ejnÄ›n {{election.result_released_at}}. Celkem bylo odevzdáno {{election.num_cast_votes}} hlasĹŻ. +Toto hlasovánĂ bylo ukonÄŤeno. VĂ˝sledek byl zveĹ™ejnÄ›n {{election.result_released_at|timezone:"Europe/Prague"}}. Celkem bylo odevzdáno {{election.num_cast_votes}} hlasĹŻ. </span><br /><br /><br /> {% endif %} @@ -185,12 +186,12 @@ PotĂ© uvidĂte vĂ˝sledek pouze vy jakoĹľto zakladatel hlasovánĂ. <br /> <br /> {% if election.voting_extended_until %} -Toto {{election.election_type}} mÄ›lo pĹŻvodnÄ› skonÄŤit v {{election.voting_ends_at}} (UTC),<br /> -ale bylo prodlouĹľeno do {{ election.voting_extended_until }} (UTC). +Toto {{election.election_type}} mÄ›lo pĹŻvodnÄ› skonÄŤit v {{election.voting_ends_at|timezone:"Europe/Prague"}},<br /> +ale bylo prodlouĹľeno do {{ election.voting_extended_until|timezone:"Europe/Prague" }}. {% else %} {% if election.voting_ends_at %} <br /> -Toto {{election.election_type}} bude uzavĹ™eno v {{election.voting_ends_at}} (UTC). +Toto {{election.election_type}} bude uzavĹ™eno v {{election.voting_ends_at|timezone:"Europe/Prague"}}. {% else %} Toto {{election.election_type}} bude uzavĹ™eno podle rozhodnutĂ jeho zakladatele. {% endif %} diff --git a/helios/templates/email/vote_body.txt b/helios/templates/email/vote_body.txt index 8503778..6e42d08 100644 --- a/helios/templates/email/vote_body.txt +++ b/helios/templates/email/vote_body.txt @@ -1,11 +1,12 @@ +{% load tz %} VáženĂ˝ {{voter.name}}, {{custom_message|safe}} URL HlasovánĂ: {{election_vote_url}} Otisk HlasovánĂ©: {{voter.election.hash}} -{% if election.voting_start_at %}HlasovánĂ zaÄŤĂná {{election.voting_start_at}} -{% endif %}{% if election.voting_end_at %}HlasovánĂ konÄŤĂ {{election.voting_end_at}} +{% if election.voting_start_at %}HlasovánĂ zaÄŤĂná {{election.voting_start_at|timezone:"Europe/Prague"}} +{% endif %}{% if election.voting_end_at %}HlasovánĂ konÄŤĂ {{election.voting_end_at|timezone:"Europe/Prague"}} {% endif %} {% ifequal voter.voter_type "password" %} diff --git a/helios/widgets.py b/helios/widgets.py index ba271ce..3e8c36d 100644 --- a/helios/widgets.py +++ b/helios/widgets.py @@ -95,16 +95,17 @@ class SelectTimeWidget(Widget): self.meridiem_val = 'a.m.' else: self.meridiem_val = None - + # If we're doing a 12-hr clock, there will be a meridiem value, so make sure the # hours get printed correctly if self.twelve_hr and self.meridiem_val: if self.meridiem_val.lower().startswith('p') and hour_val > 12 and hour_val < 24: hour_val = hour_val % 12 - elif hour_val == 0: - hour_val = 12 - + # BUG this breaks 24h clock on 0 hours + # elif hour_val == 0: + # hour_val = 12 + output = [] if 'id' in self.attrs: id_ = self.attrs['id'] diff --git a/settings.py b/settings.py index 05fed0b..a158572 100644 --- a/settings.py +++ b/settings.py @@ -62,11 +62,11 @@ if get_from_env('DATABASE_URL', None): # although not all choices may be available on all operating systems. # If running in a Windows environment this must be set to the same as your # system time zone. -TIME_ZONE = 'America/Los_Angeles' +TIME_ZONE = 'UTC' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = 'cs-cz' SITE_ID = 1 @@ -74,6 +74,8 @@ SITE_ID = 1 # to load the internationalization machinery. USE_I18N = True +USE_L10N = True + # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '' -- GitLab