class NewVersionForm(FlaskForm): version_type = RDURadioField( label=Markup("<b>New version type:</b>"), validators=[DataRequired(message="Select the type of new version you are creating")], ) def __init__(self, measure_version: MeasureVersion, *args, **kwargs): super(NewVersionForm, self).__init__(*args, **kwargs) next_minor_version = measure_version.next_minor_version() next_major_version = measure_version.next_major_version() self.version_type.choices = [ ( NewVersionType.MINOR_UPDATE.value, Markup( f"<strong class='govuk-!-margin-right-4'>" f"{next_minor_version}</strong>Edit this edition (eg for clarifications or corrections)" ), ), ( NewVersionType.MAJOR_UPDATE.value, Markup( f"<strong class='govuk-!-margin-right-4'>" f"{next_major_version}</strong>Create new edition (eg after new data becomes available)" ), ), ]
class FormForTest(FlaskForm): radio_field = RDURadioField(label="radio_field", choices=[(1, "one"), (2, "two"), (3, "three")]) radio_field_invalid = RDURadioField( label="radio_field", choices=[(1, 1), (2, 2), (3, 3)], validators=[DataRequired(message="failed validation")], ) other_field = RDUStringField(label="other_field") radio_field_with_hints = RDURadioField(label="radio_field_with_hints", choices=[(1, 1), (2, 2), (3, 3)], choices_hints={ 1: "Hint 1", 2: "Hint 2" }) radio_field_with_dividers = RDURadioField( label="radio_field_with_dividers", choices=[(1, 1), (2, 2), (3, 3)], dividers={3: "or"})
class AddUserForm(FlaskForm): email = RDUEmailField( label="Email address", validators=[ Length(min=5, max=255), DataRequired(message="Enter an email address"), Email(message="Enter a valid email address"), ValidPublisherEmailAddress(), ], ) user_type = RDURadioField( label="What type of user account?", choices=[("RDU_USER", "RDU CMS user"), ("DEPT_USER", "Departmental CMS user"), ("DEV_USER", "RDU Developer")], default="RDU_USER", validators=[DataRequired()], )
class SelectOrCreateDataSourceForm(FlaskForm): search_query = HiddenField() # Retain the original query for the redirect flow when form was submitted with errors data_source = RDURadioField( label="Select a data source or create a new one", validators=[InputRequired(message="Select a data source")] ) def _build_data_source_hint(self, data_source): return Markup(render_template("forms/labels/_data_source_choice_hint.html", data_source=data_source)) def __init__(self, data_sources: Sequence[DataSource], *args, **kwargs): super(SelectOrCreateDataSourceForm, self).__init__(data_sources=data_sources, *args, **kwargs) # If there are some data sources as options, we also need to include an option for users to create a # new data source if none of those are appropriate. This is a hard-coded choice provided by this form. if data_sources: self.data_source.choices = [(data_source.id, data_source.title) for data_source in data_sources] hints = {data_source.id: self._build_data_source_hint(data_source) for data_source in data_sources} self.data_source.choices_hints = hints self.data_source.dividers = {CREATE_NEW_DATA_SOURCE: "or"} self.data_source.choices.append((CREATE_NEW_DATA_SOURCE, "Create a new data source"))
class DataSourceMergeForm(FlaskForm): keep = RDURadioField( label="Which one would you like to keep?", validators=[InputRequired(message="Select one to keep")], coerce=int) def _build_data_source_hint(self, data_source): return Markup( render_template("forms/labels/_merge_data_source_choice_hint.html", data_source=data_source)) def __init__(self, data_sources: Sequence[DataSource], *args, **kwargs): super(DataSourceMergeForm, self).__init__(*args, **kwargs) self.keep.choices = [(data_source.id, data_source.title) for data_source in data_sources] hints = { data_source.id: self._build_data_source_hint(data_source) for data_source in data_sources } self.keep.choices_hints = hints
class DataSourceForm(FlaskForm): title = RDUStringField( label="Title of data source", hint="For example, Crime and Policing Survey", validators=[InputRequired(message="Enter a data source title"), Length(max=255)], ) type_of_data = RDUCheckboxField( label="Type of data", enum=TypeOfData, validators=[InputRequired(message="Select the type of data")] ) type_of_statistic_id = RDURadioField( label="Type of statistic", coerce=int, validators=[InputRequired("Select the type of statistic")] ) publisher_id = RDUStringField( label="Source data published by", hint="For example, Ministry of Justice", validators=[InputRequired(message="Select a department or organisation")], ) source_url = RDUURLField( label="Link to data source", hint=Markup( "Link to a web page where the data was originally published. " "Don’t link directly to a spreadsheet or a PDF. " '<a href="https://www.gov.uk/government/statistics/youth-justice-annual-statistics-2016-to-2017" ' 'target="_blank" class="govuk-link">View example (this will open a new page)</a>.' ), validators=[InputRequired(message="Enter a link to the data source"), Length(max=255)], ) publication_date = RDUStringField( label="Source data publication date (optional)", hint="For example, 26/03/2018. If you’re using a revised version of the data, give that publication date.", ) note_on_corrections_or_updates = RDUTextAreaField( label="Corrections or updates (optional)", hint="For example, explain if you’ve used a revised version of the data", ) frequency_of_release_other = RDUStringField(label="Other publication frequency", validators=[Length(max=255)]) frequency_of_release_id = RDURadioField( label="How often is the source data published?", coerce=int, validators=[ FrequencyOfReleaseOtherRequiredValidator(), InputRequired("Select the source data publication frequency"), ], ) purpose = RDUTextAreaField( label="Purpose of data source", hint="Explain why this data’s been collected and how it will be used", validators=[InputRequired(message="Explain the purpose of the data source")], ) def __init__(self, *args, **kwargs): super(DataSourceForm, self).__init__(*args, **kwargs) self.type_of_statistic_id.choices = [ (choice.id, choice.internal) for choice in TypeOfStatistic.query.order_by("position").all() ] self.frequency_of_release_id.choices = [ (choice.id, choice.description) for choice in FrequencyOfRelease.query.order_by("position").all() ] self.frequency_of_release_id.set_other_field(self.frequency_of_release_other)
class MeasureVersionForm(FlaskForm): class NotRequiredForMajorVersions: def __call__(self, form: "MeasureVersionForm", field): if not form.is_minor_update: field.errors[:] = [] raise StopValidation() class OnlyIfUpdatingDataMistake: def __call__(self, form: "MeasureVersionForm", field): if not form.update_corrects_data_mistake.data: field.errors[:] = [] raise StopValidation() db_version_id = IntegerField(widget=HiddenInput()) title = RDUStringField( label="Title", validators=[DataRequired(message="Enter a page title"), Length(max=255)], hint="For example, ‘Self-harm by young people in custody’", ) internal_reference = RDUStringField( label="Measure code (optional)", hint="This is for internal use by the Race Disparity Unit" ) published_at = DateField(label="Publication date", format="%Y-%m-%d", validators=[Optional()]) time_covered = RDUStringField( label="Time period covered", validators=[RequiredForReviewValidator(message="Enter the time period covered")], extended_hint="_time_period_covered.html", ) area_covered = RDUCheckboxField( label="Areas covered", enum=UKCountry, validators=[RequiredForReviewValidator(message="Enter the area covered")] ) lowest_level_of_geography_id = RDURadioField( label="Geographic breakdown", hint="Select the most detailed type of geographic breakdown available in the data", validators=[RequiredForReviewValidator(message="Select the geographic breakdown", else_optional=True)], ) suppression_and_disclosure = RDUTextAreaField( label="Suppression rules and disclosure control (optional)", hint="If any data has been excluded from the analysis, explain why.", extended_hint="_suppression_and_disclosure.html", ) estimation = RDUTextAreaField( label="Rounding (optional)", hint="For example, ‘Percentages are rounded to one decimal place’" ) summary = RDUTextAreaField( label="Main points", validators=[RequiredForReviewValidator(message="Enter the main points")], hint=Markup( "<p class='govuk-body govuk-hint'>Summarise the main findings. Don’t include contextual information.</p>" "<details class='govuk-details' data-module='govuk-details'>" "<summary class='govuk-details__summary'>" "<span class='govuk-details__summary-text'>" "What to include" "</span>" "</summary>" "<div class='govuk-details__text'>" "<ul class='govuk-list govuk-list--bullet'>" "<li>the time period the data covers (in the first bullet point)</li>" "<li>definitions of any terms users might not understand</li>" "<li>details of any serious issues with data quality</li>" "</ul>" "</div>" "</details>" ), extended_hint="_summary.html", ) measure_summary = RDUTextAreaField( label="What the data measures", validators=[RequiredForReviewValidator(message="Explain what the data measures")], hint=( "Explain what the data is analysing, what’s included in categories labelled as ‘Other’ and define any " "terms users might not understand" ), ) description = RDUTextAreaField( label="Description for search engines", validators=[RequiredForReviewValidator(message="Enter a description for search engines")], hint=( "Choose an up‐to‐date statistic that shows a key disparity or change over time. The figure should work as " "a stand-alone statement and end with a full stop." ), extended_hint="_description.html", character_count_limit=160, ) need_to_know = RDUTextAreaField( label="Things you need to know", validators=[RequiredForReviewValidator(message="Explain what the reader needs to know to understand the data")], hint="Outline how the data was collected and explain any limitations", extended_hint="_things_you_need_to_know.html", ) ethnicity_definition_summary = RDUTextAreaField( label="The ethnic categories used in this data", validators=[RequiredForReviewValidator(message="List the ethnic categories used in the data")], hint=Markup( "Only use this section to explain if:" "<ul class='govuk-list govuk-list--bullet govuk-hint'>" "<li>the standardised list of 18 ethnic groups isn’t being used (ONS 2011)</li>" "<li>there's only data for broad ethnic groups, not specific groups</li>" "<li>there are different ethnic classifications used in the same measure page</li>" "</ul>" "<details class='govuk-details' data-module='govuk-details'>" "<summary class='govuk-details__summary'>" "<span class='govuk-details__summary-text'>" "Example" "</span>" "</summary>" "<div class='govuk-details__text'>" "The number of people surveyed was too small to make reliable generalisations about specific ethnic groups." "So the data is broken down into [number] aggregated ethnic groups." "</div>" "</details>" ), ) methodology = RDUTextAreaField( label="Methodology", validators=[RequiredForReviewValidator(message="Enter the data’s methodology")], hint="Explain in clear, simple language how the data was collected and processed.", extended_hint="_methodology.html", ) related_publications = RDUTextAreaField( label="Related publications (optional)", extended_hint="_related_publications.html" ) qmi_url = RDUURLField(label="Link to quality and methodology information") further_technical_information = RDUTextAreaField(label="Further technical information (optional)") # Edit summaries update_corrects_data_mistake = RDURadioField( label="Are you correcting something that’s factually incorrect?", hint="For example, in the data or commentary", choices=((True, "Yes"), (False, "No")), coerce=lambda value: None if value is None else get_bool(value), validators=[ NotRequiredForMajorVersions(), RequiredForReviewValidator("Confirm whether this is a correction", else_optional=True), ], ) update_corrects_measure_version = RDURadioField( label="In which version did the mistake first appear?", coerce=int, validators=[ NotRequiredForMajorVersions(), OnlyIfUpdatingDataMistake(), RequiredForReviewValidator("Confirm when the mistake first appeared", else_optional=True), ], ) external_edit_summary = RDUTextAreaField( label="Changes to previous version", validators=[RequiredForReviewValidator(message="Summarise changes to the previous version")], hint=( "If you’ve corrected the data, explain what’s changed and why. Otherwise, summarise what you’ve updated " "(for example, ‘Updated with the latest available data’)." ), ) internal_edit_summary = RDUTextAreaField( label="Notes (for internal use - optional)", hint="Include any additional information someone might need if they’re working on this page in the future", ) def __init__( self, is_minor_update: bool, sending_to_review=False, previous_minor_versions=tuple(), *args, **kwargs ): super(MeasureVersionForm, self).__init__(*args, **kwargs) self.is_minor_update = is_minor_update self.sending_to_review = sending_to_review # Major versions are not considered "corrections to data mistakes", and the question is not shown to end users. # So let's provide the default value here. if not self.is_minor_update: self.update_corrects_data_mistake.data = False choices = [] geographic_choices = LowestLevelOfGeography.query.order_by("position").all() for choice in geographic_choices: if choice.description is not None: description = "%s %s" % (choice.name, choice.description) choices.append((choice.name, description)) else: choices.append((choice.name, choice.name)) self.lowest_level_of_geography_id.choices = choices if kwargs.get("obj", None): self.internal_reference.data = kwargs["obj"].measure.reference or "" if previous_minor_versions or kwargs.get("obj", None): self.update_corrects_measure_version.choices = tuple( [ (measure_version.id, measure_version.version) for measure_version in (previous_minor_versions or kwargs.get("obj", None).previous_minor_versions) ] ) else: self.update_corrects_measure_version.choices = tuple() def populate_obj(self, obj: MeasureVersion): super().populate_obj(obj) # We only want to record the related measure version if this is actually a correction if self.update_corrects_data_mistake.data is not True: obj.update_corrects_measure_version = None def error_items(self): return self.errors.items()