from flask_babelex import lazy_gettext as _ from flask_wtf import FlaskForm import pytz from wtforms import BooleanField, PasswordField, SelectField, StringField from wtforms.ext.dateutil.fields import DateTimeField from wtforms.validators import DataRequired, Email, ValidationError from wtforms.widgets import TextArea class RegistrationForm(FlaskForm): username = StringField(_('Username'), validators=[DataRequired()]) email = StringField(_('Email address'), validators=[Email()]) password = PasswordField(_('Password'), validators=[DataRequired()]) password_retype = PasswordField(_('Password, once more'), validators=[DataRequired()]) def validate_password_retype(self, field): if field.data != self.password.data: raise ValidationError(_('The two passwords must match!')) class TimezoneField(SelectField): def __init__(self, *args, **kwargs): kwargs.update({ 'choices': [ (pytz.timezone(tz), tz.replace('_', ' ')) for tz in pytz.common_timezones ], }) SelectField.__init__(self, *args, **kwargs) def process_formdata(self, valuelist): if not valuelist: self.data = None return try: self.data = pytz.timezone(valuelist[0]) except pytz.exceptions.UnknownTimeZoneError: self.data = None raise ValueError('Unknown time zone') @staticmethod def is_pytz_instance(value): return value is pytz.UTC or isinstance(value, pytz.tzinfo.BaseTzInfo) def process_data(self, value): if value is None: self.data = None return if self.is_pytz_instance(value): self.data = value return try: self.data = pytz.timezone(value) except Exception as exc: raise ValueError(f'Unknown time zone {value}') def iter_choices(self): for value, label in self.choices: yield (value, label, value == self.data) class EventForm(FlaskForm): title = StringField(_('Title'), validators=[DataRequired()]) time_zone = TimezoneField(_('Time zone'), validators=[DataRequired()]) start_time = DateTimeField(_('Start time'), validators=[DataRequired()]) end_time = DateTimeField(_('End time'), validators=[DataRequired()]) all_day = BooleanField(_('All day')) description = StringField(_('Description'), widget=TextArea()) def populate_obj(self, obj): FlaskForm.populate_obj(self, obj) timezone = self.time_zone.data obj.time_zone = str(timezone) obj.start_time = timezone.localize(self.start_time.data).astimezone(pytz.utc) obj.end_time = timezone.localize(self.end_time.data).astimezone(pytz.utc) def validate_end_time(self, field): if field.data < self.start_time.data: raise ValidationError(_('End time must be later than start time!'))