clinicedc 2.0.4__py3-none-any.whl → 2.0.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clinicedc might be problematic. Click here for more details.
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/METADATA +41 -45
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/RECORD +478 -478
- edc_action_item/action.py +76 -54
- edc_action_item/action_item_notification.py +1 -3
- edc_action_item/action_with_notification.py +4 -4
- edc_action_item/create_action_item.py +2 -2
- edc_action_item/create_or_update_action_type.py +1 -1
- edc_action_item/data_fixers.py +4 -5
- edc_action_item/delete_action_item.py +0 -1
- edc_action_item/modeladmin_mixins.py +2 -4
- edc_action_item/modelform_mixins/modelform_mixins.py +1 -1
- edc_action_item/models/action_item.py +53 -38
- edc_action_item/models/action_model_mixin.py +17 -17
- edc_action_item/models/action_type.py +33 -13
- edc_action_item/models/reference.py +3 -2
- edc_action_item/site_action_items.py +13 -14
- edc_action_item/stubs.py +9 -9
- edc_action_item/templatetags/action_item_extras.py +2 -2
- edc_action_item/view_utils/action_item_button.py +3 -3
- edc_action_item/view_utils/action_item_popover_list_item.py +4 -4
- edc_adherence/form_validator_mixin.py +1 -1
- edc_adverse_event/action_items/ae_followup_action.py +1 -1
- edc_adverse_event/action_items/ae_initial_action.py +1 -1
- edc_adverse_event/action_items/ae_susar_action.py +1 -1
- edc_adverse_event/action_items/ae_tmg_action.py +1 -1
- edc_adverse_event/action_items/death_report_action.py +1 -2
- edc_adverse_event/action_items/death_report_tmg_action.py +1 -1
- edc_adverse_event/action_items/death_report_tmg_second_action.py +1 -1
- edc_adverse_event/action_items/hospitalization_action.py +1 -1
- edc_adverse_event/form_validator_mixins/death_report_form_validator.py +3 -3
- edc_adverse_event/form_validator_mixins/requires_death_report_form_validator_mixin.py +3 -4
- edc_adverse_event/form_validators/death_report_tmg.py +1 -1
- edc_adverse_event/model_mixins/ae_followup/ae_followup_methods_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_followup/ae_followup_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_initial/ae_initial_fields_model_mixin.py +0 -1
- edc_adverse_event/model_mixins/ae_initial/ae_initial_methods_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_initial/ae_initial_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_special_interest/aesi_methods_model_mixin.py +4 -4
- edc_adverse_event/model_mixins/ae_special_interest/aesi_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_susar/ae_susar_methods_model_mixin.py +4 -4
- edc_adverse_event/model_mixins/ae_susar/ae_susar_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/ae_tmg/ae_tmg_fields_model_mixin.py +4 -5
- edc_adverse_event/model_mixins/ae_tmg/ae_tmg_methods_model_mixin.py +4 -4
- edc_adverse_event/model_mixins/ae_tmg/ae_tmg_model_mixin.py +3 -3
- edc_adverse_event/model_mixins/death_report/death_report_extra_fields_model_mixin.py +2 -3
- edc_adverse_event/model_mixins/death_report/death_report_model_mixin.py +11 -11
- edc_adverse_event/model_mixins/death_report/death_report_tmg_model_mixin.py +9 -11
- edc_adverse_event/model_mixins/death_report/simple_death_report_model_mixin.py +7 -6
- edc_adverse_event/model_mixins/hospitaization/hospitalization_model_mixin.py +0 -1
- edc_adverse_event/modeladmin_mixins/ae_followup_admin_mixin.py +9 -11
- edc_adverse_event/modeladmin_mixins/ae_initial_admin_mixin.py +4 -6
- edc_adverse_event/modeladmin_mixins/modeladmin_mixins.py +1 -1
- edc_adverse_event/modeladmin_mixins/utils.py +4 -4
- edc_adverse_event/models/signals.py +7 -19
- edc_adverse_event/pdf_reports/death_pdf_report.py +8 -9
- edc_adverse_event/templatetags/edc_adverse_event_extras.py +10 -20
- edc_adverse_event/urls.py +7 -3
- edc_adverse_event/utils.py +2 -2
- edc_adverse_event/view_utils/tmg_button.py +4 -4
- edc_appconfig/system_checks.py +1 -1
- edc_appointment/appointment_status_updater.py +14 -15
- edc_appointment/creators/appointment_creator.py +7 -11
- edc_appointment/creators/appointments_creator.py +1 -1
- edc_appointment/creators/unscheduled_appointment_creator.py +12 -13
- edc_appointment/form_validator_mixins/next_appointment_crf_form_validator_mixin.py +1 -3
- edc_appointment/form_validator_mixins/window_period_form_validator_mixin.py +7 -6
- edc_appointment/form_validators/appointment_form_validator.py +2 -4
- edc_appointment/form_validators/utils.py +4 -4
- edc_appointment/managers.py +4 -4
- edc_appointment/model_mixins/appointment_methods_model_mixin.py +2 -2
- edc_appointment/model_mixins/appointment_model_mixin.py +5 -5
- edc_appointment/model_mixins/window_period_model_mixin.py +1 -1
- edc_appointment/models/appointment.py +1 -1
- edc_appointment/skip_appointments.py +5 -6
- edc_appointment/stubs.py +12 -12
- edc_appointment/utils.py +41 -47
- edc_appointment/view_utils/appointment_button.py +3 -5
- edc_auth/admin/role_admin.py +5 -7
- edc_auth/auth_objects/default_roles.py +1 -3
- edc_auth/auth_updater/auth_updater.py +5 -7
- edc_auth/auth_updater/group_updater.py +8 -8
- edc_auth/auth_updater/role_updater.py +1 -2
- edc_auth/get_app_codenames.py +1 -3
- edc_auth/import_users.py +19 -19
- edc_auth/models/role.py +4 -3
- edc_auth/password_setter.py +6 -7
- edc_auth/send_new_credentials_to_user.py +2 -3
- edc_auth/site_auths.py +3 -3
- edc_consent/actions.py +4 -5
- edc_consent/consent_definition.py +8 -11
- edc_consent/consent_definition_extension.py +3 -3
- edc_consent/form_validators/__init__.py +1 -1
- edc_consent/model_mixins/__init__.py +2 -2
- edc_consent/model_mixins/consent_version_model_mixin.py +1 -1
- edc_consent/modeladmin_mixins/consent_model_admin_mixin.py +1 -2
- edc_consent/modelform_mixins/consent_modelform_mixin/consent_modelform_validation_mixin.py +3 -5
- edc_consent/site_consents.py +10 -15
- edc_consent/stubs.py +2 -2
- edc_consent/utils.py +1 -1
- edc_constants/utils.py +2 -4
- edc_crf/crf_form_validator_mixins.py +2 -2
- edc_crf/model_mixins/crf_no_manager_model_mixin.py +2 -2
- edc_crf/model_mixins/crf_status_model_mixin.py +1 -1
- edc_crf/models/crf_status.py +10 -12
- edc_crf/update_crf_status_command.py +1 -3
- edc_dashboard/management/commands/update_search_slugs.py +8 -11
- edc_dashboard/templatetags/edc_dashboard_extras.py +1 -1
- edc_dashboard/url_config.py +3 -3
- edc_dashboard/utils.py +3 -4
- edc_dashboard/views/dashboard_view.py +1 -1
- edc_data_manager/models/data_dictionary.py +1 -2
- edc_data_manager/models/data_query.py +3 -3
- edc_data_manager/models/query_rule.py +2 -2
- edc_data_manager/tasks.py +0 -2
- edc_device/device.py +1 -1
- edc_device/view_mixins.py +1 -1
- edc_document_status/fieldsets.py +1 -3
- edc_document_status/model_mixins.py +2 -2
- edc_document_status/modeladmin_mixins.py +1 -4
- edc_egfr/admin/egfr_drop_notification_admin_mixin.py +5 -7
- edc_egfr/egfr.py +6 -7
- edc_egfr/get_drop_notification_model.py +1 -1
- edc_egfr/model_mixins/egfr_drop_notification_model_mixin.py +1 -1
- edc_export/archive_exporter.py +26 -27
- edc_export/exportables.py +2 -3
- edc_export/management/commands/import_receipts.py +6 -6
- edc_export/model_exporter/file_history_updater.py +1 -1
- edc_export/model_exporter/model_exporter.py +1 -1
- edc_export/model_exporter/value_getter.py +6 -6
- edc_export/model_mixins/notification_model_mixin.py +4 -4
- edc_export/models/data_request.py +3 -3
- edc_export/models/data_request_history.py +2 -2
- edc_export/models/export_receipt.py +1 -1
- edc_export/models/file_history.py +5 -5
- edc_export/models/plan.py +4 -4
- edc_export/models/upload_export_receipt_file.py +2 -2
- edc_export/utils.py +1 -1
- edc_facility/facility.py +6 -6
- edc_facility/holidays.py +2 -5
- edc_facility/import_holidays.py +6 -6
- edc_facility/model_mixins.py +1 -1
- edc_facility/models/holiday.py +1 -1
- edc_facility/utils.py +3 -3
- edc_fieldsets/fieldsets.py +1 -1
- edc_form_describer/forms_reference.py +5 -8
- edc_form_describer/make_forms_reference.py +1 -1
- edc_form_describer/management/commands/make_forms_reference.py +1 -1
- edc_form_label/custom_label_condition.py +1 -1
- edc_form_label/form_label.py +2 -2
- edc_form_runners/exceptions.py +1 -1
- edc_form_runners/models/issue.py +3 -2
- edc_form_runners/site.py +2 -2
- edc_form_runners/templatetags/form_runners_extras.py +1 -1
- edc_form_runners/utils.py +5 -5
- edc_form_validators/applicable_field_validator.py +32 -32
- edc_form_validators/base_form_validator.py +1 -1
- edc_form_validators/extra_mixins/study_day_form_validator.py +1 -1
- edc_form_validators/many_to_many_field_validator.py +38 -43
- edc_form_validators/other_specify_field_validator.py +9 -17
- edc_form_validators/required_field_validator.py +34 -39
- edc_form_validators/test_case_mixin.py +5 -5
- edc_identifier/admin.py +1 -3
- edc_identifier/identifier.py +2 -3
- edc_identifier/model_mixins.py +5 -8
- edc_identifier/research_identifier.py +10 -12
- edc_identifier/short_identifier.py +1 -1
- edc_identifier/simple_identifier.py +22 -22
- edc_identifier/utils.py +1 -1
- edc_lab/admin/fieldsets.py +7 -9
- edc_lab/admin/modeladmin_mixins.py +5 -6
- edc_lab/form_validators/requisition_form_validator_mixin.py +2 -3
- edc_lab/forms/box_form.py +5 -11
- edc_lab/identifiers/aliquot_identifier.py +5 -8
- edc_lab/identifiers/prefix.py +2 -5
- edc_lab/lab/aliquot_creator.py +1 -2
- edc_lab/lab/aliquot_type.py +2 -4
- edc_lab/lab/manifest.py +5 -7
- edc_lab/lab/requisition_panel.py +2 -4
- edc_lab/lab/requisition_panel_group.py +5 -7
- edc_lab/model_mixins/requisition/requisition_model_mixin.py +5 -4
- edc_lab/model_mixins/shipping/manifest_model_mixin.py +6 -6
- edc_lab/model_mixins/shipping/verify_model_mixin.py +4 -3
- edc_lab/models/aliquot.py +1 -1
- edc_lab/models/box.py +2 -2
- edc_lab/models/box_item.py +1 -1
- edc_lab/models/box_type.py +1 -1
- edc_lab/models/manifest/manifest_item.py +1 -1
- edc_lab/pdf_reports/manifest_pdf_report.py +3 -7
- edc_lab/site_labs.py +3 -3
- edc_lab_dashboard/dashboard_templates.py +1 -1
- edc_lab_dashboard/view_mixins/box_view_mixin.py +4 -9
- edc_lab_dashboard/views/action_views/action_view.py +1 -2
- edc_lab_dashboard/views/action_views/manage_manifest_view.py +1 -1
- edc_lab_dashboard/views/action_views/manifest_view.py +9 -11
- edc_lab_dashboard/views/action_views/pack_view.py +11 -14
- edc_lab_results/calculate_missing.py +2 -2
- edc_lab_results/fieldsets.py +6 -6
- edc_lab_results/model_mixin_factories/__init__.py +2 -2
- edc_lab_results/model_mixin_factories/reportable_result_model_mixin_factory.py +7 -9
- edc_lab_results/model_mixin_factories/result_model_mixin_factory.py +7 -9
- edc_lab_results/model_mixins/fbg_model_mixin.py +1 -1
- edc_lab_results/model_mixins/glucose_model_mixin.py +1 -1
- edc_label/admin.py +1 -3
- edc_label/label.py +2 -4
- edc_label/label_template.py +1 -1
- edc_label/subject_label.py +1 -3
- edc_list_data/admin.py +3 -5
- edc_list_data/load_list_data.py +1 -1
- edc_list_data/load_model_data.py +1 -3
- edc_list_data/model_mixins.py +3 -5
- edc_list_data/preload_data.py +2 -4
- edc_list_data/site_list_data.py +6 -7
- edc_listboard/filters/listboard_filter.py +2 -2
- edc_listboard/views/listboard_view.py +1 -3
- edc_locator/models.py +2 -2
- edc_locator/view_mixins/subject_locator_view_mixins.py +2 -2
- edc_ltfu/action_items.py +1 -1
- edc_ltfu/model_mixins.py +0 -2
- edc_ltfu/models.py +4 -4
- edc_metadata/admin/modeladmin_mixins.py +1 -1
- edc_metadata/admin/requisition_metadata.py +2 -4
- edc_metadata/metadata/crf_metadata_getter.py +1 -3
- edc_metadata/metadata/requisition_metadata_getter.py +1 -3
- edc_metadata/metadata_handler.py +2 -2
- edc_metadata/metadata_helper/metadata_helper_mixin.py +2 -3
- edc_metadata/metadata_mixins/source_model_metadata_mixin.py +2 -2
- edc_metadata/metadata_refresher.py +1 -1
- edc_metadata/metadata_rules/crf/crf_rule_group.py +3 -5
- edc_metadata/metadata_rules/logic.py +1 -1
- edc_metadata/metadata_rules/requisition/requisition_rule.py +4 -4
- edc_metadata/metadata_rules/requisition/requisition_rule_group.py +2 -2
- edc_metadata/metadata_rules/rule_evaluator.py +5 -6
- edc_metadata/metadata_rules/rule_group_metaclass.py +2 -3
- edc_metadata/metadata_rules/site.py +6 -7
- edc_metadata/metadata_updater.py +2 -2
- edc_metadata/metadata_wrappers/metadata_wrapper.py +4 -4
- edc_metadata/model_mixins/creates/creates_metadata_model_mixin.py +6 -7
- edc_metadata/model_mixins/updates/updates_crf_metadata_model_mixin.py +2 -2
- edc_metadata/model_mixins/updates/updates_metadata_model_mixin.py +2 -2
- edc_metadata/model_mixins/updates/updates_requisition_metadata_model_mixin.py +2 -2
- edc_metadata/models/crf_metadata.py +27 -29
- edc_metadata/models/crf_metadata_model_mixin.py +1 -1
- edc_metadata/models/requisition_metadata.py +29 -31
- edc_metadata/stubs.py +17 -17
- edc_metadata/utils.py +4 -4
- edc_model/__init__.py +2 -2
- edc_model/models/historical_records.py +2 -2
- edc_model/models/signals.py +1 -1
- edc_model/models/url_model_mixin.py +1 -1
- edc_model/utils.py +0 -2
- edc_model_admin/dashboard/model_admin_dashboard_mixin.py +1 -3
- edc_model_admin/history/model_admin_simple_history.py +7 -9
- edc_model_admin/mixins/base_model_admin_redirect_mixin.py +4 -6
- edc_model_admin/mixins/model_admin_limit_to_selected_foreignkey.py +2 -2
- edc_model_admin/mixins/model_admin_model_redirect_mixin.py +2 -10
- edc_model_admin/mixins/model_admin_next_url_redirect_mixin.py +8 -9
- edc_model_admin/utils.py +6 -9
- edc_model_form/mixins/__init__.py +1 -1
- edc_model_form/mixins/base_model_form_mixin.py +1 -1
- edc_model_to_dataframe/model_to_dataframe.py +2 -4
- edc_navbar/site_navbars.py +2 -2
- edc_navbar/system_checks.py +1 -1
- edc_notification/mailing_list_manager.py +5 -8
- edc_notification/management/commands/list_recipients_by_notification.py +1 -1
- edc_notification/models/__init__.py +4 -2
- edc_notification/notification/graded_event_notification.py +2 -4
- edc_notification/notification/model_notification.py +1 -3
- edc_notification/notification/notification.py +14 -17
- edc_notification/site_notifications.py +7 -7
- edc_notification/stubs.py +6 -6
- edc_notification/update_mailing_lists_in_m2m.py +2 -2
- edc_offstudy/action_items.py +2 -2
- edc_offstudy/model_mixins/offstudy_model_mixin.py +1 -1
- edc_offstudy/models.py +1 -1
- edc_pdf_reports/crf_pdf_report.py +2 -2
- edc_pdf_reports/model_mixins.py +2 -2
- edc_pdf_reports/report.py +4 -5
- edc_pdf_reports/utils.py +1 -1
- edc_pdutils/dataframes/get_subject_consent.py +1 -3
- edc_pdutils/df_exporters/csv_exporter.py +5 -7
- edc_pdutils/df_exporters/tables_exporter.py +2 -2
- edc_pdutils/df_handlers/crf_df_handler.py +1 -2
- edc_pdutils/dialects/crf_dialect.py +3 -3
- edc_pdutils/management/commands/export_models.py +6 -7
- edc_pdutils/site_values_mappings.py +2 -2
- edc_pdutils/utils/datetime_to_date.py +1 -2
- edc_pdutils/utils/refresh_model_from_dataframe.py +1 -3
- edc_pdutils/utils/table_names.py +2 -4
- edc_pdutils/utils/undash.py +1 -1
- edc_pharmacy/admin/medication/assignment_admin.py +2 -4
- edc_pharmacy/admin/medication/dosage_guideline_admin.py +2 -4
- edc_pharmacy/admin/medication/formulation_admin.py +3 -5
- edc_pharmacy/admin/medication/medication_admin.py +3 -5
- edc_pharmacy/admin/prescription/rx_refill_admin.py +7 -9
- edc_pharmacy/admin/stock/lot_admin.py +5 -7
- edc_pharmacy/admin/stock/order_admin.py +2 -2
- edc_pharmacy/admin/stock/order_item_admin.py +6 -6
- edc_pharmacy/admin/stock/receive_admin.py +1 -1
- edc_pharmacy/admin/stock/receive_item_admin.py +2 -2
- edc_pharmacy/admin/stock/stock_request_admin.py +4 -6
- edc_pharmacy/dosage_calculator.py +4 -4
- edc_pharmacy/form_validators/crf/study_medication_form_validator.py +8 -8
- edc_pharmacy/forms/stock/stock_request_form.py +2 -4
- edc_pharmacy/model_mixins/study_medication_crf_model_mixin.py +2 -2
- edc_pharmacy/models/medication/formulation.py +3 -4
- edc_pharmacy/models/medication/medication.py +1 -2
- edc_pharmacy/models/prescription/rx.py +2 -2
- edc_pharmacy/models/prescription/rx_refill.py +7 -9
- edc_pharmacy/models/reports/stock_availability.py +3 -4
- edc_pharmacy/models/stock/confirmation_at_site.py +1 -3
- edc_pharmacy/models/stock/order.py +1 -2
- edc_pharmacy/models/stock/receive.py +3 -4
- edc_pharmacy/models/stock/receive_item.py +2 -3
- edc_pharmacy/models/stock/stock.py +1 -2
- edc_pharmacy/models/stock/stock_adjustment.py +1 -2
- edc_pharmacy/models/stock/stock_request.py +4 -5
- edc_pharmacy/models/storage/box.py +1 -1
- edc_pharmacy/models/storage/items/container_model_mixin.py +1 -1
- edc_pharmacy/models/storage/room.py +1 -1
- edc_pharmacy/models/storage/shelf.py +1 -1
- edc_pharmacy/models/storage/utils.py +15 -16
- edc_pharmacy/prescribe/create_prescription.py +5 -5
- edc_pharmacy/refill/refill_creator.py +1 -1
- edc_pharmacy/sample_usb_printing/usb_printing.py +1 -1
- edc_pharmacy/utils/__init__.py +1 -1
- edc_pharmacy/utils/confirm_stock.py +3 -3
- edc_pharmacy/utils/confirm_stock_at_site.py +9 -8
- edc_pharmacy/utils/dispense.py +5 -8
- edc_pharmacy/utils/format_qty.py +3 -3
- edc_pharmacy/utils/get_codenames.py +1 -1
- edc_pharmacy/utils/miscellaneous.py +1 -1
- edc_pharmacy/utils/process_repack_request.py +18 -19
- edc_pharmacy/utils/process_repack_request_queryset.py +0 -2
- edc_pharmacy/utils/stock_request/bulk_create_stock_request_items.py +2 -3
- edc_pharmacy/views/add_to_storage_bin_view.py +1 -1
- edc_pharmacy/views/allocate_to_subject_view.py +2 -6
- edc_pharmacy/views/confirm_stock_from_instance_view.py +4 -4
- edc_pharmacy/views/confirm_stock_from_queryset_view.py +1 -5
- edc_pharmacy/views/confirmation_at_site_view.py +2 -3
- edc_pharmacy/views/dispense_view.py +1 -1
- edc_pharmacy/views/move_to_storage_bin_view.py +2 -3
- edc_pharmacy/views/print_labels_view.py +30 -31
- edc_pharmacy/views/transfer_stock_view.py +1 -1
- edc_prn/prn.py +1 -1
- edc_prn/site_prn_forms.py +1 -2
- edc_protocol_incident/action_items.py +2 -2
- edc_protocol_incident/model_mixins/protocol_deviation_violation_model_mixin.py +3 -3
- edc_protocol_incident/model_mixins/protocol_incident_model_mixin.py +3 -5
- edc_protocol_incident/modeladmin_mixins.py +3 -5
- edc_protocol_incident/models/protocol_deviation_violation.py +5 -5
- edc_protocol_incident/models/protocol_incident.py +5 -5
- edc_pylabels/site_label_configs.py +5 -5
- edc_qareports/model_mixins/qa_report_model_mixin.py +4 -2
- edc_qareports/models/qa_reports_log.py +1 -2
- edc_qareports/utils.py +1 -2
- edc_randomization/admin.py +3 -5
- edc_randomization/auth_objects.py +2 -3
- edc_randomization/blinding.py +1 -1
- edc_randomization/constants.py +2 -4
- edc_randomization/model_mixins.py +3 -3
- edc_randomization/randomization_list_importer.py +13 -14
- edc_randomization/randomization_list_verifier.py +11 -13
- edc_randomization/randomizer.py +2 -2
- edc_randomization/system_checks.py +1 -2
- edc_randomization/utils.py +5 -5
- edc_refusal/admin.py +1 -1
- edc_refusal/model_mixins.py +1 -1
- edc_refusal/utils.py +1 -1
- edc_registration/model_mixins/updates_or_creates_registered_subject_model_mixin.py +2 -2
- edc_registration/modeladmin_mixins.py +4 -7
- edc_registration/models/registered_subject.py +7 -9
- edc_registration/utils.py +3 -4
- edc_reportable/data/grading_data/daids_july_2017.py +3 -3
- edc_reportable/evaluator.py +5 -5
- edc_reportable/formula.py +1 -1
- edc_reportable/reference_range_evaluator.py +3 -3
- edc_reportable/utils/convert_units.py +10 -12
- edc_reportable/utils/grading_data_model_cls.py +2 -2
- edc_reportable/utils/grading_exception_model_cls.py +2 -2
- edc_reportable/utils/molecular_weight_model_cls.py +2 -2
- edc_reportable/utils/normal_data_model_cls.py +2 -2
- edc_reportable/utils/reference_range_colllection_model_cls.py +2 -2
- edc_screening/age_evaluator.py +2 -4
- edc_screening/eligibility.py +1 -1
- edc_screening/form_validator_mixins.py +2 -2
- edc_screening/model_mixins/screening_methods_model_mixin.py +1 -1
- edc_screening/screening_eligibility.py +1 -1
- edc_screening/utils.py +4 -6
- edc_search/model_mixins.py +1 -1
- edc_search/search_slug.py +1 -3
- edc_search/updater.py +1 -1
- edc_sites/admin/site_model_admin_mixin.py +2 -2
- edc_sites/model_mixins/site_model_mixin.py +1 -2
- edc_sites/modelform_mixins.py +2 -2
- edc_sites/models/__init__.py +1 -1
- edc_sites/models/site_profile.py +4 -4
- edc_sites/site.py +24 -32
- edc_sites/system_checks.py +1 -1
- edc_sites/utils/get_message_text.py +1 -1
- edc_sites/utils/valid_site_for_subject_or_raise.py +5 -6
- edc_subject_dashboard/requisition_report.py +4 -5
- edc_subject_dashboard/requisition_verifier.py +4 -2
- edc_subject_dashboard/templatetags/edc_subject_dashboard_extras.py +16 -17
- edc_subject_dashboard/view_utils/__init__.py +1 -1
- edc_subject_dashboard/view_utils/crf_button.py +1 -3
- edc_subject_dashboard/view_utils/subject_consent_dashboard_button.py +2 -2
- edc_subject_dashboard/view_utils/subject_consent_listboard_button.py +2 -2
- edc_subject_dashboard/view_utils/timepoint_status_button.py +1 -4
- edc_subject_dashboard/views/requisition_print_actions_view.py +2 -2
- edc_subject_dashboard/views/subject_dashboard_view.py +1 -1
- edc_timepoint/model_mixins.py +2 -2
- edc_transfer/action_items.py +1 -1
- edc_transfer/model_mixins.py +4 -3
- edc_transfer/modeladmin_mixins.py +3 -6
- edc_unblinding/action_items.py +2 -2
- edc_unblinding/models/unblinding_request.py +3 -3
- edc_unblinding/models/unblinding_review.py +3 -3
- edc_utils/__init__.py +6 -5
- edc_utils/age.py +1 -1
- edc_utils/celery.py +3 -8
- edc_utils/get_static_file.py +1 -1
- edc_utils/text.py +5 -6
- edc_view_utils/__init__.py +3 -3
- edc_view_utils/dashboard_model_button.py +4 -4
- edc_view_utils/model_button.py +7 -8
- edc_view_utils/perms.py +3 -3
- edc_visit_schedule/action_items.py +2 -2
- edc_visit_schedule/admin/list_filters.py +11 -9
- edc_visit_schedule/admin/subject_schedule_history_admin.py +13 -15
- edc_visit_schedule/admin/visit_schedule_admin.py +7 -7
- edc_visit_schedule/fieldsets.py +4 -6
- edc_visit_schedule/management/commands/find_invalid_onschedules.py +1 -1
- edc_visit_schedule/model_mixins/off_schedule_model_mixin.py +3 -1
- edc_visit_schedule/model_mixins/on_schedule_model_mixin.py +1 -1
- edc_visit_schedule/modelform_mixins/__init__.py +1 -1
- edc_visit_schedule/modelform_mixins/off_schedule_modelform_mixin.py +1 -2
- edc_visit_schedule/models/subject_schedule_history.py +2 -1
- edc_visit_schedule/models/visit_schedule.py +2 -2
- edc_visit_schedule/schedule/schedule.py +11 -12
- edc_visit_schedule/schedule/visit_collection.py +4 -3
- edc_visit_schedule/schedule/window.py +19 -12
- edc_visit_schedule/site_visit_schedules.py +9 -9
- edc_visit_schedule/subject_schedule.py +6 -7
- edc_visit_schedule/system_checks.py +1 -1
- edc_visit_schedule/view_mixins.py +1 -1
- edc_visit_schedule/visit/crf.py +3 -7
- edc_visit_schedule/visit/requisition.py +2 -2
- edc_visit_schedule/visit/visit.py +6 -7
- edc_visit_schedule/visit/window_period.py +8 -8
- edc_visit_schedule/visit_schedule/schedules_collection.py +2 -5
- edc_visit_tracking/action_items.py +11 -13
- edc_visit_tracking/form_validators/visit_form_validator.py +5 -5
- edc_visit_tracking/model_mixins/base/visit_methods_model_mixin.py +3 -4
- edc_visit_tracking/model_mixins/crfs/visit_tracking_crf_model_mixin.py +2 -2
- edc_visit_tracking/model_mixins/subject_visit_missed_model_mixin.py +2 -3
- edc_visit_tracking/model_mixins/utils.py +1 -1
- edc_visit_tracking/model_mixins/visit_model_mixin/previous_visit_model_mixin.py +2 -2
- edc_visit_tracking/model_mixins/visit_model_mixin/visit_model_mixin.py +3 -3
- edc_visit_tracking/modeladmin_mixins/crf_model_admin_mixin.py +6 -4
- edc_visit_tracking/modeladmin_mixins/visit_model_admin_mixin.py +6 -7
- edc_visit_tracking/modelform_mixins/crf/visit_tracking_crf_modelform_mixin.py +4 -5
- edc_visit_tracking/modelform_mixins/visit_tracking_modelform_mixin.py +3 -4
- edc_visit_tracking/models/subject_visit.py +1 -1
- edc_visit_tracking/models/subject_visit_missed.py +1 -1
- edc_visit_tracking/stubs.py +7 -7
- edc_visit_tracking/typing_stubs.py +11 -11
- edc_visit_tracking/utils.py +5 -5
- edc_visit_tracking/view_utils/related_visit_button.py +5 -5
- edc_vitals/calculators/bmi.py +1 -1
- edc_vitals/form_validators/blood_pressure_form_validator_mixin.py +8 -10
- edc_vitals/form_validators/bmi_form_validator_mixin.py +1 -1
- edc_vitals/form_validators/weight_height_with_bmi_form_validator_mixin.py +5 -2
- edc_vitals/model_mixins/blood_pressure_model_mixin.py +3 -4
- edc_vitals/model_mixins/weight_height_bmi_model_mixin.py +4 -4
- edc_vitals/utils.py +1 -1
- edc_vitals/validators.py +1 -1
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional, Union
|
|
2
|
-
|
|
3
1
|
from django.forms import ValidationError
|
|
4
2
|
|
|
5
3
|
from edc_constants.constants import NOT_APPLICABLE, OTHER
|
|
@@ -28,7 +26,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
def m2m_applicable_if_true(self, cond: bool, m2m_field: str = None) -> bool:
|
|
31
|
-
code:
|
|
29
|
+
code: str | None = None
|
|
32
30
|
message: dict = {}
|
|
33
31
|
qs = self.cleaned_data.get(m2m_field)
|
|
34
32
|
if cond:
|
|
@@ -37,12 +35,11 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
37
35
|
if NOT_APPLICABLE in selected:
|
|
38
36
|
message = {m2m_field: "This field is applicable"}
|
|
39
37
|
code = APPLICABLE_ERROR
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
code = NOT_APPLICABLE_ERROR
|
|
38
|
+
elif qs and qs.count() > 0:
|
|
39
|
+
selected = self.get_m2m_selected(m2m_field)
|
|
40
|
+
if NOT_APPLICABLE not in selected:
|
|
41
|
+
message = {m2m_field: "This field is not applicable"}
|
|
42
|
+
code = NOT_APPLICABLE_ERROR
|
|
46
43
|
if message:
|
|
47
44
|
self._errors.update(message)
|
|
48
45
|
self._error_codes.append(code)
|
|
@@ -51,7 +48,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
51
48
|
|
|
52
49
|
def m2m_applicable_if(
|
|
53
50
|
self,
|
|
54
|
-
*responses:
|
|
51
|
+
*responses: str | int | bool,
|
|
55
52
|
field: str = None,
|
|
56
53
|
m2m_field: str = None,
|
|
57
54
|
) -> bool:
|
|
@@ -59,7 +56,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
59
56
|
|
|
60
57
|
m2m_field is applicable if field is in responses.
|
|
61
58
|
"""
|
|
62
|
-
code:
|
|
59
|
+
code: str | None = None
|
|
63
60
|
message: dict = {}
|
|
64
61
|
if self.cleaned_data.get(field):
|
|
65
62
|
qs = self.cleaned_data.get(m2m_field)
|
|
@@ -70,13 +67,12 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
70
67
|
if NOT_APPLICABLE not in selected:
|
|
71
68
|
message = {m2m_field: "This field is not applicable"}
|
|
72
69
|
code = NOT_APPLICABLE_ERROR
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
code = APPLICABLE_ERROR
|
|
70
|
+
# m2m should != NOT_APPLICABLE
|
|
71
|
+
elif qs and qs.count() > 0:
|
|
72
|
+
selected = self.get_m2m_selected(m2m_field)
|
|
73
|
+
if NOT_APPLICABLE in selected:
|
|
74
|
+
message = {m2m_field: "This field is applicable"}
|
|
75
|
+
code = APPLICABLE_ERROR
|
|
80
76
|
if message:
|
|
81
77
|
self._errors.update(message)
|
|
82
78
|
self._error_codes.append(code)
|
|
@@ -88,7 +84,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
88
84
|
|
|
89
85
|
m2m_field is required.
|
|
90
86
|
"""
|
|
91
|
-
code:
|
|
87
|
+
code: str | None = None
|
|
92
88
|
message: dict = {}
|
|
93
89
|
if not self.cleaned_data.get(m2m_field):
|
|
94
90
|
message = {m2m_field: "This field is required"}
|
|
@@ -104,7 +100,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
104
100
|
|
|
105
101
|
m2m_field is not required.
|
|
106
102
|
"""
|
|
107
|
-
code:
|
|
103
|
+
code: str | None = None
|
|
108
104
|
message: dict = {}
|
|
109
105
|
if self.cleaned_data.get(m2m_field):
|
|
110
106
|
message = {m2m_field: "This field is not required"}
|
|
@@ -116,18 +112,17 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
116
112
|
return False
|
|
117
113
|
|
|
118
114
|
def m2m_required_if(
|
|
119
|
-
self, response:
|
|
115
|
+
self, response: str | int | bool, field: str = None, m2m_field: str = None
|
|
120
116
|
) -> bool:
|
|
121
117
|
"""Raises an exception or returns False.
|
|
122
118
|
|
|
123
119
|
m2m_field is required if field == response
|
|
124
120
|
"""
|
|
125
|
-
code:
|
|
121
|
+
code: str | None = None
|
|
126
122
|
message: dict = {}
|
|
127
|
-
if
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
elif (
|
|
123
|
+
if (
|
|
124
|
+
self.cleaned_data.get(field) == response and not self.cleaned_data.get(m2m_field)
|
|
125
|
+
) or (
|
|
131
126
|
self.cleaned_data.get(field) == response
|
|
132
127
|
and self.cleaned_data.get(m2m_field).count() == 0
|
|
133
128
|
):
|
|
@@ -169,10 +164,10 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
169
164
|
|
|
170
165
|
def required_if_m2m(
|
|
171
166
|
self,
|
|
172
|
-
*responses:
|
|
167
|
+
*responses: str | int | bool,
|
|
173
168
|
field: str = None,
|
|
174
|
-
field_required:
|
|
175
|
-
field_other_evaluate_as_int:
|
|
169
|
+
field_required: str | None = None,
|
|
170
|
+
field_other_evaluate_as_int: bool | None = None,
|
|
176
171
|
) -> bool:
|
|
177
172
|
"""Same as required_if() but where field is an m2m field."""
|
|
178
173
|
return self.m2m_other_specify(
|
|
@@ -184,10 +179,10 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
184
179
|
|
|
185
180
|
def m2m_other_specify(
|
|
186
181
|
self,
|
|
187
|
-
*responses:
|
|
182
|
+
*responses: str | int | bool,
|
|
188
183
|
m2m_field: str = None,
|
|
189
|
-
field_other:
|
|
190
|
-
field_other_evaluate_as_int:
|
|
184
|
+
field_other: str | None = None,
|
|
185
|
+
field_other_evaluate_as_int: bool | None = None,
|
|
191
186
|
) -> bool:
|
|
192
187
|
"""Raises an exception or returns False.
|
|
193
188
|
|
|
@@ -213,7 +208,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
213
208
|
self._errors.update(message)
|
|
214
209
|
self._error_codes.append(REQUIRED_ERROR)
|
|
215
210
|
raise ValidationError(message, code=REQUIRED_ERROR)
|
|
216
|
-
|
|
211
|
+
if not found and field_other_has_value:
|
|
217
212
|
message = {field_other: "This field is not required."}
|
|
218
213
|
self._errors.update(message)
|
|
219
214
|
self._error_codes.append(NOT_REQUIRED_ERROR)
|
|
@@ -227,9 +222,9 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
227
222
|
|
|
228
223
|
def m2m_other_not_specify(
|
|
229
224
|
self,
|
|
230
|
-
*responses:
|
|
225
|
+
*responses: str | int | bool,
|
|
231
226
|
m2m_field: str = None,
|
|
232
|
-
field_other:
|
|
227
|
+
field_other: str | None = None,
|
|
233
228
|
) -> bool:
|
|
234
229
|
"""Raises an exception or returns False.
|
|
235
230
|
|
|
@@ -249,7 +244,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
249
244
|
self._errors.update(message)
|
|
250
245
|
self._error_codes.append(NOT_REQUIRED_ERROR)
|
|
251
246
|
raise ValidationError(message, code=NOT_REQUIRED_ERROR)
|
|
252
|
-
|
|
247
|
+
if not found and not self.cleaned_data.get(field_other):
|
|
253
248
|
message = {field_other: "This field is required."}
|
|
254
249
|
self._errors.update(message)
|
|
255
250
|
self._error_codes.append(REQUIRED_ERROR)
|
|
@@ -263,9 +258,9 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
263
258
|
|
|
264
259
|
def m2m_other_specify_applicable(
|
|
265
260
|
self,
|
|
266
|
-
*responses:
|
|
261
|
+
*responses: str | int | bool,
|
|
267
262
|
m2m_field: str = None,
|
|
268
|
-
field_other:
|
|
263
|
+
field_other: str | None = None,
|
|
269
264
|
) -> bool:
|
|
270
265
|
"""Raises an exception or returns False.
|
|
271
266
|
|
|
@@ -285,7 +280,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
285
280
|
self._errors.update(message)
|
|
286
281
|
self._error_codes.append(APPLICABLE_ERROR)
|
|
287
282
|
raise ValidationError(message, code=APPLICABLE_ERROR)
|
|
288
|
-
|
|
283
|
+
if not found and self.cleaned_data.get(field_other) != NOT_APPLICABLE:
|
|
289
284
|
message = {field_other: "This field is not applicable."}
|
|
290
285
|
self._errors.update(message)
|
|
291
286
|
self._error_codes.append(NOT_APPLICABLE_ERROR)
|
|
@@ -299,7 +294,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
299
294
|
|
|
300
295
|
def m2m_selection_expected(
|
|
301
296
|
self,
|
|
302
|
-
response:
|
|
297
|
+
response: str | int | bool,
|
|
303
298
|
m2m_field: str = None,
|
|
304
299
|
error_msg: str = None,
|
|
305
300
|
) -> bool:
|
|
@@ -329,9 +324,9 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
329
324
|
|
|
330
325
|
def m2m_selections_not_expected(
|
|
331
326
|
self,
|
|
332
|
-
*responses:
|
|
327
|
+
*responses: str | int | bool,
|
|
333
328
|
m2m_field: str = None,
|
|
334
|
-
error_msg:
|
|
329
|
+
error_msg: str | None = None,
|
|
335
330
|
) -> bool:
|
|
336
331
|
"""Raises an exception or returns False.
|
|
337
332
|
|
|
@@ -354,7 +349,7 @@ class ManyToManyFieldValidator(BaseFormValidator):
|
|
|
354
349
|
)
|
|
355
350
|
message = {
|
|
356
351
|
m2m_field: error_msg
|
|
357
|
-
or f"Invalid selection.
|
|
352
|
+
or f"Invalid selection. Cannot be any of: {display_names}."
|
|
358
353
|
}
|
|
359
354
|
self._errors.update(message)
|
|
360
355
|
self._error_codes.append(M2M_INVALID_SELECTION)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
from django.forms import ValidationError
|
|
4
4
|
|
|
@@ -15,12 +15,12 @@ class OtherSpecifyFieldValidator(BaseFormValidator):
|
|
|
15
15
|
def validate_other_specify(
|
|
16
16
|
self,
|
|
17
17
|
field: str,
|
|
18
|
-
other_specify_field:
|
|
19
|
-
required_msg:
|
|
20
|
-
not_required_msg:
|
|
21
|
-
other_stored_value:
|
|
22
|
-
ref:
|
|
23
|
-
fk_stored_field_name:
|
|
18
|
+
other_specify_field: str | None = None,
|
|
19
|
+
required_msg: str | None = None,
|
|
20
|
+
not_required_msg: str | None = None,
|
|
21
|
+
other_stored_value: Any | None = None,
|
|
22
|
+
ref: str | None = None,
|
|
23
|
+
fk_stored_field_name: str | None = None,
|
|
24
24
|
) -> bool:
|
|
25
25
|
"""Returns False or raises a ValidationError.
|
|
26
26
|
|
|
@@ -51,19 +51,11 @@ class OtherSpecifyFieldValidator(BaseFormValidator):
|
|
|
51
51
|
self._errors.update(message)
|
|
52
52
|
self._error_codes.append(REQUIRED_ERROR)
|
|
53
53
|
raise ValidationError(message, code=REQUIRED_ERROR)
|
|
54
|
-
|
|
54
|
+
if (
|
|
55
55
|
field_value is not None
|
|
56
56
|
and field_value != other
|
|
57
57
|
and cleaned_data.get(other_specify_field)
|
|
58
|
-
):
|
|
59
|
-
ref = "" if not ref else f" ref: {ref}"
|
|
60
|
-
message = {
|
|
61
|
-
other_specify_field: not_required_msg or f"This field is not required.{ref}"
|
|
62
|
-
}
|
|
63
|
-
self._errors.update(message)
|
|
64
|
-
self._error_codes.append(NOT_REQUIRED_ERROR)
|
|
65
|
-
raise ValidationError(message, code=NOT_REQUIRED_ERROR)
|
|
66
|
-
elif field_value is None and cleaned_data.get(other_specify_field):
|
|
58
|
+
) or (field_value is None and cleaned_data.get(other_specify_field)):
|
|
67
59
|
ref = "" if not ref else f" ref: {ref}"
|
|
68
60
|
message = {
|
|
69
61
|
other_specify_field: not_required_msg or f"This field is not required.{ref}"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from copy import copy
|
|
2
|
-
from typing import Optional, Union
|
|
3
2
|
|
|
4
3
|
from django.db.models import QuerySet
|
|
5
4
|
from django.utils.translation import gettext_lazy as _
|
|
@@ -20,7 +19,7 @@ class RequiredFieldValidatorError(Exception):
|
|
|
20
19
|
|
|
21
20
|
class RequiredFieldValidator(BaseFormValidator):
|
|
22
21
|
def raise_required(
|
|
23
|
-
self, field: str, msg:
|
|
22
|
+
self, field: str, msg: str | None = None, inline_set: str | None = None
|
|
24
23
|
) -> None:
|
|
25
24
|
if inline_set:
|
|
26
25
|
default_errmsg = _("Based on your responses, inline information is required.")
|
|
@@ -29,10 +28,9 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
29
28
|
errmsg = _("This field is required")
|
|
30
29
|
message = {field: f"{errmsg}. {msg or ''}".strip()}
|
|
31
30
|
self.raise_validation_error(message, REQUIRED_ERROR)
|
|
32
|
-
return None
|
|
33
31
|
|
|
34
32
|
def raise_not_required(
|
|
35
|
-
self, field: str, msg:
|
|
33
|
+
self, field: str, msg: str | None = None, inline_set: str | None = None
|
|
36
34
|
) -> None:
|
|
37
35
|
if inline_set:
|
|
38
36
|
default_errmsg = _("Based on your responses, inline information is not required.")
|
|
@@ -41,20 +39,19 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
41
39
|
errmsg = _("This field is not required")
|
|
42
40
|
message = {field: f"{errmsg}. {msg or ''}".strip()}
|
|
43
41
|
self.raise_validation_error(message, NOT_REQUIRED_ERROR)
|
|
44
|
-
return None
|
|
45
42
|
|
|
46
43
|
def required_if(
|
|
47
44
|
self,
|
|
48
|
-
*responses:
|
|
45
|
+
*responses: str | int | bool,
|
|
49
46
|
field: str = None,
|
|
50
47
|
field_required: str = None,
|
|
51
|
-
required_msg:
|
|
52
|
-
not_required_msg:
|
|
53
|
-
optional_if_dwta:
|
|
54
|
-
optional_if_na:
|
|
55
|
-
inverse:
|
|
56
|
-
is_instance_field:
|
|
57
|
-
field_required_evaluate_as_int:
|
|
48
|
+
required_msg: str | None = None,
|
|
49
|
+
not_required_msg: str | None = None,
|
|
50
|
+
optional_if_dwta: bool | None = None,
|
|
51
|
+
optional_if_na: bool | None = None,
|
|
52
|
+
inverse: bool | None = None,
|
|
53
|
+
is_instance_field: bool | None = None,
|
|
54
|
+
field_required_evaluate_as_int: bool | None = None,
|
|
58
55
|
fk_stored_field_name=None,
|
|
59
56
|
field_required_inline_set=None,
|
|
60
57
|
) -> bool:
|
|
@@ -88,9 +85,7 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
88
85
|
field_required_has_value = self.get(field_required, inline_set=inline_set)
|
|
89
86
|
|
|
90
87
|
if field in self.cleaned_data:
|
|
91
|
-
if DWTA in responses and optional_if_dwta and field_value == DWTA
|
|
92
|
-
pass
|
|
93
|
-
elif (
|
|
88
|
+
if (DWTA in responses and optional_if_dwta and field_value == DWTA) or (
|
|
94
89
|
NOT_APPLICABLE in responses
|
|
95
90
|
and optional_if_na
|
|
96
91
|
and field_value == NOT_APPLICABLE
|
|
@@ -123,9 +118,9 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
123
118
|
self,
|
|
124
119
|
condition: bool,
|
|
125
120
|
field_required: str = None,
|
|
126
|
-
required_msg:
|
|
127
|
-
not_required_msg:
|
|
128
|
-
inverse:
|
|
121
|
+
required_msg: str | None = None,
|
|
122
|
+
not_required_msg: str | None = None,
|
|
123
|
+
inverse: bool | None = None,
|
|
129
124
|
) -> bool:
|
|
130
125
|
inverse = True if inverse is None else inverse
|
|
131
126
|
if not field_required:
|
|
@@ -151,8 +146,8 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
151
146
|
self,
|
|
152
147
|
condition: bool,
|
|
153
148
|
field: str = None,
|
|
154
|
-
msg:
|
|
155
|
-
is_instance_field:
|
|
149
|
+
msg: str | None = None,
|
|
150
|
+
is_instance_field: bool | None = None,
|
|
156
151
|
) -> bool:
|
|
157
152
|
"""Raises a ValidationError if condition is True stating the
|
|
158
153
|
field is NOT required.
|
|
@@ -177,12 +172,12 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
177
172
|
self,
|
|
178
173
|
field: str = None,
|
|
179
174
|
field_required: str = None,
|
|
180
|
-
required_msg:
|
|
181
|
-
not_required_msg:
|
|
182
|
-
optional_if_dwta:
|
|
183
|
-
inverse:
|
|
184
|
-
field_required_evaluate_as_int:
|
|
185
|
-
is_instance_field:
|
|
175
|
+
required_msg: str | None = None,
|
|
176
|
+
not_required_msg: str | None = None,
|
|
177
|
+
optional_if_dwta: bool | None = None,
|
|
178
|
+
inverse: bool | None = None,
|
|
179
|
+
field_required_evaluate_as_int: bool | None = None,
|
|
180
|
+
is_instance_field: bool | None = None,
|
|
186
181
|
) -> bool:
|
|
187
182
|
"""Raises an exception or returns False.
|
|
188
183
|
|
|
@@ -225,15 +220,15 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
225
220
|
|
|
226
221
|
def not_required_if(
|
|
227
222
|
self,
|
|
228
|
-
*responses:
|
|
223
|
+
*responses: str | int | bool,
|
|
229
224
|
field: str = None,
|
|
230
225
|
field_required: str = None,
|
|
231
|
-
field_not_required:
|
|
232
|
-
required_msg:
|
|
233
|
-
not_required_msg:
|
|
234
|
-
optional_if_dwta:
|
|
235
|
-
inverse:
|
|
236
|
-
is_instance_field:
|
|
226
|
+
field_not_required: str | None = None,
|
|
227
|
+
required_msg: str | None = None,
|
|
228
|
+
not_required_msg: str | None = None,
|
|
229
|
+
optional_if_dwta: bool | None = None,
|
|
230
|
+
inverse: bool | None = None,
|
|
231
|
+
is_instance_field: bool | None = None,
|
|
237
232
|
) -> bool:
|
|
238
233
|
"""Raises an exception or returns False.
|
|
239
234
|
|
|
@@ -266,8 +261,8 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
266
261
|
self,
|
|
267
262
|
field: str = None,
|
|
268
263
|
field_required: str = None,
|
|
269
|
-
required_msg:
|
|
270
|
-
is_instance_field:
|
|
264
|
+
required_msg: str | None = None,
|
|
265
|
+
is_instance_field: bool | None = None,
|
|
271
266
|
) -> bool:
|
|
272
267
|
"""Required `b` if `a`; do not require `b` if not `a`"""
|
|
273
268
|
if is_instance_field:
|
|
@@ -286,16 +281,16 @@ class RequiredFieldValidator(BaseFormValidator):
|
|
|
286
281
|
|
|
287
282
|
@staticmethod
|
|
288
283
|
def _inspect_params(
|
|
289
|
-
*responses:
|
|
284
|
+
*responses: str | int | bool, field: str = None, field_required: str = None
|
|
290
285
|
) -> bool:
|
|
291
286
|
"""Inspects params and raises if any are None"""
|
|
292
287
|
if not field:
|
|
293
288
|
errmsg = _("`field` cannot be `None`")
|
|
294
289
|
raise InvalidModelFormFieldValidator(f"{errmsg}.")
|
|
295
|
-
|
|
290
|
+
if not responses:
|
|
296
291
|
errmsg = _(f"At least one valid response for field '{field}' must be provided.")
|
|
297
292
|
raise InvalidModelFormFieldValidator(errmsg)
|
|
298
|
-
|
|
293
|
+
if not field_required:
|
|
299
294
|
raise InvalidModelFormFieldValidator('"field_required" cannot be None.')
|
|
300
295
|
return False
|
|
301
296
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from django.forms import ValidationError
|
|
6
6
|
from django.test import TestCase
|
|
@@ -11,16 +11,16 @@ if TYPE_CHECKING:
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class FormValidatorTestCaseMixin:
|
|
14
|
-
form_validator_cls:
|
|
15
|
-
form_validator_model_cls:
|
|
14
|
+
form_validator_cls: type[FormValidator] = None
|
|
15
|
+
form_validator_model_cls: type[BaseUuidModel] = None
|
|
16
16
|
|
|
17
17
|
def validate_form_validator(
|
|
18
18
|
self: FormValidatorTestCaseMixin,
|
|
19
19
|
cleaned_data: dict,
|
|
20
20
|
*,
|
|
21
21
|
instance: BaseUuidModel | None = None,
|
|
22
|
-
model_cls:
|
|
23
|
-
form_validator_cls:
|
|
22
|
+
model_cls: type[BaseUuidModel] | None = None,
|
|
23
|
+
form_validator_cls: type[FormValidator] | None = None,
|
|
24
24
|
) -> FormValidator:
|
|
25
25
|
form_validator = (form_validator_cls or self.form_validator_cls)(
|
|
26
26
|
cleaned_data=cleaned_data,
|
edc_identifier/admin.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
1
|
from django.contrib import admin
|
|
4
2
|
from django_audit_fields import audit_fieldset_tuple
|
|
5
3
|
|
|
@@ -53,7 +51,7 @@ class IdentifierModelAdmin(admin.ModelAdmin):
|
|
|
53
51
|
)
|
|
54
52
|
search_fields = ("identifier", "subject_identifier", "linked_identifier")
|
|
55
53
|
|
|
56
|
-
def get_readonly_fields(self, request, obj=None) ->
|
|
54
|
+
def get_readonly_fields(self, request, obj=None) -> tuple[str, ...]:
|
|
57
55
|
return (
|
|
58
56
|
"identifier",
|
|
59
57
|
"protocol_number",
|
edc_identifier/identifier.py
CHANGED
|
@@ -114,9 +114,8 @@ class Identifier:
|
|
|
114
114
|
"""
|
|
115
115
|
if not identifier:
|
|
116
116
|
return identifier
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
return "".join(self.identifier_as_list)
|
|
117
|
+
self.identifier_as_list = identifier.split(self.separator)
|
|
118
|
+
return "".join(self.identifier_as_list)
|
|
120
119
|
|
|
121
120
|
def insert_separator(self, identifier):
|
|
122
121
|
"""Returns the identifier by reinserting the separator."""
|
edc_identifier/model_mixins.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import re
|
|
2
|
-
from typing import Union
|
|
3
2
|
from uuid import uuid4
|
|
4
3
|
|
|
5
4
|
from django.apps import apps as django_apps
|
|
@@ -21,7 +20,7 @@ class NonUniqueSubjectIdentifierFieldMixin(models.Model):
|
|
|
21
20
|
|
|
22
21
|
class Meta:
|
|
23
22
|
abstract = True
|
|
24
|
-
indexes =
|
|
23
|
+
indexes = (models.Index(fields=["subject_identifier"]),)
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
class UniqueSubjectIdentifierFieldMixin(models.Model):
|
|
@@ -66,11 +65,9 @@ class SubjectIdentifierMethodsModelMixin(models.Model):
|
|
|
66
65
|
self.subject_identifier = self.update_subject_identifier_on_save()
|
|
67
66
|
super().save(*args, **kwargs)
|
|
68
67
|
|
|
69
|
-
def update_subject_identifier_on_save(self) ->
|
|
68
|
+
def update_subject_identifier_on_save(self) -> str | models.CharField:
|
|
70
69
|
"""Returns a subject_identifier if not already set."""
|
|
71
|
-
if not self.subject_identifier:
|
|
72
|
-
self.subject_identifier = self.get_or_create_identifier()
|
|
73
|
-
elif re.match(UUID_PATTERN, self.subject_identifier):
|
|
70
|
+
if not self.subject_identifier or re.match(UUID_PATTERN, self.subject_identifier):
|
|
74
71
|
self.subject_identifier = self.get_or_create_identifier()
|
|
75
72
|
return self.subject_identifier
|
|
76
73
|
|
|
@@ -115,7 +112,7 @@ class SubjectIdentifierMethodsModelMixin(models.Model):
|
|
|
115
112
|
except MultipleObjectsReturned as e:
|
|
116
113
|
raise IdentifierError(
|
|
117
114
|
"Cannot lookup a unique RegisteredSubject instance. "
|
|
118
|
-
"Identity {} is not unique. Got {}"
|
|
115
|
+
f"Identity {self.identity} is not unique. Got {e}"
|
|
119
116
|
)
|
|
120
117
|
return obj
|
|
121
118
|
|
|
@@ -139,7 +136,7 @@ class UniqueSubjectIdentifierModelMixin(
|
|
|
139
136
|
|
|
140
137
|
def get_subject_identifier_as_pk(self):
|
|
141
138
|
"""Returns the subject_identifier_as_pk"""
|
|
142
|
-
return self.subject_identifier_as_pk
|
|
139
|
+
return self.subject_identifier_as_pk
|
|
143
140
|
|
|
144
141
|
class Meta:
|
|
145
142
|
abstract = True
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from string import Formatter
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from django.apps import apps as django_apps
|
|
7
7
|
|
|
@@ -22,22 +22,20 @@ class IdentifierMissingTemplateValue(Exception):
|
|
|
22
22
|
|
|
23
23
|
class ResearchIdentifier:
|
|
24
24
|
label: str = None # e.g. subject_identifier, plot_identifier, etc
|
|
25
|
-
identifier_type:
|
|
26
|
-
|
|
27
|
-
)
|
|
28
|
-
template: Optional[str] = None
|
|
25
|
+
identifier_type: str | None = None # e.g. 'subject', 'infant', 'plot', a.k.a subject_type
|
|
26
|
+
template: str | None = None
|
|
29
27
|
padding: int = 5
|
|
30
28
|
checkdigit = LuhnMixin()
|
|
31
29
|
|
|
32
30
|
def __init__(
|
|
33
31
|
self,
|
|
34
|
-
identifier_type:
|
|
35
|
-
template:
|
|
36
|
-
device_id:
|
|
37
|
-
protocol_number:
|
|
38
|
-
site:
|
|
39
|
-
requesting_model:
|
|
40
|
-
identifier:
|
|
32
|
+
identifier_type: str | None = None,
|
|
33
|
+
template: str | None = None,
|
|
34
|
+
device_id: str | None = None,
|
|
35
|
+
protocol_number: str | None = None,
|
|
36
|
+
site: Site | None = None,
|
|
37
|
+
requesting_model: str | None = None,
|
|
38
|
+
identifier: str | None = None,
|
|
41
39
|
) -> None:
|
|
42
40
|
self._identifier = None
|
|
43
41
|
self.requesting_model = requesting_model
|
|
@@ -75,7 +75,7 @@ class ShortIdentifier:
|
|
|
75
75
|
raise ShortIdentifierPrefixError(
|
|
76
76
|
"Prefix does not match prefix pattern. Got prefix=None."
|
|
77
77
|
)
|
|
78
|
-
|
|
78
|
+
if self.prefix and not self.prefix_pattern.match(self.prefix):
|
|
79
79
|
raise ShortIdentifierPrefixError(
|
|
80
80
|
"Prefix does not match prefix pattern. "
|
|
81
81
|
f"Got '{self.prefix}' does not match "
|