def __init__(self, *args, **kwargs): FormField.__init__(self, *args, **kwargs) DEFAULT_RED = kwargs.get('red', 0) DEFAULT_GREEN = kwargs.get('green', 0) DEFAULT_BLUE = kwargs.get('blue', 0) # DEFAULT_COLOR = Color(rgb=(DEFAULT_RED, DEFAULT_GREEN, DEFAULT_BLUE)) print("ColorPicker: color:%s,%s,%s" % (DEFAULT_RED, DEFAULT_GREEN, DEFAULT_BLUE))
class TechnologiesForm(FlaskForm): rows = FieldList(FormField(OneTech), min_entries=0)
class WorkoutsetForm(FlaskForm): reps = IntegerField('Reps', validators=[DataRequired()]) exercises = FieldList(FormField(ExcerciseForm), min_entries=2)
class OrderForm(FlaskForm): order = FormField(Order) submit = SubmitField(render_kw={"class": "btn btn-primary"})
class ProjectPermissionsForm(FlaskForm): user_permissions = FieldList(FormField(ProjectUserPermissionsForm), min_entries=0) group_permissions = FieldList(FormField(ProjectGroupPermissionsForm), min_entries=0)
class subreddit_subs(Form): subs = FieldList(FormField(sub_form), [Required()])
class MyForm(Form): a = FormField(NestedForm)
class EventForm(ModelForm, FlaskForm): class Meta: model = Event exclude = ["photo"] photo_file = FileField(validators=[FileAllowed(photos, "Image only!")]) duplicate_photo = HiddenField() event_type_id = SelectField("Type d'événement", choices=[], coerce=int) single_activity_type = SelectField("Activité", choices=[], coerce=int) multi_activity_types = SelectMultipleField("Activités", choices=[], coerce=int) add_leader = HiddenField("Encadrant supplémentaire") leader_actions = FieldList( FormField(LeaderActionForm, default=LeaderAction())) main_leader_id = RadioField("Responsable", coerce=int) update_activity = HiddenField() update_leaders = HiddenField() save_all = SubmitField("Enregistrer") multi_activities_mode = BooleanField("Événement multi-activités") tag_list = SelectMultipleField("Labels", coerce=int) parent_event_id = HiddenField(filters=[lambda id: id or None]) source_event = None current_leaders = [] main_leader_fields = [] parent_event = None def __init__(self, *args, **kwargs): """ event is only used to populate activity/leader field choices. It is different from passing obj=event, which would populate all form fields from event data. """ super().__init__(*args, **kwargs) if "obj" in kwargs: # Reading from an existing event self.source_event = kwargs["obj"] activities = self.source_event.activity_types self.multi_activities_mode.data = len(activities) != 1 or any( leaders_without_activities(activities, self.source_event.leaders)) if activities: self.single_activity_type.data = int(activities[0].id) self.multi_activity_types.data = [a.id for a in activities] self.set_current_leaders(self.source_event.leaders) self.tag_list.data = [ tag.type for tag in self.source_event.tag_refs ] else: self.set_current_leaders([]) self.update_choices() if not self.can_switch_multi_activity_mode(): self.multi_activities_mode.data = True # Populate single-activty from multi-activty, and vice versa if not self.single_activity_type.data and self.multi_activity_types.data: self.single_activity_type.data = self.multi_activity_types.data[0] if self.single_activity_type.data and not self.multi_activity_types.data: self.multi_activity_types.data = [self.single_activity_type.data] # Remove the useless aingle/multi activity field if self.multi_activities_mode.data: del self.single_activity_type else: del self.multi_activity_types if self.parent_event_id.data: self.parent_event = Event.query.get(self.parent_event_id.data) def set_current_leaders(self, leaders): """ Stores the list of current leaders, used to populate form fields :param leaders: list of current leaders :type leaders: list[:py:class:`collectives.models.user.User`] """ self.current_leaders = list(leaders) if not any(leaders): self.current_leaders.append(current_user) def update_choices(self): """Updates possible choices for activity and new leader select fields""" # Find possible even types and activities given current leaders # If there is a source event, make sure its existing settings can be reproduced source_event_type = self.source_event.event_type if self.source_event else None source_activities = (self.source_event.activity_types if self.source_event else []) event_type_choices = available_event_types(source_event_type, self.current_leaders) self.event_type_id.choices = [(t.id, t.name) for t in event_type_choices] if not self.can_switch_multi_activity_mode(): self.multi_activities_mode.data = True activity_choices = available_activities( source_activities, self.current_leaders, self.multi_activities_mode.data) if self.single_activity_type: self.single_activity_type.choices = [(a.id, a.name) for a in activity_choices] if self.multi_activity_types: self.multi_activity_types.choices = [(a.id, a.name) for a in activity_choices] self.main_leader_id.choices = [] for l in self.current_leaders: self.main_leader_id.choices.append((l.id, "Responsable")) if self.main_leader_id.raw_data is None: if self.source_event is None or self.source_event.main_leader_id is None: self.main_leader_id.default = self.current_leaders[0].id self.main_leader_id.process([]) self.main_leader_fields = list(self.main_leader_id) # Tags self.tag_list.choices = EventTag.choices() # Disallow 'Pending' status for events with existing payments (#425) if self.source_event and self.source_event.has_payments(): self.status.choices = [(k, v) for (k, v) in EventStatus.choices() if k != EventStatus.Pending] def current_event_type(self): """ :return: The currently selected event type, of the first available if none has been elected yet :rtype: :py:class:`collectives.models.eventtype.EventType` """ if self.event_type_id.data: return EventType.query.get(self.event_type_id.data) return EventType.query.get(self.event_type_id.choices[0][0]) def can_switch_multi_activity_mode(self): """ :return: Whether the current user can switch between single-activty/multi-activity modes. If False, this means that editing is restricted to multi-activty mode. :rtype: bool """ return self.current_event_type().requires_activity def setup_leader_actions(self): """ Setups action form for all current leaders """ # Remove all existing entries while len(self.leader_actions) > 0: self.leader_actions.pop_entry() # Create new entries for leader in self.current_leaders: action_form = LeaderActionForm() action_form.leader_id = leader.id action_form.delete = False self.leader_actions.append_entry(action_form) def set_default_values(self): """ Populates optional online registration fields with default value and description field with event description template """ description = current_app.config["DESCRIPTION_TEMPLATE"] columns = {i: "" for i in current_app.config["CSV_COLUMNS"].keys()} # Remove placeholders self.description.data = description.format(**columns) self.num_online_slots.data = current_app.config["DEFAULT_ONLINE_SLOTS"] if self.num_online_slots.data > 0: # Default registration opening date opening_delta = current_app.config[ "REGISTRATION_OPENING_DELTA_DAYS"] opening_hour = current_app.config["REGISTRATION_OPENING_HOUR"] self.registration_open_time.data = ( current_time() - timedelta(days=opening_delta)).replace( hour=opening_hour, minute=0, second=0, microsecond=0) # Default registration closing date closing_delta = current_app.config[ "REGISTRATION_CLOSING_DELTA_DAYS"] closing_hour = current_app.config["REGISTRATION_CLOSING_HOUR"] self.registration_close_time.data = ( current_time() - timedelta(days=closing_delta)).replace( hour=closing_hour, minute=0, second=0, microsecond=0) def current_activities(self): """ :return: the list of currently selected activities. :rtype: list[:py:class:`collectives.models.activitytype.ActivityType`] """ if self.multi_activities_mode.data: return ActivityType.query.filter( ActivityType.id.in_(self.multi_activity_types.data)).all() if self.single_activity_type.data: activity = ActivityType.query.get(self.single_activity_type.data) else: activity = ActivityType.query.get( self.single_activity_type.choices[0][0]) return [activity] if activity else [] def current_leader_ids(self): """ :return: the list of current leader ids. :rtype: list[int] """ return [l.id for l in self.current_leaders] def leader_activity_ids(self): """Returns the list of activities with which to filter potential new leaders. In multi-activty mode, returns an empty list, meaning that leaders with not be filtered by activity. :return: List of activity ids :rtype: list[id] """ if self.multi_activities_mode.data: # Multi-activity, do not filter leaders by activity type activity_ids = [] elif self.single_activity_type.data: # Single activity, already selected. Restrict leaders to that activity activity_ids = [self.single_activity_type.data] else: # Single activity, not yet selected activity_ids = [a[0] for a in self.single_activity_type.choices] return activity_ids def can_remove_leader(self, event, leader): """ Checks whether the current user has the right to remove a leader from the form. This is prevented if: - There is only one leader - The leader is the main leader - The user is not allowed to remove the leader from the event seealso:: :py::meth:`collectives.models.event.Event.can_remove_leader` :param event: Event the form is operating on :type event: :py:class:`collectives.models.event.Event`: :param leader: leader to be removed from the form :type leader: :py:class:`collectives.models.user.User`: :return: whether authorization to remove the leader is granted. :rtype: bool """ if leader.id == self.main_leader_id.data: return False if len(self.current_leaders) <= 1: return False return event.can_remove_leader(current_user, leader)
class FormUsers(FlaskForm): users = FieldList(FormField(FormUserSingle)) new_user_name = StringField('Username', render_kw = {'value':'new user'}) new_position = SelectField('User\'s position', choices = [("Teacher", "Teacher"), ("Student", "Student")], render_kw = {'value':'Teacher'}) addID = SubmitField()
class LocalDeviceFormUSB(Form): device_path = SelectField(u'Path', choices=[('/dev/ttyUSB0', u'/dev/ttyUSB0')], default='/dev/ttyUSB0', coerce=str) baudrate = SelectField(u'Baudrate', choices=[(baudrate, str(baudrate)) for baudrate in BAUDRATES], default=115200, coerce=int) confirm_management = BooleanField(u'Share AlarmDecoder on your network?', description='This setting serves the AlarmDecoder on your network with ser2sock and allows other software (Software keypad, etc.) to use it in conjunction with this webapp.') buttons = FormField(SetupButtonForm)
class LocalDeviceForm(Form): device_path = TextField(u'Path', [Required(), Length(max=255), PathExists()], description=u'Filesystem path to your AlarmDecoder.', default='/dev/ttyAMA0') baudrate = SelectField(u'Baudrate', choices=[(baudrate, str(baudrate)) for baudrate in BAUDRATES], default=115200, coerce=int) confirm_management = BooleanField(u'Share AlarmDecoder on your network?', description='This setting serves the AlarmDecoder on your network with ser2sock and allows other software (Software keypad, etc.) to use it in conjunction with this webapp.') buttons = FormField(SetupButtonForm)
class SSLForm(Form): ca_cert = FileField(u'CA Certificate', [Required()], description=u'CA certificate created for the AlarmDecoder to authorize clients.') cert = FileField(u'Certificate', [Required()], description=u'Client certificate used by this webapp.') key = FileField(u'Key', [Required()], description=u'Client certificate key used by this webapp.') buttons = FormField(SetupButtonForm)
class NetworkDeviceForm(Form): device_address = TextField(u'Address', [Required(), Length(max=255)], description=u'Hostname or IP address', default='localhost') device_port = IntegerField(u'Port', [Required(), NumberRange(1024, 65535)], description=u'', default=10000) ssl = BooleanField(u'Use SSL?') buttons = FormField(SetupButtonForm)
class DeviceTypeForm(Form): device_type = SelectField(u'Device Type', choices=[('AD2USB', u'AD2USB'), ('AD2PI', u'AD2PI'), ('AD2SERIAL', u'AD2SERIAL')], default='AD2USB') device_location = SelectField(u'Device Location', choices=[('local', 'Local Device'), ('network', 'Network')], default='local') buttons = FormField(SetupButtonForm)
class IndexForm(FlaskForm): login = FormField(LoginForm) register = FormField(RegisterForm)
class StoreBaseForm(FlaskForm): class Meta: locales = ('de_DE', 'de') name = StringField(label=_('Name'), validators=[ validators.DataRequired( message=_('Bitte geben Sie einen Namen an.')) ]) category = CategoryField(label=_('Art des Geschäfts')) source_text = StringField(label=_('Woher kommt die Information?')) source_url = StringField( label=_('Woher kommt die Information (Ergänzender Link)?'), validators=[ validators.URL( message='Bitte geben Sie eine valide URL ein (oder gar nichts)' ), validators.Optional() ]) company = StringField(label=_('Unternehmen')) address = StringField( label=_('Straße und Hausnummer'), validators=[ validators.DataRequired( message=_('Bitte geben Sie eine Straße und Hausnummer an.')) ]) postalcode = StringField( label=_('Postleitzahl'), validators=[ validators.DataRequired( message=_('Bitte geben Sie eine Postleitzahl an.')) ]) locality = StringField( label=_('Ort'), validators=[ validators.DataRequired(message=_('Bitte geben Sie einen Ort an.')) ]) website = StringField( label=_('Website'), validators=[ validators.url(message='Bitte geben Sie eine URL an'), validators.Optional() ], ) email = StringField( label=_('E-Mail'), validators=[ validators.email(message='Bitte geben Sie eine E-Mail an'), validators.Optional() ], ) phone = StringField(label=_('Telefon')) mobile = StringField(label=_('Mobiltelefon')) fax = StringField(label=_('Fax')) description = TextAreaField(label='Beschreibung') all_switch = BooleanField(label='Geschäft hat geöffnet') opening_times_all = FieldList(FormField(OpeningTimeForm), label='Öffnungszeiten', min_entries=0) delivery = BooleanField(label='Lieferung') pickup = BooleanField(label='Abholung') onlineshop = BooleanField(label='Onlineshop') delivery_switch = BooleanField(label='Abweichende Lieferzeiten') opening_times_delivery = FieldList(FormField(OpeningTimeForm), label='Öffnungszeiten', min_entries=0) pickup_switch = BooleanField(label='Abweichende Abholzeiten') opening_times_pickup = FieldList(FormField(OpeningTimeForm), label='Öffnungszeiten', min_entries=0) logo = ExtendedFileField( label='Logo', validators=[ ValidateMimeType( mimetypes=['image/jpeg', 'image/png', 'image/svg+xml'], allow_empty=True, message='Bitte ein PNG-, JPG- oder SVG-Bild hochladen!') ]) picture = ExtendedFileField( label='Bild', validators=[ ValidateMimeType( mimetypes=['image/jpeg', 'image/png', 'image/svg+xml'], allow_empty=True, message='Bitte ein PNG-, JPG- oder SVG-Bild hochladen!') ]) submit = SubmitField(_('speichern'))
class SymbolTotal(FlaskForm): Symbol_individual = FormField(SymbolSwap)
class NestedForm(Form): b = FormField(DeeplyNestedForm)
class CampaignForm(FlaskForm): next = HiddenField() name = TextField(_('Campaign Name'), [Required()]) campaign_country = DisabledSelectField(_('Country'), [Optional()], choices=COUNTRY_CHOICES) campaign_type = DisabledSelectField(_('Type'), [Optional()]) campaign_state = SelectField(_('State'), [Optional()]) campaign_subtype = SelectField(_('Subtype'), [Optional()]) # nested_type passed to data-field in template, but starts empty segment_by = RadioField(_('Segment By'), [Required()], choices=choice_items(SEGMENT_BY_CHOICES), description=True, default=SEGMENT_BY_CHOICES[0][0]) locate_by = RadioField(_('Locate By'), [Optional()], choices=choice_items(LOCATION_CHOICES), description=True, default=None) show_special = BooleanField(_('Include Special Targets'), [Optional()], default=False) include_special = SelectField( _('User\'s Representatives'), [Optional()], choices=choice_items(INCLUDE_SPECIAL_CHOCIES), description=True, default=INCLUDE_SPECIAL_CHOCIES[0][0]) target_set = FieldList(FormField(TargetForm, _('Choose Targets')), validators=[Optional()]) target_ordering = RadioField(_('Target Order'), [Optional()], description=True) target_offices = RadioField(_('Target Offices'), [Optional()], choices=choice_items(TARGET_OFFICE_CHOICES), description=True, default=TARGET_OFFICE_CHOICES[0][0]) call_limit = BooleanField(_('Limit Maximum Calls'), [Optional()], default=False) call_maximum = IntegerField(_('Call Maximum'), [Optional(), NumberRange(min=0)]) phone_number_set = QuerySelectMultipleField( _('Select Phone Numbers'), query_factory=TwilioPhoneNumber.available_numbers, validators=[Required()]) allow_call_in = BooleanField(_('Allow Call In')) submit = SubmitField(_('Edit Audio')) submit_skip_audio = SubmitField(_('Save and Test')) def __init__(self, campaign_data, *args, **kwargs): super(CampaignForm, self).__init__(*args, **kwargs) read_only(self.campaign_country) read_only(self.campaign_type) self.campaign_type.choices = choice_items( campaign_data.data_provider.campaign_type_choices) self.campaign_state.choices = choice_items( campaign_data.region_choices) self.campaign_subtype.choices = choice_items( campaign_data.subtype_choices) self.target_ordering.choices = choice_items( campaign_data.target_order_choices) def validate(self): # check default validation if not FlaskForm.validate(self): return False # check nested forms for t in self.target_set: if not t.form.validate(): error_fields = ','.join(t.form.errors.keys()) self.target_set.errors.append({ 'target': t.name, 'message': 'Invalid target ' + error_fields }) return False return True
class MyForm(Form): a = FieldList(FormField(OtherForm))
class ScheduleForm(wtforms.Form): # Default start_time and end_time are set to the user's local time using # Javascript. # # TODO: Sometimes renders "MM" as "0" (no leading zero) start_time = DateTimeField('Start Time', [validators.InputRequired()], render_kw={'placeholder': 'YYYY/MM/DD HH:MM'}) end_time = DateTimeField('End Time', [validators.InputRequired()], render_kw={'placeholder': 'YYYY/MM/DD HH:MM'}) merge_overlapping = BooleanField( 'If two events occur at the same time, merge them into a single event', default=_d.merge_overlap) event_duration_secs = SecondsField('Duration of each event in seconds', [validators.InputRequired()], default=_d.event_duration) show_busy = BooleanField('Show as busy during events', default=_d.show_busy) set_alarms = BooleanField( """Set alarms. <b>Note:</b> <i>many calendar programs will ignore alarms from imported calendars. Set the default alarm policy in your calendar program before importing</i>""", default=_d.set_alarms, render_kw={'onchange': 'updateAlarmInputsHidden()'}) alarm_before_secs = SecondsField( 'Number of seconds before the event to alarm', [validators.InputRequired()], default=_d.alarm_before) alarms_repeat = BooleanField( """Alarms repeat. <b>Note:</b> <i>most calendar programs won't repeat alarms</i>""", default=_d.alarms_repeat, render_kw={'onchange': 'updateAlarmInputsHidden()'}) alarm_repetitions = IntegerField('If alarms repeat, number of repetitions', [validators.InputRequired()], default=_d.alarm_repetitions) alarm_repetition_delay_secs = SecondsField( 'If alarms repeat, number of seconds between each repetition', [validators.InputRequired()], default=_d.alarm_repetition_delay) events = FieldList(FormField(EventForm), label='', min_entries=1, max_entries=100) had_errors = HiddenField() def validate(self): """Override validate() to only validate fields that are used according to user specified configuration. Perform additional validation that applies to relationships among multiple fields. For example, the timedelta between end_time and start_time.""" form_valid = True # Always validate: for field in (self.start_time, self.end_time, self.merge_overlapping, self.event_duration_secs, self.show_busy, self.set_alarms, self.events): if not field.validate(self): form_valid = False total_period = None if self.start_time.data is not None and self.end_time.data is not None: total_period = self.end_time.data - self.start_time.data if (total_period < datetime.timedelta(seconds=0) or total_period > datetime.timedelta(days=100)): form_valid = False self.start_time.data = None self.end_time.data = None self.end_time.errors.append( 'Invalid period between start and end times: %s' % total_period) else: # Valid total_period. Validate numbers of event repetitions. for event in self.events: if event.period.data is None: continue if event.period.data <= datetime.timedelta(0): num_reps = 0 else: # start_time and end_time are an inclusive range, so if # total_period==0 [or any value < event.period.data], there is one # event. num_reps = int(total_period / event.period.data) + 1 if num_reps > 1000: form_valid = False event.period.data = None if event.summary.data: summary = event.summary.data else: summary = 'event' event.period.errors.append( 'Invalid number of repetitions for %s: %d' % (summary, num_reps)) if self.set_alarms.data: # If alarms are enabled, validate additional required fields. for field in (self.alarm_before_secs, self.alarms_repeat): if not field.validate(self): form_valid = False if self.alarms_repeat.data: # If alarm repeats are enabled, validate additional. repeats_valid = True for field in (self.alarm_repetition_delay_secs, self.alarm_repetitions): if not field.validate(self): repeats_valid = False form_valid = False if (repeats_valid and self.alarm_repetition_delay_secs.data is not None and self.alarm_repetitions.data is not None): rep_period = (self.alarm_repetition_delay_secs.data * self.alarm_repetitions.data) if rep_period > datetime.timedelta(hours=24): form_valid = False self.alarm_repetition_delay_secs.data = None self.alarm_repetitions.data = None self.alarm_repetition_delay_secs.errors.append( 'Invalid alarm repetition duration %s' % rep_period) return form_valid def IsHidden(self, field): """Return true if field should be hidden. Used to hide irrelevant fields. E.g, alarm duration if alarms are diabled. JS will be used to hide/unhide dynamically clientside. This is for the initial view before JS loads and runs. :param field: a field of ScheduleForm.""" alarm_fields = [self.alarm_before_secs, self.alarms_repeat] alarms_repeat_fields = [ self.alarm_repetitions, self.alarm_repetition_delay_secs ] if field in alarm_fields: return not self.set_alarms.data if field in alarms_repeat_fields: return not self.set_alarms.data or not self.alarms_repeat.data return False
class InviteUserToProjectForm(FlaskForm): user_id = IntegerField(validators=[InputRequired()]) other_project_ids = FieldList(FormField(OtherProjectIdForm), min_entries=0)
class GetPredictForm(FlaskForm): category = RadioField(_categories[0], choices=[(cat, cat) for cat in _categories], default=_categories[0]) promo_budget = DecimalField() per_entry_params = FieldList(FormField(EntryForm), min_entries=1)
class BusinessForm(Form): #name = StringField('Business Name') grades = FieldList(FormField(ReusableForm), min_entries=8, max_entries=10)
class IndexForm(FlaskForm): tag_form = FormField(TagForm) subreddit_form = FormField(SubredditForm)
class AddEntryForm(FlaskForm): stock_name = StringField(label='Stock Name', validators=[DataRequired()]) indicators = FieldList(FormField(ParameterForm), min_entries=1) submit = SubmitField('Submit')
class SimilarityEvaluationForm(FlaskForm): eval_list = FieldList(FormField(SimilarRecordingEvalForm), min_entries=10, max_entries=10)
class SubmitForm(FlaskForm): kegg_id = StringField( label="kegg id", description="kegg id", validators=[DataRequired("please enter the kegg id !")], render_kw={ "placeholder": "please enter the kegg id", }) kegg_auto = SubmitField(label="auto download", render_kw={ "class": "btn btn-info icon fa-plus", }) kegg_not_auto = SubmitField(label="upload", render_kw={ "class": "btn btn-info icon fa-plus", }) gff_file = FileField( label="gff file", validators=[FileAllowed(['gff', 'gff3'], "GFF ONLY !")], render_kw={ "class": "btn btn-lg btn-primary btn-block", }) fna_file = FileField(label="fna file", validators=[ FileAllowed(['fna', 'fa', 'faa', 'fasta'], "FASTA ONLY !") ], render_kw={ "class": "btn btn-lg btn-primary btn-block", }) condition = FieldList( FormField(ConditionForm), label="Input Srr ID", min_entries=1, ) condition_add = SubmitField(label="Condition +", render_kw={ "class": "btn btn-info icon fa-plus", }) email = StringField( label="email", validators=[ DataRequired("please enter the email !"), Email("the format is wrong !") ], description="email", ) software_select = SelectField(label="software", choices=[ ("rockhopper", "rockhopper"), ("operondemmo", "operondemmo"), ]) method_select = SelectField(label="method", choices=[ ("GD", "Gamma Domain"), ("NB", "Naive Bayes"), ]) correlation_select = SelectField(label="correlation", choices=[("c_i_j", "c_i_j"), ("spearman", "spearman"), ("pearson", "pearson")]) submit = SubmitField(label="submit", render_kw={ "class": "btn btn-lg btn-primary btn-block", })
class RegisterForm(Form): recaptcha = FormField(RegistrationFormRecaptcha, separator='.')
class PortalNotesForm(Form): portal = SelectField('Portal', validators=[Required()]) notes = FieldList(FormField(NoteUnlockForm, 'Note'), min_entries=4, max_entries=24)
class SaleTransactionForm(Form): yyyy = IntegerField(label='Transaction Year', validators=[InputRequired()]) MM = IntegerField(label='Transaction Month', validators=[InputRequired()]) dd = IntegerField(label='Transaction Day', validators=[InputRequired()]) HH = IntegerField(label='Transaction Hour', validators=[InputRequired()]) mm = IntegerField(label='Transaction Minute', validators=[InputRequired()]) ss = IntegerField(label='Transaction Second', default=0, validators=[InputRequired()]) delivery_fee = IntegerField(label='Delivery Fee', validators=[Optional()]) customer_id = SelectField(label='Customer', coerce=int, validators=[InputRequired()]) courier_id = SelectField(label='Courier', coerce=int, validators=[Optional()]) transaction_medium_id = SelectField(label='Transaction Medium', coerce=int, validators=[Optional()]) notes = StringField(label='Notes', validators=[Length(max=models.NOTES_LENGTH)], widget=TextArea()) transaction_items = FieldList(FormField(ItemSaleForm), label='Sale items', min_entries=1) submit_button = SubmitField(label='Save Sale Transaction') transaction_items_choices_dict = {} def validate(self, **kwargs): if not super().validate(): self.yyyy.errors += (super().errors, 'super not validated') return False # check if the date is valid, also this include the leap year factor try: dt = datetime(self.yyyy.data, self.MM.data, self.dd.data, self.HH.data, self.mm.data, self.ss.data) except ValueError as ve: self.yyyy.errors += (ve, ) return False if dt > datetime.now(): self.yyyy.errors += ('Date time cannot be in the future', 'current date: {}'.format(datetime.now()), 'input date: {}'.format(dt)) return False int_field_convert_to_none(self.courier_id) int_field_convert_to_none(self.transaction_medium_id) return True def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.customer_id.choices = get_customer_list() self.courier_id.choices = get_courier_list() self.transaction_medium_id.choices = get_medium_list()