clinicedc 2.0.4__py3-none-any.whl → 2.0.5__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.5.dist-info}/METADATA +41 -45
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.5.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 -12
- 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.5.dist-info}/WHEEL +0 -0
- {clinicedc-2.0.4.dist-info → clinicedc-2.0.5.dist-info}/licenses/LICENSE +0 -0
edc_sites/models/__init__.py
CHANGED
edc_sites/models/site_profile.py
CHANGED
|
@@ -9,13 +9,13 @@ from django.db import models
|
|
|
9
9
|
class SiteProfile(models.Model):
|
|
10
10
|
id = models.BigAutoField(primary_key=True)
|
|
11
11
|
|
|
12
|
-
country = models.CharField(max_length=250,
|
|
12
|
+
country = models.CharField(max_length=250, default="")
|
|
13
13
|
|
|
14
|
-
country_code = models.CharField(max_length=15,
|
|
14
|
+
country_code = models.CharField(max_length=15, default="")
|
|
15
15
|
|
|
16
|
-
languages = models.TextField(
|
|
16
|
+
languages = models.TextField(default="")
|
|
17
17
|
|
|
18
|
-
title = models.CharField(max_length=250,
|
|
18
|
+
title = models.CharField(max_length=250, default="")
|
|
19
19
|
|
|
20
20
|
site = models.OneToOneField(Site, on_delete=models.PROTECT)
|
|
21
21
|
|
edc_sites/site.py
CHANGED
|
@@ -35,19 +35,19 @@ if TYPE_CHECKING:
|
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
__all__ = [
|
|
38
|
-
"SiteDoesNotExist",
|
|
39
38
|
"AlreadyRegistered",
|
|
40
|
-
"AlreadyRegisteredName",
|
|
41
39
|
"AlreadyRegisteredDomain",
|
|
40
|
+
"AlreadyRegisteredName",
|
|
41
|
+
"SiteDoesNotExist",
|
|
42
42
|
"SiteNotRegistered",
|
|
43
43
|
"SitesCheckError",
|
|
44
44
|
"SitesError",
|
|
45
|
-
"
|
|
45
|
+
"get_autodiscover_sites",
|
|
46
46
|
"get_default_country",
|
|
47
47
|
"get_default_country_code",
|
|
48
48
|
"get_default_domain",
|
|
49
49
|
"get_insert_uat_subdomain",
|
|
50
|
-
"
|
|
50
|
+
"get_register_default_site",
|
|
51
51
|
"sites",
|
|
52
52
|
]
|
|
53
53
|
|
|
@@ -160,13 +160,13 @@ class Sites:
|
|
|
160
160
|
single_site = dataclasses.replace(single_site, domain=domain)
|
|
161
161
|
|
|
162
162
|
if single_site.site_id in self._registry:
|
|
163
|
-
raise AlreadyRegistered(f"Site already registered. Got `{single_site}`.")
|
|
164
|
-
|
|
165
|
-
raise AlreadyRegisteredName(
|
|
163
|
+
raise AlreadyRegistered(f"Site already registered. Got `{single_site}`.") # noqa: TRY003
|
|
164
|
+
if single_site.name in [s.name for s in self._registry.values()]:
|
|
165
|
+
raise AlreadyRegisteredName( # noqa: TRY003
|
|
166
166
|
f"Site with this name is already registered. Got `{single_site}`."
|
|
167
167
|
)
|
|
168
|
-
|
|
169
|
-
raise AlreadyRegisteredDomain(
|
|
168
|
+
if single_site.domain in [s.domain for s in self._registry.values()]:
|
|
169
|
+
raise AlreadyRegisteredDomain( # noqa: TRY003
|
|
170
170
|
f"Site with this domain is already registered. Got `{single_site}`."
|
|
171
171
|
)
|
|
172
172
|
self._registry.update({single_site.site_id: single_site})
|
|
@@ -187,8 +187,8 @@ class Sites:
|
|
|
187
187
|
msg = "In fact, no sites have been registered!"
|
|
188
188
|
else:
|
|
189
189
|
msg = f"Expected one of {[s.site_id for s in self.all(aslist=True)]}."
|
|
190
|
-
raise SiteNotRegistered(
|
|
191
|
-
f"Site not registered. {msg} See {
|
|
190
|
+
raise SiteNotRegistered( # noqa: TRY003
|
|
191
|
+
f"Site not registered. {msg} See {self!r}. Got `{site_id}`."
|
|
192
192
|
)
|
|
193
193
|
return self._registry.get(site_id)
|
|
194
194
|
|
|
@@ -196,7 +196,7 @@ class Sites:
|
|
|
196
196
|
for single_site in self._registry.values():
|
|
197
197
|
if getattr(single_site, attrname) == value:
|
|
198
198
|
return single_site
|
|
199
|
-
raise SiteDoesNotExist(f"No site exists with `{attrname}`==`{value}`.")
|
|
199
|
+
raise SiteDoesNotExist(f"No site exists with `{attrname}`==`{value}`.") # noqa: TRY003
|
|
200
200
|
|
|
201
201
|
def all(self, aslist: bool | None = None) -> dict[int, SingleSite] | list[SingleSite]:
|
|
202
202
|
if aslist:
|
|
@@ -237,9 +237,10 @@ class Sites:
|
|
|
237
237
|
if request:
|
|
238
238
|
user = request.user
|
|
239
239
|
site_id = request.site.id
|
|
240
|
-
return [
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
return [
|
|
241
|
+
site_id,
|
|
242
|
+
*self.get_view_only_site_ids_for_user(request=request, user=user, site_id=site_id),
|
|
243
|
+
]
|
|
243
244
|
|
|
244
245
|
@staticmethod
|
|
245
246
|
def get_view_only_site_ids_for_user(
|
|
@@ -276,14 +277,6 @@ class Sites:
|
|
|
276
277
|
add_to_messages_once(
|
|
277
278
|
request, messages.WARNING, get_message_text(messages.WARNING)
|
|
278
279
|
)
|
|
279
|
-
# else:
|
|
280
|
-
# if self.has_viewallsites_permission(request):
|
|
281
|
-
# site_ids = [
|
|
282
|
-
# s.id
|
|
283
|
-
# for s in request.user.userprofile.sites.all()
|
|
284
|
-
# if s.id != request.site.id
|
|
285
|
-
# ]
|
|
286
|
-
|
|
287
280
|
return site_ids
|
|
288
281
|
|
|
289
282
|
def user_may_view_other_sites(
|
|
@@ -291,25 +284,24 @@ class Sites:
|
|
|
291
284
|
request: WSGIRequest = None,
|
|
292
285
|
user: User = None,
|
|
293
286
|
site_id: int = None,
|
|
294
|
-
) ->
|
|
295
|
-
|
|
287
|
+
) -> list[int]:
|
|
288
|
+
return self.get_view_only_site_ids_for_user(
|
|
296
289
|
request=request,
|
|
297
290
|
user=user,
|
|
298
291
|
site_id=site_id,
|
|
299
|
-
)
|
|
300
|
-
return True
|
|
301
|
-
return False
|
|
292
|
+
)
|
|
302
293
|
|
|
303
294
|
@staticmethod
|
|
304
295
|
def site_in_profile_or_raise(user: User, site_id: int) -> None:
|
|
305
296
|
"""Raises if user does not have site in their UserProfile."""
|
|
306
297
|
try:
|
|
307
|
-
user.userprofile.sites.get(id=site_id).id
|
|
308
|
-
except ObjectDoesNotExist:
|
|
309
|
-
|
|
298
|
+
user.userprofile.sites.get(id=site_id).id # noqa: B018
|
|
299
|
+
except ObjectDoesNotExist as e:
|
|
300
|
+
errmsg = (
|
|
310
301
|
"User is not configured to access this site. See also UserProfile. "
|
|
311
302
|
f"Got {site_id}."
|
|
312
303
|
)
|
|
304
|
+
raise InvalidSiteForUser(errmsg) from e
|
|
313
305
|
|
|
314
306
|
def get_language_choices_tuple(
|
|
315
307
|
self, site: Site | None = None, site_id: int | None = None, other=None
|
|
@@ -363,7 +355,7 @@ class Sites:
|
|
|
363
355
|
except ImportError as e:
|
|
364
356
|
sites._registry = before_import_registry
|
|
365
357
|
if module_has_submodule(mod, module_name):
|
|
366
|
-
raise SitesError(str(e))
|
|
358
|
+
raise SitesError(str(e)) from e
|
|
367
359
|
except ImportError:
|
|
368
360
|
pass
|
|
369
361
|
|
edc_sites/system_checks.py
CHANGED
|
@@ -79,7 +79,7 @@ def match_country_and_country_code_or_raise(single_site: SingleSite, site_obj):
|
|
|
79
79
|
|
|
80
80
|
def match_languages_or_raise(single_site: SingleSite, site_obj):
|
|
81
81
|
value1 = single_site.languages
|
|
82
|
-
value2 = json.loads(
|
|
82
|
+
value2 = json.loads(site_obj.siteprofile.languages)
|
|
83
83
|
if value1 != value2:
|
|
84
84
|
raise SitesCheckError(
|
|
85
85
|
f"Site table is out of sync. Checking {site_obj.id} "
|
|
@@ -7,7 +7,7 @@ def get_message_text(level: int) -> str:
|
|
|
7
7
|
return _(
|
|
8
8
|
"You have permissions to view forms and data from sites other than the current. "
|
|
9
9
|
)
|
|
10
|
-
|
|
10
|
+
if level == messages.ERROR:
|
|
11
11
|
return _(
|
|
12
12
|
"Showing data from the current site only. Although you have permissions to view "
|
|
13
13
|
"data from multiple sites you also have permissions to add, change or delete "
|
|
@@ -46,10 +46,9 @@ def valid_site_for_subject_or_raise(
|
|
|
46
46
|
"Site not defined for registered subject! "
|
|
47
47
|
f"Subject identifier=`{subject_identifier}`. "
|
|
48
48
|
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
)
|
|
49
|
+
raise InvalidSiteForSubjectError(
|
|
50
|
+
f"Invalid site for subject. Subject identifier=`{subject_identifier}`. "
|
|
51
|
+
f"Expected `{registered_subject.site.name}`. "
|
|
52
|
+
f"Got site_id=`{site_obj.id}`"
|
|
53
|
+
)
|
|
55
54
|
return site_obj
|
|
@@ -122,8 +122,8 @@ class RequisitionReport(Report):
|
|
|
122
122
|
[Paragraph("TEL/MOBILE/FAX", self.styles["line_label"]), ""],
|
|
123
123
|
[
|
|
124
124
|
Paragraph(
|
|
125
|
-
f
|
|
126
|
-
f
|
|
125
|
+
f"T:{self.shipper.telephone or '?'} M:{self.shipper.mobile or '?'} "
|
|
126
|
+
f"F:{self.shipper.fax or '?'}",
|
|
127
127
|
self.styles["line_data_large"],
|
|
128
128
|
),
|
|
129
129
|
"",
|
|
@@ -257,7 +257,7 @@ class RequisitionReport(Report):
|
|
|
257
257
|
if not self._requisitions:
|
|
258
258
|
for k, v in self.appointment.related_visit_model_cls().__dict__.items():
|
|
259
259
|
try:
|
|
260
|
-
model_cls =
|
|
260
|
+
model_cls = v.rel.related_model
|
|
261
261
|
except AttributeError:
|
|
262
262
|
pass
|
|
263
263
|
else:
|
|
@@ -350,8 +350,7 @@ class RequisitionReport(Report):
|
|
|
350
350
|
barcode,
|
|
351
351
|
Paragraph(str(index + 1), self.styles["row_data"]),
|
|
352
352
|
Paragraph(
|
|
353
|
-
f"{requisition.human_readable_identifier} "
|
|
354
|
-
f"({count}/{item_count})",
|
|
353
|
+
f"{requisition.human_readable_identifier} ({count}/{item_count})",
|
|
355
354
|
self.styles["row_data"],
|
|
356
355
|
),
|
|
357
356
|
Paragraph(
|
|
@@ -35,7 +35,9 @@ class RequisitionVerifier:
|
|
|
35
35
|
return f"{self.requisition_identifier} {self.verified}"
|
|
36
36
|
|
|
37
37
|
def __repr__(self):
|
|
38
|
-
return
|
|
38
|
+
return (
|
|
39
|
+
f"<{self.__class__.__name__}({{self.requisition_identifier}}) {{self.verified}}>"
|
|
40
|
+
)
|
|
39
41
|
|
|
40
42
|
@property
|
|
41
43
|
def requisition(self):
|
|
@@ -60,7 +62,7 @@ class RequisitionVerifier:
|
|
|
60
62
|
visit_model_cls = self.appointment.related_visit.__class__
|
|
61
63
|
for attr in dir(visit_model_cls):
|
|
62
64
|
try:
|
|
63
|
-
obj = getattr(
|
|
65
|
+
obj = getattr(visit_model_cls, attr).rel
|
|
64
66
|
except AttributeError:
|
|
65
67
|
pass
|
|
66
68
|
else:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections import namedtuple
|
|
4
|
-
from typing import TYPE_CHECKING,
|
|
4
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
5
5
|
|
|
6
6
|
from dateutil.relativedelta import relativedelta
|
|
7
7
|
from django import template
|
|
@@ -56,16 +56,16 @@ if TYPE_CHECKING:
|
|
|
56
56
|
|
|
57
57
|
__all__ = [
|
|
58
58
|
"appointment_in_progress",
|
|
59
|
-
"render_appointment_status_icon",
|
|
60
59
|
"print_requisition_popover",
|
|
61
|
-
"render_prn_button",
|
|
62
60
|
"render_appointment_button",
|
|
61
|
+
"render_appointment_status_icon",
|
|
63
62
|
"render_crf_button_group",
|
|
64
|
-
"render_gotoforms_button",
|
|
65
|
-
"requisition_panel_actions",
|
|
66
63
|
"render_crf_totals",
|
|
64
|
+
"render_gotoforms_button",
|
|
65
|
+
"render_prn_button",
|
|
67
66
|
"render_subject_consent_dashboard_button",
|
|
68
67
|
"render_unscheduled_appointment_button",
|
|
68
|
+
"requisition_panel_actions",
|
|
69
69
|
]
|
|
70
70
|
|
|
71
71
|
register = template.Library()
|
|
@@ -302,7 +302,7 @@ def render_related_visit_button(context, appointment: Appointment = None):
|
|
|
302
302
|
# from model_wrapper
|
|
303
303
|
appointment = getattr(appointment, "object", appointment)
|
|
304
304
|
related_visit: VisitModel = appointment.related_visit
|
|
305
|
-
related_visit_model_cls:
|
|
305
|
+
related_visit_model_cls: type[VisitModel] = appointment.related_visit_model_cls()
|
|
306
306
|
btn = RelatedVisitButton(
|
|
307
307
|
model_obj=related_visit,
|
|
308
308
|
model_cls=related_visit_model_cls,
|
|
@@ -322,7 +322,7 @@ def render_gotoforms_button(context, appointment: Appointment = None):
|
|
|
322
322
|
# from model_wrapper
|
|
323
323
|
appointment: Appointment = getattr(appointment, "object", appointment)
|
|
324
324
|
related_visit: VisitModel = appointment.related_visit
|
|
325
|
-
related_visit_model_cls:
|
|
325
|
+
related_visit_model_cls: type[VisitModel] = appointment.related_visit_model_cls()
|
|
326
326
|
btn = GotToFormsButton(
|
|
327
327
|
model_obj=related_visit,
|
|
328
328
|
model_cls=related_visit_model_cls,
|
|
@@ -424,8 +424,7 @@ def render_unscheduled_appointment_button(
|
|
|
424
424
|
):
|
|
425
425
|
show_button = True
|
|
426
426
|
anchor_id = (
|
|
427
|
-
f"unscheduled_appt_btn_{appointment.visit_code}_"
|
|
428
|
-
f"{appointment.visit_code_sequence}"
|
|
427
|
+
f"unscheduled_appt_btn_{appointment.visit_code}_{appointment.visit_code_sequence}"
|
|
429
428
|
)
|
|
430
429
|
if view_appointment and appointment.site.id == context["request"].site.id:
|
|
431
430
|
url = get_unscheduled_appointment_url(appointment)
|
|
@@ -482,9 +481,9 @@ def render_refresh_appointments_button(
|
|
|
482
481
|
visit_schedule_name: str = None,
|
|
483
482
|
schedule_name: str = None,
|
|
484
483
|
) -> dict:
|
|
485
|
-
if context["request"].user.userprofile.is_multisite_viewer
|
|
486
|
-
|
|
487
|
-
|
|
484
|
+
if context["request"].user.userprofile.is_multisite_viewer or context[
|
|
485
|
+
"request"
|
|
486
|
+
].user.userprofile.roles.filter(name=AUDITOR_ROLE):
|
|
488
487
|
url = None
|
|
489
488
|
else:
|
|
490
489
|
url = reverse(
|
|
@@ -508,11 +507,11 @@ def render_refresh_data_collection_schedule_button(
|
|
|
508
507
|
visit_schedule_name: str = None,
|
|
509
508
|
schedule_name: str = None,
|
|
510
509
|
) -> dict:
|
|
511
|
-
if
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
510
|
+
if (
|
|
511
|
+
context["request"].user.userprofile.is_multisite_viewer
|
|
512
|
+
or context["request"].user.userprofile.roles.filter(name=AUDITOR_ROLE)
|
|
513
|
+
or not related_visit_id
|
|
514
|
+
):
|
|
516
515
|
url = None
|
|
517
516
|
else:
|
|
518
517
|
url = reverse(
|
|
@@ -19,9 +19,7 @@ class CrfButton(DashboardModelButton):
|
|
|
19
19
|
@property
|
|
20
20
|
def disabled(self) -> str:
|
|
21
21
|
disabled = "disabled"
|
|
22
|
-
if not self.model_obj and self.perms.add:
|
|
23
|
-
disabled = ""
|
|
24
|
-
elif self.perms.change:
|
|
22
|
+
if (not self.model_obj and self.perms.add) or self.perms.change:
|
|
25
23
|
disabled = ""
|
|
26
24
|
elif self.perms.view:
|
|
27
25
|
if self.model_obj:
|
|
@@ -5,7 +5,7 @@ from uuid import UUID
|
|
|
5
5
|
|
|
6
6
|
__all__ = ["SubjectConsentDashboardButton"]
|
|
7
7
|
|
|
8
|
-
from typing import TYPE_CHECKING,
|
|
8
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
9
9
|
|
|
10
10
|
from edc_view_utils import ModelButton
|
|
11
11
|
|
|
@@ -21,7 +21,7 @@ class SubjectConsentDashboardButton(ModelButton):
|
|
|
21
21
|
"""For the subject dashboard"""
|
|
22
22
|
|
|
23
23
|
model_obj: ConsentModel = None
|
|
24
|
-
model_cls:
|
|
24
|
+
model_cls: type[ConsentModel] = None
|
|
25
25
|
appointment: Appointment = None
|
|
26
26
|
|
|
27
27
|
def __post_init__(self):
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from typing import TYPE_CHECKING,
|
|
5
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
6
6
|
from uuid import UUID
|
|
7
7
|
|
|
8
8
|
from edc_consent import site_consents
|
|
@@ -37,7 +37,7 @@ class SubjectConsentListboardButton(ModelButton):
|
|
|
37
37
|
|
|
38
38
|
screening_obj: ScreeningModel = field(default=None)
|
|
39
39
|
model_obj: ConsentModel = field(default=None)
|
|
40
|
-
model_cls:
|
|
40
|
+
model_cls: type[ConsentModel] = field(default=None)
|
|
41
41
|
consent_version: str = field(default=None, init=False)
|
|
42
42
|
|
|
43
43
|
def __post_init__(self):
|
|
@@ -88,9 +88,6 @@ class TimepointStatusButton(AppointmentButton):
|
|
|
88
88
|
@property
|
|
89
89
|
def disabled(self) -> str:
|
|
90
90
|
disabled = "disabled"
|
|
91
|
-
if not self.model_obj and self.perms.add:
|
|
91
|
+
if (not self.model_obj and self.perms.add) or self.perms.change or self.perms.view:
|
|
92
92
|
disabled = ""
|
|
93
|
-
else:
|
|
94
|
-
if self.perms.change or self.perms.view:
|
|
95
|
-
disabled = ""
|
|
96
93
|
return disabled
|
|
@@ -151,7 +151,7 @@ class RequisitionPrintActionsView(BaseRequisitionView):
|
|
|
151
151
|
if not self._requisition_model_cls:
|
|
152
152
|
for v in self.appointment.visit_model_cls().__dict__.values():
|
|
153
153
|
try:
|
|
154
|
-
model_cls =
|
|
154
|
+
model_cls = v.rel.related_model
|
|
155
155
|
except AttributeError:
|
|
156
156
|
pass
|
|
157
157
|
else:
|
|
@@ -167,7 +167,7 @@ class RequisitionPrintActionsView(BaseRequisitionView):
|
|
|
167
167
|
verified_requisitions = []
|
|
168
168
|
for k, v in self.appointment.visit_model_cls().__dict__.items():
|
|
169
169
|
try:
|
|
170
|
-
model_cls =
|
|
170
|
+
model_cls = v.rel.related_model
|
|
171
171
|
except AttributeError:
|
|
172
172
|
pass
|
|
173
173
|
else:
|
|
@@ -50,7 +50,7 @@ class SubjectDashboardView(
|
|
|
50
50
|
|
|
51
51
|
def __init__(self, **kwargs):
|
|
52
52
|
if not self.navbar_name:
|
|
53
|
-
raise ImproperlyConfigured(f"'navbar_name' cannot be None. See {
|
|
53
|
+
raise ImproperlyConfigured(f"'navbar_name' cannot be None. See {self!r}.")
|
|
54
54
|
super().__init__(**kwargs)
|
|
55
55
|
|
|
56
56
|
@property
|
edc_timepoint/model_mixins.py
CHANGED
|
@@ -142,9 +142,9 @@ class TimepointModelMixin(models.Model):
|
|
|
142
142
|
"""Formats and returns the status for the change_list."""
|
|
143
143
|
if self.timepoint_status == OPEN_TIMEPOINT:
|
|
144
144
|
return '<span style="color:green;">Open</span>'
|
|
145
|
-
|
|
145
|
+
if self.timepoint_status == CLOSED_TIMEPOINT:
|
|
146
146
|
return '<span style="color:red;">Closed</span>'
|
|
147
|
-
|
|
147
|
+
if self.timepoint_status == FEEDBACK:
|
|
148
148
|
return '<span style="color:orange;">Feedback</span>'
|
|
149
149
|
|
|
150
150
|
# timepoint.allow_tags = True
|
edc_transfer/action_items.py
CHANGED
|
@@ -12,7 +12,7 @@ class SubjectTransferAction(ActionWithNotification):
|
|
|
12
12
|
name = SUBJECT_TRANSFER_ACTION
|
|
13
13
|
display_name = "Submit Subject Transfer"
|
|
14
14
|
notification_display_name = " Subject Transfer"
|
|
15
|
-
parent_action_names =
|
|
15
|
+
parent_action_names = ()
|
|
16
16
|
show_link_to_changelist = True
|
|
17
17
|
show_link_to_add = True
|
|
18
18
|
priority = HIGH_PRIORITY
|
edc_transfer/model_mixins.py
CHANGED
|
@@ -69,13 +69,14 @@ class SubjectTransferModelMixin(
|
|
|
69
69
|
return f"{self.subject_identifier} on {transfer_date}."
|
|
70
70
|
|
|
71
71
|
def natural_key(self):
|
|
72
|
-
return (self.subject_identifier,)
|
|
72
|
+
return (self.subject_identifier,)
|
|
73
73
|
|
|
74
74
|
class Meta(SiteModelMixin.Meta, ActionModelMixin.Meta):
|
|
75
75
|
abstract = True
|
|
76
76
|
verbose_name = "Subject Transfer"
|
|
77
77
|
verbose_name_plural = "Subject Transfers"
|
|
78
|
-
indexes =
|
|
78
|
+
indexes = (
|
|
79
|
+
*ActionModelMixin.Meta.indexes,
|
|
79
80
|
models.Index(
|
|
80
81
|
fields=[
|
|
81
82
|
"subject_identifier",
|
|
@@ -84,4 +85,4 @@ class SubjectTransferModelMixin(
|
|
|
84
85
|
"site",
|
|
85
86
|
]
|
|
86
87
|
),
|
|
87
|
-
|
|
88
|
+
)
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
1
|
from django.contrib import admin
|
|
4
2
|
from django_audit_fields.admin import audit_fieldset_tuple
|
|
5
3
|
|
|
@@ -38,7 +36,7 @@ class SubjectTransferModelAdminMixin:
|
|
|
38
36
|
"may_contact": admin.VERTICAL,
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
def get_list_display(self, request) ->
|
|
39
|
+
def get_list_display(self, request) -> tuple[str, ...]:
|
|
42
40
|
list_display = super().get_list_display(request)
|
|
43
41
|
custom_fields = (
|
|
44
42
|
"subject_identifier",
|
|
@@ -50,8 +48,7 @@ class SubjectTransferModelAdminMixin:
|
|
|
50
48
|
)
|
|
51
49
|
return custom_fields + tuple(f for f in list_display if f not in custom_fields)
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
def get_list_filter(self, request) -> Tuple[str, ...]:
|
|
51
|
+
def get_list_filter(self, request) -> tuple[str, ...]:
|
|
55
52
|
list_filter = super().get_list_filter(request)
|
|
56
53
|
custom_fields = (
|
|
57
54
|
"transfer_date",
|
|
@@ -61,7 +58,7 @@ class SubjectTransferModelAdminMixin:
|
|
|
61
58
|
)
|
|
62
59
|
return custom_fields + tuple(f for f in list_filter if f not in custom_fields)
|
|
63
60
|
|
|
64
|
-
def get_search_fields(self, request) ->
|
|
61
|
+
def get_search_fields(self, request) -> tuple[str, ...]:
|
|
65
62
|
search_fields = super().get_search_fields(request)
|
|
66
63
|
custom_fields = ("subject_identifier",)
|
|
67
64
|
return tuple(set(custom_fields + search_fields))
|
edc_unblinding/action_items.py
CHANGED
|
@@ -12,7 +12,7 @@ class UnblindingRequestAction(ActionWithNotification):
|
|
|
12
12
|
name = UNBLINDING_REQUEST_ACTION
|
|
13
13
|
display_name = "Unblinding request"
|
|
14
14
|
notification_display_name = " Unblinding request"
|
|
15
|
-
parent_action_names =
|
|
15
|
+
parent_action_names = ()
|
|
16
16
|
show_link_to_changelist = True
|
|
17
17
|
show_link_to_add = True
|
|
18
18
|
priority = HIGH_PRIORITY
|
|
@@ -34,7 +34,7 @@ class UnblindingReviewAction(ActionWithNotification):
|
|
|
34
34
|
name = UNBLINDING_REVIEW_ACTION
|
|
35
35
|
display_name = "Unblinding review pending"
|
|
36
36
|
notification_display_name = " Unblinding review needed"
|
|
37
|
-
parent_action_names =
|
|
37
|
+
parent_action_names = (UNBLINDING_REQUEST_ACTION,)
|
|
38
38
|
show_link_to_changelist = True
|
|
39
39
|
priority = HIGH_PRIORITY
|
|
40
40
|
color_style = "info"
|
|
@@ -66,6 +66,6 @@ class UnblindingRequest(
|
|
|
66
66
|
class Meta(BaseUuidModel.Meta, NonUniqueSubjectIdentifierFieldMixin.Meta):
|
|
67
67
|
verbose_name = "Unblinding Request"
|
|
68
68
|
verbose_name_plural = "Unblinding Requests"
|
|
69
|
-
indexes =
|
|
70
|
-
models.Index(fields=["subject_identifier", "action_identifier", "site", "id"])
|
|
71
|
-
|
|
69
|
+
indexes = (
|
|
70
|
+
models.Index(fields=["subject_identifier", "action_identifier", "site", "id"]),
|
|
71
|
+
)
|
|
@@ -48,6 +48,6 @@ class UnblindingReview(
|
|
|
48
48
|
class Meta(BaseUuidModel.Meta, NonUniqueSubjectIdentifierFieldMixin.Meta):
|
|
49
49
|
verbose_name = "Unblinding Review"
|
|
50
50
|
verbose_name_plural = "Unblinding Reviews"
|
|
51
|
-
indexes =
|
|
52
|
-
models.Index(fields=["subject_identifier", "action_identifier", "site", "id"])
|
|
53
|
-
|
|
51
|
+
indexes = (
|
|
52
|
+
models.Index(fields=["subject_identifier", "action_identifier", "site", "id"]),
|
|
53
|
+
)
|
edc_utils/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from .age import AgeValueError, age, formatted_age, get_age_in_days, get_dob
|
|
2
2
|
from .context_processors_check import edc_context_processors_check
|
|
3
3
|
from .dashboard_middleware_check import edc_middleware_check
|
|
4
|
-
from .date import ceil_secs, floor_secs, get_utcnow, get_utcnow_as_date, to_utc
|
|
4
|
+
from .date import ceil_secs, floor_secs, get_utcnow, get_utcnow_as_date, to_local, to_utc
|
|
5
5
|
from .disable_signals import DisableSignals
|
|
6
6
|
from .get_datetime_from_env import get_datetime_from_env
|
|
7
7
|
from .get_static_file import get_static_file
|
|
@@ -25,7 +25,6 @@ __all__ = [
|
|
|
25
25
|
"DisableSignals",
|
|
26
26
|
"age",
|
|
27
27
|
"ceil_secs",
|
|
28
|
-
"message_in_queue",
|
|
29
28
|
"convert_from_camel",
|
|
30
29
|
"convert_php_dateformat",
|
|
31
30
|
"edc_context_processors_check",
|
|
@@ -43,13 +42,15 @@ __all__ = [
|
|
|
43
42
|
"get_utcnow",
|
|
44
43
|
"get_utcnow_as_date",
|
|
45
44
|
"get_uuid",
|
|
45
|
+
"message_in_queue",
|
|
46
46
|
"paths_for_urlpatterns",
|
|
47
|
-
"round_up",
|
|
48
|
-
"round_half_up",
|
|
49
47
|
"round_half_away_from_zero",
|
|
48
|
+
"round_half_up",
|
|
49
|
+
"round_up",
|
|
50
50
|
"safe_allowed_chars",
|
|
51
51
|
"show_url_names",
|
|
52
52
|
"show_urls",
|
|
53
|
-
"
|
|
53
|
+
"to_local",
|
|
54
54
|
"to_utc",
|
|
55
|
+
"truncate_string",
|
|
55
56
|
]
|
edc_utils/age.py
CHANGED
|
@@ -52,7 +52,7 @@ def age(
|
|
|
52
52
|
rdelta = relativedelta(reference_dt_utc, born_utc)
|
|
53
53
|
if born_utc > reference_dt_utc:
|
|
54
54
|
raise AgeValueError(
|
|
55
|
-
f"Reference date {reference_dt} {
|
|
55
|
+
f"Reference date {reference_dt} {reference_dt.tzinfo!s} "
|
|
56
56
|
f"precedes DOB {born} {timezone}. Got {rdelta}"
|
|
57
57
|
)
|
|
58
58
|
return rdelta
|
edc_utils/celery.py
CHANGED
|
@@ -9,14 +9,9 @@ celery_enabled = getattr(settings, "CELERY_ENABLED", False)
|
|
|
9
9
|
def run_task_sync_or_async(task, *args, **kwargs) -> AsyncResult:
|
|
10
10
|
"""Run a task with celery if running"""
|
|
11
11
|
|
|
12
|
-
if not celery_enabled:
|
|
12
|
+
if not celery_enabled or not celery_is_active() or current_app.conf.task_always_eager:
|
|
13
13
|
return task(*args, **kwargs)
|
|
14
|
-
|
|
15
|
-
return task(*args, **kwargs)
|
|
16
|
-
elif current_app.conf.task_always_eager:
|
|
17
|
-
return task(*args, **kwargs)
|
|
18
|
-
else:
|
|
19
|
-
return task.delay(*args, **kwargs)
|
|
14
|
+
return task.delay(*args, **kwargs)
|
|
20
15
|
|
|
21
16
|
|
|
22
17
|
def celery_is_active() -> dict:
|
|
@@ -45,4 +40,4 @@ def get_task_result(obj) -> AsyncResult | None:
|
|
|
45
40
|
return result
|
|
46
41
|
|
|
47
42
|
|
|
48
|
-
__all__ = ["
|
|
43
|
+
__all__ = ["celery_is_active", "get_task_result", "run_task_sync_or_async"]
|
edc_utils/get_static_file.py
CHANGED
|
@@ -9,7 +9,7 @@ def get_static_file(app_label: str, filename: str) -> str:
|
|
|
9
9
|
path = os.path.join(settings.STATIC_ROOT or "", app_label, filename)
|
|
10
10
|
if os.path.isfile(path):
|
|
11
11
|
try:
|
|
12
|
-
with open(path
|
|
12
|
+
with open(path):
|
|
13
13
|
pass
|
|
14
14
|
except FileNotFoundError:
|
|
15
15
|
path = os.path.join(f"https://{settings.STATIC_URL}", app_label, filename)
|