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
|
@@ -6,7 +6,7 @@ style = color_style()
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class Command(BaseCommand):
|
|
9
|
-
help = "Update search slugs if 'get_slugs'
|
|
9
|
+
help = "Update search slugs if 'get_slugs' method definitions have changed."
|
|
10
10
|
|
|
11
11
|
def add_arguments(self, parser):
|
|
12
12
|
parser.add_argument(
|
|
@@ -31,9 +31,7 @@ class Command(BaseCommand):
|
|
|
31
31
|
if "historical" not in model._meta.label_lower:
|
|
32
32
|
if hasattr(model, "update_search_slugs"):
|
|
33
33
|
self.stdout.write(
|
|
34
|
-
self.style.WARNING(
|
|
35
|
-
"Updating '{}' ...".format(model._meta.label_lower)
|
|
36
|
-
),
|
|
34
|
+
self.style.WARNING(f"Updating '{model._meta.label_lower}' ..."),
|
|
37
35
|
ending="\r",
|
|
38
36
|
)
|
|
39
37
|
try:
|
|
@@ -42,20 +40,19 @@ class Command(BaseCommand):
|
|
|
42
40
|
if "update_search_slugs" in str(e):
|
|
43
41
|
raise CommandError(
|
|
44
42
|
"Missing manager method 'update_search_slugs'. "
|
|
45
|
-
"See model {}. Got {}"
|
|
43
|
+
f"See model {model._meta.label_lower}. Got {e!s}"
|
|
46
44
|
)
|
|
47
|
-
|
|
48
|
-
raise CommandError(e)
|
|
45
|
+
raise CommandError(e)
|
|
49
46
|
except Exception as e:
|
|
50
47
|
raise CommandError(
|
|
51
|
-
"An exception occurred when updating model {}. "
|
|
52
|
-
"Got {}"
|
|
48
|
+
f"An exception occurred when updating model {model._meta.label_lower}. "
|
|
49
|
+
f"Got {e}"
|
|
53
50
|
)
|
|
54
51
|
else:
|
|
55
52
|
self.stdout.write(
|
|
56
53
|
self.style.SUCCESS(
|
|
57
|
-
"Updating '{}' ... Done"
|
|
54
|
+
f"Updating '{model._meta.label_lower}' ... Done"
|
|
58
55
|
)
|
|
59
56
|
)
|
|
60
57
|
else:
|
|
61
|
-
self.stdout.write("------- {
|
|
58
|
+
self.stdout.write(f"------- {model._meta.label_lower}")
|
|
@@ -58,7 +58,7 @@ def page_numbers(page, numpages):
|
|
|
58
58
|
min_n = page - 5
|
|
59
59
|
min_n = 1 if min_n <= 0 else min_n
|
|
60
60
|
max_n = min_n + 9
|
|
61
|
-
max_n = numpages
|
|
61
|
+
max_n = min(numpages, max_n)
|
|
62
62
|
page_numbers_ = [x for x in range(min_n, max_n + 1)]
|
|
63
63
|
return page_numbers_ or []
|
|
64
64
|
|
edc_dashboard/url_config.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
4
|
|
|
5
5
|
from django.urls.conf import re_path
|
|
6
6
|
|
|
@@ -14,7 +14,7 @@ if TYPE_CHECKING:
|
|
|
14
14
|
|
|
15
15
|
from .view_mixins import UrlRequestContextMixin
|
|
16
16
|
|
|
17
|
-
class View(UrlRequestContextMixin, BaseView): ...
|
|
17
|
+
class View(UrlRequestContextMixin, BaseView): ...
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class UrlConfig:
|
|
@@ -22,7 +22,7 @@ class UrlConfig:
|
|
|
22
22
|
self,
|
|
23
23
|
url_name: str = None,
|
|
24
24
|
namespace: str = None,
|
|
25
|
-
view_class:
|
|
25
|
+
view_class: type[View | UrlRequestContextMixin] = None,
|
|
26
26
|
label: str = None,
|
|
27
27
|
identifier_label: str = None,
|
|
28
28
|
identifier_pattern: str = None,
|
edc_dashboard/utils.py
CHANGED
|
@@ -30,12 +30,11 @@ def splitall(path):
|
|
|
30
30
|
if parts[0] == path: # sentinel for absolute paths
|
|
31
31
|
allparts.insert(0, parts[0])
|
|
32
32
|
break
|
|
33
|
-
|
|
33
|
+
if parts[1] == path: # sentinel for relative paths
|
|
34
34
|
allparts.insert(0, parts[1])
|
|
35
35
|
break
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
allparts.insert(0, parts[1])
|
|
36
|
+
path = parts[0]
|
|
37
|
+
allparts.insert(0, parts[1])
|
|
39
38
|
return allparts
|
|
40
39
|
|
|
41
40
|
|
|
@@ -18,7 +18,7 @@ class DashboardView(UrlRequestContextMixin, TemplateRequestContextMixin, Templat
|
|
|
18
18
|
if not self.dashboard_template and not self.dashboard_template_name:
|
|
19
19
|
raise ImproperlyConfigured(
|
|
20
20
|
f"Both 'dashboard_template' and 'dashboard_template_name' "
|
|
21
|
-
f"cannot be None. See {
|
|
21
|
+
f"cannot be None. See {self!r}."
|
|
22
22
|
)
|
|
23
23
|
super().__init__(**kwargs)
|
|
24
24
|
|
|
@@ -11,7 +11,6 @@ class DataDictionaryManager(models.Manager):
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class DataDictionary(BaseUuidModel):
|
|
14
|
-
|
|
15
14
|
# see edc_model_to_dataframe
|
|
16
15
|
m2m_related_field = "model"
|
|
17
16
|
|
|
@@ -74,4 +73,4 @@ class DataDictionary(BaseUuidModel):
|
|
|
74
73
|
verbose_name_plural = "Data Dictionary Items"
|
|
75
74
|
default_permissions = ("view", "export")
|
|
76
75
|
unique_together = (("model", "field_name"),)
|
|
77
|
-
indexes =
|
|
76
|
+
indexes = (Index(fields=["model", "number", "prompt"]),)
|
|
@@ -193,7 +193,7 @@ class DataQuery(DataQueryModelMixin, ActionModelMixin, SiteModelMixin, BaseUuidM
|
|
|
193
193
|
verbose_name = "Data Query"
|
|
194
194
|
verbose_name_plural = "Data Queries"
|
|
195
195
|
unique_together = ["registered_subject", "rule_reference", "visit_schedule"]
|
|
196
|
-
indexes =
|
|
196
|
+
indexes = (
|
|
197
197
|
models.Index(
|
|
198
198
|
fields=[
|
|
199
199
|
"subject_identifier",
|
|
@@ -203,5 +203,5 @@ class DataQuery(DataQueryModelMixin, ActionModelMixin, SiteModelMixin, BaseUuidM
|
|
|
203
203
|
"registered_subject",
|
|
204
204
|
"visit_schedule",
|
|
205
205
|
]
|
|
206
|
-
)
|
|
207
|
-
|
|
206
|
+
),
|
|
207
|
+
)
|
|
@@ -207,7 +207,7 @@ class QueryRule(BaseUuidModel):
|
|
|
207
207
|
super().save(*args, **kwargs)
|
|
208
208
|
|
|
209
209
|
def natural_key(self):
|
|
210
|
-
return (self.title,)
|
|
210
|
+
return (self.title,)
|
|
211
211
|
|
|
212
212
|
natural_key.dependencies = [
|
|
213
213
|
"edc_data_manager.CrfDataDictionary",
|
|
@@ -238,4 +238,4 @@ class QueryRule(BaseUuidModel):
|
|
|
238
238
|
class Meta(BaseUuidModel.Meta):
|
|
239
239
|
verbose_name = "Query Rule"
|
|
240
240
|
verbose_name_plural = "Query Rules"
|
|
241
|
-
indexes =
|
|
241
|
+
indexes = (models.Index(fields=["title", "active"]),)
|
edc_data_manager/tasks.py
CHANGED
edc_device/device.py
CHANGED
|
@@ -36,7 +36,7 @@ class Device:
|
|
|
36
36
|
f"Central server ID may not be included with node IDs. "
|
|
37
37
|
f"Got {self.central_server_id}, nodes={self.nodes}."
|
|
38
38
|
)
|
|
39
|
-
|
|
39
|
+
if central_server_id in self.middlemen:
|
|
40
40
|
raise DeviceIdError(
|
|
41
41
|
f"Central server ID may not be included with middleman IDs. "
|
|
42
42
|
f"Got {self.central_server_id}, middlemen={self.middlemen}."
|
edc_device/view_mixins.py
CHANGED
edc_document_status/fieldsets.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
document_status_fieldset_tuple: Tuple[str, Dict[str, Tuple[str, ...]]] = (
|
|
1
|
+
document_status_fieldset_tuple: tuple[str, dict[str, tuple[str, ...]]] = (
|
|
4
2
|
"Document status",
|
|
5
3
|
{"classes": ("collapse",), "fields": ("document_status",)},
|
|
6
4
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
|
|
@@ -26,7 +26,7 @@ class DocumentStatusModelMixin(models.Model):
|
|
|
26
26
|
self.update_document_status_on_save(kwargs.get("update_fields"))
|
|
27
27
|
super().save(*args, **kwargs)
|
|
28
28
|
|
|
29
|
-
def update_document_status_on_save(self, update_fields:
|
|
29
|
+
def update_document_status_on_save(self, update_fields: list | None = None) -> None:
|
|
30
30
|
"""Updates `document_status` as complete unless field is listed
|
|
31
31
|
in update_fields.
|
|
32
32
|
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
from typing import Tuple
|
|
2
|
-
|
|
3
|
-
|
|
4
1
|
class DocumentStatusModelAdminMixin:
|
|
5
|
-
def get_readonly_fields(self, request, obj=None) ->
|
|
2
|
+
def get_readonly_fields(self, request, obj=None) -> tuple[str, ...]:
|
|
6
3
|
readonly_fields = super().get_readonly_fields(request, obj=obj)
|
|
7
4
|
return tuple(set(readonly_fields + ("document_status",)))
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Dict, Optional, Tuple
|
|
2
|
-
|
|
3
1
|
from django.contrib import admin
|
|
4
2
|
from django_audit_fields.admin import audit_fieldset_tuple
|
|
5
3
|
|
|
@@ -15,7 +13,7 @@ class EgfrDropNotificationAdminMixin:
|
|
|
15
13
|
|
|
16
14
|
def get_fieldsets(
|
|
17
15
|
self, request, obj=None
|
|
18
|
-
) ->
|
|
16
|
+
) -> tuple[tuple[str | None, dict[str, tuple[str, ...]]], ...]:
|
|
19
17
|
fieldsets = [
|
|
20
18
|
(None, {"fields": ("subject_visit", "report_datetime")}),
|
|
21
19
|
(
|
|
@@ -40,7 +38,7 @@ class EgfrDropNotificationAdminMixin:
|
|
|
40
38
|
fieldsets.append(audit_fieldset_tuple)
|
|
41
39
|
return tuple(fieldsets)
|
|
42
40
|
|
|
43
|
-
def get_search_fields(self, request) ->
|
|
41
|
+
def get_search_fields(self, request) -> tuple[str, ...]:
|
|
44
42
|
fields = super().get_search_fields(request)
|
|
45
43
|
custom_fields = (
|
|
46
44
|
"subject_visit__subject_identifier",
|
|
@@ -48,7 +46,7 @@ class EgfrDropNotificationAdminMixin:
|
|
|
48
46
|
)
|
|
49
47
|
return tuple(f for f in fields if f not in custom_fields) + custom_fields
|
|
50
48
|
|
|
51
|
-
def get_readonly_fields(self, request, obj=None) ->
|
|
49
|
+
def get_readonly_fields(self, request, obj=None) -> tuple[str, ...]:
|
|
52
50
|
fields = super().get_readonly_fields(request, obj)
|
|
53
51
|
return (
|
|
54
52
|
"creatinine_date",
|
|
@@ -57,11 +55,11 @@ class EgfrDropNotificationAdminMixin:
|
|
|
57
55
|
"egfr_percent_change",
|
|
58
56
|
) + fields
|
|
59
57
|
|
|
60
|
-
def get_list_filter(self, request) ->
|
|
58
|
+
def get_list_filter(self, request) -> tuple[str, ...]:
|
|
61
59
|
list_filter = super().get_list_filter(request)
|
|
62
60
|
return ("report_status", "creatinine_date") + list_filter
|
|
63
61
|
|
|
64
|
-
def get_list_display(self, request) ->
|
|
62
|
+
def get_list_display(self, request) -> tuple[str, ...]:
|
|
65
63
|
list_display = super().get_list_display(request)
|
|
66
64
|
custom_fields = (
|
|
67
65
|
"report_status",
|
edc_egfr/egfr.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from datetime import date, datetime
|
|
4
4
|
from decimal import Decimal
|
|
5
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
6
|
from zoneinfo import ZoneInfo
|
|
7
7
|
|
|
8
8
|
from dateutil.relativedelta import relativedelta
|
|
@@ -63,8 +63,7 @@ class Egfr:
|
|
|
63
63
|
f"Invalid formula_name. Expected one of {list(self.calculators.keys())}. "
|
|
64
64
|
f"Got {formula_name}."
|
|
65
65
|
)
|
|
66
|
-
|
|
67
|
-
self.calculator_cls = self.calculators.get(formula_name)
|
|
66
|
+
self.calculator_cls = self.calculators.get(formula_name)
|
|
68
67
|
self.age_in_years = age_in_years
|
|
69
68
|
self.dob = dob
|
|
70
69
|
self.weight_in_kgs = weight_in_kgs
|
|
@@ -153,7 +152,7 @@ class Egfr:
|
|
|
153
152
|
return self._egfr_value
|
|
154
153
|
|
|
155
154
|
@property
|
|
156
|
-
def egfr_grade(self) ->
|
|
155
|
+
def egfr_grade(self) -> int | None:
|
|
157
156
|
if self._egfr_grade is None:
|
|
158
157
|
grading_data, _ = get_grade_for_value(
|
|
159
158
|
self.reference_range_collection,
|
|
@@ -178,11 +177,11 @@ class Egfr:
|
|
|
178
177
|
)
|
|
179
178
|
else:
|
|
180
179
|
egfr_drop_value = 0.0000
|
|
181
|
-
self._egfr_drop_value =
|
|
180
|
+
self._egfr_drop_value = max(egfr_drop_value, 0.0)
|
|
182
181
|
return self._egfr_drop_value
|
|
183
182
|
|
|
184
183
|
@property
|
|
185
|
-
def egfr_drop_grade(self) ->
|
|
184
|
+
def egfr_drop_grade(self) -> int | None:
|
|
186
185
|
if self._egfr_drop_grade is None:
|
|
187
186
|
grading_data, _ = get_grade_for_value(
|
|
188
187
|
self.reference_range_collection,
|
|
@@ -198,7 +197,7 @@ class Egfr:
|
|
|
198
197
|
self._egfr_drop_grade = grading_data.grade
|
|
199
198
|
return self._egfr_drop_grade
|
|
200
199
|
|
|
201
|
-
def get_weight_in_kgs(self) ->
|
|
200
|
+
def get_weight_in_kgs(self) -> float | None:
|
|
202
201
|
return self.weight_in_kgs
|
|
203
202
|
|
|
204
203
|
def create_or_update_egfr_drop_notification(self):
|
edc_export/archive_exporter.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from tempfile import mkdtemp
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
6
|
from edc_pdutils.df_exporters import CsvModelExporter
|
|
7
7
|
from edc_sites.site import sites
|
|
@@ -32,9 +32,9 @@ class ArchiveExporter:
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
date_format: str = "%Y%m%d%H%M%S"
|
|
35
|
-
csv_exporter_cls:
|
|
36
|
-
files_emailer_cls:
|
|
37
|
-
files_archiver_cls:
|
|
35
|
+
csv_exporter_cls: type[CsvModelExporter] = CsvModelExporter
|
|
36
|
+
files_emailer_cls: type[FilesEmailer] = FilesEmailer
|
|
37
|
+
files_archiver_cls: type[FilesArchiver] = FilesArchiver
|
|
38
38
|
|
|
39
39
|
def __init__(
|
|
40
40
|
self,
|
|
@@ -65,29 +65,28 @@ class ArchiveExporter:
|
|
|
65
65
|
self.exported.append(csv_exporter.to_csv())
|
|
66
66
|
if not self.exported:
|
|
67
67
|
raise ArchiveExporterNothingExported(f"Nothing exported. Got models={models}.")
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if archive:
|
|
69
|
+
archiver = self.files_archiver_cls(
|
|
70
|
+
path=tmp_folder,
|
|
71
|
+
user=user,
|
|
72
|
+
exported_datetime=self.exported_datetime,
|
|
73
|
+
date_format=self.date_format,
|
|
74
|
+
)
|
|
75
|
+
self.archive_filename = archiver.archive_filename
|
|
76
|
+
self.exported_datetime = archiver.exported_datetime
|
|
77
|
+
if email_to_user:
|
|
78
|
+
summary = [str(x) for x in self.exported]
|
|
79
|
+
summary.sort()
|
|
80
|
+
try:
|
|
81
|
+
self.files_emailer_cls(
|
|
71
82
|
path=tmp_folder,
|
|
72
83
|
user=user,
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
file_ext=".zip" if archive else ".csv",
|
|
85
|
+
summary="\n".join(summary),
|
|
75
86
|
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
self.files_emailer_cls(
|
|
83
|
-
path=tmp_folder,
|
|
84
|
-
user=user,
|
|
85
|
-
file_ext=".zip" if archive else ".csv",
|
|
86
|
-
summary="\n".join(summary),
|
|
87
|
-
)
|
|
88
|
-
except FilesEmailerError as e:
|
|
89
|
-
raise ArchiveExporterEmailError(e)
|
|
90
|
-
else:
|
|
91
|
-
self.emailed_to = user.email
|
|
92
|
-
self.emailed_datetime = get_utcnow()
|
|
93
|
-
self.exported_datetime = self.exported_datetime or self.emailed_datetime
|
|
87
|
+
except FilesEmailerError as e:
|
|
88
|
+
raise ArchiveExporterEmailError(e)
|
|
89
|
+
else:
|
|
90
|
+
self.emailed_to = user.email
|
|
91
|
+
self.emailed_datetime = get_utcnow()
|
|
92
|
+
self.exported_datetime = self.exported_datetime or self.emailed_datetime
|
edc_export/exportables.py
CHANGED
|
@@ -42,9 +42,8 @@ class Exportable(OrderedDict):
|
|
|
42
42
|
self.historical_models.append(model_opts)
|
|
43
43
|
elif model_opts.is_list_model:
|
|
44
44
|
self.list_models.append(model_opts)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
self.models.append(model_opts)
|
|
45
|
+
elif not model._meta.proxy:
|
|
46
|
+
self.models.append(model_opts)
|
|
48
47
|
self.models.sort(key=lambda x: x.verbose_name.title())
|
|
49
48
|
self.historical_models.sort(key=lambda x: x.verbose_name.title())
|
|
50
49
|
self.list_models.sort(key=lambda x: x.verbose_name.title())
|
|
@@ -31,7 +31,7 @@ class Command(BaseCommand):
|
|
|
31
31
|
error_filepath = os.path.join(
|
|
32
32
|
os.path.expanduser(self.export_plan.target_path) or "", self.error_filename
|
|
33
33
|
)
|
|
34
|
-
with open(self.ack_filename
|
|
34
|
+
with open(self.ack_filename) as f, open(error_filepath, "w") as error_file:
|
|
35
35
|
rows = csv.reader(f, delimiter="|")
|
|
36
36
|
writer = csv.writer(error_file, delimiter="|")
|
|
37
37
|
for row in rows:
|
|
@@ -42,8 +42,8 @@ class Command(BaseCommand):
|
|
|
42
42
|
try:
|
|
43
43
|
export_uuid = row[header.index("export_UUID")]
|
|
44
44
|
except ValueError as e:
|
|
45
|
-
writer.writerow("error reading file. Got {
|
|
46
|
-
print("Failed to process file {
|
|
45
|
+
writer.writerow(f"error reading file. Got {e}")
|
|
46
|
+
print(f"Failed to process file {self.ack_filename}")
|
|
47
47
|
raise ValueError(e)
|
|
48
48
|
if ExportTransaction.objects.filter(export_uuid=export_uuid):
|
|
49
49
|
for export_transaction in ExportTransaction.objects.filter(
|
|
@@ -79,7 +79,7 @@ class Command(BaseCommand):
|
|
|
79
79
|
except ValueError:
|
|
80
80
|
CommandError(
|
|
81
81
|
"Invalid file name. Expected format xxx_app_label_objectname_timestamp.xxx. "
|
|
82
|
-
"Got {
|
|
82
|
+
f"Got {self.ack_filename}"
|
|
83
83
|
)
|
|
84
84
|
return error_filename
|
|
85
85
|
|
|
@@ -91,9 +91,9 @@ class Command(BaseCommand):
|
|
|
91
91
|
)
|
|
92
92
|
except ExportPlan.DoesNotExist as e:
|
|
93
93
|
CommandError(
|
|
94
|
-
"ExportPlan not found for {
|
|
94
|
+
f"ExportPlan not found for {self.app_label}, {self.object_name}. "
|
|
95
95
|
"Check filename format or create an ExportPlan. "
|
|
96
|
-
"Got {
|
|
96
|
+
f"Got {e}"
|
|
97
97
|
)
|
|
98
98
|
return export_plan
|
|
99
99
|
|
|
@@ -29,7 +29,7 @@ class FileHistoryUpdater:
|
|
|
29
29
|
def update(self):
|
|
30
30
|
exported_pks = []
|
|
31
31
|
export_uuids = []
|
|
32
|
-
with open(self.path
|
|
32
|
+
with open(self.path) as f:
|
|
33
33
|
csv_reader = csv.DictReader(f, delimiter=self.delimiter)
|
|
34
34
|
for row in csv_reader:
|
|
35
35
|
exported_pks.append(row["id"])
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import string
|
|
4
|
-
from typing import TYPE_CHECKING, Any
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
6
|
from django.core.exceptions import ValidationError
|
|
7
7
|
from django.db.models import Manager
|
|
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
|
|
|
16
16
|
pass
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
__all__ = ["ValueGetter", "
|
|
19
|
+
__all__ = ["ValueGetter", "ValueGetterInvalidLookup", "ValueGetterUnknownField"]
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class ValueGetterUnknownField(ValidationError):
|
|
@@ -82,7 +82,7 @@ class ValueGetter:
|
|
|
82
82
|
self._value = self.strip_value(self._value)
|
|
83
83
|
return self._value
|
|
84
84
|
|
|
85
|
-
def _get_field_value(self, model_obj:
|
|
85
|
+
def _get_field_value(self, model_obj: type[MyModel] = None, field_name: str = None) -> Any:
|
|
86
86
|
"""Returns a field value.
|
|
87
87
|
|
|
88
88
|
1. Tries to access a field as a model instance attribute;
|
|
@@ -105,7 +105,7 @@ class ValueGetter:
|
|
|
105
105
|
value = self.get_lookup_value(model_obj=model_obj, field_name=field_name)
|
|
106
106
|
else:
|
|
107
107
|
raise ValueGetterUnknownField(
|
|
108
|
-
f"Unknown field name. Perhaps add a lookup.
|
|
108
|
+
f"Unknown field name. Perhaps add a lookup. Got {field_name}.",
|
|
109
109
|
code=field_name,
|
|
110
110
|
)
|
|
111
111
|
if isinstance(value, Manager):
|
|
@@ -117,7 +117,7 @@ class ValueGetter:
|
|
|
117
117
|
)
|
|
118
118
|
return value
|
|
119
119
|
|
|
120
|
-
def get_lookup_value(self, model_obj:
|
|
120
|
+
def get_lookup_value(self, model_obj: type[MyModel] = None, field_name: str = None) -> Any:
|
|
121
121
|
"""Returns the field value by following the lookup string
|
|
122
122
|
to a related instance.
|
|
123
123
|
"""
|
|
@@ -137,7 +137,7 @@ class ValueGetter:
|
|
|
137
137
|
"""Returns the list of m2m field names for this model."""
|
|
138
138
|
return [m2m.name for m2m in self.model_cls._meta.many_to_many]
|
|
139
139
|
|
|
140
|
-
def get_m2m_value(self, model_obj:
|
|
140
|
+
def get_m2m_value(self, model_obj: type[MyModel] = None, field_name: str = None) -> str:
|
|
141
141
|
"""Returns an m2m field value as a delimited string."""
|
|
142
142
|
return self.m2m_delimiter.join(
|
|
143
143
|
[value.name for value in getattr(model_obj, field_name).all()]
|
|
@@ -9,11 +9,11 @@ class NotificationMixin(models.Model):
|
|
|
9
9
|
|
|
10
10
|
subject = models.CharField(max_length=200)
|
|
11
11
|
|
|
12
|
-
recipient_list = models.TextField(null=True)
|
|
12
|
+
recipient_list = models.TextField(null=True, default="")
|
|
13
13
|
|
|
14
|
-
cc_list = models.TextField(null=True)
|
|
14
|
+
cc_list = models.TextField(null=True, default="")
|
|
15
15
|
|
|
16
|
-
body = models.TextField(null=True)
|
|
16
|
+
body = models.TextField(null=True, default="")
|
|
17
17
|
|
|
18
18
|
status = models.CharField(
|
|
19
19
|
max_length=15,
|
|
@@ -27,4 +27,4 @@ class NotificationMixin(models.Model):
|
|
|
27
27
|
|
|
28
28
|
class Meta:
|
|
29
29
|
abstract = True
|
|
30
|
-
indexes =
|
|
30
|
+
indexes = (Index(fields=["notification_datetime"]),)
|
|
@@ -13,14 +13,14 @@ from ..model_options import ModelOptions
|
|
|
13
13
|
class DataRequest(SiteModelMixin, BaseUuidModel):
|
|
14
14
|
name = models.CharField(max_length=25)
|
|
15
15
|
|
|
16
|
-
description = models.TextField(
|
|
16
|
+
description = models.TextField(default="")
|
|
17
17
|
|
|
18
18
|
decrypt = models.BooleanField(default=False)
|
|
19
19
|
|
|
20
20
|
export_format = models.CharField(max_length=25, choices=EXPORT_FORMATS, default=CSV)
|
|
21
21
|
|
|
22
22
|
models = models.TextField(
|
|
23
|
-
help_text='List one table per line, no commas. Use "label lower" format.'
|
|
23
|
+
default="", help_text='List one table per line, no commas. Use "label lower" format.'
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
history = HistoricalRecords()
|
|
@@ -41,4 +41,4 @@ class DataRequest(SiteModelMixin, BaseUuidModel):
|
|
|
41
41
|
return [ModelOptions(x) for x in models_as_list]
|
|
42
42
|
|
|
43
43
|
class Meta:
|
|
44
|
-
indexes =
|
|
44
|
+
indexes = (Index(fields=["name"]),)
|
|
@@ -18,11 +18,11 @@ class DataRequestHistory(SiteModelMixin, BaseUuidModel):
|
|
|
18
18
|
|
|
19
19
|
emailed_datetime = models.DateTimeField(null=True)
|
|
20
20
|
|
|
21
|
-
summary = models.TextField(
|
|
21
|
+
summary = models.TextField(default="")
|
|
22
22
|
|
|
23
23
|
exported_datetime = models.DateTimeField(default=get_utcnow)
|
|
24
24
|
|
|
25
25
|
class Meta:
|
|
26
26
|
verbose_name = "Data Request History"
|
|
27
27
|
verbose_name_plural = "Data Request History"
|
|
28
|
-
indexes =
|
|
28
|
+
indexes = (Index(fields=["exported_datetime"]),)
|
|
@@ -23,11 +23,11 @@ class FileHistory(BaseUuidModel):
|
|
|
23
23
|
model = models.CharField(max_length=50)
|
|
24
24
|
|
|
25
25
|
export_uuid_list = models.TextField(
|
|
26
|
-
|
|
26
|
+
default="", help_text="list of export_uuid's of model app_label.model_name"
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
pk_list = models.TextField(
|
|
30
|
-
|
|
30
|
+
default="", help_text="list of pk's of model app_label.model_name"
|
|
31
31
|
)
|
|
32
32
|
|
|
33
33
|
exit_message = models.CharField(
|
|
@@ -41,14 +41,14 @@ class FileHistory(BaseUuidModel):
|
|
|
41
41
|
filename = models.CharField(max_length=250, help_text="original filename on export")
|
|
42
42
|
|
|
43
43
|
file_contents = models.TextField(
|
|
44
|
-
|
|
44
|
+
default="", help_text="save contents of file as a list of rows"
|
|
45
45
|
)
|
|
46
46
|
|
|
47
47
|
exported = models.BooleanField(default=False, help_text="exported to a file")
|
|
48
48
|
|
|
49
49
|
exported_datetime = models.DateTimeField(null=True)
|
|
50
50
|
|
|
51
|
-
notification_plan_name = models.CharField(max_length=50,
|
|
51
|
+
notification_plan_name = models.CharField(max_length=50, default="")
|
|
52
52
|
|
|
53
53
|
sent = models.BooleanField(default=False, help_text="export file sent to recipient")
|
|
54
54
|
|
|
@@ -78,4 +78,4 @@ class FileHistory(BaseUuidModel):
|
|
|
78
78
|
return (self.history_uuid,)
|
|
79
79
|
|
|
80
80
|
class Meta(BaseUuidModel.Meta):
|
|
81
|
-
indexes =
|
|
81
|
+
indexes = (Index(fields=["sent_datetime"]),)
|