class Meta: model = Sponsorship fields = '__all__' widgets = { 'organization': ModelSelect2(url='organization-lookup'), 'event': ModelSelect2(url='event-lookup'), 'contact': ModelSelect2(url='person-lookup'), }
class Meta: model = Task fields = '__all__' widgets = { 'person': ModelSelect2(url='person-lookup', attrs=SIDEBAR_DAL_WIDTH), 'event': ModelSelect2(url='event-lookup', attrs=SIDEBAR_DAL_WIDTH), }
class TrainingProgressForm(forms.ModelForm): trainee = forms.ModelChoiceField(label='Trainee', required=True, queryset=Person.objects.all(), widget=ModelSelect2(url='person-lookup')) evaluated_by = forms.ModelChoiceField( label='Evaluated by', required=False, queryset=Person.objects.all(), widget=ModelSelect2(url='admin-lookup')) event = forms.ModelChoiceField(label='Event', required=False, queryset=Event.objects.all(), widget=ModelSelect2(url='event-lookup')) # helper used in edit view helper = BootstrapHelper(duplicate_buttons_on_top=True, submit_label='Update', add_delete_button=True, additional_form_class='training-progress', add_cancel_button=False) # helper used in create view create_helper = BootstrapHelper(duplicate_buttons_on_top=True, submit_label='Add', additional_form_class='training-progress', add_cancel_button=False) class Meta: model = TrainingProgress fields = [ 'trainee', 'evaluated_by', 'requirement', 'state', 'discarded', 'event', 'url', 'notes', ] widgets = { 'state': RadioSelect, } def clean(self): cleaned_data = super().clean() trainee = cleaned_data.get('trainee') # check if trainee has at least one training task training_tasks = trainee.get_training_tasks() if not training_tasks: raise ValidationError("It's not possible to add training progress " "to a trainee without any training task.")
class Meta: model = Award fields = '__all__' widgets = { 'person': ModelSelect2(url='person-lookup', attrs=SIDEBAR_DAL_WIDTH), 'event': ModelSelect2(url='event-lookup', attrs=SIDEBAR_DAL_WIDTH), 'awarded_by': ModelSelect2(url='admin-lookup', attrs=SIDEBAR_DAL_WIDTH), }
class EventsSelectionForm(forms.Form): event_a = forms.ModelChoiceField(label='Event A', required=True, queryset=Event.objects.all(), widget=ModelSelect2(url='event-lookup')) event_b = forms.ModelChoiceField(label='Event B', required=True, queryset=Event.objects.all(), widget=ModelSelect2(url='event-lookup')) helper = BootstrapHelper(use_get_method=True, add_cancel_button=False)
class PersonsSelectionForm(forms.Form): person_a = forms.ModelChoiceField(label='Person From', required=True, queryset=Person.objects.all(), widget=ModelSelect2(url='person-lookup')) person_b = forms.ModelChoiceField(label='Person To', required=True, queryset=Person.objects.all(), widget=ModelSelect2(url='person-lookup')) helper = BootstrapHelper(use_get_method=True, add_cancel_button=False)
class TrainingRequestsSelectionForm(forms.Form): trainingrequest_a = forms.ModelChoiceField( label='Training request A', required=True, queryset=TrainingRequest.objects.all(), widget=ModelSelect2(url='trainingrequest-lookup')) trainingrequest_b = forms.ModelChoiceField( label='Training request B', required=True, queryset=TrainingRequest.objects.all(), widget=ModelSelect2(url='trainingrequest-lookup')) helper = BootstrapHelper(use_get_method=True, add_cancel_button=False)
class AdminLookupForm(forms.Form): person = forms.ModelChoiceField(label='Administrator', required=True, queryset=Person.objects.all(), widget=ModelSelect2(url='admin-lookup')) helper = BootstrapHelper(add_cancel_button=False)
class EventLookupForm(forms.Form): event = forms.ModelChoiceField(label='Event', required=True, queryset=Event.objects.all(), widget=ModelSelect2(url='event-lookup')) helper = BootstrapHelper(add_cancel_button=False)
class TaskForm(WidgetOverrideMixin, forms.ModelForm): helper = BootstrapHelper(add_cancel_button=False) SEAT_MEMBERSHIP_HELP_TEXT = ( '{}<br><b>Hint:</b> you can use input format YYYY-MM-DD to display ' 'memberships available on that date.'.format( Task._meta.get_field('seat_membership').help_text)) seat_membership = forms.ModelChoiceField( label=Task._meta.get_field('seat_membership').verbose_name, help_text=SEAT_MEMBERSHIP_HELP_TEXT, required=False, queryset=Membership.objects.all(), widget=ModelSelect2( url='membership-lookup', attrs=SIDEBAR_DAL_WIDTH, )) class Meta: model = Task fields = '__all__' widgets = { 'person': ModelSelect2(url='person-lookup', attrs=SIDEBAR_DAL_WIDTH), 'event': ModelSelect2(url='event-lookup', attrs=SIDEBAR_DAL_WIDTH), }
class TrainingRequestUpdateForm(forms.ModelForm): person = forms.ModelChoiceField(label='Matched Trainee', required=False, queryset=Person.objects.all(), widget=ModelSelect2(url='person-lookup')) score_auto = forms.IntegerField( disabled=True, label=TrainingRequest._meta.get_field('score_auto').verbose_name, help_text=TrainingRequest._meta.get_field('score_auto').help_text, ) helper = BootstrapHelper(duplicate_buttons_on_top=True, submit_label='Update') class Meta: model = TrainingRequest exclude = () widgets = { 'occupation': forms.RadioSelect(), 'domains': forms.CheckboxSelectMultiple(), 'gender': forms.RadioSelect(), 'previous_involvement': forms.CheckboxSelectMultiple(), 'previous_training': forms.RadioSelect(), 'previous_experience': forms.RadioSelect(), 'programming_language_usage_frequency': forms.RadioSelect(), 'teaching_frequency_expectation': forms.RadioSelect(), 'max_travelling_frequency': forms.RadioSelect(), 'state': forms.RadioSelect() }
class TaskFilter(AMYFilterSet): event = django_filters.ModelChoiceFilter( queryset=Event.objects.all(), label='Event', widget=ModelSelect2( url='event-lookup', attrs=SIDEBAR_DAL_WIDTH, ), ) order_by = django_filters.OrderingFilter( fields=( ('event__slug', 'event'), ('person__family', 'person'), ('role', 'role'), ), field_labels={ 'event__slug': 'Event', 'person__family': 'Person', 'role': 'Role', } ) class Meta: model = Task fields = [ 'event', # can't filter on person because person's name contains 3 fields: # person.personal, person.middle, person.family # 'person', 'role', ]
class BadgeAwardsFilter(AMYFilterSet): awarded_after = django_filters.DateFilter(field_name='awarded', lookup_expr='gte') awarded_before = django_filters.DateFilter(field_name='awarded', lookup_expr='lte') event = django_filters.ModelChoiceFilter( queryset=Event.objects.all(), label='Event', widget=ModelSelect2( url='event-lookup', attrs=SIDEBAR_DAL_WIDTH, ), ) order_by = django_filters.OrderingFilter( fields=( 'awarded', 'person__family', ), field_labels={ 'awarded': 'Awarded date', 'person__family': 'Person', } ) class Meta: model = Award fields = ( 'awarded_after', 'awarded_before', 'event', )
class BulkAddTrainingProgressForm(forms.ModelForm): event = forms.ModelChoiceField( label='Training', required=False, queryset=Event.objects.filter(tags__name='TTT'), widget=ModelSelect2(url='ttt-event-lookup')) trainees = forms.ModelMultipleChoiceField(queryset=Person.objects.all()) # TODO: add trainees lookup? # trainees = forms.ModelMultipleChoiceField( # label='Trainees', # required=False, # queryset=Person.objects.all(), # widget=ModelSelect2(url='person-lookup'), # ) helper = BootstrapHelper(additional_form_class='training-progress', submit_label='Add', form_tag=False, add_cancel_button=False) helper.layout = Layout( # no 'trainees' -- you should take care of generating it manually in # the template where this form is used 'requirement', 'state', 'event', 'url', 'notes', ) class Meta: model = TrainingProgress fields = [ # no 'trainees' 'requirement', 'state', 'event', 'url', 'notes', ] widgets = { 'state': RadioSelect, 'notes': TextInput, } def clean(self): cleaned_data = super().clean() trainees = cleaned_data.get('trainees') # check if all trainees have at least one training task for trainee in trainees: training_tasks = trainee.get_training_tasks() if not training_tasks: raise ValidationError("It's not possible to add training " "progress to a trainee without any " "training task.")
class TraineeFilter(AMYFilterSet): search = django_filters.CharFilter( method=filter_trainees_by_trainee_name_or_email, label='Name or Email') all_persons = django_filters.BooleanFilter( label='Include all people, not only trainees', method=filter_all_persons, widget=widgets.CheckboxInput) homework = django_filters.BooleanFilter( label='Only trainees with unevaluated homework', widget=widgets.CheckboxInput, method=filter_trainees_by_unevaluated_homework_presence, ) training_request = django_filters.BooleanFilter( label='Is training request present?', method=filter_trainees_by_training_request_presence, ) is_instructor = django_filters.ChoiceFilter( label='Is SWC/DC instructor?', method=filter_trainees_by_instructor_status, choices=[ ('', 'Unknown'), ('swc-and-dc', 'Both SWC and DC'), ('swc-or-dc', 'SWC or DC '), ('swc', 'SWC instructor'), ('dc', 'DC instructor'), ('eligible', 'No, but eligible to be certified'), ('no', 'No'), ]) training = django_filters.ModelChoiceFilter( queryset=Event.objects.ttt(), method=filter_trainees_by_training, label='Training', widget=ModelSelect2( url='ttt-event-lookup', attrs=SIDEBAR_DAL_WIDTH, ), ) order_by = NamesOrderingFilter(fields=( 'last_login', 'email', ), ) class Meta: model = Person fields = [ 'search', 'all_persons', 'homework', 'is_instructor', 'training', ]
class PersonForm(forms.ModelForm): airport = forms.ModelChoiceField(label='Airport', required=False, queryset=Airport.objects.all(), widget=ModelSelect2(url='airport-lookup')) languages = forms.ModelMultipleChoiceField( label='Languages', required=False, queryset=Language.objects.all(), widget=ModelSelect2Multiple(url='language-lookup')) helper = BootstrapHelper(add_cancel_button=False, duplicate_buttons_on_top=True) class Meta: model = Person # don't display the 'password', 'user_permissions', # 'groups' or 'is_superuser' fields # + reorder fields fields = [ 'username', 'personal', 'middle', 'family', 'may_contact', 'publish_profile', 'lesson_publication_consent', 'data_privacy_agreement', 'email', 'gender', 'country', 'airport', 'affiliation', 'github', 'twitter', 'url', 'occupation', 'orcid', 'user_notes', 'lessons', 'domains', 'languages', ] widgets = { 'country': ListSelect2(), }
class MembershipForm(forms.ModelForm): helper = BootstrapHelper(add_cancel_button=False) organization = forms.ModelChoiceField( label='Organization', required=True, queryset=Organization.objects.all(), widget=ModelSelect2(url='organization-lookup') ) class Meta: model = Membership fields = [ 'organization', 'variant', 'agreement_start', 'agreement_end', 'contribution_type', 'workshops_without_admin_fee_per_agreement', 'self_organized_workshops_per_agreement', 'seats_instructor_training', 'additional_instructor_training_seats', ]
class MatchTrainingRequestForm(forms.Form): """Form used to match a training request to a Person.""" person = forms.ModelChoiceField( label='Trainee Account', required=False, queryset=Person.objects.all(), widget=ModelSelect2(url='person-lookup'), ) helper = BootstrapHelper(add_submit_button=False, add_cancel_button=False) helper.layout = Layout( 'person', FormActions( Submit('match-selected-person', 'Match to selected trainee account'), HTML(' <strong>OR</strong> '), Submit('create-new-person', 'Create new trainee account'), )) def clean(self): super().clean() if 'match-selected-person' in self.data: self.person_required = True self.action = 'match' elif 'create-new-person' in self.data: self.person_required = False self.action = 'create' else: raise ValidationError('Unknown action.') if self.person_required and self.cleaned_data['person'] is None: raise ValidationError({'person': 'No person was selected.'}) class Meta: fields = [ 'person', ]
class SWCEventRequestNoCaptchaForm(PrivacyConsentMixin, forms.ModelForm): workshop_type = forms.CharField(initial='swc', widget=forms.HiddenInput()) understand_admin_fee = forms.BooleanField( required=True, initial=False, label='I understand the Software Carpentry Foundation\'s ' 'administration fee.', help_text='<a href="http://software-carpentry.org/blog/2015/07/changes' '-to-admin-fee.html" target="_blank">Look up administration ' 'fees</a>.', ) language = forms.ModelChoiceField( label='Language', required=False, queryset=Language.objects.all(), widget=ModelSelect2(url='language-lookup')) helper = BootstrapHelper(wider_labels=True, add_cancel_button=False, duplicate_buttons_on_top=True) class Meta: model = EventRequest exclude = ( 'created_at', 'last_updated_at', 'assigned_to', 'data_types', 'data_types_other', 'attendee_data_analysis_level', 'fee_waiver_request', ) widgets = { 'event': Select2(), 'approx_attendees': forms.RadioSelect(), 'attendee_domains': CheckboxSelectMultipleWithOthers('attendee_domains_other'), 'attendee_academic_levels': forms.CheckboxSelectMultiple(), 'attendee_computing_levels': forms.CheckboxSelectMultiple(), 'travel_reimbursement': RadioSelectWithOther('travel_reimbursement_other'), 'admin_fee_payment': forms.RadioSelect(), 'country': ListSelect2(), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # set up a layout object for the helper self.helper.layout = self.helper.build_default_layout(self) # set up RadioSelectWithOther widget so that it can display additional # field inline self['attendee_domains'].field.widget.other_field = \ self['attendee_domains_other'] self['travel_reimbursement'].field.widget.other_field = \ self['travel_reimbursement_other'] # remove that additional field self.helper.layout.fields.remove('attendee_domains_other') self.helper.layout.fields.remove('travel_reimbursement_other')
class EventForm(forms.ModelForm): host = forms.ModelChoiceField( label='Host', required=True, help_text=Event._meta.get_field('host').help_text, queryset=Organization.objects.all(), widget=ModelSelect2(url='organization-lookup')) administrator = forms.ModelChoiceField( label='Administrator', required=False, help_text=Event._meta.get_field('administrator').help_text, queryset=Organization.objects.all(), widget=ModelSelect2(url='organization-lookup')) assigned_to = forms.ModelChoiceField( label='Assigned to', required=False, queryset=Person.objects.all(), widget=ModelSelect2(url='admin-lookup')) language = forms.ModelChoiceField( label='Language', required=False, queryset=Language.objects.all(), widget=ModelSelect2(url='language-lookup')) country = CountryField().formfield( required=False, help_text=Event._meta.get_field('country').help_text, widget=ListSelect2(), ) comment = MarkdownxFormField( label='Comment', help_text='Any content in here will be added to comments after this ' 'event is saved.', widget=forms.Textarea, required=False, ) helper = BootstrapHelper(add_cancel_button=False, duplicate_buttons_on_top=True) class Meta: model = Event fields = [ 'slug', 'completed', 'start', 'end', 'host', 'administrator', 'assigned_to', 'tags', 'url', 'language', 'reg_key', 'venue', 'manual_attendance', 'contact', 'country', 'address', 'latitude', 'longitude', 'open_TTT_applications', 'curricula', 'comment', ] widgets = { 'manual_attendance': TextInput, 'latitude': TextInput, 'longitude': TextInput, 'invoice_status': RadioSelect, 'tags': SelectMultiple(attrs={'size': Tag.ITEMS_VISIBLE_IN_SELECT_WIDGET}), 'curricula': CheckboxSelectMultiple(), } class Media: # thanks to this, {{ form.media }} in the template will generate # a <link href=""> (for CSS files) or <script src=""> (for JS files) js = ( 'date_yyyymmdd.js', 'edit_from_url.js', 'online_country.js', ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper.layout = Layout( Field('slug', placeholder='YYYY-MM-DD-location'), 'completed', Field('start', placeholder='YYYY-MM-DD'), Field('end', placeholder='YYYY-MM-DD'), 'host', 'administrator', 'assigned_to', 'tags', 'open_TTT_applications', 'curricula', 'url', 'language', 'reg_key', 'manual_attendance', 'contact', Div(Div(HTML('Location details'), css_class='card-header'), Div('country', 'venue', 'address', 'latitude', 'longitude', css_class='card-body'), css_class='card mb-2'), 'comment', ) def clean_slug(self): # Ensure slug is in "YYYY-MM-DD-location" format data = self.cleaned_data['slug'] match = re.match(r'(\d{4}|x{4})-(\d{2}|x{2})-(\d{2}|x{2})-.+', data) if not match: raise forms.ValidationError('Slug must be in "YYYY-MM-DD-location"' ' format, where "YYYY", "MM", "DD" can' ' be unspecified (ie. "xx").') return data def clean_end(self): """Ensure end >= start.""" start = self.cleaned_data['start'] end = self.cleaned_data['end'] if start and end and end < start: raise forms.ValidationError('Must not be earlier than start date.') return end def clean_open_TTT_applications(self): """Ensure there's a TTT tag applied to the event, if the `open_TTT_applications` is True.""" open_TTT_applications = self.cleaned_data['open_TTT_applications'] tags = self.cleaned_data.get('tags', None) error_msg = 'You cannot open applications on a non-TTT event.' if open_TTT_applications and tags: # find TTT tag TTT_tag = False for tag in tags: if tag.name == 'TTT': TTT_tag = True break if not TTT_tag: raise forms.ValidationError(error_msg) elif open_TTT_applications: raise forms.ValidationError(error_msg) return open_TTT_applications def clean_curricula(self): """Validate tags when some curricula are selected.""" curricula = self.cleaned_data['curricula'] tags = self.cleaned_data['tags'] try: expected_tags = [ c.slug.split("-")[0].upper() for c in curricula if c.active and not c.unknown ] except (ValueError, TypeError): expected_tags = [] for tag in expected_tags: if not tags.filter(name=tag): raise forms.ValidationError( "You must add tags corresponding to these curricula.") return curricula def save(self, *args, **kwargs): res = super().save(*args, **kwargs) create_comment_signal.send(sender=self.__class__, content_object=res, comment=self.cleaned_data['comment'], timestamp=None) return res
class BulkMatchTrainingRequestForm(forms.Form): requests = forms.ModelMultipleChoiceField( queryset=TrainingRequest.objects.all()) event = forms.ModelChoiceField( label='Training', required=True, queryset=Event.objects.filter(tags__name='TTT'), widget=ModelSelect2(url='ttt-event-lookup')) seat_membership = forms.ModelChoiceField( label='Membership seats', required=False, queryset=Membership.objects.all(), help_text='Assigned users will take instructor seats from selected ' 'member site.', widget=ModelSelect2(url='membership-lookup'), ) seat_open_training = forms.BooleanField( label='Open training seat', required=False, help_text="Some TTT events allow for open training; check this field " "to count this person into open applications.", ) helper = BootstrapHelper(add_submit_button=False, form_tag=False, add_cancel_button=False) helper.layout = Layout( 'event', 'seat_membership', 'seat_open_training', ) helper.add_input( Submit( 'match', 'Accept & match selected trainees to chosen training', **{ 'data-toggle': 'popover', 'data-html': 'true', 'data-trigger': 'hover', 'data-content': 'If you want to <strong>re</strong>match ' 'trainees to other training, first ' '<strong>unmatch</strong> them!', })) def clean(self): super().clean() event = self.cleaned_data['event'] member_site = self.cleaned_data['seat_membership'] open_training = self.cleaned_data['seat_open_training'] if any(r.person is None for r in self.cleaned_data.get('requests', [])): raise ValidationError('Some of the requests are not matched ' 'to a trainee yet. Before matching them to ' 'a training, you need to accept them ' 'and match with a trainee.') if member_site and open_training: raise ValidationError( "Cannot simultaneously match as open training and use " "a Membership instructor training seat.") if open_training and not event.open_TTT_applications: raise ValidationError({ 'seat_open_training': ValidationError( 'Selected TTT event does not allow for open training ' 'seats.'), })
class WorkshopStaffForm(forms.Form): '''Represent instructor matching form.''' latitude = forms.FloatField(label='Latitude', min_value=-90.0, max_value=90.0, required=False) longitude = forms.FloatField(label='Longitude', min_value=-180.0, max_value=180.0, required=False) airport = forms.ModelChoiceField(label='Airport', required=False, queryset=Airport.objects.all(), widget=ModelSelect2( url='airport-lookup', attrs=SIDEBAR_DAL_WIDTH, )) languages = forms.ModelMultipleChoiceField(label='Languages', required=False, queryset=Language.objects.all(), widget=ModelSelect2Multiple( url='language-lookup', attrs=SIDEBAR_DAL_WIDTH, )) country = forms.MultipleChoiceField( choices=list(Countries()), required=False, widget=Select2Multiple, ) continent = forms.ChoiceField( choices=continent_list, required=False, widget=Select2, ) lessons = forms.ModelMultipleChoiceField( queryset=Lesson.objects.all(), widget=SelectMultiple(), required=False, ) badges = forms.ModelMultipleChoiceField( queryset=Badge.objects.instructor_badges(), widget=CheckboxSelectMultiple(), required=False, ) is_trainer = forms.BooleanField(required=False, label='Has Trainer badge') GENDER_CHOICES = ((None, '---------'), ) + Person.GENDER_CHOICES gender = forms.ChoiceField(choices=GENDER_CHOICES, required=False) was_helper = forms.BooleanField(required=False, label='Was helper at least once before') was_organizer = forms.BooleanField( required=False, label='Was organizer at least once before') is_in_progress_trainee = forms.BooleanField( required=False, label='Is an in-progress instructor trainee') def __init__(self, *args, **kwargs): '''Build form layout dynamically.''' super().__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.form_method = 'get' self.helper.layout = Layout( Div( Div(HTML('<h5 class="card-title">Location</h5>'), 'airport', HTML('<hr>'), 'country', HTML('<hr>'), 'continent', HTML('<hr>'), 'latitude', 'longitude', css_class='card-body'), css_class='card', ), 'badges', 'is_trainer', HTML('<hr>'), 'was_helper', 'was_organizer', 'is_in_progress_trainee', 'languages', 'gender', 'lessons', Submit('', 'Submit'), ) def clean(self): cleaned_data = super().clean() lat = bool(cleaned_data.get('latitude')) lng = bool(cleaned_data.get('longitude')) airport = bool(cleaned_data.get('airport')) country = bool(cleaned_data.get('country')) latlng = lat and lng # if searching by coordinates, then there must be both lat & lng # present if lat ^ lng: raise forms.ValidationError( 'Must specify both latitude and longitude if searching by ' 'coordinates') # User must search by airport, or country, or coordinates, or none # of them. Sum of boolean elements must be equal 0 (if general search) # or 1 (if searching by airport OR country OR lat/lng). if sum([airport, country, latlng]) not in [0, 1]: raise forms.ValidationError( 'Must specify an airport OR a country, OR use coordinates, OR ' 'none of them.') return cleaned_data