Example #1
0
    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.helper = FormHelper()

        if form.instance.id:
            action = 'Save'
            cancel_edit_url = reverse('logged-template-question-detail',
                                      kwargs={'pk': form.instance.id})
        else:
            action = 'Create'
            cancel_edit_url = reverse('logged-template-question-add')

        form.fields['question_text'].widget.attrs = {'rows': 2}
        form.fields['question_description'].widget.attrs = {'rows': 2}

        form.helper.layout = Layout(
            Div(Div('question_text', css_class='col-12'), css_class='row'),
            Div(Div('question_description', css_class='col-12'),
                css_class='row'),
            Div(Div('answer_type', css_class='col-6'),
                Div('answer_max_length', css_class='col-6'),
                css_class='row'),
            Div(Div('answer_required', css_class='col-12'), css_class='row'),
            Div(Div(HTML('<br>'), css_class='col-12'), css_class='row'),
            FormActions(Submit('save', f'{action} Template Question'),
                        cancel_edit_button(cancel_edit_url)))

        return form
Example #2
0
    def __init__(self, *args, **kwargs):
        self._call_part_pk = kwargs.pop('call_part_pk', None)

        super().__init__(*args, **kwargs)

        assert self.instance.pk, 'Needs to exist - they are created adding template questions'

        self.helper = FormHelper(self)

        cancel_edit_url = reverse('logged-call-part-question-detail',
                                  kwargs={'call_pk': self.instance.call_part.call.pk,
                                          'call_question_pk': self.instance.pk
                                          })
        call = self.instance.call_part.call

        self.fields['call_part'].queryset = CallPart.objects.filter(call=call).order_by('order')

        self.helper.layout = Layout(
            Div(
                Div('order', css_class='col-6'),
                css_class='row'
            ),
            Div(
                Div('call_part', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('question_text', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('question_description', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('answer_max_length', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('answer_required', css_class='col-12'),
                css_class='row'
            ),

            FormActions(
                Submit('save', 'Save Call Question'),
                cancel_edit_button(cancel_edit_url)
            )
        )
Example #3
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.helper = FormHelper(self)

        is_edit = self.instance and self.instance.pk

        if is_edit:
            cancel_html = cancel_edit_button(
                reverse('logged-evaluation_criterion-detail',
                        kwargs={'pk': self.instance.pk}))
        else:
            cancel_html = cancel_button(
                reverse('logged-evaluation_criteria-list'))

        self.helper.layout = Layout(
            Div(Div('name', css_class='col-6'), css_class='row'),
            Div(Div('description', css_class='col-12'), css_class='row'),
            FormActions(Submit('save', 'Save Criterion'), cancel_html))
Example #4
0
    def __init__(self, *args, **kwargs):
        call_part_pk = kwargs.pop('call_part_pk')

        super().__init__(*args, **kwargs)

        self._call_part = CallPart.objects.get(pk=call_part_pk)

        used_templates = []

        for call_question in self._call_part.callquestion_set.all():
            used_templates.append(call_question.template_question)

        create_template_question_url = reverse('logged-template-question-add')

        self.fields['template_questions'] = forms.ModelMultipleChoiceField(initial=used_templates,
                                                                           label='',
                                                                           queryset=TemplateQuestion.objects.all(),
                                                                           required=False,
                                                                           widget=FilteredSelectMultiple(
                                                                               is_stacked=True,
                                                                               verbose_name='questions',
                                                                           ))

        template_questions_label = f'Template questions <div class="pr-3 float-right"><a target="_blank" href="{create_template_question_url}">Create template question <i class="fas fa-external-link-alt"></i></a></div>'

        cancel_edit_url = reverse('logged-call-update', kwargs={'pk': self._call_part.call.pk})
        cancel_edit_url += "#parts"

        self.helper = FormHelper(self)

        self.helper.layout = Layout(
            Div(Div(HTML(template_questions_label), css_class='col-4'),
                css_class='row'),
            Div(
                Div('template_questions', css_class='col-6'),
                css_class='row'
            ),
            FormActions(
                Submit('save', 'Save Template Questions'),
                cancel_edit_button(cancel_edit_url)
            )
        )
Example #5
0
    def __init__(self, *args, **kwargs):
        project = kwargs.pop('project')
        super().__init__(*args, **kwargs)

        XDSoftYearMonthDayPickerInput.set_format_to_field(
            self.fields['signed_date'])
        self.fields['project'].initial = project.id

        self.helper = FormHelper(self)
        cancel_url = reverse('logged-grant_management-project-detail',
                             kwargs={'pk': project.id})

        self.helper.layout = Layout(
            Div(Div('project', hidden=True),
                Div('file', css_class='col-6'),
                css_class='row'),
            Div(Div('signed_date', css_class='col-6'), css_class='row'),
            Div(Div('signed_by', css_class='col-6'), css_class='row'),
            FormActions(Submit('save', 'Save Grant Agreement'),
                        cancel_edit_button(cancel_url)))
Example #6
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        XDSoftYearMonthDayPickerInput.set_format_to_field(
            self.fields['start_date'])
        XDSoftYearMonthDayPickerInput.set_format_to_field(
            self.fields['end_date'])

        self.helper = FormHelper(self)
        cancel_url = reverse('logged-project-detail',
                             kwargs={'pk': self.instance.id})

        self.helper.layout = Layout(
            Div(Div('title', css_class='col-12'), css_class='row'),
            Div(Div('keywords', css_class='col-12'), css_class='row'),
            Div(Div('geographical_areas', css_class='col-12'),
                css_class='row'),
            Div(Div('location', css_class='col-12'), css_class='row'),
            Div(Div('start_date', css_class='col-6'), css_class='row'),
            Div(Div('end_date', css_class='col-6'), css_class='row'),
            FormActions(Submit('save', 'Save Project'),
                        cancel_edit_button(cancel_url)))
Example #7
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        XDSoftYearMonthDayPickerInput.set_format_to_field(
            self.fields['start_date'])
        XDSoftYearMonthDayPickerInput.set_format_to_field(
            self.fields['end_date'])

        self.fields['allocated_budget'].disabled = True
        self.fields[
            'allocated_budget'].help_text = 'The allocated budget cannot be changed after the project is created'

        self.helper = FormHelper(self)
        cancel_url = reverse('logged-grant_management-project-detail',
                             kwargs={'pk': self.instance.id})

        self.helper.layout = Layout(
            Div(Div('allocated_budget', css_class='col-6'), css_class='row'),
            Div(Div('start_date', css_class='col-6'), css_class='row'),
            Div(Div('end_date', css_class='col-6'), css_class='row'),
            FormActions(Submit('save', 'Save Information'),
                        cancel_edit_button(cancel_url)))
    def __init__(self, *args, **kwargs):
        self._proposal_id = kwargs.pop('proposal_id')
        assert 'form_action' not in kwargs

        super().__init__(*args, **kwargs)

        YES_NO_CHOICES = [(True, 'Yes'), (False, 'No')]
        self.fields['eligible'] = forms.ChoiceField(choices=YES_NO_CHOICES,
                                                    widget=forms.RadioSelect)
        self.fields['comment'] = forms.CharField(
            label='Comment<span class="asteriskField">*</span>',
            required=False,
            max_length=1000,
            help_text=
            'Maximum length 1000 characters. Required if proposal is not eligible.',
            widget=forms.Textarea(attrs={'rows': 4}))

        proposal = Proposal.objects.get(id=self._proposal_id)

        if proposal.eligibility == Proposal.ELIGIBLE:
            self.fields['eligible'].initial = True
        elif proposal.eligibility == Proposal.NOTELIGIBLE:
            self.fields['eligible'].initial = False

        self.fields['comment'].initial = proposal.eligibility_comment

        self.helper = FormHelper(self)
        self.helper.form_id = 'eligibility_form'
        self.helper.form_action = reverse('logged-proposal-eligibility-update',
                                          kwargs={'pk': self._proposal_id})

        self.helper.layout = Layout(
            Div(Div('eligible')), Div(Div('comment')),
            FormActions(
                Submit('save', 'Save Eligibility'), HTML('<p></p>'),
                cancel_edit_button(
                    reverse('logged-call-evaluation-proposal-detail',
                            kwargs={'pk': proposal.id}))))
Example #9
0
    def __init__(self, *args, **kwargs):
        call = kwargs.pop('call', None)
        super().__init__(*args, **kwargs)

        self.helper = FormHelper(self)

        if self.instance.id:
            self.helper.form_action = reverse('logged-call-evaluation-update', kwargs={'pk': self.instance.id})
            self.fields['call'].initial = call = self.instance.call
        else:
            self.helper.form_action = reverse('logged-call-evaluation-add') + f'?call={call.id}'
            self.fields['call'].initial = call

        XDSoftYearMonthDayPickerInput.set_format_to_field(self.fields['panel_date'])

        if hasattr(call, 'callevaluation'):
            cancel_edit_url = reverse('logged-call-evaluation-detail', kwargs={'pk': call.callevaluation.id})
            initial_reviewers = call.reviewer_set.all()
        else:
            cancel_edit_url = reverse('logged-call-evaluation-add') + f'?call={call.id}'
            initial_reviewers = []

        self.fields['reviewers'] = ReviewerMultipleChoiceField(initial=initial_reviewers,
                                                               queryset=Reviewer.objects.all(),
                                                               required=True,
                                                               widget=FilteredSelectMultiple(
                                                                   is_stacked=True,
                                                                   verbose_name='reviewers'),
                                                               help_text=self.Meta.help_texts['reviewers'])

        criterion_choices, criterion_initial = CheckboxSelectMultipleSortable.get_choices_initial(
            CriterionCallEvaluation,
            self.instance, 'call_evaluation',
            Criterion, 'criterion',
            label_from_instance=lambda obj: format_html('{} <small>({})</small>', obj.name, obj.description)
        )

        self.fields['criteria'] = forms.MultipleChoiceField(choices=criterion_choices,
                                                            initial=criterion_initial,
                                                            widget=CheckboxSelectMultipleSortable,
                                                            label='Criteria (drag and drop to order them)',
                                                            help_text='These criteria are used in the Excel Evaluation sheet'
                                                            )

        self.criteria_order_key = f'criteria-{CheckboxSelectMultipleSortable.order_of_values_name}'

        self.helper.layout = Layout(
            Div(
                Div('call', css_class='col-12', hidden=True),
                css_class='row'
            ),
            Div(
                Div('reviewers', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('panel_date', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('criteria', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('post_panel_management_table', css_class='col-12'),
                css_class='row'
            ),
            FormActions(
                Submit('save', 'Save Call Evaluation'),
                cancel_edit_button(cancel_edit_url)
            )
        )
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        XDSoftYearMonthDayPickerInput.set_format_to_field(self.fields['start_date'])
        XDSoftYearMonthDayPickerInput.set_format_to_field(self.fields['end_date'])

        self.helper = FormHelper(self)

        is_standalone_project = self.instance.id is None or self.instance.call is None

        if self.instance.id:
            submit_text = 'Save Project'
            cancel_button_html = cancel_edit_button(reverse('logged-project-detail', kwargs={'pk': self.instance.id}))

            grant_management_deliverables_url = reverse('logged-grant_management-project-detail',
                                                        kwargs={'pk': self.instance.id})

            locations_link = f'{grant_management_deliverables_url}?tab=other#locations'
            locations_message = f'Use the <a href="{locations_link}">"Other" tab under grant management</a> to edit the project locations.'

        else:
            submit_text = 'Create Project'
            cancel_button_html = cancel_button(reverse('logged-project-list'))
            locations_message = 'Use the "Other" tab under grant management to add the project locations after creating it. '

        # Applicants need to enter 5 keywords in the proposal, SPI at the moment can create or save them without keywords
        self.fields['keywords'].required = False

        if is_standalone_project:
            self.fields['funding_instrument'].queryset = FundingInstrument.objects.order_by('long_name')
            self.fields['geographical_areas'].required = False

            principal_investigator_div = Div(
                Div('principal_investigator', css_class='col-12'),
                css_class='row'
            )

            self.fields[
                'principal_investigator'].help_text += new_person_message()

            create_funding_instrument_url = reverse('logged-funding-instrument-add')
            self.fields[
                'funding_instrument'].help_text += f'. If the funding instrument is not found <a href="{create_funding_instrument_url}">create</a> the funding instrument, reload the page, then try again'

            funding_instrument_div = Div(
                Div('funding_instrument', css_class='col-12'),
                css_class='row'
            )
            finance_year_div = Div(
                Div('finance_year', css_class='col-6'),
                css_class='row'
            )
            allocated_budget_div = Div(
                Div('allocated_budget', css_class='col-6'),
                css_class='row'
            )

        else:
            principal_investigator_div = None
            funding_instrument_div = None
            finance_year_div = None

            allocated_budget_div = Div(
                Div('allocated_budget', css_class='col-6'),
                css_class='row'
            )

            if self.instance and self.instance.id and self.instance.status != Project.ONGOING:
                self.fields['allocated_budget'].disabled = True
                self.fields['allocated_budget'].help_text += '. Can only be changed for ONGOING projects.'

            # Used when the project is standalone (not coming from a call)
            # In Meta.fields they are added - IMHO it's easier to delete them
            # than add them here
            del self.fields['principal_investigator']
            del self.fields['finance_year']
            del self.fields['funding_instrument']

        self.helper.layout = Layout(
            Div(
                Div('title', css_class='col-12'),
                css_class='row'
            ),
            principal_investigator_div,
            funding_instrument_div,
            finance_year_div,
            allocated_budget_div,
            Div(
                Div('keywords', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('geographical_areas', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('location', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div(HTML('Locations'), css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div(HTML(
                    locations_message),
                    css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div(HTML('<br>'), css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('start_date', css_class='col-6'),
                css_class='row'
            ),
            Div(
                Div('end_date', css_class='col-6'),
                css_class='row'
            ),
            Div(
                Div('on_website', css_class='col-6'),
                css_class='row'
            ),
            FormActions(
                Submit('save', submit_text),
                cancel_button_html
            )
        )
    def __init__(self, *args, **kwargs):
        call = kwargs.pop('call', None)
        super().__init__(*args, **kwargs)

        self.helper = FormHelper(self)

        if self.instance.id:
            self.helper.form_action = reverse('logged-call-evaluation-update', kwargs={'pk': self.instance.id})
            self.fields['call'].initial = call = self.instance.call
        else:
            self.helper.form_action = reverse('logged-call-evaluation-add') + f'?call={call.id}'
            self.fields['call'].initial = call

        XDSoftYearMonthDayPickerInput.set_format_to_field(self.fields['panel_date'])

        if hasattr(call, 'callevaluation'):
            cancel_edit_url = reverse('logged-call-evaluation-detail', kwargs={'pk': call.callevaluation.id})
            initial_reviewers = call.reviewer_set.all()
        else:
            cancel_edit_url = reverse('logged-call-evaluation-add') + f'?call={call.id}'
            initial_reviewers = []

        reviewer_add_url = reverse('logged-user-add')

        reviewers_help_text = \
            f'Select the reviewers that you would like to be added for this call. This is everyone that ' \
            f'will review individual proposals and be on the review panel. If you cannot find the person ' \
            f'you are looking for, please <a href="{reviewer_add_url}">add a Reviewer type of user</a> and reload ' \
            f'this page.'

        self.fields['reviewers'] = ReviewerMultipleChoiceField(initial=initial_reviewers,
                                                               queryset=Reviewer.objects.all(),
                                                               required=True,
                                                               widget=FilteredSelectMultiple(
                                                                   is_stacked=True,
                                                                   verbose_name='reviewers'),
                                                               help_text=reviewers_help_text)

        criterion_choices, criterion_initial = CheckboxSelectMultipleSortable.get_choices_initial(
            CriterionCallEvaluation,
            self.instance, 'call_evaluation',
            Criterion, 'criterion',
            label_from_instance=lambda obj: format_html('{} <small>({})</small>', obj.name, obj.description)
        )

        evaluation_criteria_list_url = reverse('logged-evaluation_criteria-list')
        list_edit_criteria = f'You can list and create criteria in <a href="{evaluation_criteria_list_url}">Evaluation criteria list</a>.'

        self.fields['criteria'] = forms.MultipleChoiceField(choices=criterion_choices,
                                                            initial=criterion_initial,
                                                            widget=CheckboxSelectMultipleSortable,
                                                            label='Criteria (drag and drop to order them)',
                                                            help_text=f'These criteria are used in the Excel Evaluation sheet. '
                                                                      f'{list_edit_criteria}'
                                                            )

        self.criteria_order_key = f'criteria-{CheckboxSelectMultipleSortable.order_of_values_name}'

        self.helper.layout = Layout(
            Div(
                Div('call', css_class='col-12', hidden=True),
                css_class='row'
            ),
            Div(
                Div('reviewers', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('panel_date', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('criteria', css_class='col-12'),
                css_class='row'
            ),
            Div(
                Div('post_panel_management_table', css_class='col-12'),
                css_class='row'
            ),
            FormActions(
                Submit('save', 'Save Call Evaluation'),
                cancel_edit_button(cancel_edit_url)
            )
        )
    def __init__(self, *args, **kwargs):
        initial_type_of_user = kwargs.pop('type_of_user', None)

        super().__init__(*args, **kwargs)

        self.new_password = None
        self.user_id_new_password = None

        self.helper = FormHelper(self)

        self.fields['type_of_user'] = forms.ChoiceField(required=True,
                                                        choices=[(settings.MANAGEMENT_GROUP_NAME, 'Management'),
                                                                 (settings.REVIEWER_GROUP_NAME, 'Reviewer'),
                                                                 ],
                                                        widget=forms.RadioSelect,
                                                        help_text='Reviewers only have access to proposals. Management users have access to everything in Nestor'
                                                        )

        self._is_edit_action = bool(self.instance.id)
        self._is_create_action = not self._is_edit_action

        if self._is_edit_action:
            self._original_username = self.instance.username
        else:
            self._original_username = None

        initial_physical_person = None

        reviewer = None

        if self._is_edit_action:
            cancel_html = cancel_edit_button(reverse('logged-user-detail', kwargs={'pk': self.instance.id}))

            group_count = 0
            if self.instance.groups.filter(name=settings.REVIEWER_GROUP_NAME).exists():
                self.fields['type_of_user'].initial = settings.REVIEWER_GROUP_NAME

                try:
                    reviewer = Reviewer.objects.get(user=self.instance)
                except Reviewer.DoesNotExist:
                    pass

                if reviewer:
                    initial_physical_person = reviewer.person

                group_count += 1
            if self.instance.groups.filter(name=settings.MANAGEMENT_GROUP_NAME).exists():
                self.fields['type_of_user'].initial = settings.MANAGEMENT_GROUP_NAME
                group_count += 1

            assert group_count < 2, 'A user cannot be a reviewer and management at the same time'

        else:
            self.fields['is_active'].required = True
            self.fields['is_active'].initial = True
            self.fields['is_active'].disabled = True
            cancel_html = cancel_button(reverse('logged-user-list'))

        if initial_type_of_user:
            self.fields['type_of_user'].initial = initial_type_of_user

        used_users = list(Reviewer.objects.all().values_list('person', flat=True))

        my_physical_person_id = 0

        if self.instance and self.instance.id:
            my_user = self.instance
            reviewer = Reviewer.objects.filter(user=my_user).first()
            # It might exist because the User might have been previously a reviewer and then changed to management
            if reviewer:
                my_physical_person_id = reviewer.user_id

        if reviewer:
            initial_physical_person = reviewer.person
            used_users.remove(initial_physical_person.id)

        self.fields['physical_person'] = forms.ModelChoiceField(
            label='Person<span class="asteriskField">*</span>',
            required=False,
            help_text=f"Choose the reviewer's name from the list{new_person_message()}.<br>"
                      f"To give access to different calls add the reviewer to the Call Evaluation.",
            queryset=PhysicalPerson.objects.all().exclude(id__in=used_users),
            initial=initial_physical_person,
            widget=autocomplete.ModelSelect2(url=reverse(
                'logged-autocomplete-physical-people-non-reviewers') + f'?force_include={my_physical_person_id}'))

        self.fields['generate_new_password'] = forms.BooleanField(required=self._is_create_action,
                                                                  disabled=self._is_create_action,
                                                                  initial=self._is_create_action,
                                                                  help_text='If enabled, a new password will be generated for this user. If editing a user, this option can be used to change a forgotten password')

        self.helper.layout = Layout(
            Div(
                Div('username', css_class='col-6'),
                css_class='row'
            ),
            Div(
                Div('type_of_user', css_class='col-6'),
                css_class='row'
            ),
            Div(
                Div('physical_person', css_class='col-6'),
                css_class='row reviewer_information',
            ),
            Div(
                Div('first_name', css_class='col-6'),
                Div('last_name', css_class='col-6'),
                css_class='row management_information',
            ),
            Div(
                Div('is_active', css_class='col-6'),
                Div('generate_new_password', css_class='col-6'),
                css_class='row'
            ),
            FormActions(
                Submit('save', 'Save User'),
                cancel_html
            )
        )
Example #13
0
    def __init__(self, *args, **kwargs):
        assert 'instance' not in kwargs

        self._proposal = kwargs.pop('proposal')

        try:
            proposal_evaluation = ProposalEvaluation.objects.get(
                proposal=self._proposal)
            kwargs['instance'] = proposal_evaluation
        except ObjectDoesNotExist:
            pass

        super().__init__(*args, **kwargs)

        self.helper = FormHelper(self)
        self.helper.form_action = reverse('logged-proposal-evaluation-update',
                                          kwargs={'pk': self._proposal.id})

        XDSoftYearMonthDayPickerInput.set_format_to_field(
            self.fields['decision_date'])
        XDSoftYearMonthDayPickerInput.set_format_to_field(
            self.fields['decision_letter_date'])
        self.fields['proposal'].initial = self._proposal
        requested_budget = self._proposal.total_budget()

        self.fields['allocated_budget'] = FlexibleDecimalField(required=False)
        self.fields[
            'allocated_budget'].help_text = f'Requested: {thousands_separator(requested_budget)} CHF'
        self.fields['allocated_budget'].label = 'Allocated budget (CHF)'

        if hasattr(self._proposal, 'proposalevaluation'):
            cancel_button_url = reverse(
                'logged-proposal-evaluation-detail',
                kwargs={'pk': self._proposal.proposalevaluation.id})
            initial_reviewers = self._proposal.reviewer_set.all()
        else:
            cancel_button_url = reverse('logged-proposal-evaluation-add'
                                        ) + f'?proposal={self._proposal.id}'
            initial_reviewers = []

        self.fields['reviewers'] = ReviewerMultipleChoiceField(
            initial=initial_reviewers,
            queryset=Reviewer.objects.filter(calls=self._proposal.call),
            required=True,
            widget=FilteredSelectMultiple(is_stacked=True,
                                          verbose_name='reviewers'),
            help_text=self.Meta.help_texts['reviewers'])

        self.helper.layout = Layout(
            Div(Div('proposal', css_class='col-12'), css_class='row'),
            Div(Div('reviewers', css_class='col-12'), css_class='row'),
            Div(Div('panel_remarks', css_class='col-12'), css_class='row'),
            Div(Div('feedback_to_applicant', css_class='col-12'),
                css_class='row'),
            Div(Div('panel_recommendation', css_class='col-6'),
                Div('allocated_budget', css_class='col-6'),
                css_class='row'),
            Div(Div('board_decision', css_class='col-6'),
                Div('decision_date', css_class='col-6'),
                css_class='row'),
            Div(Div('decision_letter', css_class='col-6'),
                Div('decision_letter_date', css_class='col-6'),
                css_class='row'),
            FormActions(Submit('save', 'Save Evaluation'),
                        cancel_edit_button(cancel_button_url)))