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,5 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from typing import Any
|
|
2
|
+
from typing import Any
|
|
3
3
|
from warnings import warn
|
|
4
4
|
|
|
5
5
|
from django.apps import apps as django_apps
|
|
@@ -36,13 +36,13 @@ class GroupUpdater:
|
|
|
36
36
|
|
|
37
37
|
def __init__(
|
|
38
38
|
self,
|
|
39
|
-
groups:
|
|
39
|
+
groups: dict | None = None,
|
|
40
40
|
apps=None,
|
|
41
41
|
verbose=None,
|
|
42
|
-
pii_models:
|
|
43
|
-
custom_permissions_tuples:
|
|
42
|
+
pii_models: list | None = None,
|
|
43
|
+
custom_permissions_tuples: dict | None = None,
|
|
44
44
|
warn_only=None,
|
|
45
|
-
codename_prefixes:
|
|
45
|
+
codename_prefixes: list | None = None,
|
|
46
46
|
):
|
|
47
47
|
self.apps = apps or django_apps
|
|
48
48
|
self.content_type_model_cls = self.apps.get_model("contenttypes.contenttype")
|
|
@@ -102,7 +102,7 @@ class GroupUpdater:
|
|
|
102
102
|
group.permissions.add(permission)
|
|
103
103
|
|
|
104
104
|
def get_permissions_qs_from_codenames(
|
|
105
|
-
self, codenames:
|
|
105
|
+
self, codenames: list[Any], allow_multiple_objects: bool | None = None
|
|
106
106
|
):
|
|
107
107
|
"""Returns a list of permission model instances for the given
|
|
108
108
|
codenames.
|
|
@@ -281,8 +281,8 @@ class GroupUpdater:
|
|
|
281
281
|
def remove_permissions_by_codenames(
|
|
282
282
|
self,
|
|
283
283
|
group: Any = None,
|
|
284
|
-
codenames:
|
|
285
|
-
allow_multiple_objects:
|
|
284
|
+
codenames: list[str] = None,
|
|
285
|
+
allow_multiple_objects: bool | None = None,
|
|
286
286
|
):
|
|
287
287
|
"""Remove the given codenames from the given group."""
|
|
288
288
|
permissions = self.get_permissions_qs_from_codenames(
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
from typing import Optional
|
|
3
2
|
|
|
4
3
|
from django.apps import apps as django_apps
|
|
5
4
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -15,7 +14,7 @@ class RoleUpdaterError(Exception):
|
|
|
15
14
|
class RoleUpdater:
|
|
16
15
|
def __init__(
|
|
17
16
|
self,
|
|
18
|
-
roles:
|
|
17
|
+
roles: dict | None = None,
|
|
19
18
|
verbose=None,
|
|
20
19
|
):
|
|
21
20
|
self.roles = roles
|
edc_auth/get_app_codenames.py
CHANGED
|
@@ -70,9 +70,7 @@ def get_codename(
|
|
|
70
70
|
autocomplete_models = autocomplete_models or []
|
|
71
71
|
label_lower: str = model_cls._meta.label_lower
|
|
72
72
|
model_name: str = model_cls._meta.model_name
|
|
73
|
-
if "historical" in label_lower:
|
|
74
|
-
codenames.append(f"{app_name}.view_{model_name}")
|
|
75
|
-
elif label_lower in autocomplete_models:
|
|
73
|
+
if "historical" in label_lower or label_lower in autocomplete_models:
|
|
76
74
|
codenames.append(f"{app_name}.view_{model_name}")
|
|
77
75
|
else:
|
|
78
76
|
permissions = override_permissions or model_cls._meta.default_permissions
|
edc_auth/import_users.py
CHANGED
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
import re
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from string import Template
|
|
6
|
-
from typing import Any
|
|
6
|
+
from typing import Any
|
|
7
7
|
|
|
8
8
|
from django.contrib.auth.models import User
|
|
9
9
|
from django.contrib.sites.models import Site
|
|
@@ -67,12 +67,12 @@ fieldnames = [
|
|
|
67
67
|
|
|
68
68
|
def import_users(
|
|
69
69
|
path: str,
|
|
70
|
-
resource_name:
|
|
71
|
-
send_email_to_user:
|
|
72
|
-
resend_as_newly_created:
|
|
73
|
-
verbose:
|
|
74
|
-
export_to_file:
|
|
75
|
-
limit_to_username:
|
|
70
|
+
resource_name: str | None = None,
|
|
71
|
+
send_email_to_user: Any | None = None,
|
|
72
|
+
resend_as_newly_created: Any | None = None,
|
|
73
|
+
verbose: Any | None = None,
|
|
74
|
+
export_to_file: Any | None = None,
|
|
75
|
+
limit_to_username: str | None = None,
|
|
76
76
|
**kwargs,
|
|
77
77
|
):
|
|
78
78
|
"""Import users from a CSV file with columns:
|
|
@@ -140,20 +140,20 @@ class UserImporter:
|
|
|
140
140
|
email: str = None,
|
|
141
141
|
alternate_email: str = None,
|
|
142
142
|
mobile: str = None,
|
|
143
|
-
site_names:
|
|
144
|
-
role_names:
|
|
143
|
+
site_names: list[str] = None,
|
|
144
|
+
role_names: list[str] = None,
|
|
145
145
|
is_active: bool = None,
|
|
146
146
|
is_staff: bool = None,
|
|
147
147
|
resource_name: str = None,
|
|
148
|
-
created_email_template:
|
|
149
|
-
updated_email_template:
|
|
150
|
-
send_email_to_user:
|
|
151
|
-
test_email_address:
|
|
152
|
-
resend_as_new:
|
|
153
|
-
nwords:
|
|
148
|
+
created_email_template: Template | None = None,
|
|
149
|
+
updated_email_template: Template | None = None,
|
|
150
|
+
send_email_to_user: Any | None = None,
|
|
151
|
+
test_email_address: Any | None = None,
|
|
152
|
+
resend_as_new: Any | None = None,
|
|
153
|
+
nwords: int | None = None,
|
|
154
154
|
**kwargs,
|
|
155
155
|
):
|
|
156
|
-
self._messages:
|
|
156
|
+
self._messages: list[str] = []
|
|
157
157
|
self._user = None
|
|
158
158
|
self.created = False
|
|
159
159
|
self.password = None
|
|
@@ -236,7 +236,7 @@ class UserImporter:
|
|
|
236
236
|
self._user.save()
|
|
237
237
|
return self._user
|
|
238
238
|
|
|
239
|
-
def update_user_sites(self, site_names:
|
|
239
|
+
def update_user_sites(self, site_names: list[str]) -> None:
|
|
240
240
|
self.user.userprofile.sites.clear()
|
|
241
241
|
for site_name in site_names:
|
|
242
242
|
try:
|
|
@@ -250,7 +250,7 @@ class UserImporter:
|
|
|
250
250
|
else:
|
|
251
251
|
self.user.userprofile.sites.add(site)
|
|
252
252
|
|
|
253
|
-
def update_user_roles(self, role_names:
|
|
253
|
+
def update_user_roles(self, role_names: list[str]) -> None:
|
|
254
254
|
self.user.userprofile.roles.clear()
|
|
255
255
|
role_names.extend(required_role_names)
|
|
256
256
|
for role_name in role_names:
|
|
@@ -277,7 +277,7 @@ class UserImporter:
|
|
|
277
277
|
to=(self.test_email_address or self.user.email,),
|
|
278
278
|
)
|
|
279
279
|
|
|
280
|
-
def get_username(self) ->
|
|
280
|
+
def get_username(self) -> str | None:
|
|
281
281
|
if self.first_name and self.last_name:
|
|
282
282
|
last_name = "".join(self.last_name.split(" ")).lower()
|
|
283
283
|
return f"{self.first_name.lower()[0]}{last_name}"
|
edc_auth/models/role.py
CHANGED
|
@@ -50,6 +50,7 @@ class Role(BaseUuidModel):
|
|
|
50
50
|
return (self.name,)
|
|
51
51
|
|
|
52
52
|
class Meta(BaseUuidModel.Meta):
|
|
53
|
-
indexes =
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
indexes = (
|
|
54
|
+
*BaseUuidModel.Meta.indexes,
|
|
55
|
+
models.Index(fields=["display_name", "display_index"]),
|
|
56
|
+
)
|
edc_auth/password_setter.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from string import Template
|
|
2
|
-
from typing import List, Optional
|
|
3
2
|
|
|
4
3
|
from django.contrib.auth.models import User
|
|
5
4
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -26,8 +25,8 @@ class PasswordSetter:
|
|
|
26
25
|
def __init__(
|
|
27
26
|
self,
|
|
28
27
|
super_username: str,
|
|
29
|
-
alternate_email:
|
|
30
|
-
nwords:
|
|
28
|
+
alternate_email: str | None = None,
|
|
29
|
+
nwords: int | None = None,
|
|
31
30
|
**kwargs,
|
|
32
31
|
) -> None:
|
|
33
32
|
self.administrator_fullname = self.get_administrator_fullname(super_username)
|
|
@@ -48,7 +47,7 @@ class PasswordSetter:
|
|
|
48
47
|
users = User.objects.filter(is_active=True, is_staff=True, is_superuser=False)
|
|
49
48
|
self._reset(users)
|
|
50
49
|
|
|
51
|
-
def reset_by_groups(self, group_names:
|
|
50
|
+
def reset_by_groups(self, group_names: list[str] = None) -> None:
|
|
52
51
|
users = User.objects.filter(
|
|
53
52
|
groups__name__in=group_names,
|
|
54
53
|
is_active=True,
|
|
@@ -57,7 +56,7 @@ class PasswordSetter:
|
|
|
57
56
|
)
|
|
58
57
|
self._reset(users)
|
|
59
58
|
|
|
60
|
-
def reset_users(self, usernames:
|
|
59
|
+
def reset_users(self, usernames: list[str]) -> None:
|
|
61
60
|
users = User.objects.filter(username__in=usernames, is_active=True, is_staff=True)
|
|
62
61
|
self._reset(users)
|
|
63
62
|
|
|
@@ -65,7 +64,7 @@ class PasswordSetter:
|
|
|
65
64
|
usernames = [username]
|
|
66
65
|
self.reset_users(usernames)
|
|
67
66
|
|
|
68
|
-
def reset_by_sites(self, site_names:
|
|
67
|
+
def reset_by_sites(self, site_names: list[str] = None) -> None:
|
|
69
68
|
users = User.objects.filter(
|
|
70
69
|
userprofile__sites__name__in=site_names,
|
|
71
70
|
is_active=True,
|
|
@@ -74,7 +73,7 @@ class PasswordSetter:
|
|
|
74
73
|
)
|
|
75
74
|
self._reset(users)
|
|
76
75
|
|
|
77
|
-
def _reset(self, users:
|
|
76
|
+
def _reset(self, users: list[User]) -> None:
|
|
78
77
|
for user in users:
|
|
79
78
|
password = self.password_generator.get_password()
|
|
80
79
|
user.set_password(password)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
1
|
from urllib.parse import urlparse
|
|
3
2
|
|
|
4
3
|
from django.core.mail.message import EmailMessage
|
|
@@ -10,7 +9,7 @@ from edc_protocol.research_protocol_config import ResearchProtocolConfig
|
|
|
10
9
|
from .import_users import change_user_template
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
def change_password(user, nwords:
|
|
12
|
+
def change_password(user, nwords: int | None = None) -> str:
|
|
14
13
|
nwords = nwords or 4
|
|
15
14
|
pwdgen = PasswordGenerator(nwords=nwords)
|
|
16
15
|
password = pwdgen.get_password()
|
|
@@ -19,7 +18,7 @@ def change_password(user, nwords: Optional[int] = None) -> str:
|
|
|
19
18
|
return password
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
def send_new_credentials_to_user(user, nwords:
|
|
21
|
+
def send_new_credentials_to_user(user, nwords: int | None = None) -> EmailMessage:
|
|
23
22
|
body = change_user_template
|
|
24
23
|
site_names = "\n - ".join([s.name for s in user.userprofile.sites.all()])
|
|
25
24
|
role_names = "\n - ".join([r.display_name for r in user.userprofile.roles.all()])
|
edc_auth/site_auths.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
from copy import deepcopy
|
|
5
|
-
from typing import Callable, Tuple
|
|
6
6
|
from warnings import warn
|
|
7
7
|
|
|
8
8
|
from django.apps import apps as django_apps
|
|
@@ -206,7 +206,7 @@ class SiteAuths:
|
|
|
206
206
|
self.registry[key].update({name: existing_group_names})
|
|
207
207
|
|
|
208
208
|
def add_custom_permissions_tuples(
|
|
209
|
-
self, model: str, codename_tuples:
|
|
209
|
+
self, model: str, codename_tuples: tuple[tuple[str, str], ...]
|
|
210
210
|
):
|
|
211
211
|
try:
|
|
212
212
|
self.registry["custom_permissions_tuples"][model]
|
|
@@ -278,7 +278,7 @@ class SiteAuths:
|
|
|
278
278
|
return self.registry["pre_update_funcs"]
|
|
279
279
|
|
|
280
280
|
@property
|
|
281
|
-
def post_update_funcs(self) ->
|
|
281
|
+
def post_update_funcs(self) -> tuple[str, Callable]:
|
|
282
282
|
return self.registry["post_update_funcs"]
|
|
283
283
|
|
|
284
284
|
@property
|
edc_consent/actions.py
CHANGED
|
@@ -6,11 +6,10 @@ from edc_utils import get_utcnow
|
|
|
6
6
|
def verify_consent(request=None, consent_obj=None):
|
|
7
7
|
if consent_obj.is_verified:
|
|
8
8
|
return None
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
consent_obj.save(update_fields=["is_verified", "is_verified_datetime", "verified_by"])
|
|
9
|
+
consent_obj.is_verified = True
|
|
10
|
+
consent_obj.is_verified_datetime = get_utcnow()
|
|
11
|
+
consent_obj.verified_by = request.user.username
|
|
12
|
+
consent_obj.save(update_fields=["is_verified", "is_verified_datetime", "verified_by"])
|
|
14
13
|
return consent_obj
|
|
15
14
|
|
|
16
15
|
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import KW_ONLY, dataclass, field
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from django.apps import apps as django_apps
|
|
8
8
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -27,9 +27,7 @@ if TYPE_CHECKING:
|
|
|
27
27
|
from .consent_definition_extension import ConsentDefinitionExtension
|
|
28
28
|
from .stubs import ConsentLikeModel
|
|
29
29
|
|
|
30
|
-
class SubjectScreening(
|
|
31
|
-
ScreeningModelMixin, EligibilityModelMixin, BaseUuidModel
|
|
32
|
-
): ... # noqa
|
|
30
|
+
class SubjectScreening(ScreeningModelMixin, EligibilityModelMixin, BaseUuidModel): ...
|
|
33
31
|
|
|
34
32
|
|
|
35
33
|
@dataclass(order=True)
|
|
@@ -84,13 +82,13 @@ class ConsentDefinition:
|
|
|
84
82
|
raise ConsentDefinitionError(f"Invalid gender. Got {self.gender}.")
|
|
85
83
|
if not self.start.tzinfo:
|
|
86
84
|
raise ConsentDefinitionError(f"Naive datetime not allowed. Got {self.start}.")
|
|
87
|
-
|
|
85
|
+
if str(self.start.tzinfo).upper() != "UTC":
|
|
88
86
|
raise ConsentDefinitionError(
|
|
89
87
|
f"Start date must be UTC. Got {self.start} / {self.start.tzinfo}."
|
|
90
88
|
)
|
|
91
89
|
if not self.end.tzinfo:
|
|
92
90
|
raise ConsentDefinitionError(f"Naive datetime not allowed Got {self.end}.")
|
|
93
|
-
|
|
91
|
+
if str(self.end.tzinfo).upper() != "UTC":
|
|
94
92
|
raise ConsentDefinitionError(
|
|
95
93
|
f"End date must be UTC. Got {self.end} / {self.start.tzinfo}."
|
|
96
94
|
)
|
|
@@ -110,13 +108,13 @@ class ConsentDefinition:
|
|
|
110
108
|
raise ConsentDefinitionError(
|
|
111
109
|
f"Model class must be a proxy. See {self.name}. Got {model_cls}"
|
|
112
110
|
)
|
|
113
|
-
|
|
111
|
+
if not isinstance(model_cls.objects, (ConsentObjectsByCdefManager,)):
|
|
114
112
|
raise ConsentDefinitionError(
|
|
115
113
|
"Incorrect 'objects' model manager for consent model. "
|
|
116
114
|
f"Expected {ConsentObjectsByCdefManager}. See {self.name}. "
|
|
117
115
|
f"Got {model_cls.objects.__class__}"
|
|
118
116
|
)
|
|
119
|
-
|
|
117
|
+
if not isinstance(model_cls.on_site, (CurrentSiteByCdefManager,)):
|
|
120
118
|
raise ConsentDefinitionError(
|
|
121
119
|
"Incorrect 'on_site' model manager for consent model. "
|
|
122
120
|
f"Expected {CurrentSiteByCdefManager}. See {self.name}. "
|
|
@@ -170,7 +168,7 @@ class ConsentDefinition:
|
|
|
170
168
|
return consent_obj
|
|
171
169
|
|
|
172
170
|
@property
|
|
173
|
-
def model_cls(self) ->
|
|
171
|
+
def model_cls(self) -> type[ConsentLikeModel]:
|
|
174
172
|
return django_apps.get_model(self.model)
|
|
175
173
|
|
|
176
174
|
@property
|
|
@@ -229,5 +227,4 @@ class ConsentDefinition:
|
|
|
229
227
|
)
|
|
230
228
|
if previous_consent.count() > 0:
|
|
231
229
|
return previous_consent.last()
|
|
232
|
-
|
|
233
|
-
raise ObjectDoesNotExist("Previous consent does not exist")
|
|
230
|
+
raise ObjectDoesNotExist("Previous consent does not exist")
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import KW_ONLY, dataclass, field
|
|
4
4
|
from datetime import datetime
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from django.apps import apps as django_apps
|
|
8
8
|
from django.core.exceptions import ObjectDoesNotExist
|
|
@@ -72,7 +72,7 @@ class ConsentDefinitionExtension:
|
|
|
72
72
|
self.sort_index = self.name
|
|
73
73
|
if not self.start.tzinfo:
|
|
74
74
|
raise ConsentDefinitionError(f"Naive datetime not allowed. Got {self.start}.")
|
|
75
|
-
|
|
75
|
+
if str(self.start.tzinfo) != "UTC":
|
|
76
76
|
raise ConsentDefinitionError(f"Start date must be UTC. Got {self.start}.")
|
|
77
77
|
self.extends.check_date_within_study_period()
|
|
78
78
|
|
|
@@ -98,7 +98,7 @@ class ConsentDefinitionExtension:
|
|
|
98
98
|
return visits
|
|
99
99
|
|
|
100
100
|
@property
|
|
101
|
-
def model_cls(self) ->
|
|
101
|
+
def model_cls(self) -> type[ConsentExtensionLikeModel]:
|
|
102
102
|
return django_apps.get_model(self.model)
|
|
103
103
|
|
|
104
104
|
@property
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
from .consent_definition_form_validator_mixin import ConsentDefinitionFormValidatorMixin
|
|
2
2
|
from .subject_consent_form_validator import SubjectConsentFormValidatorMixin
|
|
3
3
|
|
|
4
|
-
__all__ = ["
|
|
4
|
+
__all__ = ["ConsentDefinitionFormValidatorMixin", "SubjectConsentFormValidatorMixin"]
|
|
@@ -4,8 +4,8 @@ from .consent_version_model_mixin import ConsentVersionModelMixin
|
|
|
4
4
|
from .requires_consent_fields_model_mixin import RequiresConsentFieldsModelMixin
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
|
+
"ConsentExtensionModelMixin",
|
|
7
8
|
"ConsentModelMixin",
|
|
8
|
-
"RequiresConsentFieldsModelMixin",
|
|
9
9
|
"ConsentVersionModelMixin",
|
|
10
|
-
"
|
|
10
|
+
"RequiresConsentFieldsModelMixin",
|
|
11
11
|
]
|
|
@@ -69,7 +69,7 @@ class ConsentVersionModelMixin(models.Model):
|
|
|
69
69
|
f"to 'create' consent version '{cdef.version}'. Expected "
|
|
70
70
|
f"'{cdef.model}'. Got '{self._meta.label_lower}'."
|
|
71
71
|
)
|
|
72
|
-
|
|
72
|
+
if cdef.updates and self._meta.label_lower != cdef.updates.updated_by.model:
|
|
73
73
|
raise ConsentDefinitionModelError(
|
|
74
74
|
f"Incorrect model to update a consent. This model cannot be used "
|
|
75
75
|
f"to 'update' consent version '{cdef.version}'. Expected "
|
|
@@ -72,8 +72,7 @@ class ConsentModelAdminMixin:
|
|
|
72
72
|
return (
|
|
73
73
|
fields + ("consent_datetime", "identity", "confirm_identity") + readonly_fields
|
|
74
74
|
)
|
|
75
|
-
|
|
76
|
-
return fields + readonly_fields
|
|
75
|
+
return fields + readonly_fields
|
|
77
76
|
|
|
78
77
|
def get_search_fields(self, request) -> tuple[str, ...]:
|
|
79
78
|
search_fields: tuple[str] = super().get_search_fields(request)
|
|
@@ -121,8 +121,7 @@ class ConsentModelFormValidationMixin:
|
|
|
121
121
|
)
|
|
122
122
|
if "identity" in self.cleaned_data:
|
|
123
123
|
raise forms.ValidationError({"identity": msg})
|
|
124
|
-
|
|
125
|
-
raise forms.ValidationError({"__all__": msg})
|
|
124
|
+
raise forms.ValidationError({"__all__": msg})
|
|
126
125
|
|
|
127
126
|
def validate_identity_plus_version_is_unique(self) -> None:
|
|
128
127
|
"""Enforce a unique constraint on personal identity number
|
|
@@ -146,8 +145,7 @@ class ConsentModelFormValidationMixin:
|
|
|
146
145
|
)
|
|
147
146
|
if "identity" in self.cleaned_data:
|
|
148
147
|
raise forms.ValidationError({"identity": msg})
|
|
149
|
-
|
|
150
|
-
raise forms.ValidationError({"__all__": msg})
|
|
148
|
+
raise forms.ValidationError({"__all__": msg})
|
|
151
149
|
|
|
152
150
|
def validate_identity_with_unique_fields(self) -> None:
|
|
153
151
|
cleaned_data = self.cleaned_data
|
|
@@ -189,7 +187,7 @@ class ConsentModelFormValidationMixin:
|
|
|
189
187
|
gender = self.cleaned_data.get("gender")
|
|
190
188
|
if gender not in self.consent_definition.gender:
|
|
191
189
|
raise forms.ValidationError(
|
|
192
|
-
"Gender of consent can only be
|
|
190
|
+
"Gender of consent can only be '%(gender_of_consent)s'. Got '%(gender)s'.",
|
|
193
191
|
params={
|
|
194
192
|
"gender_of_consent": "' or '".join(self.consent_definition.gender),
|
|
195
193
|
"gender": gender,
|
edc_consent/site_consents.py
CHANGED
|
@@ -71,14 +71,14 @@ class SiteConsents:
|
|
|
71
71
|
f"Updates unregistered consent definition. See {cdef.name}. "
|
|
72
72
|
f"Got {cdef.updates.name}"
|
|
73
73
|
)
|
|
74
|
-
|
|
74
|
+
if cdef.updates and cdef.updates.updated_by is None:
|
|
75
75
|
raise ConsentDefinitionError(
|
|
76
76
|
f"Cdef mismatch with consent definition configured to update another. "
|
|
77
77
|
f"'{cdef.name}' is configured to update "
|
|
78
78
|
f"'{cdef.updates.name}' but '{cdef.updates.name}' "
|
|
79
79
|
f"updated_by is None. "
|
|
80
80
|
)
|
|
81
|
-
|
|
81
|
+
if cdef.updates and cdef.updates.updated_by != cdef:
|
|
82
82
|
raise ConsentDefinitionError(
|
|
83
83
|
f"Cdef mismatch with consent definition configured to update another. "
|
|
84
84
|
f"'{cdef.name}' is configured to update "
|
|
@@ -164,7 +164,7 @@ class SiteConsents:
|
|
|
164
164
|
f"'{consent_definition.version}' "
|
|
165
165
|
f"of consent on or after report_datetime='{dte}'?"
|
|
166
166
|
)
|
|
167
|
-
|
|
167
|
+
if consent_definition.start <= to_utc(report_datetime) <= consent_definition.end:
|
|
168
168
|
# ensures the higher version is returned if there is overlap
|
|
169
169
|
pass
|
|
170
170
|
elif (
|
|
@@ -284,8 +284,7 @@ class SiteConsents:
|
|
|
284
284
|
raise ConsentDefinitionDoesNotExist(
|
|
285
285
|
f"There are no consent definitions using this model. Got {model}."
|
|
286
286
|
)
|
|
287
|
-
|
|
288
|
-
errror_messages.append(f"model={model}")
|
|
287
|
+
errror_messages.append(f"model={model}")
|
|
289
288
|
return cdefs, errror_messages
|
|
290
289
|
|
|
291
290
|
@staticmethod
|
|
@@ -303,16 +302,13 @@ class SiteConsents:
|
|
|
303
302
|
if model == screening_model:
|
|
304
303
|
if cdef not in cdefs:
|
|
305
304
|
cdefs.append(cdef)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
cdefs.append(cdef)
|
|
305
|
+
elif model == cdef.screening_model:
|
|
306
|
+
cdefs.append(cdef)
|
|
309
307
|
if not cdefs:
|
|
310
308
|
raise ConsentDefinitionDoesNotExist(
|
|
311
|
-
"There are no consent definitions using this screening model."
|
|
312
|
-
f"Got {model}."
|
|
309
|
+
f"There are no consent definitions using this screening model.Got {model}."
|
|
313
310
|
)
|
|
314
|
-
|
|
315
|
-
errror_messages.append(f"model={model}")
|
|
311
|
+
errror_messages.append(f"model={model}")
|
|
316
312
|
return cdefs, errror_messages
|
|
317
313
|
|
|
318
314
|
def _filter_cdefs_by_report_datetime_or_raise(
|
|
@@ -336,9 +332,8 @@ class SiteConsents:
|
|
|
336
332
|
f"consent definition. Got {date_string}. {using_msg}. "
|
|
337
333
|
f"Possible consent definitions are: {consent_definitions}. "
|
|
338
334
|
)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
errror_messages.append(f"report_datetime={date_string}")
|
|
335
|
+
date_string = formatted_date(report_datetime)
|
|
336
|
+
errror_messages.append(f"report_datetime={date_string}")
|
|
342
337
|
return cdefs, errror_messages
|
|
343
338
|
|
|
344
339
|
def _filter_cdefs_by_version_or_raise(
|
edc_consent/stubs.py
CHANGED
|
@@ -11,7 +11,7 @@ from .model_mixins import ConsentModelMixin
|
|
|
11
11
|
class ConsentModelStub(Protocol):
|
|
12
12
|
subject_identifier: str
|
|
13
13
|
report_datetime: datetime
|
|
14
|
-
...
|
|
14
|
+
...
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class ConsentLikeModel(
|
|
@@ -19,4 +19,4 @@ class ConsentLikeModel(
|
|
|
19
19
|
ConsentModelMixin,
|
|
20
20
|
NonUniqueSubjectIdentifierModelMixin,
|
|
21
21
|
BaseUuidModel,
|
|
22
|
-
): ...
|
|
22
|
+
): ...
|
edc_consent/utils.py
CHANGED
edc_constants/utils.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
from typing import Optional, Tuple
|
|
2
|
-
|
|
3
1
|
from .constants import QUESTION_RETIRED
|
|
4
2
|
|
|
5
3
|
|
|
6
|
-
def get_display(choices, label) ->
|
|
4
|
+
def get_display(choices, label) -> str | None:
|
|
7
5
|
"""Returns the display value of a choices tuple for label."""
|
|
8
6
|
for choice in choices:
|
|
9
7
|
store_value, display_value = choice
|
|
@@ -12,7 +10,7 @@ def get_display(choices, label) -> Optional[str]:
|
|
|
12
10
|
return None
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
def append_question_retired_choice(choices) ->
|
|
13
|
+
def append_question_retired_choice(choices) -> tuple[tuple[str, str], ...]:
|
|
16
14
|
choices = list(choices)
|
|
17
15
|
choices.append((QUESTION_RETIRED, QUESTION_RETIRED))
|
|
18
16
|
choices = tuple(choices)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from django.core.exceptions import ObjectDoesNotExist
|
|
7
7
|
|
|
@@ -83,7 +83,7 @@ class CrfFormValidatorMixin(BaseFormValidatorMixin):
|
|
|
83
83
|
return get_related_visit(self, related_visit_model_attr=self.related_visit_model_attr)
|
|
84
84
|
|
|
85
85
|
@property
|
|
86
|
-
def related_visit_model_cls(self) ->
|
|
86
|
+
def related_visit_model_cls(self) -> type[RelatedVisitModel]:
|
|
87
87
|
"""Returns a subject visit model instance or None"""
|
|
88
88
|
return get_related_visit(
|
|
89
89
|
self, related_visit_model_attr=self.related_visit_model_attr
|
|
@@ -31,8 +31,8 @@ class CrfNoManagerModelMixin(
|
|
|
31
31
|
|
|
32
32
|
class Meta:
|
|
33
33
|
abstract = True
|
|
34
|
-
indexes =
|
|
34
|
+
indexes = (
|
|
35
35
|
models.Index(fields=["subject_visit", "site"]),
|
|
36
36
|
models.Index(fields=["subject_visit", "report_datetime"]),
|
|
37
|
-
|
|
37
|
+
)
|
|
38
38
|
default_permissions = ("add", "change", "delete", "view", "export", "import")
|
|
@@ -29,8 +29,8 @@ class CrfStatusModelMixin(models.Model):
|
|
|
29
29
|
|
|
30
30
|
crf_status_comments = models.TextField(
|
|
31
31
|
verbose_name=_("Any comments related to status of this CRF"),
|
|
32
|
-
null=True,
|
|
33
32
|
blank=True,
|
|
33
|
+
default="",
|
|
34
34
|
help_text=_("for example, why some data is still pending"),
|
|
35
35
|
)
|
|
36
36
|
|
edc_crf/models/crf_status.py
CHANGED
|
@@ -21,16 +21,14 @@ class CrfStatus(
|
|
|
21
21
|
verbose_name = "CRF Status"
|
|
22
22
|
verbose_name_plural = "CRF Status"
|
|
23
23
|
indexes = (
|
|
24
|
-
BaseUuidModel.Meta.indexes
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
)
|
|
35
|
-
]
|
|
24
|
+
*BaseUuidModel.Meta.indexes,
|
|
25
|
+
*NonUniqueSubjectIdentifierFieldMixin.Meta.indexes,
|
|
26
|
+
Index(
|
|
27
|
+
fields=[
|
|
28
|
+
"schedule_name",
|
|
29
|
+
"visit_schedule_name",
|
|
30
|
+
"visit_code",
|
|
31
|
+
"visit_code_sequence",
|
|
32
|
+
]
|
|
33
|
+
),
|
|
36
34
|
)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
1
|
from django.apps import apps as django_apps
|
|
4
2
|
|
|
5
3
|
from edc_constants.constants import INCOMPLETE
|
|
@@ -8,7 +6,7 @@ from .model_mixins import CrfStatusModelMixin
|
|
|
8
6
|
from .update_crf_status_for_instance import update_crf_status_for_instance
|
|
9
7
|
|
|
10
8
|
|
|
11
|
-
def update_crf_status_command(app_label:
|
|
9
|
+
def update_crf_status_command(app_label: str | None = None) -> None:
|
|
12
10
|
if app_label:
|
|
13
11
|
app_configs = [django_apps.get_app_config(app_label)]
|
|
14
12
|
else:
|