Copy disabled (too large)
Download .txt
Showing preview only (94,261K chars total). Download the full file to get everything.
Repository: dimagi/commcare-hq
Branch: master
Commit: 0b44fd0ec82c
Files: 9878
Total size: 85.4 MB
Directory structure:
gitextract_3yjwnnlc/
├── .coderabbit.yaml
├── .coveragerc
├── .dockerignore
├── .editorconfig
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ └── commcare-enhancement-proposal.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ ├── labels.yml
│ ├── release.yml
│ └── workflows/
│ ├── build-static.yml
│ ├── codeql-analysis.yml
│ ├── dependency-metrics.yml
│ ├── docker-image.yml
│ ├── lint.yml
│ ├── rebuild-staging.yml
│ ├── required-labels.yml
│ ├── test-docs.yml
│ ├── tests.yml
│ └── update-translations.yml
├── .gitignore
├── .gitmodules
├── .isort.cfg
├── .python-version
├── .pytype.cfg
├── .readthedocs.yml
├── .scss-lint.yml
├── .transifexrc.example
├── .tx/
│ └── config
├── AGENTS.md
├── CODE_STANDARDS.md
├── CONTRIBUTING.rst
├── DEV_FAQ.md
├── DEV_SETUP.md
├── DEV_SETUP_MAC.md
├── Dockerfile
├── Dockerfile_incl
├── Gruntfile.js
├── LICENSE
├── Makefile
├── README.md
├── STANDARDS.rst
├── codecov.yml
├── corehq/
│ ├── README.rst
│ ├── __init__.py
│ ├── apps/
│ │ ├── README.rst
│ │ ├── __init__.py
│ │ ├── accounting/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── async_handlers.py
│ │ │ ├── automated_reports.py
│ │ │ ├── bootstrap/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── config/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── annual_plans_may_2024.py
│ │ │ │ │ ├── enterprise.py
│ │ │ │ │ ├── form_submitting_mobile_worker_feature_rate.py
│ │ │ │ │ ├── new_plans_dec_2019.py
│ │ │ │ │ ├── remove_free_50_sms_sep_2023.py
│ │ │ │ │ ├── report_builder_v0.py
│ │ │ │ │ ├── resellers_and_managed_hosting.py
│ │ │ │ │ ├── standard_pricing_march_2018.py
│ │ │ │ │ ├── standard_update_april_2025.py
│ │ │ │ │ ├── standard_user_limit_march_2018.py
│ │ │ │ │ ├── standard_user_limit_october_2018.py
│ │ │ │ │ ├── testing.py
│ │ │ │ │ ├── user_buckets_august_2018.py
│ │ │ │ │ ├── user_buckets_jan_2017.py
│ │ │ │ │ └── web_user_feature_rate.py
│ │ │ │ ├── features.py
│ │ │ │ └── utils.py
│ │ │ ├── const.py
│ │ │ ├── decorators.py
│ │ │ ├── dispatcher.py
│ │ │ ├── emails.py
│ │ │ ├── exceptions.py
│ │ │ ├── filters.py
│ │ │ ├── forms.py
│ │ │ ├── interface.py
│ │ │ ├── invoice_pdf.py
│ │ │ ├── invoicing.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── add_operations_user.py
│ │ │ │ ├── change_role_for_software_plan_version.py
│ │ │ │ ├── create_test_pdf_templates.py
│ │ │ │ ├── find_inactive_custom_modules.py
│ │ │ │ ├── get_minimum_features_by_domain.py
│ │ │ │ ├── get_partner_domain_user_history.py
│ │ │ │ ├── list_customer_billing_account_software_plan.py
│ │ │ │ ├── list_prepayments_by_year.py
│ │ │ │ └── make_domain_enterprise_level.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_squashed_0052_ensure_report_builder_plans.py
│ │ │ │ ├── 0002_auto_20170222_2008.py
│ │ │ │ ├── 0003_auto_20170328_2102.py
│ │ │ │ ├── 0004_auto_20170404_0028.py
│ │ │ │ ├── 0005_automatic_downgrade_adjustment_method.py
│ │ │ │ ├── 0006_unique_active_domain_subscription.py
│ │ │ │ ├── 0007_practice_mobile_workers.py
│ │ │ │ ├── 0008_update_report_builder_included_feature_numbers.py
│ │ │ │ ├── 0009_make_billingaccount_name_unique.py
│ │ │ │ ├── 0010_remove_softwareproduct_product_type.py
│ │ │ │ ├── 0011_remove_softwareproduct.py
│ │ │ │ ├── 0012_replace__product_type__with__is_product.py
│ │ │ │ ├── 0013_subscription_dates_check.py
│ │ │ │ ├── 0014_paymentmethod__web_user__nonnullable.py
│ │ │ │ ├── 0015_grandfather_login_as.py
│ │ │ │ ├── 0016_grandfather_reportbuilder_5_pro.py
│ │ │ │ ├── 0017_nonnullable_char_fields.py
│ │ │ │ ├── 0018_alter_nonnullable_char_fields.py
│ │ │ │ ├── 0019_standard_pricing_march_2018.py
│ │ │ │ ├── 0020_payment_method__unique_together.py
│ │ │ │ ├── 0021_standard_user_limit_march_2018.py
│ │ │ │ ├── 0022_add__skip_auto_downgrade_reason.py
│ │ │ │ ├── 0023_auto_20180501_1813.py
│ │ │ │ ├── 0024_unique__transaction_id.py
│ │ │ │ ├── 0025_auto_20180508_1952.py
│ │ │ │ ├── 0026_auto_20180508_1956.py
│ │ │ │ ├── 0027_auto_20180509_1857.py
│ │ │ │ ├── 0028_auto_20180604_1757.py
│ │ │ │ ├── 0029_auto_20180605_1826.py
│ │ │ │ ├── 0030_softwareplan_max_domains.py
│ │ │ │ ├── 0031_billingaccount_billing_admin_emails.py
│ │ │ │ ├── 0032_billingaccount_invoicing_plan.py
│ │ │ │ ├── 0032_customerinvoice_squashed_0036_customerbillingrecord.py
│ │ │ │ ├── 0033_auto_20180709_1837.py
│ │ │ │ ├── 0034_merge_20180711_1828.py
│ │ │ │ ├── 0034_remove_subscription_date_delay_invoicing.py
│ │ │ │ ├── 0035_enterprise_settings.py
│ │ │ │ ├── 0035_merge_20180711_2039.py
│ │ │ │ ├── 0036_domainuserhistory.py
│ │ │ │ ├── 0037_merge_20180807_0915.py
│ │ │ │ ├── 0038_remove_billingaccount_restrict_signup_email.py
│ │ │ │ ├── 0039_auto_20180828_2258.py
│ │ │ │ ├── 0040_auto_20181002_1721.py
│ │ │ │ ├── 0041_auto_20190130_1709.py
│ │ │ │ ├── 0042_domain_user_history__unique__and__nonnullable.py
│ │ │ │ ├── 0043_grandfather_case_privs.py
│ │ │ │ ├── 0044_grandfather_odata_privs.py
│ │ │ │ ├── 0045_grandfather_data_forwarding_privs.py
│ │ │ │ ├── 0046_new_plans.py
│ │ │ │ ├── 0047_invoice_communication.py
│ │ │ │ ├── 0048_friendly_writeoff.py
│ │ │ │ ├── 0049_auto_20200924_1753.py
│ │ │ │ ├── 0050_app_user_profiles.py
│ │ │ │ ├── 0051_hubspot_restrictions.py
│ │ │ │ ├── 0052_geocoder_permissions.py
│ │ │ │ ├── 0053_app_user_profiles_advanced.py
│ │ │ │ ├── 0054_default_export_settings.py
│ │ │ │ ├── 0055_linked_projects.py
│ │ │ │ ├── 0056_add_release_management.py
│ │ │ │ ├── 0057_add_sms_report_toggle.py
│ │ │ │ ├── 0058_delete_linked_projects_role.py
│ │ │ │ ├── 0059_add_lite_release_management_priv.py
│ │ │ │ ├── 0060_add_loadtest_users_priv.py
│ │ │ │ ├── 0061_remove_enterprise_v1.py
│ │ │ │ ├── 0062_add_release_management_to_enterprise.py
│ │ │ │ ├── 0063_replace_linked_projects_ff_with_erm.py
│ │ │ │ ├── 0064_add_form_link_workflow_priv.py
│ │ │ │ ├── 0065_phone_apk_heartbeat_privs.py
│ │ │ │ ├── 0066_data_file_download_priv.py
│ │ │ │ ├── 0067_add_view_app_diff_priv.py
│ │ │ │ ├── 0068_regex_field_validation_privilege.py
│ │ │ │ ├── 0069_location_safe_case_imports_priv.py
│ │ │ │ ├── 0070_form_case_ids_case_importer_priv.py
│ │ │ │ ├── 0071_add_billingaccountwebuserhistory.py
│ │ │ │ ├── 0072_export_multisort_priv.py
│ │ │ │ ├── 0073_export_ownership_priv.py
│ │ │ │ ├── 0074_filtered_bulk_user_download_priv.py
│ │ │ │ ├── 0075_application_error_report_priv.py
│ │ │ │ ├── 0076_location_owner_in_report_builder_priv.py
│ │ │ │ ├── 0077_case_list_explorer_priv.py
│ │ │ │ ├── 0078_revert_location_owner_in_report_builder_priv.py
│ │ │ │ ├── 0079_add_web_user_feature.py
│ │ │ │ ├── 0080_add_web_user_feature_in_other_models.py
│ │ │ │ ├── 0081_billingaccount_bill_web_user.py
│ │ │ │ ├── 0082_application_error_report_priv.py
│ │ │ │ ├── 0083_data_dictionary_priv.py
│ │ │ │ ├── 0084_copy_cases_priv.py
│ │ │ │ ├── 0085_remove_free_50_sms.py
│ │ │ │ ├── 0086_add_duplicate_invoice_id_to_invoice_model.py
│ │ │ │ ├── 0087_invoice_unique_constraints.py
│ │ │ │ ├── 0088_add_new_softwareplan_visibility.py
│ │ │ │ ├── 0089_dedupe_priv.py
│ │ │ │ ├── 0090_custom_domain_alerts_priv.py
│ │ │ │ ├── 0091_remove_custom_banner_alerts_feature_flag.py
│ │ │ │ ├── 0092_revert_application_error_report_priv.py
│ │ │ │ ├── 0093_defaultproductplan_is_annual_plan.py
│ │ │ │ ├── 0094_add_annual_softwareplans.py
│ │ │ │ ├── 0095_update_softwareplan_visibilities.py
│ │ │ │ ├── 0096_formsubmittingmobileworkerhistory_and_featuretype_choice.py
│ │ │ │ ├── 0097_add_form_submitting_mobile_worker_feature.py
│ │ │ │ ├── 0098_app_dependencies_priv.py
│ │ │ │ ├── 0099_data_cleaning_priv.py
│ │ │ │ ├── 0100_alter_customerinvoicecommunicationhistory_communication_type_and_more.py
│ │ │ │ ├── 0101_update_standard_plan_pricing_users_and_privs.py
│ │ │ │ ├── 0102_alter_defaultproductplan_edition_and_more.py
│ │ │ │ ├── 0103_bulk_data_cleaning_priv.py
│ │ │ │ ├── 0104_fix_priv_community_rename.py
│ │ │ │ ├── 0105_alter_billingcontactinfo_city_and_more.py
│ │ │ │ ├── 0106_alter_billingcontactinfo_company_name.py
│ │ │ │ ├── 0107_two_stage_mobile_worker_creation_priv.py
│ │ │ │ ├── 0108_subscription_auto_renew_and_more.py
│ │ │ │ ├── 0109_enable_all_add_ons_priv.py
│ │ │ │ ├── 0110_alter_customerinvoicecommunicationhistory_communication_type_and_more.py
│ │ │ │ ├── 0111_rename_is_auto_invoiceable_billingaccount_require_auto_pay.py
│ │ │ │ ├── 0112_data_dict_types_priv.py
│ │ │ │ ├── 0113_geojson_export_priv.py
│ │ │ │ ├── 0114_custom_icon_badges_priv.py
│ │ │ │ ├── 0115_loc_cols_in_user_last_activity_priv.py
│ │ │ │ ├── 0116_creditadjustment_payment_type.py
│ │ │ │ └── __init__.py
│ │ │ ├── mixins.py
│ │ │ ├── models.py
│ │ │ ├── payment_handlers.py
│ │ │ ├── signals.py
│ │ │ ├── static/
│ │ │ │ └── accounting/
│ │ │ │ └── js/
│ │ │ │ ├── base_subscriptions_main.js
│ │ │ │ ├── billing_account_form.js
│ │ │ │ ├── confirm_plan.js
│ │ │ │ ├── credits.js
│ │ │ │ ├── credits_tab.js
│ │ │ │ ├── payment_method_handler.js
│ │ │ │ ├── pricing_table.js
│ │ │ │ ├── renew_plan_selection.js
│ │ │ │ ├── report_filter_actions.js
│ │ │ │ ├── software_plan_version_handler.js
│ │ │ │ ├── stripe.js
│ │ │ │ ├── subscriptions_main.js
│ │ │ │ └── widgets.js
│ │ │ ├── subscription_changes.py
│ │ │ ├── task_utils.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── accounting/
│ │ │ │ ├── accounting_admins.html
│ │ │ │ ├── accounts.html
│ │ │ │ ├── accounts_base.html
│ │ │ │ ├── email/
│ │ │ │ │ ├── autopay_card_removed.html
│ │ │ │ │ ├── autopay_failed.html
│ │ │ │ │ ├── autopay_failed.txt
│ │ │ │ │ ├── bookkeeper.html
│ │ │ │ │ ├── bookkeeper.txt
│ │ │ │ │ ├── bulk_payment_receipt.html
│ │ │ │ │ ├── bulk_payment_receipt.txt
│ │ │ │ │ ├── credit_receipt.html
│ │ │ │ │ ├── credit_receipt.txt
│ │ │ │ │ ├── credits_on_hq.html
│ │ │ │ │ ├── credits_on_hq.txt
│ │ │ │ │ ├── customer_invoice.html
│ │ │ │ │ ├── customer_invoice.txt
│ │ │ │ │ ├── digest.html
│ │ │ │ │ ├── digest.txt
│ │ │ │ │ ├── downgrade.html
│ │ │ │ │ ├── downgrade.txt
│ │ │ │ │ ├── downgrade_warning.html
│ │ │ │ │ ├── downgrade_warning.txt
│ │ │ │ │ ├── invoice.html
│ │ │ │ │ ├── invoice.txt
│ │ │ │ │ ├── invoice_autopay_setup.html
│ │ │ │ │ ├── invoice_autopayment.html
│ │ │ │ │ ├── invoice_autopayment.txt
│ │ │ │ │ ├── invoice_contracted.html
│ │ │ │ │ ├── invoice_contracted.txt
│ │ │ │ │ ├── invoice_receipt.html
│ │ │ │ │ ├── invoice_receipt.txt
│ │ │ │ │ ├── invoice_reminder.html
│ │ │ │ │ ├── invoice_reminder.txt
│ │ │ │ │ ├── overdue_notice.html
│ │ │ │ │ ├── overdue_notice.txt
│ │ │ │ │ ├── pay_annually_unpaid.html
│ │ │ │ │ ├── pay_annually_unpaid.txt
│ │ │ │ │ ├── sales_request.html
│ │ │ │ │ ├── subscription_change.html
│ │ │ │ │ ├── subscription_change.txt
│ │ │ │ │ ├── subscription_ending.html
│ │ │ │ │ ├── subscription_ending.txt
│ │ │ │ │ ├── subscription_ending_reminder_dimagi.html
│ │ │ │ │ ├── subscription_ending_reminder_dimagi.txt
│ │ │ │ │ ├── subscription_renewal_reminder.html
│ │ │ │ │ ├── subscription_renewal_reminder.txt
│ │ │ │ │ ├── subscription_renewed.html
│ │ │ │ │ ├── subscription_renewed.txt
│ │ │ │ │ ├── wire_invoice.html
│ │ │ │ │ └── wire_invoice.txt
│ │ │ │ ├── invoice.html
│ │ │ │ ├── invoice_list.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── add_new_item_button.html
│ │ │ │ │ ├── adjust_balance.html
│ │ │ │ │ ├── anchor_tag.html
│ │ │ │ │ ├── confirm_pause_summary.html
│ │ │ │ │ ├── confirm_plan_summary.html
│ │ │ │ │ ├── credits_tab.html
│ │ │ │ │ ├── customer_plan_version_tools.html
│ │ │ │ │ ├── downgrade_messages.html
│ │ │ │ │ ├── invoice_table.html
│ │ │ │ │ ├── renew_plan_selection.html
│ │ │ │ │ ├── stripe_card_ko_template.html
│ │ │ │ │ ├── subscriptions_tab.html
│ │ │ │ │ └── version_summary_tab.html
│ │ │ │ ├── plan_version.html
│ │ │ │ ├── plans.html
│ │ │ │ ├── plans_base.html
│ │ │ │ ├── report_filter_actions.html
│ │ │ │ ├── subscriptions.html
│ │ │ │ ├── subscriptions_base.html
│ │ │ │ ├── test_reminder_emails.html
│ │ │ │ ├── trigger_accounting_tests.html
│ │ │ │ ├── trigger_bookkeeper.html
│ │ │ │ ├── trigger_customer_invoice.html
│ │ │ │ └── trigger_invoice.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base_tests.py
│ │ │ │ ├── data/
│ │ │ │ │ ├── app-commcare-icon-build.json
│ │ │ │ │ └── app-commcare-icon-standard.json
│ │ │ │ ├── generator.py
│ │ │ │ ├── test_admin_software_plans.py
│ │ │ │ ├── test_autopay.py
│ │ │ │ ├── test_autopay_with_api.py
│ │ │ │ ├── test_card_utils.py
│ │ │ │ ├── test_change_role_for_software_plan_version.py
│ │ │ │ ├── test_credit_lines.py
│ │ │ │ ├── test_customer_invoicing.py
│ │ │ │ ├── test_domain_user_history.py
│ │ │ │ ├── test_emails.py
│ │ │ │ ├── test_ensure_plans.py
│ │ │ │ ├── test_enterprise_mode.py
│ │ │ │ ├── test_forms.py
│ │ │ │ ├── test_invoice_line_items.py
│ │ │ │ ├── test_invoice_pdf.py
│ │ │ │ ├── test_invoicing.py
│ │ │ │ ├── test_migrations.py
│ │ │ │ ├── test_model_validation.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_new_domain_subscription.py
│ │ │ │ ├── test_race_condition_is_prevented.py
│ │ │ │ ├── test_renew_subscription.py
│ │ │ │ ├── test_revoke_grants.py
│ │ │ │ ├── test_software_plan_version_filter.py
│ │ │ │ ├── test_stripe_payment.py
│ │ │ │ ├── test_stripe_payment_method_with_api.py
│ │ │ │ ├── test_stripe_utils_with_api.py
│ │ │ │ ├── test_subscription_changes.py
│ │ │ │ ├── test_subscription_permissions_changes.py
│ │ │ │ ├── test_task_utils.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_unpaid_invoice.py
│ │ │ │ ├── test_utils.py
│ │ │ │ ├── test_wire_invoice.py
│ │ │ │ └── utils.py
│ │ │ ├── urls.py
│ │ │ ├── usage.py
│ │ │ ├── user_text.py
│ │ │ ├── utils/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── account.py
│ │ │ │ ├── cards.py
│ │ │ │ ├── invoicing.py
│ │ │ │ ├── software_plans.py
│ │ │ │ ├── stripe.py
│ │ │ │ ├── subscription.py
│ │ │ │ └── unpaid_invoice.py
│ │ │ └── views.py
│ │ ├── analytics/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── ab_tests.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── audit_user_in_hubspot.py
│ │ │ │ ├── blocked_hubspot_users_summary.py
│ │ │ │ ├── list_blocked_from_hubspot.py
│ │ │ │ ├── manually_cleanup_blocked_hubspot_contacts.py
│ │ │ │ └── update_hubspot_properties.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_data_point_unique_constraint.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── signals.py
│ │ │ ├── static/
│ │ │ │ └── analytix/
│ │ │ │ └── js/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── cta_forms.js
│ │ │ │ │ └── hubspot.js
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── cta_forms.js
│ │ │ │ │ └── hubspot.js
│ │ │ │ ├── google.js
│ │ │ │ ├── gtx.js
│ │ │ │ ├── initial.js
│ │ │ │ ├── logging.js
│ │ │ │ ├── noopMetrics.js
│ │ │ │ └── utils.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── analytics/
│ │ │ │ ├── email/
│ │ │ │ │ ├── partner_analytics_report.html
│ │ │ │ │ └── partner_analytics_report.txt
│ │ │ │ ├── forms/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ └── hubspot_cta_form.html
│ │ │ │ │ └── bootstrap5/
│ │ │ │ │ └── hubspot_cta_form.html
│ │ │ │ ├── google.html
│ │ │ │ └── initial/
│ │ │ │ ├── all.html
│ │ │ │ ├── global.html
│ │ │ │ ├── google.html
│ │ │ │ ├── gtm.html
│ │ │ │ └── hubspot.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_hubspot.py
│ │ │ │ ├── test_partner_analytics_utils.py
│ │ │ │ ├── test_subscription_properties.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ └── test_utils.py
│ │ │ ├── urls.py
│ │ │ ├── utils/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── hubspot.py
│ │ │ │ └── partner_analytics.py
│ │ │ └── views.py
│ │ ├── api/
│ │ │ ├── __init__.py
│ │ │ ├── accounting.py
│ │ │ ├── cors.py
│ │ │ ├── decorators.py
│ │ │ ├── domain_metadata.py
│ │ │ ├── es.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── keyset_paginator.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_alter_permissions.py
│ │ │ │ ├── 0003_populate_apiuser.py
│ │ │ │ ├── 0004_rename_apiuser.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── object_fetch_api.py
│ │ │ ├── odata/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── serializers.py
│ │ │ │ ├── tests/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── data/
│ │ │ │ │ │ ├── populated_case_odata_metadata_document_from_config.xml
│ │ │ │ │ │ └── populated_form_odata_metadata_document_from_config.xml
│ │ │ │ │ ├── test_auth.py
│ │ │ │ │ ├── test_feed.py
│ │ │ │ │ ├── test_metadata.py
│ │ │ │ │ ├── test_models.py
│ │ │ │ │ ├── test_serializers.py
│ │ │ │ │ ├── test_service.py
│ │ │ │ │ └── utils.py
│ │ │ │ ├── urls.py
│ │ │ │ ├── utils.py
│ │ │ │ └── views.py
│ │ │ ├── query_adapters.py
│ │ │ ├── resources/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── auth.py
│ │ │ │ ├── messaging_event/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── filters.py
│ │ │ │ │ ├── pagination.py
│ │ │ │ │ ├── serializers.py
│ │ │ │ │ ├── utils.py
│ │ │ │ │ └── view.py
│ │ │ │ ├── meta.py
│ │ │ │ ├── pagination.py
│ │ │ │ ├── serializers.py
│ │ │ │ ├── v0_1.py
│ │ │ │ ├── v0_3.py
│ │ │ │ ├── v0_4.py
│ │ │ │ ├── v0_5.py
│ │ │ │ └── v1_0.py
│ │ │ ├── serializers.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── api/
│ │ │ │ └── odata_metadata.xml
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_resources.py
│ │ │ │ ├── audit_resources.py
│ │ │ │ ├── case_resources.py
│ │ │ │ ├── core_api.py
│ │ │ │ ├── form_resources.py
│ │ │ │ ├── group_resources.py
│ │ │ │ ├── internal_resources.py
│ │ │ │ ├── keyset_paginator_tests.py
│ │ │ │ ├── lookup_table_resources.py
│ │ │ │ ├── test_auth.py
│ │ │ │ ├── test_det_export_resource.py
│ │ │ │ ├── test_es.py
│ │ │ │ ├── test_messaging_event_api.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_ucr_resources.py
│ │ │ │ ├── test_user_resources.py
│ │ │ │ ├── test_user_updates.py
│ │ │ │ ├── test_validation.py
│ │ │ │ └── utils.py
│ │ │ ├── urls.py
│ │ │ ├── user_updates.py
│ │ │ ├── util.py
│ │ │ └── validation.py
│ │ ├── app_execution/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── api.py
│ │ │ ├── apps.py
│ │ │ ├── const.py
│ │ │ ├── data_model/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base.py
│ │ │ │ ├── expectations.py
│ │ │ │ └── steps.py
│ │ │ ├── db_accessors.py
│ │ │ ├── exceptions.py
│ │ │ ├── forms.py
│ │ │ ├── har_parser.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ └── clean_har_file.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_alter_appworkflowconfig_unique_together.py
│ │ │ │ ├── 0003_appexecutionlog_updated.py
│ │ │ │ ├── 0004_alter_appworkflowconfig_notification_emails_and_more.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── app_execution/
│ │ │ │ └── js/
│ │ │ │ ├── workflow_charts.js
│ │ │ │ └── workflow_logs.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── app_execution/
│ │ │ │ ├── components/
│ │ │ │ │ ├── logs.html
│ │ │ │ │ └── title_bar.html
│ │ │ │ ├── workflow_form.html
│ │ │ │ ├── workflow_list.html
│ │ │ │ ├── workflow_log.html
│ │ │ │ ├── workflow_log_list.html
│ │ │ │ └── workflow_run.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ ├── case_list.json
│ │ │ │ │ ├── case_list_action.json
│ │ │ │ │ ├── reg_form.json
│ │ │ │ │ ├── search_again.json
│ │ │ │ │ ├── split_case_search_search.json
│ │ │ │ │ └── split_case_search_select.json
│ │ │ │ ├── mock_formplayer.py
│ │ │ │ ├── response_factory.py
│ │ │ │ ├── test_dsl.py
│ │ │ │ ├── test_execution.py
│ │ │ │ ├── test_expect_xpath.py
│ │ │ │ ├── test_expectations.py
│ │ │ │ ├── test_har_extract.py
│ │ │ │ ├── test_question_value.py
│ │ │ │ ├── test_steps.py
│ │ │ │ └── utils.py
│ │ │ ├── urls.py
│ │ │ ├── views.py
│ │ │ └── workflow_dsl.py
│ │ ├── app_manager/
│ │ │ ├── __init__.py
│ │ │ ├── _design/
│ │ │ │ ├── filters/
│ │ │ │ │ └── all_apps.js
│ │ │ │ └── views/
│ │ │ │ ├── applications/
│ │ │ │ │ └── map.js
│ │ │ │ ├── applications_brief/
│ │ │ │ │ └── map.js
│ │ │ │ ├── builds_by_date/
│ │ │ │ │ ├── map.js
│ │ │ │ │ └── reduce.js
│ │ │ │ └── saved_app/
│ │ │ │ └── map.js
│ │ │ ├── add_ons.py
│ │ │ ├── analytics.py
│ │ │ ├── app_schemas/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_case_metadata.py
│ │ │ │ ├── case_properties.py
│ │ │ │ ├── casedb_schema.py
│ │ │ │ ├── form_metadata.py
│ │ │ │ ├── session_schema.py
│ │ │ │ └── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_app_diffs.py
│ │ │ │ ├── test_case_meta.py
│ │ │ │ ├── test_case_properties.py
│ │ │ │ ├── test_form_metadata.py
│ │ │ │ └── test_schema.py
│ │ │ ├── app_strings.py
│ │ │ ├── commcare_settings.py
│ │ │ ├── const.py
│ │ │ ├── current_builds.py
│ │ │ ├── dbaccessors.py
│ │ │ ├── decorators.py
│ │ │ ├── detail_screen.py
│ │ │ ├── download_urls.py
│ │ │ ├── exceptions.py
│ │ │ ├── feature_support.py
│ │ │ ├── fields.py
│ │ │ ├── fixtures/
│ │ │ │ ├── __init__.py
│ │ │ │ └── mobile_ucr.py
│ │ │ ├── forms.py
│ │ │ ├── helpers/
│ │ │ │ ├── __init__.py
│ │ │ │ └── validators.py
│ │ │ ├── id_strings.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── _form_iterator.py
│ │ │ │ ├── add_case_tile_template_field.py
│ │ │ │ ├── add_resource_overrides.py
│ │ │ │ ├── applications_with_add_ons.py
│ │ │ │ ├── audit_case_search_config.py
│ │ │ │ ├── audit_form_questions.py
│ │ │ │ ├── benchmark_build_times.py
│ │ │ │ ├── benchmark_direct_ccz.py
│ │ │ │ ├── check_for_form_entities.py
│ │ │ │ ├── check_for_xss_attempts.py
│ │ │ │ ├── delete_case_list_custom_variables_xml.py
│ │ │ │ ├── download_app_forms.py
│ │ │ │ ├── fix_app_docs_with_empty_keys.py
│ │ │ │ ├── fix_broken_blob_references.py
│ │ │ │ ├── helpers.py
│ │ │ │ ├── import_app.py
│ │ │ │ ├── migrate_address_popup.py
│ │ │ │ ├── migrate_case_list_custom_variables.py
│ │ │ │ ├── overwrite_app.py
│ │ │ │ └── upload_app_forms.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_linked_app_domain.py
│ │ │ │ ├── 0002_latestenabledbuildprofiles.py
│ │ │ │ ├── 0003_auto_20190326_0853.py
│ │ │ │ ├── 0004_latestenabledbuildprofiles_active.py
│ │ │ │ ├── 0005_latestenabledbuildprofiles_domain.py
│ │ │ │ ├── 0006_multi_master_linked_apps.py
│ │ │ │ ├── 0007_add_linked_app_fields_to_es.py
│ │ │ │ ├── 0008_remove_uses_master_app_form_ids.py
│ │ │ │ ├── 0009_add_sqlglobalappconfig.py
│ │ │ │ ├── 0009_resourceoverride.py
│ │ │ │ ├── 0010_merge_20191211_1921.py
│ │ │ │ ├── 0010_run_add_resource_overrides.py
│ │ │ │ ├── 0010_sqlglobalappconfig.py
│ │ │ │ ├── 0011_merge_20191212_0106.py
│ │ │ │ ├── 0011_merge_20191212_0936.py
│ │ │ │ ├── 0012_merge_20191217_0605.py
│ │ │ │ ├── 0013_rename_sqlglobalappconfig.py
│ │ │ │ ├── 0014_create_exchangeapplication.py
│ │ │ │ ├── 0015_alter_exchangeapplication.py
│ │ │ │ ├── 0016_alter_exchangeapplication.py
│ │ │ │ ├── 0017_migrate_case_search_relevant.py
│ │ │ │ ├── 0018_migrate_case_search_labels.py
│ │ │ │ ├── 0019_exchangeapplication_required_privileges.py
│ │ │ │ ├── 0020_exchangeapplication_allow_blank_privilege.py
│ │ │ │ ├── 0021_migrate_case_search_itemset_ids.py
│ │ │ │ ├── 0022_migrate_to_conditional_case_update.py
│ │ │ │ ├── 0023_applicationreleaselog.py
│ │ │ │ ├── 0024_applicationreleaselog_info.py
│ │ │ │ ├── 0025_migrate_to_search_filter_flag.py
│ │ │ │ ├── 0026_conditional_case_update_deleted_apps.py
│ │ │ │ ├── 0027_add_case_tile_template_field.py
│ │ │ │ ├── 0028_case_list_custom_variable_xml_to_dict.py
│ │ │ │ ├── 0029_delete_case_list_custom_variable_xml.py
│ │ │ │ ├── 0030_credentialapplication.py
│ │ │ │ ├── 0031_delete_latestenabledbuildprofiles.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── remote_app.py
│ │ │ ├── signals.py
│ │ │ ├── static/
│ │ │ │ └── app_manager/
│ │ │ │ ├── css/
│ │ │ │ │ └── diff.css
│ │ │ │ ├── js/
│ │ │ │ │ ├── app_exchange.js
│ │ │ │ │ ├── app_manager_utils.js
│ │ │ │ │ ├── app_view.js
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── add_ons.js
│ │ │ │ │ │ ├── app_manager.js
│ │ │ │ │ │ ├── app_manager_media.js
│ │ │ │ │ │ ├── download_async_modal.js
│ │ │ │ │ │ ├── download_index_main.js
│ │ │ │ │ │ ├── manage_releases_by_location.js
│ │ │ │ │ │ ├── multimedia_size_util.js
│ │ │ │ │ │ ├── nav_menu_media.js
│ │ │ │ │ │ ├── nav_menu_media_common.js
│ │ │ │ │ │ ├── preview_app.js
│ │ │ │ │ │ ├── source_files.js
│ │ │ │ │ │ ├── supported_languages.js
│ │ │ │ │ │ └── visit_scheduler.js
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── add_ons.js
│ │ │ │ │ │ ├── app_manager.js
│ │ │ │ │ │ ├── app_manager_media.js
│ │ │ │ │ │ ├── download_async_modal.js
│ │ │ │ │ │ ├── download_index_main.js
│ │ │ │ │ │ ├── manage_releases_by_location.js
│ │ │ │ │ │ ├── multimedia_size_util.js
│ │ │ │ │ │ ├── nav_menu_media.js
│ │ │ │ │ │ ├── nav_menu_media_common.js
│ │ │ │ │ │ ├── preview_app.js
│ │ │ │ │ │ ├── source_files.js
│ │ │ │ │ │ ├── supported_languages.js
│ │ │ │ │ │ └── visit_scheduler.js
│ │ │ │ │ ├── case_config_utils.js
│ │ │ │ │ ├── custom_assertions.js
│ │ │ │ │ ├── custom_icon.js
│ │ │ │ │ ├── details/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── case_list_callout.js
│ │ │ │ │ │ │ ├── column.js
│ │ │ │ │ │ │ ├── detail_tab_nodeset.js
│ │ │ │ │ │ │ ├── graph_config.js
│ │ │ │ │ │ │ ├── screen.js
│ │ │ │ │ │ │ ├── screen_config.js
│ │ │ │ │ │ │ └── sort_rows.js
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ ├── case_list_callout.js
│ │ │ │ │ │ │ ├── column.js
│ │ │ │ │ │ │ ├── detail_tab_nodeset.js
│ │ │ │ │ │ │ ├── graph_config.js
│ │ │ │ │ │ │ ├── screen.js
│ │ │ │ │ │ │ ├── screen_config.js
│ │ │ │ │ │ │ └── sort_rows.js
│ │ │ │ │ │ ├── case_claim.js
│ │ │ │ │ │ ├── filter.js
│ │ │ │ │ │ ├── fixture_select.js
│ │ │ │ │ │ ├── parent_select.js
│ │ │ │ │ │ └── utils.js
│ │ │ │ │ ├── forms/
│ │ │ │ │ │ ├── advanced/
│ │ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ │ ├── actions.js
│ │ │ │ │ │ │ │ └── case_config_ui.js
│ │ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ │ ├── actions.js
│ │ │ │ │ │ │ │ └── case_config_ui.js
│ │ │ │ │ │ │ └── case_properties.js
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── case_config_ui.js
│ │ │ │ │ │ │ ├── form_designer.js
│ │ │ │ │ │ │ ├── form_view.js
│ │ │ │ │ │ │ └── form_workflow.js
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ ├── case_config_ui.js
│ │ │ │ │ │ │ ├── form_designer.js
│ │ │ │ │ │ │ ├── form_view.js
│ │ │ │ │ │ │ └── form_workflow.js
│ │ │ │ │ │ ├── case_knockout_bindings.js
│ │ │ │ │ │ ├── copy_form_to_app.js
│ │ │ │ │ │ ├── custom_instances.js
│ │ │ │ │ │ ├── edit_form_details.js
│ │ │ │ │ │ ├── form_action_diffs.js
│ │ │ │ │ │ └── form_designer_analytics.js
│ │ │ │ │ ├── menu.js
│ │ │ │ │ ├── modules/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── module_view.js
│ │ │ │ │ │ │ ├── module_view_report.js
│ │ │ │ │ │ │ └── report_module.js
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ ├── module_view.js
│ │ │ │ │ │ │ ├── module_view_report.js
│ │ │ │ │ │ │ └── report_module.js
│ │ │ │ │ │ ├── case_list_setting.js
│ │ │ │ │ │ └── shadow_module_settings.js
│ │ │ │ │ ├── releases/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── app_view_release_manager.js
│ │ │ │ │ │ │ ├── language_profiles.js
│ │ │ │ │ │ │ ├── releases.js
│ │ │ │ │ │ │ └── update_prompt.js
│ │ │ │ │ │ └── bootstrap5/
│ │ │ │ │ │ ├── app_view_release_manager.js
│ │ │ │ │ │ ├── language_profiles.js
│ │ │ │ │ │ ├── releases.js
│ │ │ │ │ │ └── update_prompt.js
│ │ │ │ │ ├── section_changer.js
│ │ │ │ │ ├── settings/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── app_logos.js
│ │ │ │ │ │ │ └── commcare_settings.js
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ ├── app_logos.js
│ │ │ │ │ │ │ └── commcare_settings.js
│ │ │ │ │ │ └── translations.js
│ │ │ │ │ ├── summary/
│ │ │ │ │ │ ├── case_summary.js
│ │ │ │ │ │ ├── form_diff.js
│ │ │ │ │ │ ├── form_models.js
│ │ │ │ │ │ ├── form_summary.js
│ │ │ │ │ │ ├── models.js
│ │ │ │ │ │ └── utils.js
│ │ │ │ │ ├── vellum/
│ │ │ │ │ │ ├── 1307.js
│ │ │ │ │ │ ├── 150.js
│ │ │ │ │ │ ├── 1664.js
│ │ │ │ │ │ ├── 1879.js
│ │ │ │ │ │ ├── 2229.js
│ │ │ │ │ │ ├── 2773.js
│ │ │ │ │ │ ├── 2979.js
│ │ │ │ │ │ ├── 3333.js
│ │ │ │ │ │ ├── 3534.js
│ │ │ │ │ │ ├── 3576.js
│ │ │ │ │ │ ├── 358.js
│ │ │ │ │ │ ├── 3588.js
│ │ │ │ │ │ ├── 3606.js
│ │ │ │ │ │ ├── 3720.js
│ │ │ │ │ │ ├── 375.js
│ │ │ │ │ │ ├── 395.js
│ │ │ │ │ │ ├── 3984.js
│ │ │ │ │ │ ├── 4002.js
│ │ │ │ │ │ ├── 4054.js
│ │ │ │ │ │ ├── 4141.js
│ │ │ │ │ │ ├── 4194.js
│ │ │ │ │ │ ├── 4334.js
│ │ │ │ │ │ ├── 4437.js
│ │ │ │ │ │ ├── 4518.js
│ │ │ │ │ │ ├── 4580.js
│ │ │ │ │ │ ├── 4636.js
│ │ │ │ │ │ ├── 4730.js
│ │ │ │ │ │ ├── 477.js
│ │ │ │ │ │ ├── 5051.js
│ │ │ │ │ │ ├── 5319.js
│ │ │ │ │ │ ├── 5455.js
│ │ │ │ │ │ ├── 5460.js
│ │ │ │ │ │ ├── 5498.js
│ │ │ │ │ │ ├── 571.js
│ │ │ │ │ │ ├── 576.js
│ │ │ │ │ │ ├── 6077.js
│ │ │ │ │ │ ├── 6129.js
│ │ │ │ │ │ ├── 6367.js
│ │ │ │ │ │ ├── 6414.js
│ │ │ │ │ │ ├── 6794.js
│ │ │ │ │ │ ├── 6940.js
│ │ │ │ │ │ ├── 711.js
│ │ │ │ │ │ ├── 711.js.LICENSE.txt
│ │ │ │ │ │ ├── 7574.js
│ │ │ │ │ │ ├── 7828.js
│ │ │ │ │ │ ├── 8324.js
│ │ │ │ │ │ ├── 8324.js.LICENSE.txt
│ │ │ │ │ │ ├── 8454.js
│ │ │ │ │ │ ├── 8985.js
│ │ │ │ │ │ ├── 9084.js
│ │ │ │ │ │ ├── 9084.js.LICENSE.txt
│ │ │ │ │ │ ├── 9928.js
│ │ │ │ │ │ ├── lib/
│ │ │ │ │ │ │ ├── SaveButton.js
│ │ │ │ │ │ │ └── diff_match_patch.js
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ ├── main.js.LICENSE.txt
│ │ │ │ │ │ ├── manifest.txt
│ │ │ │ │ │ ├── tests.js
│ │ │ │ │ │ ├── tests.js.LICENSE.txt
│ │ │ │ │ │ └── version.txt
│ │ │ │ │ ├── widgets.js
│ │ │ │ │ └── xpathValidator.js
│ │ │ │ ├── json/
│ │ │ │ │ ├── case-reserved-words.json
│ │ │ │ │ ├── commcare-app-settings.yml
│ │ │ │ │ ├── commcare-profile-settings.yml
│ │ │ │ │ ├── commcare-settings-layout.yml
│ │ │ │ │ └── vellum-app-callout-templates.yml
│ │ │ │ ├── spec/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── download_async_modal_spec.js
│ │ │ │ │ │ ├── form_workflow_spec.js
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ └── releases_spec.js
│ │ │ │ │ └── bootstrap5/
│ │ │ │ │ ├── download_async_modal_spec.js
│ │ │ │ │ ├── form_workflow_spec.js
│ │ │ │ │ ├── main.js
│ │ │ │ │ └── releases_spec.js
│ │ │ │ └── template_apps/
│ │ │ │ ├── agriculture/
│ │ │ │ │ ├── app.json
│ │ │ │ │ └── multimedia.json
│ │ │ │ ├── health/
│ │ │ │ │ ├── app.json
│ │ │ │ │ └── multimedia.json
│ │ │ │ ├── wash/
│ │ │ │ │ ├── app.json
│ │ │ │ │ └── multimedia.json
│ │ │ │ └── wash_before_cond_case_update/
│ │ │ │ └── app.json
│ │ │ ├── static_strings.py
│ │ │ ├── suite_xml/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── case_tile_templates/
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── icon_text_grid.json
│ │ │ │ │ ├── icon_text_grid.xml
│ │ │ │ │ ├── person_simple.json
│ │ │ │ │ └── person_simple.xml
│ │ │ │ ├── const.py
│ │ │ │ ├── contributors.py
│ │ │ │ ├── features/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── case_tiles.py
│ │ │ │ │ ├── mobile_ucr.py
│ │ │ │ │ └── scheduler.py
│ │ │ │ ├── generator.py
│ │ │ │ ├── post_process/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── endpoints.py
│ │ │ │ │ ├── instances.py
│ │ │ │ │ ├── menu.py
│ │ │ │ │ ├── remote_requests.py
│ │ │ │ │ ├── resources.py
│ │ │ │ │ └── workflow.py
│ │ │ │ ├── sections/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── details.py
│ │ │ │ │ ├── entries.py
│ │ │ │ │ ├── fixtures.py
│ │ │ │ │ ├── menus.py
│ │ │ │ │ └── resources.py
│ │ │ │ ├── utils.py
│ │ │ │ └── xml_models.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── app_manager/
│ │ │ │ ├── CommCare.jad
│ │ │ │ ├── app_exchange.html
│ │ │ │ ├── app_view_release_manager.html
│ │ │ │ ├── app_view_settings.html
│ │ │ │ ├── base_summary.html
│ │ │ │ ├── blank_form.xml
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── app_diff.html
│ │ │ │ │ ├── app_view.html
│ │ │ │ │ ├── apps_base.html
│ │ │ │ │ ├── download_index.html
│ │ │ │ │ ├── form_designer.html
│ │ │ │ │ ├── form_view.html
│ │ │ │ │ ├── module_view.html
│ │ │ │ │ ├── module_view_advanced.html
│ │ │ │ │ ├── module_view_report.html
│ │ │ │ │ ├── no_longer_supported.html
│ │ │ │ │ ├── odk_install.html
│ │ │ │ │ └── source_files.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── app_diff.html
│ │ │ │ │ ├── app_view.html
│ │ │ │ │ ├── apps_base.html
│ │ │ │ │ ├── download_index.html
│ │ │ │ │ ├── form_designer.html
│ │ │ │ │ ├── form_view.html
│ │ │ │ │ ├── module_view.html
│ │ │ │ │ ├── module_view_advanced.html
│ │ │ │ │ ├── module_view_report.html
│ │ │ │ │ ├── no_longer_supported.html
│ │ │ │ │ ├── odk_install.html
│ │ │ │ │ └── source_files.html
│ │ │ │ ├── case_summary.html
│ │ │ │ ├── default_followup_form.xml
│ │ │ │ ├── form_summary.html
│ │ │ │ ├── form_summary_base.html
│ │ │ │ ├── form_summary_diff.html
│ │ │ │ ├── languages.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── add_item_templates.html
│ │ │ │ │ │ ├── add_new_module_modal.html
│ │ │ │ │ │ ├── app_summary_button.html
│ │ │ │ │ │ ├── appnav_menu_header.html
│ │ │ │ │ │ ├── appnav_menu_langs.html
│ │ │ │ │ │ ├── apps_stylesheets.html
│ │ │ │ │ │ ├── build_errors.html
│ │ │ │ │ │ ├── build_profiles_select.html
│ │ │ │ │ │ ├── case_list_session_endpoint.html
│ │ │ │ │ │ ├── confirm_delete_app.html
│ │ │ │ │ │ ├── custom_icon.html
│ │ │ │ │ │ ├── define_case_type_modal.html
│ │ │ │ │ │ ├── download_async_modal.html
│ │ │ │ │ │ ├── form_session_endpoint.html
│ │ │ │ │ │ ├── function_datum_endpoints.html
│ │ │ │ │ │ ├── load_save_questions.html
│ │ │ │ │ │ ├── module_session_endpoint.html
│ │ │ │ │ │ ├── nav_menu_media.html
│ │ │ │ │ │ ├── nav_menu_media_single_type.html
│ │ │ │ │ │ ├── toggle_diff_modal.html
│ │ │ │ │ │ └── undo_delete_app.html
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── add_item_templates.html
│ │ │ │ │ │ ├── add_new_module_modal.html
│ │ │ │ │ │ ├── app_summary_button.html
│ │ │ │ │ │ ├── appnav_menu_header.html
│ │ │ │ │ │ ├── appnav_menu_langs.html
│ │ │ │ │ │ ├── apps_stylesheets.html
│ │ │ │ │ │ ├── build_errors.html
│ │ │ │ │ │ ├── build_profiles_select.html
│ │ │ │ │ │ ├── case_list_session_endpoint.html
│ │ │ │ │ │ ├── confirm_delete_app.html
│ │ │ │ │ │ ├── custom_icon.html
│ │ │ │ │ │ ├── define_case_type_modal.html
│ │ │ │ │ │ ├── download_async_modal.html
│ │ │ │ │ │ ├── form_session_endpoint.html
│ │ │ │ │ │ ├── function_datum_endpoints.html
│ │ │ │ │ │ ├── load_save_questions.html
│ │ │ │ │ │ ├── module_session_endpoint.html
│ │ │ │ │ │ ├── nav_menu_media.html
│ │ │ │ │ │ ├── nav_menu_media_single_type.html
│ │ │ │ │ │ ├── toggle_diff_modal.html
│ │ │ │ │ │ └── undo_delete_app.html
│ │ │ │ │ ├── bulk_upload_app_translations.html
│ │ │ │ │ ├── case_summary_case_details.html
│ │ │ │ │ ├── form_error_message.html
│ │ │ │ │ ├── form_summary_header.html
│ │ │ │ │ ├── forms/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── case_config.html
│ │ │ │ │ │ │ ├── case_config_advanced.html
│ │ │ │ │ │ │ ├── case_config_ko_templates.html
│ │ │ │ │ │ │ ├── custom_assertions.html
│ │ │ │ │ │ │ ├── custom_instances.html
│ │ │ │ │ │ │ ├── form_filter.html
│ │ │ │ │ │ │ ├── form_gps_capture.html
│ │ │ │ │ │ │ ├── form_tab_advanced.html
│ │ │ │ │ │ │ ├── form_tab_settings.html
│ │ │ │ │ │ │ ├── form_tab_visit_scheduler.html
│ │ │ │ │ │ │ ├── form_view_modals.html
│ │ │ │ │ │ │ ├── form_workflow.html
│ │ │ │ │ │ │ ├── release_notes_setting.html
│ │ │ │ │ │ │ ├── shadow_parent_select.html
│ │ │ │ │ │ │ └── usercase_config.html
│ │ │ │ │ │ └── bootstrap5/
│ │ │ │ │ │ ├── case_config.html
│ │ │ │ │ │ ├── case_config_advanced.html
│ │ │ │ │ │ ├── case_config_ko_templates.html
│ │ │ │ │ │ ├── custom_assertions.html
│ │ │ │ │ │ ├── custom_instances.html
│ │ │ │ │ │ ├── form_filter.html
│ │ │ │ │ │ ├── form_gps_capture.html
│ │ │ │ │ │ ├── form_tab_advanced.html
│ │ │ │ │ │ ├── form_tab_settings.html
│ │ │ │ │ │ ├── form_tab_visit_scheduler.html
│ │ │ │ │ │ ├── form_view_modals.html
│ │ │ │ │ │ ├── form_workflow.html
│ │ │ │ │ │ ├── release_notes_setting.html
│ │ │ │ │ │ ├── shadow_parent_select.html
│ │ │ │ │ │ └── usercase_config.html
│ │ │ │ │ ├── menu/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── appnav_menu.html
│ │ │ │ │ │ │ ├── form_link.html
│ │ │ │ │ │ │ └── module_link.html
│ │ │ │ │ │ └── bootstrap5/
│ │ │ │ │ │ ├── appnav_menu.html
│ │ │ │ │ │ ├── form_link.html
│ │ │ │ │ │ └── module_link.html
│ │ │ │ │ ├── mobile_ucr_v1_warning.html
│ │ │ │ │ ├── module_options/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── module_option_advanced.html
│ │ │ │ │ │ │ ├── module_option_biometrics.html
│ │ │ │ │ │ │ ├── module_option_button_base.html
│ │ │ │ │ │ │ ├── module_option_case.html
│ │ │ │ │ │ │ ├── module_option_report.html
│ │ │ │ │ │ │ ├── module_option_shadow.html
│ │ │ │ │ │ │ ├── module_option_shadow_v1.html
│ │ │ │ │ │ │ ├── module_option_survey.html
│ │ │ │ │ │ │ └── module_option_training.html
│ │ │ │ │ │ └── bootstrap5/
│ │ │ │ │ │ ├── module_option_advanced.html
│ │ │ │ │ │ ├── module_option_biometrics.html
│ │ │ │ │ │ ├── module_option_button_base.html
│ │ │ │ │ │ ├── module_option_case.html
│ │ │ │ │ │ ├── module_option_report.html
│ │ │ │ │ │ ├── module_option_shadow.html
│ │ │ │ │ │ ├── module_option_shadow_v1.html
│ │ │ │ │ │ ├── module_option_survey.html
│ │ │ │ │ │ └── module_option_training.html
│ │ │ │ │ ├── modules/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── add_property_button.html
│ │ │ │ │ │ │ ├── case_detail.html
│ │ │ │ │ │ │ ├── case_list.html
│ │ │ │ │ │ │ ├── case_list_filtering.html
│ │ │ │ │ │ │ ├── case_list_form_setting.html
│ │ │ │ │ │ │ ├── case_list_lazy_loading.html
│ │ │ │ │ │ │ ├── case_list_lookup.html
│ │ │ │ │ │ │ ├── case_list_missing_warning.html
│ │ │ │ │ │ │ ├── case_list_module_overwrite.html
│ │ │ │ │ │ │ ├── case_list_multi_select.html
│ │ │ │ │ │ │ ├── case_list_properties.html
│ │ │ │ │ │ │ ├── case_list_setting.html
│ │ │ │ │ │ │ ├── case_search_properties.html
│ │ │ │ │ │ │ ├── case_search_property.html
│ │ │ │ │ │ │ ├── case_tile_preview.html
│ │ │ │ │ │ │ ├── case_tile_templates.html
│ │ │ │ │ │ │ ├── custom_detail_variables.html
│ │ │ │ │ │ │ ├── enable_schedule.html
│ │ │ │ │ │ │ ├── filter_configs.html
│ │ │ │ │ │ │ ├── fixture_case_selection.html
│ │ │ │ │ │ │ ├── graph_configuration_modal.html
│ │ │ │ │ │ │ ├── mobile_report_configs.html
│ │ │ │ │ │ │ ├── module_actions.html
│ │ │ │ │ │ │ ├── module_filter.html
│ │ │ │ │ │ │ ├── module_view_case_type.html
│ │ │ │ │ │ │ ├── module_view_heading.html
│ │ │ │ │ │ │ ├── module_view_settings.html
│ │ │ │ │ │ │ ├── report_context_tile.html
│ │ │ │ │ │ │ └── style_configuration_modal.html
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ ├── add_property_button.html
│ │ │ │ │ │ │ ├── case_detail.html
│ │ │ │ │ │ │ ├── case_list.html
│ │ │ │ │ │ │ ├── case_list_filtering.html
│ │ │ │ │ │ │ ├── case_list_form_setting.html
│ │ │ │ │ │ │ ├── case_list_lazy_loading.html
│ │ │ │ │ │ │ ├── case_list_lookup.html
│ │ │ │ │ │ │ ├── case_list_missing_warning.html
│ │ │ │ │ │ │ ├── case_list_module_overwrite.html
│ │ │ │ │ │ │ ├── case_list_multi_select.html
│ │ │ │ │ │ │ ├── case_list_properties.html
│ │ │ │ │ │ │ ├── case_list_setting.html
│ │ │ │ │ │ │ ├── case_search_properties.html
│ │ │ │ │ │ │ ├── case_search_property.html
│ │ │ │ │ │ │ ├── case_tile_preview.html
│ │ │ │ │ │ │ ├── case_tile_templates.html
│ │ │ │ │ │ │ ├── custom_detail_variables.html
│ │ │ │ │ │ │ ├── enable_schedule.html
│ │ │ │ │ │ │ ├── filter_configs.html
│ │ │ │ │ │ │ ├── fixture_case_selection.html
│ │ │ │ │ │ │ ├── graph_configuration_modal.html
│ │ │ │ │ │ │ ├── mobile_report_configs.html
│ │ │ │ │ │ │ ├── module_actions.html
│ │ │ │ │ │ │ ├── module_filter.html
│ │ │ │ │ │ │ ├── module_view_case_type.html
│ │ │ │ │ │ │ ├── module_view_heading.html
│ │ │ │ │ │ │ ├── module_view_settings.html
│ │ │ │ │ │ │ ├── report_context_tile.html
│ │ │ │ │ │ │ └── style_configuration_modal.html
│ │ │ │ │ │ └── graph_configs.html
│ │ │ │ │ ├── multimedia_sizes.html
│ │ │ │ │ ├── preview_app.html
│ │ │ │ │ ├── releases/
│ │ │ │ │ │ ├── app_release_logs.html
│ │ │ │ │ │ ├── build_profiles.html
│ │ │ │ │ │ ├── releases.html
│ │ │ │ │ │ ├── releases_deploy_modal.html
│ │ │ │ │ │ ├── releases_deploy_modal_sms.html
│ │ │ │ │ │ └── releases_table.html
│ │ │ │ │ ├── settings/
│ │ │ │ │ │ ├── add_ons.html
│ │ │ │ │ │ ├── app_settings.html
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ └── supported_languages.html
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ └── supported_languages.html
│ │ │ │ │ │ ├── commcare_settings.html
│ │ │ │ │ │ └── multimedia_ajax.html
│ │ │ │ │ ├── vellum_case_management_warning.html
│ │ │ │ │ └── view_submissions_button.html
│ │ │ │ ├── profile.xml
│ │ │ │ ├── simprints_enrolment_form.xml
│ │ │ │ ├── simprints_followup_form.xml
│ │ │ │ └── spec/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ └── mocha.html
│ │ │ │ └── bootstrap5/
│ │ │ │ └── mocha.html
│ │ │ ├── templatetags/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_manager_extras.py
│ │ │ │ ├── url_extras.py
│ │ │ │ └── xforms_extras.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_factory.py
│ │ │ │ ├── data/
│ │ │ │ │ ├── bad_case_tile_config.json
│ │ │ │ │ ├── case_in_form.xml
│ │ │ │ │ ├── case_list_form/
│ │ │ │ │ │ ├── case-list-form-advanced-autoload.xml
│ │ │ │ │ │ ├── case-list-form-advanced.xml
│ │ │ │ │ │ ├── case-list-form-suite-form-nav-entry.xml
│ │ │ │ │ │ ├── case-list-form-suite-multiple-references.xml
│ │ │ │ │ │ ├── case-list-form-suite-no-media-partial.xml
│ │ │ │ │ │ ├── case-list-form-suite-parent-child-advanced.xml
│ │ │ │ │ │ ├── case-list-form-suite-parent-child-basic.xml
│ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-advanced-rename-var.xml
│ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-advanced.xml
│ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-basic.xml
│ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-mixed.xml
│ │ │ │ │ │ ├── case-list-form-suite-usercase.xml
│ │ │ │ │ │ ├── case-list-form-suite.xml
│ │ │ │ │ │ ├── case_list_form_advanced_form.xml
│ │ │ │ │ │ ├── case_list_form_basic_form.xml
│ │ │ │ │ │ ├── case_list_form_end_of_form_case_list.xml
│ │ │ │ │ │ ├── case_list_form_end_of_form_case_list_clmi_only.xml
│ │ │ │ │ │ ├── case_list_form_reg_form_creates_child_case.xml
│ │ │ │ │ │ ├── source_requires_case_target_doesnt.xml
│ │ │ │ │ │ └── target_module_different_datums.xml
│ │ │ │ │ ├── case_meta/
│ │ │ │ │ │ └── standard_questions.xml
│ │ │ │ │ ├── cyclical-app.json
│ │ │ │ │ ├── days_ago_migration.json
│ │ │ │ │ ├── days_ago_suite.xml
│ │ │ │ │ ├── duplicate_text_questions.xml
│ │ │ │ │ ├── extension_case/
│ │ │ │ │ │ ├── case_index_relationship_question.xml
│ │ │ │ │ │ ├── open_subcase.xml
│ │ │ │ │ │ ├── open_subcase_child.xml
│ │ │ │ │ │ ├── original.xml
│ │ │ │ │ │ ├── preload_host_case.xml
│ │ │ │ │ │ ├── preload_host_case_adv.xml
│ │ │ │ │ │ ├── update_host_case.xml
│ │ │ │ │ │ └── update_host_case_adv.xml
│ │ │ │ │ ├── fixtures/
│ │ │ │ │ │ ├── expected_v1_report.xml
│ │ │ │ │ │ ├── expected_v2_report.xml
│ │ │ │ │ │ └── expected_v2_report_total.xml
│ │ │ │ │ ├── form_preparation_v2/
│ │ │ │ │ │ ├── attachment.xml
│ │ │ │ │ │ ├── close_case.xml
│ │ │ │ │ │ ├── complex-case-sharing.json
│ │ │ │ │ │ ├── complex-case-sharing.xml
│ │ │ │ │ │ ├── gps.json
│ │ │ │ │ │ ├── gps_no_question.xml
│ │ │ │ │ │ ├── gps_no_question_auto.xml
│ │ │ │ │ │ ├── gps_with_question.xml
│ │ │ │ │ │ ├── gps_with_question_auto.xml
│ │ │ │ │ │ ├── ignore_retain.xml
│ │ │ │ │ │ ├── ignore_retain_stripped.xml
│ │ │ │ │ │ ├── missing_instances.xml
│ │ │ │ │ │ ├── multi_no_actions.xml
│ │ │ │ │ │ ├── multi_open_update_case.xml
│ │ │ │ │ │ ├── multiple_subcase_repeat.json
│ │ │ │ │ │ ├── multiple_subcase_repeat.xml
│ │ │ │ │ │ ├── no_actions.xml
│ │ │ │ │ │ ├── normal_suite.xml
│ │ │ │ │ │ ├── open_case.xml
│ │ │ │ │ │ ├── open_case_external_id.xml
│ │ │ │ │ │ ├── open_update_case.xml
│ │ │ │ │ │ ├── subcase-parent-ref.json
│ │ │ │ │ │ ├── subcase-parent-ref.xml
│ │ │ │ │ │ ├── subcase-repeat-sharing.xml
│ │ │ │ │ │ ├── subcase-repeat.json
│ │ │ │ │ │ ├── subcase-repeat.xml
│ │ │ │ │ │ ├── subcase_repeat_mixed_form_post.xml
│ │ │ │ │ │ ├── subcase_repeat_mixed_form_pre.xml
│ │ │ │ │ │ ├── task_mode_update_preload_case.xml
│ │ │ │ │ │ ├── update_attachment_case.xml
│ │ │ │ │ │ ├── update_case.xml
│ │ │ │ │ │ ├── update_parent_case.xml
│ │ │ │ │ │ └── update_preload_case.xml
│ │ │ │ │ ├── form_preparation_v2_advanced/
│ │ │ │ │ │ ├── attachment.xml
│ │ │ │ │ │ ├── child_module_adjusted_case_id_advanced.xml
│ │ │ │ │ │ ├── child_module_adjusted_case_id_basic.xml
│ │ │ │ │ │ ├── close_case.xml
│ │ │ │ │ │ ├── extension-case.xml
│ │ │ │ │ │ ├── no_actions.xml
│ │ │ │ │ │ ├── open_case.xml
│ │ │ │ │ │ ├── open_close_case.xml
│ │ │ │ │ │ ├── open_update_case.xml
│ │ │ │ │ │ ├── schedule.xml
│ │ │ │ │ │ ├── subcase-open.xml
│ │ │ │ │ │ ├── subcase-repeat-multiple.xml
│ │ │ │ │ │ ├── subcase-repeat-sharing.xml
│ │ │ │ │ │ ├── subcase-repeat.xml
│ │ │ │ │ │ ├── subcase.xml
│ │ │ │ │ │ ├── subcase_original.xml
│ │ │ │ │ │ ├── update_attachment_case.xml
│ │ │ │ │ │ ├── update_case.xml
│ │ │ │ │ │ ├── update_parent_case.xml
│ │ │ │ │ │ ├── update_preload_case.xml
│ │ │ │ │ │ └── update_preload_case_multiple.xml
│ │ │ │ │ ├── form_with_fixtures.xml
│ │ │ │ │ ├── form_with_repeats.xml
│ │ │ │ │ ├── form_workflow/
│ │ │ │ │ │ ├── form_link_basic_form.xml
│ │ │ │ │ │ ├── form_link_child_modules.xml
│ │ │ │ │ │ ├── form_link_create_update_case.xml
│ │ │ │ │ │ ├── form_link_enikshay.xml
│ │ │ │ │ │ ├── form_link_followup_module.xml
│ │ │ │ │ │ ├── form_link_multiple.xml
│ │ │ │ │ │ ├── form_link_registration_module.xml
│ │ │ │ │ │ ├── form_link_submodule.xml
│ │ │ │ │ │ ├── form_link_tdh.xml
│ │ │ │ │ │ ├── form_link_tdh_with_fallback_module.xml
│ │ │ │ │ │ ├── form_link_tdh_with_fallback_previous.xml
│ │ │ │ │ │ ├── form_link_tdh_with_fallback_root.xml
│ │ │ │ │ │ ├── form_link_update_case.xml
│ │ │ │ │ │ ├── suite-workflow-module-in-root.xml
│ │ │ │ │ │ ├── suite-workflow-module.xml
│ │ │ │ │ │ ├── suite-workflow-previous.xml
│ │ │ │ │ │ └── suite-workflow-root.xml
│ │ │ │ │ ├── itext_form.xml
│ │ │ │ │ ├── itext_form_normalized.xml
│ │ │ │ │ ├── label_form.xml
│ │ │ │ │ ├── list_apps/
│ │ │ │ │ │ └── list_apps.xml
│ │ │ │ │ ├── one_question_two_images.xml
│ │ │ │ │ ├── original_form.xml
│ │ │ │ │ ├── save_to_case_in_groups.xml
│ │ │ │ │ ├── session_endpoint_remote_request.xml
│ │ │ │ │ ├── session_endpoint_remote_request_multi_select.xml
│ │ │ │ │ ├── subcase-details.json
│ │ │ │ │ ├── suite/
│ │ │ │ │ │ ├── advanced_module_parent.xml
│ │ │ │ │ │ ├── advanced_module_parent_filters.xml
│ │ │ │ │ │ ├── advanced_module_parent_null_relationship.xml
│ │ │ │ │ │ ├── advanced_submodule_xform.xml
│ │ │ │ │ │ ├── app.json
│ │ │ │ │ │ ├── app_attached_image.json
│ │ │ │ │ │ ├── app_audio_format.json
│ │ │ │ │ │ ├── app_case_detail_instances.json
│ │ │ │ │ │ ├── app_case_detail_tabs.json
│ │ │ │ │ │ ├── app_case_detail_tabs_with_nodesets.json
│ │ │ │ │ │ ├── app_case_sharing.json
│ │ │ │ │ │ ├── app_case_tiles.json
│ │ │ │ │ │ ├── app_fixture_graphing.json
│ │ │ │ │ │ ├── app_graphing.json
│ │ │ │ │ │ ├── app_no_case_sharing.json
│ │ │ │ │ │ ├── app_video_inline.json
│ │ │ │ │ │ ├── autoload_supplypoint.xml
│ │ │ │ │ │ ├── basic_submodule_xform.xml
│ │ │ │ │ │ ├── call-center.json
│ │ │ │ │ │ ├── call-center.xml
│ │ │ │ │ │ ├── case-search-again-with-action.xml
│ │ │ │ │ │ ├── case-search-again-with-localized-action.xml
│ │ │ │ │ │ ├── case-search-with-action.xml
│ │ │ │ │ │ ├── case-search-with-localized-action.xml
│ │ │ │ │ │ ├── case-tile-case-detail-tabs.xml
│ │ │ │ │ │ ├── case-tile-case-detail.xml
│ │ │ │ │ │ ├── case_list_media_suite.xml
│ │ │ │ │ │ ├── case_tile_pulldown_session.xml
│ │ │ │ │ │ ├── case_tile_template_format.xml
│ │ │ │ │ │ ├── child-module-entry-datums-added-advanced.xml
│ │ │ │ │ │ ├── child-module-entry-datums-added-basic.xml
│ │ │ │ │ │ ├── child-module-entry-datums-added-usercase.xml
│ │ │ │ │ │ ├── child-module-entry-datums-parent-select-other-same-case-type.xml
│ │ │ │ │ │ ├── child-module-entry-datums-parent-select-other.xml
│ │ │ │ │ │ ├── child-module-entry-datums.xml
│ │ │ │ │ │ ├── child-module-form-workflow-previous.xml
│ │ │ │ │ │ ├── child-module-grandchild-case.xml
│ │ │ │ │ │ ├── child-module-preload-parent-ref.xml
│ │ │ │ │ │ ├── child-module-rename-session-vars.xml
│ │ │ │ │ │ ├── child-module-with-parent-select-and-case-list.xml
│ │ │ │ │ │ ├── child-module-with-parent-select-entry-datums-added.xml
│ │ │ │ │ │ ├── custom-parent-ref.xml
│ │ │ │ │ │ ├── fixture-to-case-selection-localization.xml
│ │ │ │ │ │ ├── fixture-to-case-selection-parent-child.xml
│ │ │ │ │ │ ├── fixture-to-case-selection-with-form-filtering.xml
│ │ │ │ │ │ ├── fixture-to-case-selection.xml
│ │ │ │ │ │ ├── form_media_suite.xml
│ │ │ │ │ │ ├── form_media_suite_all.xml
│ │ │ │ │ │ ├── form_media_suite_en.xml
│ │ │ │ │ │ ├── form_media_suite_hin.xml
│ │ │ │ │ │ ├── form_with_media_refs.xml
│ │ │ │ │ │ ├── load_case_from_custom_fixture_instance.xml
│ │ │ │ │ │ ├── load_case_from_custom_fixture_session.xml
│ │ │ │ │ │ ├── load_case_from_fixture_arbitrary_datum.xml
│ │ │ │ │ │ ├── load_case_from_fixture_instance.xml
│ │ │ │ │ │ ├── load_case_from_fixture_session.xml
│ │ │ │ │ │ ├── load_case_from_report_fixture_instance.xml
│ │ │ │ │ │ ├── load_case_from_report_fixture_session.xml
│ │ │ │ │ │ ├── load_from_fixture_autoselect_session.xml
│ │ │ │ │ │ ├── load_from_fixture_instance.xml
│ │ │ │ │ │ ├── load_from_fixture_session.xml
│ │ │ │ │ │ ├── media-suite-lazy-false.xml
│ │ │ │ │ │ ├── media-suite-lazy-true.xml
│ │ │ │ │ │ ├── module-filter-user-entry.xml
│ │ │ │ │ │ ├── module-filter-user.xml
│ │ │ │ │ │ ├── module-filter.xml
│ │ │ │ │ │ ├── multi-sort.json
│ │ │ │ │ │ ├── multi-sort.xml
│ │ │ │ │ │ ├── multi_select_case_list/
│ │ │ │ │ │ │ └── basic_remote_request.xml
│ │ │ │ │ │ ├── normal-suite.xml
│ │ │ │ │ │ ├── open_case_and_subcase.xml
│ │ │ │ │ │ ├── owner-name.json
│ │ │ │ │ │ ├── owner-name.xml
│ │ │ │ │ │ ├── parent_null_relationship_same_type.xml
│ │ │ │ │ │ ├── remote_request.xml
│ │ │ │ │ │ ├── reports_module_data_detail-translated-mixed.xml
│ │ │ │ │ │ ├── reports_module_data_detail-translated-simple.xml
│ │ │ │ │ │ ├── reports_module_data_detail-translated.xml
│ │ │ │ │ │ ├── reports_module_data_detail.xml
│ │ │ │ │ │ ├── reports_module_data_entry.xml
│ │ │ │ │ │ ├── reports_module_data_entry_mobile_ucr_v1.xml
│ │ │ │ │ │ ├── reports_module_menu.xml
│ │ │ │ │ │ ├── reports_module_menu_multimedia.xml
│ │ │ │ │ │ ├── reports_module_select_detail.xml
│ │ │ │ │ │ ├── reports_module_summary_detail_hide_data_table.xml
│ │ │ │ │ │ ├── reports_module_summary_detail_use_localized_description.xml
│ │ │ │ │ │ ├── reports_module_summary_detail_use_xpath_description.xml
│ │ │ │ │ │ ├── schedule-entry.xml
│ │ │ │ │ │ ├── schedule-fixture.xml
│ │ │ │ │ │ ├── search_command_detail.xml
│ │ │ │ │ │ ├── search_config_blacklisted_owners.xml
│ │ │ │ │ │ ├── search_config_default_only.xml
│ │ │ │ │ │ ├── shadow_module.json
│ │ │ │ │ │ ├── shadow_module.xml
│ │ │ │ │ │ ├── shadow_module_cases.json
│ │ │ │ │ │ ├── shadow_module_cases.xml
│ │ │ │ │ │ ├── shadow_module_families_source_child.xml
│ │ │ │ │ │ ├── shadow_module_families_source_parent.xml
│ │ │ │ │ │ ├── shadow_module_families_source_parent_forms_only.xml
│ │ │ │ │ │ ├── shadow_module_families_source_parent_forms_only_v1.xml
│ │ │ │ │ │ ├── shadow_module_families_source_parent_v1.xml
│ │ │ │ │ │ ├── shadow_module_forms_only.json
│ │ │ │ │ │ ├── shadow_module_forms_only.xml
│ │ │ │ │ │ ├── shadow_module_source_parent_v1_v2.xml
│ │ │ │ │ │ ├── smart_link_remote_request.xml
│ │ │ │ │ │ ├── sort-cache-search.xml
│ │ │ │ │ │ ├── sort-cache.xml
│ │ │ │ │ │ ├── sort-only-value.json
│ │ │ │ │ │ ├── sort-only-value.xml
│ │ │ │ │ │ ├── suite-advanced-autoselect-case-mobile.xml
│ │ │ │ │ │ ├── suite-advanced-autoselect-case.xml
│ │ │ │ │ │ ├── suite-advanced-autoselect-fixture.xml
│ │ │ │ │ │ ├── suite-advanced-autoselect-raw.xml
│ │ │ │ │ │ ├── suite-advanced-autoselect-user.xml
│ │ │ │ │ │ ├── suite-advanced-autoselect-usercase.xml
│ │ │ │ │ │ ├── suite-advanced-autoselect-with-filter.xml
│ │ │ │ │ │ ├── suite-advanced-details.xml
│ │ │ │ │ │ ├── suite-advanced-filter.xml
│ │ │ │ │ │ ├── suite-advanced.json
│ │ │ │ │ │ ├── suite-advanced.xml
│ │ │ │ │ │ ├── suite-attached-image.xml
│ │ │ │ │ │ ├── suite-case-detail-instances.xml
│ │ │ │ │ │ ├── suite-case-detail-tabs-with-nodesets-for-sorting-search-only.xml
│ │ │ │ │ │ ├── suite-case-detail-tabs-with-nodesets.xml
│ │ │ │ │ │ ├── suite-case-detail-tabs.xml
│ │ │ │ │ │ ├── suite-case-sharing.xml
│ │ │ │ │ │ ├── suite-case-tiles.xml
│ │ │ │ │ │ ├── suite-fixture-graphing.xml
│ │ │ │ │ │ ├── suite-graphing.xml
│ │ │ │ │ │ ├── suite-no-case-sharing.xml
│ │ │ │ │ │ ├── task-mode-suite.xml
│ │ │ │ │ │ ├── tiered-select-3.json
│ │ │ │ │ │ ├── tiered-select-3.xml
│ │ │ │ │ │ ├── tiered-select.json
│ │ │ │ │ │ ├── tiered-select.xml
│ │ │ │ │ │ ├── update_case_and_subcase.xml
│ │ │ │ │ │ └── usercase_entry.xml
│ │ │ │ │ ├── suite_inline_search/
│ │ │ │ │ │ ├── detail_tabs.xml
│ │ │ │ │ │ └── shadow_module_entry.xml
│ │ │ │ │ ├── suite_registry/
│ │ │ │ │ │ ├── detail_tabs.xml
│ │ │ │ │ │ ├── form_link_followup_module_registry.xml
│ │ │ │ │ │ ├── shadow_module_entry.xml
│ │ │ │ │ │ └── shadow_module_remote_request.xml
│ │ │ │ │ ├── unicode_error_form.xhtml
│ │ │ │ │ ├── very_simple_form.xml
│ │ │ │ │ ├── xform_builder/
│ │ │ │ │ │ ├── data_types.xml
│ │ │ │ │ │ ├── group.xml
│ │ │ │ │ │ ├── question_params.xml
│ │ │ │ │ │ ├── repeat_group.xml
│ │ │ │ │ │ ├── select1_question.xml
│ │ │ │ │ │ ├── select_question.xml
│ │ │ │ │ │ ├── unicode_translation.xml
│ │ │ │ │ │ └── xform_title.xml
│ │ │ │ │ ├── xform_test/
│ │ │ │ │ │ └── MySuperSpecialForm.xml
│ │ │ │ │ ├── yesno.json
│ │ │ │ │ ├── yesno_default_app_strings.txt
│ │ │ │ │ ├── yesno_en_app_strings.txt
│ │ │ │ │ └── yesno_es_app_strings.txt
│ │ │ │ ├── mocks/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── mobile_ucr.py
│ │ │ │ ├── templatetags/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── test_xforms_extras.py
│ │ │ │ ├── test_advanced_suite.py
│ │ │ │ ├── test_analytics.py
│ │ │ │ ├── test_app_import_api.py
│ │ │ │ ├── test_app_strings.py
│ │ │ │ ├── test_app_view_context.py
│ │ │ │ ├── test_apps.py
│ │ │ │ ├── test_build_errors.py
│ │ │ │ ├── test_build_errors_inline_search.py
│ │ │ │ ├── test_bulk_ui_translation.py
│ │ │ │ ├── test_case_detail_distance.py
│ │ │ │ ├── test_case_list_form.py
│ │ │ │ ├── test_case_list_lookup.py
│ │ │ │ ├── test_case_refs_from_fb.py
│ │ │ │ ├── test_ccz.py
│ │ │ │ ├── test_child_module.py
│ │ │ │ ├── test_commcare_settings.py
│ │ │ │ ├── test_commcare_versioning.py
│ │ │ │ ├── test_copy_form.py
│ │ │ │ ├── test_days_ago_migration.py
│ │ │ │ ├── test_dbaccessors.py
│ │ │ │ ├── test_delete_case_list_custom_variables_xml.py
│ │ │ │ ├── test_exceptions.py
│ │ │ │ ├── test_filters.py
│ │ │ │ ├── test_fix_app_docs_with_empty_keys.py
│ │ │ │ ├── test_form_actions_diff.py
│ │ │ │ ├── test_form_preparation_v2.py
│ │ │ │ ├── test_form_versioning.py
│ │ │ │ ├── test_form_workflow.py
│ │ │ │ ├── test_form_workflow_multiple_case_types.py
│ │ │ │ ├── test_form_xmlns.py
│ │ │ │ ├── test_generators.py
│ │ │ │ ├── test_get_questions.py
│ │ │ │ ├── test_grid_menus.py
│ │ │ │ ├── test_handle_shadow_child_modules.py
│ │ │ │ ├── test_id_strings.py
│ │ │ │ ├── test_list_apps_view.py
│ │ │ │ ├── test_location_xpath.py
│ │ │ │ ├── test_media_suite.py
│ │ │ │ ├── test_migrate_address_popup.py
│ │ │ │ ├── test_migrate_case_list_custom_variables.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_modules.py
│ │ │ │ ├── test_practice_user_config.py
│ │ │ │ ├── test_profile.py
│ │ │ │ ├── test_release_notes_form.py
│ │ │ │ ├── test_repeater.py
│ │ │ │ ├── test_report_config.py
│ │ │ │ ├── test_report_fixtures_provider.py
│ │ │ │ ├── test_schedule.py
│ │ │ │ ├── test_shadow_forms.py
│ │ │ │ ├── test_shadow_modules.py
│ │ │ │ ├── test_suite.py
│ │ │ │ ├── test_suite_assertions.py
│ │ │ │ ├── test_suite_case_tiles.py
│ │ │ │ ├── test_suite_case_tiles_grouping.py
│ │ │ │ ├── test_suite_commtrack.py
│ │ │ │ ├── test_suite_custom_case_tiles.py
│ │ │ │ ├── test_suite_detail_tabs.py
│ │ │ │ ├── test_suite_filters.py
│ │ │ │ ├── test_suite_fixture_to_case_selection.py
│ │ │ │ ├── test_suite_form_filter_errors.py
│ │ │ │ ├── test_suite_formats.py
│ │ │ │ ├── test_suite_graphing.py
│ │ │ │ ├── test_suite_inline_search.py
│ │ │ │ ├── test_suite_instances.py
│ │ │ │ ├── test_suite_multi_select_case_list.py
│ │ │ │ ├── test_suite_regex.py
│ │ │ │ ├── test_suite_registry_search.py
│ │ │ │ ├── test_suite_remote_request.py
│ │ │ │ ├── test_suite_resource_overrides.py
│ │ │ │ ├── test_suite_session_endpoints.py
│ │ │ │ ├── test_suite_shadow_module.py
│ │ │ │ ├── test_suite_sorting.py
│ │ │ │ ├── test_suite_split_screen_case_search.py
│ │ │ │ ├── test_suite_subcases.py
│ │ │ │ ├── test_suite_training_module.py
│ │ │ │ ├── test_suite_usercase.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_translation_file_paths.py
│ │ │ │ ├── test_util.py
│ │ │ │ ├── test_views.py
│ │ │ │ ├── test_xform.py
│ │ │ │ ├── test_xform_builder.py
│ │ │ │ ├── test_xform_parsing.py
│ │ │ │ ├── test_xml_parsing.py
│ │ │ │ ├── test_xpath.py
│ │ │ │ ├── util.py
│ │ │ │ └── views/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_apply_patch.py
│ │ │ │ ├── test_get_form_datums.py
│ │ │ │ ├── test_paginate_releases.py
│ │ │ │ ├── test_release_build.py
│ │ │ │ └── test_session_endpoint_utils.py
│ │ │ ├── ui_translations/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── commcare_versioning.py
│ │ │ │ └── ui_translations.py
│ │ │ ├── urls.py
│ │ │ ├── util.py
│ │ │ ├── view_helpers.py
│ │ │ ├── views/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_import_api.py
│ │ │ │ ├── app_summary.py
│ │ │ │ ├── apps.py
│ │ │ │ ├── cli.py
│ │ │ │ ├── download.py
│ │ │ │ ├── formdesigner.py
│ │ │ │ ├── forms.py
│ │ │ │ ├── media_utils.py
│ │ │ │ ├── modules.py
│ │ │ │ ├── multimedia.py
│ │ │ │ ├── phone.py
│ │ │ │ ├── releases.py
│ │ │ │ ├── schedules.py
│ │ │ │ ├── settings.py
│ │ │ │ ├── utils.py
│ │ │ │ └── view_generic.py
│ │ │ ├── xform.py
│ │ │ ├── xform_builder.py
│ │ │ ├── xpath.py
│ │ │ └── xpath_validator/
│ │ │ ├── __init__.py
│ │ │ ├── config.py
│ │ │ ├── exceptions.py
│ │ │ ├── nodejs/
│ │ │ │ ├── xpathConfig.js
│ │ │ │ └── xpathValidator.js
│ │ │ ├── tests.py
│ │ │ └── wrapper.py
│ │ ├── appstore/
│ │ │ ├── __init__.py
│ │ │ ├── exceptions.py
│ │ │ ├── management/
│ │ │ │ └── commands/
│ │ │ │ └── export_exchange.py
│ │ │ └── models.py
│ │ ├── auditcare/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── decorators/
│ │ │ │ └── __init__.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── export_audit_data.py
│ │ │ │ ├── export_domain_logins.py
│ │ │ │ ├── gdpr_scrub_user_auditcare.py
│ │ │ │ ├── generate_request_report.py
│ │ │ │ └── resolve_urls.py
│ │ │ ├── middleware.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_sqlmodels.py
│ │ │ │ ├── 0002_uniques.py
│ │ │ │ ├── 0003_truncatechars.py
│ │ │ │ ├── 0004_add_couch_id.py
│ │ │ │ ├── 0005_auditcaremigrationmeta.py
│ │ │ │ ├── 0006_auto_20210811_1215.py
│ │ │ │ ├── 0007_delete_auditcaremigrationmeta.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ └── auditcare_migration.py
│ │ │ │ ├── test_auth.py
│ │ │ │ ├── test_export.py
│ │ │ │ ├── test_gdpr_scrub_user_auditcare.py
│ │ │ │ ├── test_middleware.py
│ │ │ │ ├── test_models.py
│ │ │ │ └── testutils.py
│ │ │ ├── urls.py
│ │ │ ├── utils/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── export.py
│ │ │ │ ├── resolver.py
│ │ │ │ └── show_urls.py
│ │ │ └── views.py
│ │ ├── builds/
│ │ │ ├── README.rst
│ │ │ ├── __init__.py
│ │ │ ├── _design/
│ │ │ │ ├── filters/
│ │ │ │ │ └── all_builds.js
│ │ │ │ └── views/
│ │ │ │ └── all/
│ │ │ │ ├── map.js
│ │ │ │ └── reduce.js
│ │ │ ├── fixtures.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ └── add_commcare_build.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── builds/
│ │ │ │ └── js/
│ │ │ │ └── edit_builds.js
│ │ │ ├── templates/
│ │ │ │ └── builds/
│ │ │ │ ├── all.html
│ │ │ │ └── edit_menu.html
│ │ │ ├── tests/
│ │ │ │ └── test_utils.py
│ │ │ ├── tests.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── cachehq/
│ │ │ ├── __init__.py
│ │ │ ├── cachemodels.py
│ │ │ ├── invalidate.py
│ │ │ ├── mixins.py
│ │ │ ├── models.py
│ │ │ ├── signals.py
│ │ │ └── tests.py
│ │ ├── callcenter/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── app_parser.py
│ │ │ ├── checks.py
│ │ │ ├── const.py
│ │ │ ├── data_source.py
│ │ │ ├── data_sources/
│ │ │ │ ├── call_center_case_actions.json
│ │ │ │ ├── call_center_cases.json
│ │ │ │ └── call_center_forms.json
│ │ │ ├── fixturegenerators.py
│ │ │ ├── indicator_sets.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ └── rebuild_call_center_datasources.py
│ │ │ ├── models.py
│ │ │ ├── queries.py
│ │ │ ├── sync_usercase.py
│ │ │ ├── tasks.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ └── form_template.xml
│ │ │ │ ├── sql_fixture.py
│ │ │ │ ├── test_datasources.py
│ │ │ │ ├── test_indicator_fixture.py
│ │ │ │ ├── test_indicators.py
│ │ │ │ ├── test_location_owners.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_owner_options_view.py
│ │ │ │ ├── test_sync_usercases_tasks.py
│ │ │ │ ├── test_use_fixtures_configuration.py
│ │ │ │ └── test_utils.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── campaign/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── const.py
│ │ │ ├── forms.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_dashboardmap_dashboardreport.py
│ │ │ │ ├── 0003_add_dashboard_widget_fields.py
│ │ │ │ ├── 0004_gauge.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── services.py
│ │ │ ├── static/
│ │ │ │ └── campaign/
│ │ │ │ └── js/
│ │ │ │ └── dashboard.js
│ │ │ ├── templates/
│ │ │ │ └── campaign/
│ │ │ │ ├── dashboard.html
│ │ │ │ └── partials/
│ │ │ │ ├── add_widget_button.html
│ │ │ │ ├── case_properties_dropdown.html
│ │ │ │ ├── dashboard_gauge.html
│ │ │ │ ├── dashboard_map.html
│ │ │ │ ├── delete_widget_button.html
│ │ │ │ ├── delete_widget_confirmation_modal.html
│ │ │ │ ├── edit_widget_button.html
│ │ │ │ ├── widget_form.html
│ │ │ │ └── widget_modal.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_services.py
│ │ │ │ └── test_views.py
│ │ │ ├── urls.py
│ │ │ └── views.py
│ │ ├── case_importer/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── base.py
│ │ │ ├── const.py
│ │ │ ├── do_import.py
│ │ │ ├── exceptions.py
│ │ │ ├── extension_points.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── import_cases.py
│ │ │ │ ├── terminate_case_import.py
│ │ │ │ └── update_case_property.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_auto_20161206_1937.py
│ │ │ │ ├── 0003_caseuploadrecord_couch_user_id.py
│ │ │ │ ├── 0004_caseuploadrecord_case_type.py
│ │ │ │ ├── 0005_caseuploadfilemeta.py
│ │ │ │ ├── 0006_caseuploadrecord_upload_file_meta.py
│ │ │ │ ├── 0007_auto_20161209_2004.py
│ │ │ │ ├── 0008_caseuploadrecord_task_status_json.py
│ │ │ │ ├── 0009_caseuploadrecord_comment.py
│ │ │ │ ├── 0010_caseuploadformrecord.py
│ │ │ │ ├── 0011_update_blob_paths.py
│ │ │ │ ├── 0012_auto_20190405_1747.py
│ │ │ │ ├── 0013_make_duplicates_errors.py
│ │ │ │ ├── 0014_rename_caseuploadrecord_domain_created_case_import_domain_21a470_idx.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── case_importer/
│ │ │ │ ├── js/
│ │ │ │ │ ├── excel_fields.js
│ │ │ │ │ ├── import_history.js
│ │ │ │ │ └── main.js
│ │ │ │ └── spec/
│ │ │ │ ├── excel_fields_spec.js
│ │ │ │ └── main.js
│ │ │ ├── suggested_fields.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── case_importer/
│ │ │ │ ├── excel_config.html
│ │ │ │ ├── excel_fields.html
│ │ │ │ ├── import_cases.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── excel_field_rows.html
│ │ │ │ │ ├── help_message.html
│ │ │ │ │ ├── ko_import_history.html
│ │ │ │ │ └── ko_import_status.html
│ │ │ │ └── spec/
│ │ │ │ └── mocha.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_importer.py
│ │ │ │ ├── test_importer_config_compat.py
│ │ │ │ ├── test_suggested_fields.py
│ │ │ │ └── test_util.py
│ │ │ ├── tracking/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── analytics.py
│ │ │ │ ├── case_upload_tracker.py
│ │ │ │ ├── dbaccessors.py
│ │ │ │ ├── filestorage.py
│ │ │ │ ├── jsmodels.py
│ │ │ │ ├── models.py
│ │ │ │ ├── permissions.py
│ │ │ │ ├── task_status.py
│ │ │ │ ├── tests/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── test_dbaccessors.py
│ │ │ │ │ └── test_filestorage.py
│ │ │ │ └── views.py
│ │ │ ├── urls.py
│ │ │ ├── util.py
│ │ │ └── views.py
│ │ ├── case_search/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── const.py
│ │ │ ├── dsl_utils.py
│ │ │ ├── exceptions.py
│ │ │ ├── filter_dsl.py
│ │ │ ├── fixtures.py
│ │ │ ├── forms.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_auto_20161114_1901.py
│ │ │ │ ├── 0003_casesearchqueryaddition.py
│ │ │ │ ├── 0004_auto_20170518_2018.py
│ │ │ │ ├── 0005_migrate_json_config.py
│ │ │ │ ├── 0006_remove_casesearchconfig__config.py
│ │ │ │ ├── 0007_auto_20170522_1506.py
│ │ │ │ ├── 0008_auto_20180119_1716.py
│ │ │ │ ├── 0009_delete_casesearchqueryaddition.py
│ │ │ │ ├── 0010_casesearchconfig_synchronous_web_apps.py
│ │ │ │ ├── 0011_domainsnotincasesearchindex.py
│ │ │ │ ├── 0012_casesearchconfig_sync_cases_on_form_entry.py
│ │ │ │ ├── 0013_casesearchconfig_fuzzy_prefix_length.py
│ │ │ │ ├── 0014_casesearchconfig_index_name.py
│ │ │ │ ├── 0015_csqlfixtureexpression_csqlfixtureexpressionlog.py
│ │ │ │ ├── 0016_csqlfixtureexpression_user_data_criteria.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── case_search/
│ │ │ │ └── js/
│ │ │ │ ├── case_search.js
│ │ │ │ └── profile_case_search.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── case_search/
│ │ │ │ ├── case_search.html
│ │ │ │ ├── csql_expression_form.html
│ │ │ │ ├── csql_fixture_configuration.html
│ │ │ │ ├── csql_modal_form.html
│ │ │ │ ├── csql_user_data_criteria_fields.html
│ │ │ │ └── profile_case_search.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_ancestor_functions.py
│ │ │ │ ├── test_case_search_endpoint.py
│ │ │ │ ├── test_case_search_es_queries.py
│ │ │ │ ├── test_case_search_filters.py
│ │ │ │ ├── test_case_search_registry.py
│ │ │ │ ├── test_filter_dsl.py
│ │ │ │ ├── test_fixtures.py
│ │ │ │ ├── test_get_related_cases.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_subcase_query_parser.py
│ │ │ │ ├── test_utils.py
│ │ │ │ ├── test_value_functions.py
│ │ │ │ ├── test_views.py
│ │ │ │ ├── test_xpath_functions.py
│ │ │ │ └── utils.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ ├── views.py
│ │ │ └── xpath_functions/
│ │ │ ├── __init__.py
│ │ │ ├── ancestor_functions.py
│ │ │ ├── comparison.py
│ │ │ ├── query_functions.py
│ │ │ ├── subcase_functions.py
│ │ │ ├── utils.py
│ │ │ └── value_functions.py
│ │ ├── casegroups/
│ │ │ ├── __init__.py
│ │ │ ├── dbaccessors.py
│ │ │ ├── models.py
│ │ │ └── tests.py
│ │ ├── celery/
│ │ │ ├── __init__.py
│ │ │ ├── analytics.py
│ │ │ ├── durable.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ └── requeue_task_records.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_taskrecord.py
│ │ │ │ ├── 0002_taskrecord_kombu_serialization.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── periodic.py
│ │ │ ├── serial.py
│ │ │ ├── shared_task.py
│ │ │ └── tests/
│ │ │ ├── __init__.py
│ │ │ ├── test_celery_app.py
│ │ │ ├── test_durable_task.py
│ │ │ └── test_requeue_task_records.py
│ │ ├── change_feed/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── connection.py
│ │ │ ├── consumer/
│ │ │ │ ├── __init__.py
│ │ │ │ └── feed.py
│ │ │ ├── data_sources.py
│ │ │ ├── document_types.py
│ │ │ ├── exceptions.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── create_kafka_topics.py
│ │ │ │ ├── list_kafka_topics.py
│ │ │ │ ├── pillow_topic_assignments.py
│ │ │ │ └── validate_kafka_pillow_checkpoints.py
│ │ │ ├── pillow.py
│ │ │ ├── producer.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_connection.py
│ │ │ │ ├── test_data_sources.py
│ │ │ │ ├── test_document_types.py
│ │ │ │ ├── test_kafka_change_feed.py
│ │ │ │ ├── test_pillow.py
│ │ │ │ ├── test_topics.py
│ │ │ │ └── utils.py
│ │ │ └── topics.py
│ │ ├── cleanup/
│ │ │ ├── __init__.py
│ │ │ ├── dbaccessors.py
│ │ │ ├── deletable_doc_types.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── couch_integrity/
│ │ │ │ │ └── basic.json
│ │ │ │ ├── couch_integrity.py
│ │ │ │ ├── delete_case_indices.py
│ │ │ │ ├── delete_couch_dbs.py
│ │ │ │ ├── delete_doc_conflicts.py
│ │ │ │ ├── delete_duplicate_users.py
│ │ │ │ ├── delete_es_docs_for_domain.py
│ │ │ │ ├── delete_forms.py
│ │ │ │ ├── evaluate_couch_model_for_sql.py
│ │ │ │ ├── fire_repeaters.py
│ │ │ │ ├── get_doc_info.py
│ │ │ │ ├── multi_populate.py
│ │ │ │ ├── pillow_reset.py
│ │ │ │ ├── populate_sql_model_from_couch_model.py
│ │ │ │ ├── purge_docs.py
│ │ │ │ ├── rebuild_cases.py
│ │ │ │ ├── remove_deleted_cases_from_es.py
│ │ │ │ ├── reprocess_error_forms.py
│ │ │ │ ├── reprocess_incomplete_submissions.py
│ │ │ │ ├── republish_forms_rebuild_cases.py
│ │ │ │ ├── templates/
│ │ │ │ │ ├── couch_model_additions.j2
│ │ │ │ │ ├── migration_attr_equality_test.j2
│ │ │ │ │ ├── populate_command.j2
│ │ │ │ │ └── sql_model.j2
│ │ │ │ ├── undelete_docs.py
│ │ │ │ └── undo_uuid_clash.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_convert_change_feed_checkpoint_to_sql.py
│ │ │ │ ├── 0002_convert_mc_checkpoint_to_sql.py
│ │ │ │ ├── 0003_convert_fluff_checkpoints_to_sql.py
│ │ │ │ ├── 0004_convert_ucr_checkpoints_to_sql.py
│ │ │ │ ├── 0005_convert_mvp_checkpoints_to_sql.py
│ │ │ │ ├── 0006_convert_report_es_checkpoints_to_sql.py
│ │ │ │ ├── 0007_convert_es_checkpoints_to_sql.py
│ │ │ │ ├── 0008_convert_sofabed_checkpoints_to_sql.py
│ │ │ │ ├── 0009_convert_final_checkpoints_to_sql.py
│ │ │ │ ├── 0010_rename_default_change_feed_checkpoint.py
│ │ │ │ ├── 0011_merge_couch_sql_pillows.py
│ │ │ │ ├── 0012_add_es_index_to_checkpoint_ids.py
│ │ │ │ ├── 0013_migrate_kafka_checkpoint_format.py
│ │ │ │ ├── 0014_deletedcouchdoc.py
│ │ │ │ ├── 0015_deletedcouchdoc_unique_id_and_type.py
│ │ │ │ ├── 0016_add_deletedsqldoc.py
│ │ │ │ ├── 0017_delete_oauth_integrations_models.py
│ │ │ │ ├── 0018_delete_ewsghana_models.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── tasks.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_dbaccessors.py
│ │ │ │ ├── test_delete_es_docs_for_domain.py
│ │ │ │ ├── test_populate_sql_model_from_couch_model.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_utils.py
│ │ │ │ └── util.py
│ │ │ └── utils.py
│ │ ├── cloudcare/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── api.py
│ │ │ ├── const.py
│ │ │ ├── dbaccessors.py
│ │ │ ├── decorators.py
│ │ │ ├── esaccessors.py
│ │ │ ├── exceptions.py
│ │ │ ├── middleware.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_sqlapplicationaccess.py
│ │ │ │ ├── 0003_rename_sqlapplicationaccess.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── cloudcare/
│ │ │ │ └── js/
│ │ │ │ ├── config.js
│ │ │ │ ├── debugger/
│ │ │ │ │ └── debugger.js
│ │ │ │ ├── form_entry/
│ │ │ │ │ ├── const.js
│ │ │ │ │ ├── entries.js
│ │ │ │ │ ├── errors.js
│ │ │ │ │ ├── form_ui.js
│ │ │ │ │ ├── spec/
│ │ │ │ │ │ ├── entries_spec.js
│ │ │ │ │ │ ├── fixtures.js
│ │ │ │ │ │ ├── form_ui_spec.js
│ │ │ │ │ │ ├── integration_spec.js
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ ├── utils_spec.js
│ │ │ │ │ │ └── web_form_session_spec.js
│ │ │ │ │ ├── task_queue.js
│ │ │ │ │ ├── utils.js
│ │ │ │ │ └── web_form_session.js
│ │ │ │ ├── formplayer/
│ │ │ │ │ ├── app.js
│ │ │ │ │ ├── apps/
│ │ │ │ │ │ ├── api.js
│ │ │ │ │ │ ├── collections.js
│ │ │ │ │ │ ├── controller.js
│ │ │ │ │ │ ├── models.js
│ │ │ │ │ │ └── views.js
│ │ │ │ │ ├── constants.js
│ │ │ │ │ ├── hq_events.js
│ │ │ │ │ ├── layout/
│ │ │ │ │ │ └── views/
│ │ │ │ │ │ ├── progress_bar.js
│ │ │ │ │ │ └── settings.js
│ │ │ │ │ ├── main.js
│ │ │ │ │ ├── menus/
│ │ │ │ │ │ ├── api.js
│ │ │ │ │ │ ├── collections.js
│ │ │ │ │ │ ├── controller.js
│ │ │ │ │ │ ├── utils.js
│ │ │ │ │ │ ├── views/
│ │ │ │ │ │ │ └── query.js
│ │ │ │ │ │ └── views.js
│ │ │ │ │ ├── middleware.js
│ │ │ │ │ ├── router.js
│ │ │ │ │ ├── sessions/
│ │ │ │ │ │ ├── api.js
│ │ │ │ │ │ ├── collections.js
│ │ │ │ │ │ ├── controller.js
│ │ │ │ │ │ ├── models.js
│ │ │ │ │ │ └── views.js
│ │ │ │ │ ├── spec/
│ │ │ │ │ │ ├── case_list_pagination_spec.js
│ │ │ │ │ │ ├── debugger_spec.js
│ │ │ │ │ │ ├── fake_formplayer.js
│ │ │ │ │ │ ├── fixtures/
│ │ │ │ │ │ │ ├── case_grid_list.js
│ │ │ │ │ │ │ ├── case_list.js
│ │ │ │ │ │ │ ├── case_tile_list.js
│ │ │ │ │ │ │ ├── menu_list.js
│ │ │ │ │ │ │ └── split_screen_case_list.js
│ │ │ │ │ │ ├── hq_events_spec.js
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ ├── menu_list_spec.js
│ │ │ │ │ │ ├── menu_utils_spec.js
│ │ │ │ │ │ ├── query_spec.js
│ │ │ │ │ │ ├── session_middleware_spec.js
│ │ │ │ │ │ ├── split_screen_case_search_spec.js
│ │ │ │ │ │ ├── user_spec.js
│ │ │ │ │ │ └── utils_spec.js
│ │ │ │ │ ├── users/
│ │ │ │ │ │ ├── collections.js
│ │ │ │ │ │ ├── controller.js
│ │ │ │ │ │ ├── models.js
│ │ │ │ │ │ ├── utils.js
│ │ │ │ │ │ └── views.js
│ │ │ │ │ └── utils/
│ │ │ │ │ ├── calendar-picker-translations.js
│ │ │ │ │ └── utils.js
│ │ │ │ ├── gtx.js
│ │ │ │ ├── markdown.js
│ │ │ │ ├── preview_app/
│ │ │ │ │ ├── dragscroll.js
│ │ │ │ │ ├── main.js
│ │ │ │ │ └── preview_app.js
│ │ │ │ ├── sentry.js
│ │ │ │ ├── spec/
│ │ │ │ │ ├── markdown_spec.js
│ │ │ │ │ └── utils_spec.js
│ │ │ │ └── utils.js
│ │ │ ├── templates/
│ │ │ │ └── cloudcare/
│ │ │ │ ├── block_preview_app.html
│ │ │ │ ├── block_web_apps.html
│ │ │ │ ├── config.html
│ │ │ │ ├── formplayer_home.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── all_templates.html
│ │ │ │ │ ├── case_detail.html
│ │ │ │ │ ├── case_list/
│ │ │ │ │ │ ├── cell_container_style.html
│ │ │ │ │ │ ├── cell_grid_style.html
│ │ │ │ │ │ ├── cell_layout_style.html
│ │ │ │ │ │ ├── config.html
│ │ │ │ │ │ ├── config_modal.html
│ │ │ │ │ │ ├── detail.html
│ │ │ │ │ │ ├── item.html
│ │ │ │ │ │ ├── list.html
│ │ │ │ │ │ ├── menu_header.html
│ │ │ │ │ │ ├── multi_select_continue_button.html
│ │ │ │ │ │ ├── no_items_text.html
│ │ │ │ │ │ ├── search_controls.html
│ │ │ │ │ │ ├── show_map_button.html
│ │ │ │ │ │ ├── table_container.html
│ │ │ │ │ │ ├── tile_grouped_item.html
│ │ │ │ │ │ └── tile_item.html
│ │ │ │ │ ├── confirmation_modal.html
│ │ │ │ │ ├── debugger.html
│ │ │ │ │ ├── dependencies.html
│ │ │ │ │ ├── form_entry/
│ │ │ │ │ │ ├── entry_address.html
│ │ │ │ │ │ ├── entry_blank.html
│ │ │ │ │ │ ├── entry_button.html
│ │ │ │ │ │ ├── entry_choice_label.html
│ │ │ │ │ │ ├── entry_datetime.html
│ │ │ │ │ │ ├── entry_dropdown.html
│ │ │ │ │ │ ├── entry_ethiopian_date.html
│ │ │ │ │ │ ├── entry_file.html
│ │ │ │ │ │ ├── entry_geo.html
│ │ │ │ │ │ ├── entry_multidropdown.html
│ │ │ │ │ │ ├── entry_numeric.html
│ │ │ │ │ │ ├── entry_password.html
│ │ │ │ │ │ ├── entry_select.html
│ │ │ │ │ │ ├── entry_signature.html
│ │ │ │ │ │ ├── entry_text.html
│ │ │ │ │ │ ├── entry_unsupported.html
│ │ │ │ │ │ ├── form.html
│ │ │ │ │ │ ├── form_navigation.html
│ │ │ │ │ │ ├── grouped_element_tile_row.html
│ │ │ │ │ │ ├── help_multimedia.html
│ │ │ │ │ │ ├── multimedia.html
│ │ │ │ │ │ ├── question.html
│ │ │ │ │ │ └── sub_group.html
│ │ │ │ │ ├── grid_view/
│ │ │ │ │ │ ├── grid.html
│ │ │ │ │ │ ├── row.html
│ │ │ │ │ │ └── single_app.html
│ │ │ │ │ ├── menu/
│ │ │ │ │ │ ├── audio.html
│ │ │ │ │ │ ├── breadcrumbs.html
│ │ │ │ │ │ ├── dropdown.html
│ │ │ │ │ │ ├── grid.html
│ │ │ │ │ │ ├── grid_item.html
│ │ │ │ │ │ ├── list.html
│ │ │ │ │ │ ├── persistent_menu.html
│ │ │ │ │ │ └── row.html
│ │ │ │ │ ├── new_app_version_modal.html
│ │ │ │ │ ├── pagination.html
│ │ │ │ │ ├── progress.html
│ │ │ │ │ ├── query/
│ │ │ │ │ │ ├── group.html
│ │ │ │ │ │ ├── item.html
│ │ │ │ │ │ └── list.html
│ │ │ │ │ ├── sessions/
│ │ │ │ │ │ ├── item.html
│ │ │ │ │ │ └── list.html
│ │ │ │ │ ├── settings_view.html
│ │ │ │ │ └── users/
│ │ │ │ │ ├── restore_as.html
│ │ │ │ │ ├── restore_as_banner.html
│ │ │ │ │ ├── user_data.html
│ │ │ │ │ └── user_row.html
│ │ │ │ ├── preview_app.html
│ │ │ │ ├── preview_app_base.html
│ │ │ │ ├── spec/
│ │ │ │ │ ├── form_entry/
│ │ │ │ │ │ └── mocha.html
│ │ │ │ │ └── mocha.html
│ │ │ │ └── web_apps_disabled.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_api.py
│ │ │ │ ├── test_doc_tests.py
│ │ │ │ ├── test_esaccessors.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_session.py
│ │ │ │ └── test_utils.py
│ │ │ ├── touchforms_api.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── commtrack/
│ │ │ ├── __init__.py
│ │ │ ├── const.py
│ │ │ ├── consumption.py
│ │ │ ├── exceptions.py
│ │ │ ├── fixtures.py
│ │ │ ├── forms.py
│ │ │ ├── helpers.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ └── __init__.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_stockstate_last_modified_form_id.py
│ │ │ │ ├── 0003_create_config_models.py
│ │ │ │ ├── 0004_update_overstock_threshold.py
│ │ │ │ ├── 0005_populate_config_models.py
│ │ │ │ ├── 0006_remove_sqlcommtrackconfig_couch_id.py
│ │ │ │ ├── 0007_rename_sql_models.py
│ │ │ │ ├── 0008_delete_stockstate.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── processing.py
│ │ │ ├── resources/
│ │ │ │ ├── __init__.py
│ │ │ │ └── v0_1.py
│ │ │ ├── sms.py
│ │ │ ├── static/
│ │ │ │ └── commtrack/
│ │ │ │ └── js/
│ │ │ │ ├── base_list_view_model.js
│ │ │ │ ├── products_and_programs_main.js
│ │ │ │ └── sms.js
│ │ │ ├── templates/
│ │ │ │ └── commtrack/
│ │ │ │ └── manage/
│ │ │ │ ├── default_consumption.html
│ │ │ │ └── partials/
│ │ │ │ └── pagination.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── balances.py
│ │ │ │ │ └── xml/
│ │ │ │ │ └── device_log.xml
│ │ │ │ ├── test_dbaccessors.py
│ │ │ │ ├── test_fixture.py
│ │ │ │ ├── test_locations.py
│ │ │ │ ├── test_products.py
│ │ │ │ ├── test_programs.py
│ │ │ │ ├── test_rebuild.py
│ │ │ │ ├── test_settings.py
│ │ │ │ ├── test_sms_reporting.py
│ │ │ │ ├── test_stock_report.py
│ │ │ │ ├── test_supply_points.py
│ │ │ │ ├── test_utils.py
│ │ │ │ ├── test_xml.py
│ │ │ │ └── util.py
│ │ │ ├── urls.py
│ │ │ ├── util.py
│ │ │ ├── views.py
│ │ │ └── xmlutil.py
│ │ ├── consumption/
│ │ │ ├── __init__.py
│ │ │ ├── const.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_populate_sqldefaultconsumption.py
│ │ │ │ ├── 0003_remove_sqldefaultconsumption_couch_id.py
│ │ │ │ ├── 0004_rename_sqldefaultconsumption.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── shortcuts.py
│ │ │ └── tests.py
│ │ ├── custom_data_fields/
│ │ │ ├── README.rst
│ │ │ ├── __init__.py
│ │ │ ├── edit_entity.py
│ │ │ ├── edit_model.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_populate_customdatafieldsdefinition.py
│ │ │ │ ├── 0003_remove_sqlcustomdatafieldsdefinition_couch_id.py
│ │ │ │ ├── 0004_rename_tables.py
│ │ │ │ ├── 0005_customdatafieldsprofile.py
│ │ │ │ ├── 0006_auto_20200924_1753.py
│ │ │ │ ├── 0007_custom_data_fields_erm_support.py
│ │ │ │ ├── 0008_custom_data_fields_upstream_ids.py
│ │ │ │ ├── 0009_field_required_for.py
│ │ │ │ ├── 0010_customdatafieldsdefinition_profile_required_for_user_type.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── custom_data_fields/
│ │ │ │ └── js/
│ │ │ │ └── custom_data_fields.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── custom_data_fields/
│ │ │ │ ├── custom_data_fields.html
│ │ │ │ └── partials/
│ │ │ │ ├── fields_tab.html
│ │ │ │ ├── modal_delete.html
│ │ │ │ ├── modal_edit.html
│ │ │ │ ├── profiles_tab.html
│ │ │ │ └── purge_existing_fields.html
│ │ │ └── tests/
│ │ │ ├── __init__.py
│ │ │ ├── test_edit_model.py
│ │ │ ├── test_fields.py
│ │ │ ├── test_form.py
│ │ │ ├── test_models.py
│ │ │ ├── test_profiles.py
│ │ │ └── test_verification.py
│ │ ├── dashboard/
│ │ │ ├── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── dashboard/
│ │ │ │ └── js/
│ │ │ │ └── dashboard.js
│ │ │ ├── templates/
│ │ │ │ └── dashboard/
│ │ │ │ └── base.html
│ │ │ ├── urls.py
│ │ │ └── views.py
│ │ ├── data_analytics/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── const.py
│ │ │ ├── daily_calcs.py
│ │ │ ├── esaccessors.py
│ │ │ ├── feature_calcs.py
│ │ │ ├── gir_generator.py
│ │ │ ├── malt_generator.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── add_to_malt_table.py
│ │ │ │ ├── collect_feature_metrics.py
│ │ │ │ ├── update_gir_table.py
│ │ │ │ └── update_malt_table.py
│ │ │ ├── metric_registry.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_squashed_0004_auto_20150810_1710.py
│ │ │ │ ├── 0002_maltrow_threshold.py
│ │ │ │ ├── 0003_auto_20160205_0927.py
│ │ │ │ ├── 0004_experienced_threshold.py
│ │ │ │ ├── 0005_girrow.py
│ │ │ │ ├── 0006_unique_girrow.py
│ │ │ │ ├── 0007_auto_20160819_1423.py
│ │ │ │ ├── 0008_auto_20161114_1903.py
│ │ │ │ ├── 0009_remove_girrow_wam.py
│ │ │ │ ├── 0010_maltrow_last_run_date.py
│ │ │ │ ├── 0011_domainmetrics.py
│ │ │ │ ├── 0012_update_domainmetrics_fields.py
│ │ │ │ ├── 0013_domainmetrics_bulk_case_editing_sessions_and_more.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── tasks.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_app_feature_metrics.py
│ │ │ │ ├── test_collect_feature_metrics_command.py
│ │ │ │ ├── test_data_export_feature_metrics.py
│ │ │ │ ├── test_esaccessors.py
│ │ │ │ ├── test_gir.py
│ │ │ │ ├── test_malt_generator.py
│ │ │ │ ├── test_metric_registry.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_user_security_feature_metrics.py
│ │ │ │ ├── test_util.py
│ │ │ │ └── utils.py
│ │ │ └── util.py
│ │ ├── data_cleaning/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── columns.py
│ │ │ ├── decorators.py
│ │ │ ├── exceptions.py
│ │ │ ├── filters.py
│ │ │ ├── forms/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── bulk_edit.py
│ │ │ │ ├── columns.py
│ │ │ │ ├── filters.py
│ │ │ │ └── start_session.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── dc_create_test_app.py
│ │ │ │ ├── dc_create_test_data.py
│ │ │ │ ├── dc_create_test_users.py
│ │ │ │ └── utils/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── apps/
│ │ │ │ │ └── plant_care_app.json
│ │ │ │ ├── fake_data_users.py
│ │ │ │ ├── fake_plant_data.py
│ │ │ │ ├── input_validation.py
│ │ │ │ └── issues.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_update_fields_and_ordering.py
│ │ │ │ ├── 0003_column_filter_unique_ids_match_type_updates.py
│ │ │ │ ├── 0004_alter_bulkeditcolumn_data_type_and_more.py
│ │ │ │ ├── 0005_rename_bulkeditcolumnfilter_bulkeditfilter.py
│ │ │ │ ├── 0006_alter_bulkeditchange_action_type.py
│ │ │ │ ├── 0007_alter_bulkeditrecord_doc_id_and_more.py
│ │ │ │ └── __init__.py
│ │ │ ├── models/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── change.py
│ │ │ │ ├── column.py
│ │ │ │ ├── filters.py
│ │ │ │ ├── record.py
│ │ │ │ ├── session.py
│ │ │ │ └── types.py
│ │ │ ├── records.py
│ │ │ ├── static/
│ │ │ │ └── data_cleaning/
│ │ │ │ └── js/
│ │ │ │ ├── bulk_edit_main.js
│ │ │ │ ├── bulk_edit_session.js
│ │ │ │ └── directives/
│ │ │ │ └── dynamic_options_select2.js
│ │ │ ├── tables.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── data_cleaning/
│ │ │ │ ├── bulk_edit_main.html
│ │ │ │ ├── bulk_edit_session.html
│ │ │ │ ├── columns/
│ │ │ │ │ ├── column_editable.html
│ │ │ │ │ ├── column_main.html
│ │ │ │ │ ├── column_system.html
│ │ │ │ │ ├── selection.html
│ │ │ │ │ ├── selection_header.html
│ │ │ │ │ ├── selection_read_only.html
│ │ │ │ │ ├── task_form_ids.html
│ │ │ │ │ ├── task_status.html
│ │ │ │ │ └── values/
│ │ │ │ │ ├── empty.html
│ │ │ │ │ ├── null.html
│ │ │ │ │ └── text.html
│ │ │ │ ├── filters/
│ │ │ │ │ ├── formatted_value.html
│ │ │ │ │ └── pinned/
│ │ │ │ │ ├── base.html
│ │ │ │ │ ├── multi_option.html
│ │ │ │ │ └── single_option.html
│ │ │ │ ├── forms/
│ │ │ │ │ ├── edit_selected_records_form.html
│ │ │ │ │ ├── manage_columns_form.html
│ │ │ │ │ ├── manage_filters_form.html
│ │ │ │ │ ├── partials/
│ │ │ │ │ │ ├── active_session_exists.html
│ │ │ │ │ │ ├── column_list_item.html
│ │ │ │ │ │ └── filter_list_item.html
│ │ │ │ │ └── pinned_filter_form.html
│ │ │ │ ├── modals/
│ │ │ │ │ ├── base_confirm.html
│ │ │ │ │ ├── confirm_apply.html
│ │ │ │ │ ├── confirm_clear.html
│ │ │ │ │ ├── confirm_select_all.html
│ │ │ │ │ ├── confirm_undo.html
│ │ │ │ │ └── select_all_not_possible.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── button_bar.html
│ │ │ │ │ ├── data_type_icon.html
│ │ │ │ │ ├── loading_indicator.html
│ │ │ │ │ └── offcanvas.html
│ │ │ │ ├── status/
│ │ │ │ │ ├── base_modal_body.html
│ │ │ │ │ ├── complete.html
│ │ │ │ │ ├── in_progress.html
│ │ │ │ │ ├── modal.html
│ │ │ │ │ ├── partial/
│ │ │ │ │ │ └── task_progress_bar.html
│ │ │ │ │ └── previous_session.html
│ │ │ │ ├── summary/
│ │ │ │ │ ├── apply_changes.html
│ │ │ │ │ ├── clear_changes.html
│ │ │ │ │ ├── partial/
│ │ │ │ │ │ ├── change_detail.html
│ │ │ │ │ │ └── change_history.html
│ │ │ │ │ └── undo_changes.html
│ │ │ │ └── tables/
│ │ │ │ ├── bulk_edit_session.html
│ │ │ │ └── recent_sessions.html
│ │ │ ├── templatetags/
│ │ │ │ ├── __init__.py
│ │ │ │ └── data_cleaning.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── mixins.py
│ │ │ │ ├── test_add_column_form.py
│ │ │ │ ├── test_add_filter_form.py
│ │ │ │ ├── test_bulk_edit_form.py
│ │ │ │ ├── test_changes.py
│ │ │ │ ├── test_filter_outputs.py
│ │ │ │ ├── test_filters.py
│ │ │ │ ├── test_records.py
│ │ │ │ ├── test_session.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_utils_cases.py
│ │ │ │ └── test_views.py
│ │ │ ├── urls.py
│ │ │ ├── utils/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cases.py
│ │ │ │ └── decorators.py
│ │ │ └── views/
│ │ │ ├── __init__.py
│ │ │ ├── bulk_edit.py
│ │ │ ├── columns.py
│ │ │ ├── filters.py
│ │ │ ├── main.py
│ │ │ ├── mixins.py
│ │ │ ├── start.py
│ │ │ ├── status.py
│ │ │ ├── summary.py
│ │ │ └── tables.py
│ │ ├── data_dictionary/
│ │ │ ├── __init__.py
│ │ │ ├── bulk.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── generate_data_dictionary.py
│ │ │ │ └── refresh_data_dictionary.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_squashed_0002_auto_20161116_2209.py
│ │ │ │ ├── 0002_auto_20161118_1537.py
│ │ │ │ ├── 0003_auto_20161128_2047.py
│ │ │ │ ├── 0004_auto_20161130_2125.py
│ │ │ │ ├── 0005_casetype_fully_generated.py
│ │ │ │ ├── 0006_caseproperty_group.py
│ │ │ │ ├── 0007_property_type_choices.py
│ │ │ │ ├── 0008_casepropertyallowedvalue.py
│ │ │ │ ├── 0009_caseproperty_label.py
│ │ │ │ ├── 0010_caseproperty_index.py
│ │ │ │ ├── 0011_casepropertygroup.py
│ │ │ │ ├── 0012_populate_case_property_groups.py
│ │ │ │ ├── 0013_auto_20230529_1614.py
│ │ │ │ ├── 0014_auto_20230705_2007.py
│ │ │ │ ├── 0015_casetype_is_deprecated.py
│ │ │ │ ├── 0016_remove_case_property_group_and_rename_group_obj_caseproperty_group.py
│ │ │ │ ├── 0017_refresh_data_dictionary.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── data_dictionary/
│ │ │ │ └── js/
│ │ │ │ ├── data_dictionary.js
│ │ │ │ └── partials/
│ │ │ │ └── case_property_warning.js
│ │ │ ├── templates/
│ │ │ │ └── data_dictionary/
│ │ │ │ ├── base.html
│ │ │ │ └── partials/
│ │ │ │ ├── case_property_warning.html
│ │ │ │ ├── confirmation_modals.html
│ │ │ │ └── valid_values_th_content.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ ├── broken_data_dictionary.xlsx
│ │ │ │ │ └── clean_data_dictionary.xlsx
│ │ │ │ ├── test_import_export.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_util.py
│ │ │ │ ├── test_views.py
│ │ │ │ └── utils.py
│ │ │ ├── urls.py
│ │ │ ├── util.py
│ │ │ └── views.py
│ │ ├── data_interfaces/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── app_config.py
│ │ │ ├── deduplication.py
│ │ │ ├── dispatcher.py
│ │ │ ├── forms.py
│ │ │ ├── interfaces.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── get_case_rule_submissions.py
│ │ │ │ └── undo_case_rule_submissions.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_remove_exists_option.py
│ │ │ │ ├── 0003_update__automaticupdaterulecriteria__match_type__choices.py
│ │ │ │ ├── 0004_optional_modified_date_and_prop_type_choices.py
│ │ │ │ ├── 0005_remove_match_type_choices.py
│ │ │ │ ├── 0006_case_rule_refactor.py
│ │ │ │ ├── 0007_logging_models.py
│ │ │ │ ├── 0008_update_case_rulesubmission.py
│ │ │ │ ├── 0009_scheduling_integration.py
│ │ │ │ ├── 0010_automaticupdaterule_workflow.py
│ │ │ │ ├── 0011_domaincaserulerun_num_creates.py
│ │ │ │ ├── 0012_createscheduleinstanceactiondefinition_reset_case_property_name.py
│ │ │ │ ├── 0013_createscheduleinstanceactiondefinition_scheduler_module_info.py
│ │ │ │ ├── 0014_createscheduleinstanceactiondefinition_start_date_case_property.py
│ │ │ │ ├── 0015_automaticupdaterule_locked_for_editing.py
│ │ │ │ ├── 0016_createscheduleinstanceactiondefinition_specific_start_date.py
│ │ │ │ ├── 0017_alter_domaincaserulerun.py
│ │ │ │ ├── 0018_check_for_rule_migration.py
│ │ │ │ ├── 0019_remove_old_rule_models.py
│ │ │ │ ├── 0020_make_migrated_nullable.py
│ │ │ │ ├── 0021_remove_automaticupdaterule_migrated.py
│ │ │ │ ├── 0022_domaincaserulerun_case_type.py
│ │ │ │ ├── 0023_auto_20210914_1726.py
│ │ │ │ ├── 0024_add_automaticupdaterule_upstream_id.py
│ │ │ │ ├── 0025_domaincaserulerun_num_errors.py
│ │ │ │ ├── 0026_automaticupdaterule_criteria_operator.py
│ │ │ │ ├── 0027_auto_20220511_2017.py
│ │ │ │ ├── 0028_auto_20220420_1301.py
│ │ │ │ ├── 0029_locationfilterdefinition_include_child_locations.py
│ │ │ │ ├── 0030_add_workflow_choices.py
│ │ │ │ ├── 0031_add_domaincaserulerun_status_choices.py
│ │ │ │ ├── 0032_bootstrap_audit_events_for_update_rules.py
│ │ │ │ ├── 0033_automaticupdaterule_deleted_on.py
│ │ │ │ ├── 0034_case_name_actions.py
│ │ │ │ ├── 0035_add_case_duplicate_new.py
│ │ │ │ ├── 0036_backfill_dedupe_match_values.py
│ │ │ │ ├── 0037_add_dedupe_update_toggle.py
│ │ │ │ ├── 0038_alter_caseduplicate_potential_duplicates.py
│ │ │ │ ├── 0039_rename_caserulesubmission_domain_created_on_data_interf_domain_718740_idx_and_more.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── pillow.py
│ │ │ ├── signals.py
│ │ │ ├── static/
│ │ │ │ └── data_interfaces/
│ │ │ │ ├── js/
│ │ │ │ │ ├── archive_forms.js
│ │ │ │ │ ├── auto_update_rules.js
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── case_dedupe_main.js
│ │ │ │ │ │ ├── case_management.js
│ │ │ │ │ │ ├── deduplication_rules.js
│ │ │ │ │ │ └── manage_case_groups.js
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── case_dedupe_main.js
│ │ │ │ │ │ ├── case_management.js
│ │ │ │ │ │ ├── deduplication_rules.js
│ │ │ │ │ │ └── manage_case_groups.js
│ │ │ │ │ ├── case_property_input.js
│ │ │ │ │ ├── case_rule_actions.js
│ │ │ │ │ ├── case_rule_criteria.js
│ │ │ │ │ ├── case_rule_main.js
│ │ │ │ │ ├── find_by_id.js
│ │ │ │ │ └── make_read_only.js
│ │ │ │ └── xlsx/
│ │ │ │ └── cases_bulk_example.xlsx
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── data_interfaces/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── auto_update_rules.html
│ │ │ │ │ ├── case_rule.html
│ │ │ │ │ ├── edit_deduplication_rule.html
│ │ │ │ │ ├── find_by_id.html
│ │ │ │ │ ├── list_case_groups.html
│ │ │ │ │ ├── list_deduplication_rules.html
│ │ │ │ │ └── manage_case_groups.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── auto_update_rules.html
│ │ │ │ │ ├── case_rule.html
│ │ │ │ │ ├── edit_deduplication_rule.html
│ │ │ │ │ ├── find_by_id.html
│ │ │ │ │ ├── list_case_groups.html
│ │ │ │ │ ├── list_deduplication_rules.html
│ │ │ │ │ └── manage_case_groups.html
│ │ │ │ ├── interfaces/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── archive_forms.html
│ │ │ │ │ │ └── case_management.html
│ │ │ │ │ └── bootstrap5/
│ │ │ │ │ ├── archive_forms.html
│ │ │ │ │ └── case_management.html
│ │ │ │ └── partials/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── auto_update_rule_list.html
│ │ │ │ │ ├── auto_update_rule_run_history.html
│ │ │ │ │ ├── case_rule_actions.html
│ │ │ │ │ ├── case_rule_criteria.html
│ │ │ │ │ ├── find_by_id_form.html
│ │ │ │ │ └── modal_edit.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── auto_update_rule_list.html
│ │ │ │ │ ├── auto_update_rule_run_history.html
│ │ │ │ │ ├── case_rule_actions.html
│ │ │ │ │ ├── case_rule_criteria.html
│ │ │ │ │ ├── find_by_id_form.html
│ │ │ │ │ └── modal_edit.html
│ │ │ │ ├── case_copy_complete_email.html
│ │ │ │ ├── case_copy_status.html
│ │ │ │ ├── case_reassign_complete_email.html
│ │ │ │ ├── case_reassign_status.html
│ │ │ │ └── xform_management_status.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── deduplication_helpers.py
│ │ │ │ ├── files/
│ │ │ │ │ ├── basic_forms_bulk.xlsx
│ │ │ │ │ ├── missing_forms_bulk.xlsx
│ │ │ │ │ └── wrong_file.xyz
│ │ │ │ ├── test_auto_case_updates.py
│ │ │ │ ├── test_auto_update_rules.py
│ │ │ │ ├── test_case_deduplication.py
│ │ │ │ ├── test_deduplication.py
│ │ │ │ ├── test_deduplication_rules.py
│ │ │ │ ├── test_form_validation.py
│ │ │ │ ├── test_forms.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_pillow.py
│ │ │ │ ├── test_scheduling_integration.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_utils.py
│ │ │ │ ├── test_views.py
│ │ │ │ ├── test_xform_management.py
│ │ │ │ └── util.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── data_pipeline_audit/
│ │ │ ├── __init__.py
│ │ │ ├── dbacessors.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── compare_doc_counts.py
│ │ │ │ ├── compare_doc_ids.py
│ │ │ │ └── find_sql_forms_not_in_es.py
│ │ │ ├── tools.py
│ │ │ └── utils.py
│ │ ├── domain/
│ │ │ ├── __init__.py
│ │ │ ├── _design/
│ │ │ │ ├── filters/
│ │ │ │ │ ├── all_docs.js
│ │ │ │ │ ├── all_domains.js
│ │ │ │ │ └── domains_inclusive.js
│ │ │ │ ├── fulltext/
│ │ │ │ │ └── snapshot_search/
│ │ │ │ │ └── index.js
│ │ │ │ └── views/
│ │ │ │ ├── by_status/
│ │ │ │ │ ├── map.js
│ │ │ │ │ └── reduce.js
│ │ │ │ ├── copied_from_snapshot/
│ │ │ │ │ └── map.js
│ │ │ │ ├── deleted_domains/
│ │ │ │ │ ├── map.js
│ │ │ │ │ └── reduce.js
│ │ │ │ ├── domains/
│ │ │ │ │ ├── map.js
│ │ │ │ │ └── reduce.js
│ │ │ │ ├── not_snapshots/
│ │ │ │ │ └── map.js
│ │ │ │ ├── published_snapshots/
│ │ │ │ │ └── map.js
│ │ │ │ └── snapshots/
│ │ │ │ ├── map.js
│ │ │ │ └── reduce.js
│ │ │ ├── admin.py
│ │ │ ├── auth.py
│ │ │ ├── calculations.py
│ │ │ ├── dbaccessors.py
│ │ │ ├── decorators.py
│ │ │ ├── deletion.py
│ │ │ ├── exceptions.py
│ │ │ ├── extension_points.py
│ │ │ ├── forms.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── bootstrap_app.py
│ │ │ │ ├── calculate_physical_size.py
│ │ │ │ ├── delete_domain.py
│ │ │ │ ├── disable_toggles_for_deleted_domains.py
│ │ │ │ ├── first_superuser.py
│ │ │ │ ├── hard_delete_forms_and_cases_in_domain.py
│ │ │ │ ├── make_superuser.py
│ │ │ │ ├── redirect_url.py
│ │ │ │ ├── remove_duplicate_domains.py
│ │ │ │ ├── reset_case_search_toggles.py
│ │ │ │ ├── tests/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── test_make_superuser.py
│ │ │ │ └── update_case_search_toggles.py
│ │ │ ├── middleware.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_auto_20171020_1428.py
│ │ │ │ ├── 0003_auto_20180525_1551.py
│ │ │ │ ├── 0004_domainauditrecordentry.py
│ │ │ │ ├── 0005_ga_opt_out.py
│ │ │ │ ├── 0006_fix_domain_es_docs.py
│ │ │ │ ├── 0007_auto_20200924_1753.py
│ │ │ │ ├── 0008_use_livequery.py
│ │ │ │ ├── 0009_restrict_mob_access_from_FF.py
│ │ │ │ ├── 0010_projectlimit.py
│ │ │ │ ├── 0011_alloweducrexpressionsettings.py
│ │ │ │ ├── 0012_operatorcalllimitsettings.py
│ │ │ │ ├── 0013_accountconfirmationsettings_squashed_0016_alter_smsaccountconfirmationsettings_project_name.py
│ │ │ │ ├── 0014_appreleasemodesetting.py
│ │ │ │ ├── 0015_delete_projectlimit.py
│ │ │ │ ├── 0016_rename_superuserprojectentryrecord_domain_username_domain_supe_domain_c3d32e_idx.py
│ │ │ │ ├── 0017_appmanagerdomainsettings.py
│ │ │ │ ├── 0018_enable_all_add_ons.py
│ │ │ │ ├── 0019_remove_usercase_enabled_field.py
│ │ │ │ ├── 0020_delete_transferdomainrequest.py
│ │ │ │ ├── 0021_delete_smsaccountconfirmationsettings.py
│ │ │ │ ├── __init__.py
│ │ │ │ └── sql_templates/
│ │ │ │ └── update_tables1.sql
│ │ │ ├── models.py
│ │ │ ├── project_access/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── middleware.py
│ │ │ │ └── models.py
│ │ │ ├── shortcuts.py
│ │ │ ├── signals.py
│ │ │ ├── static/
│ │ │ │ └── domain/
│ │ │ │ ├── js/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── billing_statements.js
│ │ │ │ │ │ ├── info_basic.js
│ │ │ │ │ │ ├── internal_subscription_management.js
│ │ │ │ │ │ └── toggles.js
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── billing_statements.js
│ │ │ │ │ │ ├── info_basic.js
│ │ │ │ │ │ ├── internal_subscription_management.js
│ │ │ │ │ │ └── toggles.js
│ │ │ │ │ ├── case_search.js
│ │ │ │ │ ├── commtrack_settings.js
│ │ │ │ │ ├── confirm_billing_info.js
│ │ │ │ │ ├── current_subscription.js
│ │ │ │ │ ├── feature_previews.js
│ │ │ │ │ ├── internal_calculations.js
│ │ │ │ │ ├── internal_settings.js
│ │ │ │ │ ├── ip_access_config.js
│ │ │ │ │ ├── manage_alerts.js
│ │ │ │ │ ├── my_project_settings.js
│ │ │ │ │ ├── new_stripe_card_manager.js
│ │ │ │ │ ├── select.js
│ │ │ │ │ └── update_billing_contact_info.js
│ │ │ │ └── json/
│ │ │ │ └── stop_words.yml
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ ├── domain/
│ │ │ │ │ ├── admin/
│ │ │ │ │ │ ├── application_credentials.html
│ │ │ │ │ │ ├── case_search.html
│ │ │ │ │ │ ├── commtrack_settings.html
│ │ │ │ │ │ ├── edit_alert.html
│ │ │ │ │ │ ├── feature_previews.html
│ │ │ │ │ │ ├── flags_and_privileges.html
│ │ │ │ │ │ ├── global_sms_rates.html
│ │ │ │ │ │ ├── info_basic.html
│ │ │ │ │ │ ├── ip_access_config.html
│ │ │ │ │ │ ├── location_fixture.html
│ │ │ │ │ │ ├── manage_alerts.html
│ │ │ │ │ │ ├── my_project_settings.html
│ │ │ │ │ │ ├── partials/
│ │ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ │ └── project_limits_table.html
│ │ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ │ └── project_limits_table.html
│ │ │ │ │ │ │ ├── case_search_templates.html
│ │ │ │ │ │ │ ├── commtrack_action_table.html
│ │ │ │ │ │ │ └── ocs_chatbot_enable_modal.html
│ │ │ │ │ │ ├── project_limits.html
│ │ │ │ │ │ ├── project_privacy.html
│ │ │ │ │ │ ├── recovery_measures_history.html
│ │ │ │ │ │ ├── sms_rates.html
│ │ │ │ │ │ └── sms_settings.html
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── base_change_plan.html
│ │ │ │ │ │ ├── billing_statements.html
│ │ │ │ │ │ ├── confirm_plan.html
│ │ │ │ │ │ ├── confirm_subscription_renewal.html
│ │ │ │ │ │ ├── current_subscription.html
│ │ │ │ │ │ ├── data_migration_in_progress.html
│ │ │ │ │ │ ├── deactivated_notice.html
│ │ │ │ │ │ ├── insufficient_privilege_notification.html
│ │ │ │ │ │ ├── internal_subscription_management.html
│ │ │ │ │ │ ├── manage_releases_by_location.html
│ │ │ │ │ │ ├── renew_plan.html
│ │ │ │ │ │ ├── select.html
│ │ │ │ │ │ ├── select_plan.html
│ │ │ │ │ │ ├── selected_plan_contact.html
│ │ │ │ │ │ └── tombstone_management.html
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── base_change_plan.html
│ │ │ │ │ │ ├── billing_statements.html
│ │ │ │ │ │ ├── confirm_plan.html
│ │ │ │ │ │ ├── confirm_subscription_renewal.html
│ │ │ │ │ │ ├── current_subscription.html
│ │ │ │ │ │ ├── data_migration_in_progress.html
│ │ │ │ │ │ ├── deactivated_notice.html
│ │ │ │ │ │ ├── insufficient_privilege_notification.html
│ │ │ │ │ │ ├── internal_subscription_management.html
│ │ │ │ │ │ ├── manage_releases_by_location.html
│ │ │ │ │ │ ├── renew_plan.html
│ │ │ │ │ │ ├── select.html
│ │ │ │ │ │ ├── select_plan.html
│ │ │ │ │ │ ├── selected_plan_contact.html
│ │ │ │ │ │ └── tombstone_management.html
│ │ │ │ │ ├── confirm_billing_info.html
│ │ │ │ │ ├── email/
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ └── domain_invite.html
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ └── domain_invite.html
│ │ │ │ │ │ ├── domain_invite.txt
│ │ │ │ │ │ ├── domain_request_approval.html
│ │ │ │ │ │ ├── domain_request_approval.txt
│ │ │ │ │ │ ├── invite_confirmation.html
│ │ │ │ │ │ ├── invite_confirmation.txt
│ │ │ │ │ │ ├── password_reset_confirmation.html
│ │ │ │ │ │ ├── password_reset_confirmation.txt
│ │ │ │ │ │ ├── self_starter.html
│ │ │ │ │ │ ├── self_starter.txt
│ │ │ │ │ │ ├── support_handoff.html
│ │ │ │ │ │ └── support_handoff.txt
│ │ │ │ │ ├── import_app_from_another_server_main.html
│ │ │ │ │ ├── internal_calculations.html
│ │ │ │ │ ├── internal_settings.html
│ │ │ │ │ ├── partials/
│ │ │ │ │ │ ├── app_list.html
│ │ │ │ │ │ ├── autopay_terms.html
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── auto_renew_modal.html
│ │ │ │ │ │ │ ├── license_explanations.html
│ │ │ │ │ │ │ ├── new_stripe_card_template.html
│ │ │ │ │ │ │ └── payment_modal.html
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ ├── auto_renew_modal.html
│ │ │ │ │ │ │ ├── license_explanations.html
│ │ │ │ │ │ │ ├── new_stripe_card_template.html
│ │ │ │ │ │ │ ├── payment_modal.html
│ │ │ │ │ │ │ └── restrictive_license.html
│ │ │ │ │ │ ├── disable_auto_renew.html
│ │ │ │ │ │ ├── enable_auto_renew.html
│ │ │ │ │ │ ├── how_to_download_app_json.html
│ │ │ │ │ │ ├── how_to_import_multimedia.html
│ │ │ │ │ │ ├── how_to_start_with_import_app_feature.html
│ │ │ │ │ │ ├── new_stripe_card_alpinejs.html
│ │ │ │ │ │ ├── payment_methods.html
│ │ │ │ │ │ └── select_autopay_method.html
│ │ │ │ │ └── update_billing_contact_info.html
│ │ │ │ ├── error.html
│ │ │ │ └── login_and_password/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── login.html
│ │ │ │ │ └── password_change_done.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── login.html
│ │ │ │ │ └── password_change_done.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── login_full.html
│ │ │ │ │ │ └── server_location_select.html
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── login_full.html
│ │ │ │ │ │ └── server_location_select.html
│ │ │ │ │ └── password_reset_form_only.html
│ │ │ │ ├── password_reset_complete.html
│ │ │ │ ├── password_reset_confirm.html
│ │ │ │ ├── password_reset_done.html
│ │ │ │ ├── password_reset_email.html
│ │ │ │ ├── password_reset_form.html
│ │ │ │ └── two_factor/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── _wizard_actions.html
│ │ │ │ │ └── _wizard_forms.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── _wizard_actions.html
│ │ │ │ │ └── _wizard_forms.html
│ │ │ │ ├── core/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── backup_tokens.html
│ │ │ │ │ │ ├── login.html
│ │ │ │ │ │ ├── login_form.html
│ │ │ │ │ │ ├── phone_register.html
│ │ │ │ │ │ ├── setup.html
│ │ │ │ │ │ └── setup_complete.html
│ │ │ │ │ └── bootstrap5/
│ │ │ │ │ ├── backup_tokens.html
│ │ │ │ │ ├── login.html
│ │ │ │ │ ├── login_form.html
│ │ │ │ │ ├── otp_required.html
│ │ │ │ │ ├── phone_register.html
│ │ │ │ │ ├── setup.html
│ │ │ │ │ └── setup_complete.html
│ │ │ │ └── profile/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── disable.html
│ │ │ │ │ └── profile.html
│ │ │ │ └── bootstrap5/
│ │ │ │ ├── disable.html
│ │ │ │ └── profile.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_api_key_auth.py
│ │ │ │ ├── test_auth.py
│ │ │ │ ├── test_auth_decorators.py
│ │ │ │ ├── test_auth_type.py
│ │ │ │ ├── test_dbaccessors.py
│ │ │ │ ├── test_delete_domain.py
│ │ │ │ ├── test_deletion_models.py
│ │ │ │ ├── test_domain_calculated_properties.py
│ │ │ │ ├── test_domain_name_generation.py
│ │ │ │ ├── test_forms.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_oauth_superuser_access.py
│ │ │ │ ├── test_password_reset.py
│ │ │ │ ├── test_password_strength.py
│ │ │ │ ├── test_redirect_url.py
│ │ │ │ ├── test_remove_duplicate_domains.py
│ │ │ │ ├── test_two_factor_check.py
│ │ │ │ ├── test_utils.py
│ │ │ │ └── test_views.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views/
│ │ │ ├── __init__.py
│ │ │ ├── accounting.py
│ │ │ ├── base.py
│ │ │ ├── feedback.py
│ │ │ ├── fixtures.py
│ │ │ ├── import_apps.py
│ │ │ ├── internal.py
│ │ │ ├── releases.py
│ │ │ ├── settings.py
│ │ │ ├── sms.py
│ │ │ └── tombstone.py
│ │ ├── domain_migration_flags/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── api.py
│ │ │ ├── exceptions.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_migrate_data_from_tzmigration.py
│ │ │ │ ├── 0003_add_migration_dates.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ └── tests/
│ │ │ ├── __init__.py
│ │ │ └── test_domain_migration_progress.py
│ │ ├── dropbox/
│ │ │ ├── __init__.py
│ │ │ ├── decorators.py
│ │ │ ├── exceptions.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ └── upload_file_to_dropbox.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── dropbox/
│ │ │ │ └── emails/
│ │ │ │ ├── upload_error.html
│ │ │ │ ├── upload_error.txt
│ │ │ │ ├── upload_success.html
│ │ │ │ └── upload_success.txt
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_dropbox_upload_helper.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── dump_reload/
│ │ │ ├── __init__.py
│ │ │ ├── const.py
│ │ │ ├── couch/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── dump.py
│ │ │ │ ├── id_providers.py
│ │ │ │ └── load.py
│ │ │ ├── exceptions.py
│ │ │ ├── interface.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── dump_case_data.py
│ │ │ │ ├── dump_domain_data.py
│ │ │ │ ├── dump_domain_data_raw.py
│ │ │ │ ├── load_domain_data.py
│ │ │ │ └── print_domain_stats.py
│ │ │ ├── sql/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── dump.py
│ │ │ │ ├── filters.py
│ │ │ │ ├── load.py
│ │ │ │ └── serialization.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ └── old-model-names.json
│ │ │ │ ├── test_couch_dump_load.py
│ │ │ │ ├── test_dump_models.py
│ │ │ │ ├── test_serialization.py
│ │ │ │ ├── test_slugs.py
│ │ │ │ ├── test_sql_data_loader.py
│ │ │ │ ├── test_sql_dump_load.py
│ │ │ │ └── test_sql_filters.py
│ │ │ └── util.py
│ │ ├── email/
│ │ │ ├── __init__.py
│ │ │ ├── forms.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_emailsettings_return_path_email.py
│ │ │ │ ├── 0003_emailsettings_password_cbc.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── email/
│ │ │ │ └── js/
│ │ │ │ └── email_settings.js
│ │ │ ├── templates/
│ │ │ │ └── email/
│ │ │ │ └── email_settings.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_email_form.py
│ │ │ ├── urls.py
│ │ │ └── views.py
│ │ ├── enterprise/
│ │ │ ├── __init__.py
│ │ │ ├── api/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api.py
│ │ │ │ └── resources.py
│ │ │ ├── decorators.py
│ │ │ ├── dispatcher.py
│ │ │ ├── enterprise.py
│ │ │ ├── exceptions.py
│ │ │ ├── filters.py
│ │ │ ├── forms.py
│ │ │ ├── interface.py
│ │ │ ├── iterators.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ └── report_on_enterprise_domain.py
│ │ │ ├── metric_events.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_enterprisepermissions_account_unique.py
│ │ │ │ ├── 0003_enterprisepermissions_modify_account.py
│ │ │ │ ├── 0004_enterprisemobileworkersettings.py
│ │ │ │ └── __init__.py
│ │ │ ├── mixins.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── enterprise/
│ │ │ │ └── js/
│ │ │ │ ├── enterprise_settings.js
│ │ │ │ └── project_dashboard.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── enterprise/
│ │ │ │ ├── enterprise_permissions.html
│ │ │ │ ├── enterprise_settings.html
│ │ │ │ ├── manage_mobile_workers.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── date_range_modal.html
│ │ │ │ │ ├── enterprise_permissions_table.html
│ │ │ │ │ └── project_tile.html
│ │ │ │ └── project_dashboard.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ └── test_resources.py
│ │ │ │ ├── test_apis.py
│ │ │ │ ├── test_decorators.py
│ │ │ │ ├── test_enterprise.py
│ │ │ │ ├── test_enterprise_mobile_worker_settings.py
│ │ │ │ ├── test_enterprise_tasks.py
│ │ │ │ ├── test_forms.py
│ │ │ │ ├── test_interface.py
│ │ │ │ ├── test_iterators.py
│ │ │ │ ├── test_permissions.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_views.py
│ │ │ │ └── utils.py
│ │ │ ├── urls.py
│ │ │ └── views.py
│ │ ├── es/
│ │ │ ├── README.rst
│ │ │ ├── REINDEX_PROCESS.md
│ │ │ ├── __init__.py
│ │ │ ├── aggregations.py
│ │ │ ├── app_config.py
│ │ │ ├── apps.py
│ │ │ ├── case_search.py
│ │ │ ├── case_search_sub.py
│ │ │ ├── cases.py
│ │ │ ├── client.py
│ │ │ ├── const.py
│ │ │ ├── domains.py
│ │ │ ├── es_query.py
│ │ │ ├── exceptions.py
│ │ │ ├── fake/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── es_query_fake.py
│ │ │ │ ├── forms_fake.py
│ │ │ │ ├── groups_fake.py
│ │ │ │ └── users_fake.py
│ │ │ ├── filters.py
│ │ │ ├── forms.py
│ │ │ ├── groups.py
│ │ │ ├── index/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── analysis.py
│ │ │ │ └── settings.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── elastic_sync_multiplexed.py
│ │ │ │ ├── ensure_indices_reindexed.py
│ │ │ │ ├── es_version_for_index.py
│ │ │ │ ├── make_elastic_migration.py
│ │ │ │ ├── resave_failed_forms_and_cases.py
│ │ │ │ ├── restore_es_snapshot.py
│ │ │ │ ├── verify_reindex.py
│ │ │ │ └── wipe_es.py
│ │ │ ├── mappings/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── app_mapping.py
│ │ │ │ ├── case_mapping.py
│ │ │ │ ├── case_search_mapping.py
│ │ │ │ ├── const.py
│ │ │ │ ├── domain_mapping.py
│ │ │ │ ├── group_mapping.py
│ │ │ │ ├── sms_mapping.py
│ │ │ │ ├── tests/
│ │ │ │ │ └── __init__.py
│ │ │ │ ├── user_mapping.py
│ │ │ │ └── xform_mapping.py
│ │ │ ├── migration_operations.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_bootstrap_es_indexes.py
│ │ │ │ ├── 0002_add_tombstones.py
│ │ │ │ ├── 0003_add_assigned_location_ids.py
│ │ │ │ ├── 0004_make_new_indexes.py
│ │ │ │ ├── 0005_add_epoch_as_valid_date_to_forms.py
│ │ │ │ ├── 0006_verify_es2_indices_reindexed.py
│ │ │ │ ├── 0007_init_indices_for_fresh_es_5.py
│ │ │ │ ├── 0008_add_doc_id_to_all_mappings.py
│ │ │ │ ├── 0009_add_indices_for_reindex_in_es5.py
│ │ │ │ ├── 0010_delete_reverted_indices.py
│ │ │ │ ├── 0011_add_indices_for_es5_reindex.py
│ │ │ │ ├── 0012_add_new_index_for_bha.py
│ │ │ │ ├── 0013_add_last_modifed.py
│ │ │ │ ├── 0014_enable_slowlogs.py
│ │ │ │ ├── 0015_add_user_domain_memberships.py
│ │ │ │ ├── 0016_add_new_index_for_cc_perf.py
│ │ │ │ ├── 0017_add_is_account_confirmed.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── profiling.py
│ │ │ ├── queries.py
│ │ │ ├── sms.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── index/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── test_analysis.py
│ │ │ │ │ └── test_settings.py
│ │ │ │ ├── test_aggregations.py
│ │ │ │ ├── test_app_adapter.py
│ │ │ │ ├── test_case_adapter.py
│ │ │ │ ├── test_case_search_adapter.py
│ │ │ │ ├── test_case_search_es.py
│ │ │ │ ├── test_client.py
│ │ │ │ ├── test_command_make_elastic_migration.py
│ │ │ │ ├── test_domain_adapter.py
│ │ │ │ ├── test_elastic_sync_multiplexed_command.py
│ │ │ │ ├── test_ensure_indices_reindexed.py
│ │ │ │ ├── test_esquery.py
│ │ │ │ ├── test_esqueryset.py
│ │ │ │ ├── test_filters.py
│ │ │ │ ├── test_form_adapter.py
│ │ │ │ ├── test_migration_operations.py
│ │ │ │ ├── test_queries.py
│ │ │ │ ├── test_sms.py
│ │ │ │ ├── test_sorting.py
│ │ │ │ ├── test_test_utils.py
│ │ │ │ ├── test_user_adapter.py
│ │ │ │ ├── test_users_es.py
│ │ │ │ ├── test_utils.py
│ │ │ │ ├── test_verify_reindex.py
│ │ │ │ └── utils.py
│ │ │ ├── transient_util.py
│ │ │ ├── users.py
│ │ │ └── utils.py
│ │ ├── events/
│ │ │ ├── __init__.py
│ │ │ └── migrations/
│ │ │ ├── 0001_add_events_model.py
│ │ │ ├── 0002_attendancetrackingconfig.py
│ │ │ ├── 0003_event_attendance_taker_ids.py
│ │ │ ├── 0004_event_id_case_id.py
│ │ │ ├── 0005_rename_alter_event__attendance_taker_ids.py
│ │ │ ├── 0006_remove_end_date_constraint.py
│ │ │ ├── 0007_alter_event_attendee_list_status.py
│ │ │ ├── 0008_alter_event__case_id.py
│ │ │ ├── 0009_attendeemodel.py
│ │ │ ├── 0010_event_location.py
│ │ │ └── __init__.py
│ │ ├── experiments/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── experiment.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ └── tests/
│ │ │ ├── __init__.py
│ │ │ ├── test_enabler.py
│ │ │ └── test_experiment.py
│ │ ├── export/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── const.py
│ │ │ ├── dbaccessors.py
│ │ │ ├── det/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base.py
│ │ │ │ ├── exceptions.py
│ │ │ │ └── schema_generator.py
│ │ │ ├── esaccessors.py
│ │ │ ├── exceptions.py
│ │ │ ├── export.py
│ │ │ ├── filters.py
│ │ │ ├── forms.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── deid_export.py
│ │ │ │ ├── delete_exports.py
│ │ │ │ ├── download_saved_export.py
│ │ │ │ ├── process_skipped_pages.py
│ │ │ │ ├── rebuild_export.py
│ │ │ │ ├── update_export_with_newest_data.py
│ │ │ │ └── upload_saved_export.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_datafile.py
│ │ │ │ ├── 0003_emailexportwhendonerequest.py
│ │ │ │ ├── 0004_datafile_delete_after.py
│ │ │ │ ├── 0005_datafile_blobmeta.py
│ │ │ │ ├── 0006_delete_dailysavedexportnotification.py
│ │ │ │ ├── 0007_auto_20190906_0149.py
│ │ │ │ ├── 0008_auto_20190906_2008.py
│ │ │ │ ├── 0009_incrementalexport_incrementalexportcheckpoint.py
│ │ │ │ ├── 0010_defaultexportsettings.py
│ │ │ │ ├── 0011_defaultexportsettings_usecouchfiletypes.py
│ │ │ │ ├── 0012_defaultexportsettings_remove_duplicates_option.py
│ │ │ │ ├── 0013_rm_incrementalexport.py
│ │ │ │ ├── 0014_deidhash.py
│ │ │ │ └── __init__.py
│ │ │ ├── models/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── deid_export.py
│ │ │ │ ├── export_settings.py
│ │ │ │ └── new.py
│ │ │ ├── multiprocess.py
│ │ │ ├── static/
│ │ │ │ └── export/
│ │ │ │ ├── js/
│ │ │ │ │ ├── const.js
│ │ │ │ │ ├── create_export.js
│ │ │ │ │ ├── customize_export_new.js
│ │ │ │ │ ├── datasource_export.js
│ │ │ │ │ ├── download_data_files.js
│ │ │ │ │ ├── download_export.js
│ │ │ │ │ ├── export_list.js
│ │ │ │ │ ├── export_list_main.js
│ │ │ │ │ ├── models.js
│ │ │ │ │ └── utils.js
│ │ │ │ └── spec/
│ │ │ │ ├── ExportColumn.spec.js
│ │ │ │ ├── ExportInstance.spec.js
│ │ │ │ ├── Exports.Utils.spec.js
│ │ │ │ ├── data/
│ │ │ │ │ └── export_instances.js
│ │ │ │ └── main.js
│ │ │ ├── system_properties.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── export/
│ │ │ │ ├── commcare_analytics.html
│ │ │ │ ├── customize_export_new.html
│ │ │ │ ├── datasource_export_view.html
│ │ │ │ ├── dialogs/
│ │ │ │ │ ├── bulk_delete_custom_export_dialog.html
│ │ │ │ │ ├── delete_custom_export_dialog.html
│ │ │ │ │ ├── process_deleted_questions.html
│ │ │ │ │ └── process_deprecated_properties.html
│ │ │ │ ├── download_data_files.html
│ │ │ │ ├── download_export.html
│ │ │ │ ├── export_list.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── customize_export_header.html
│ │ │ │ │ ├── delete_bulk_notice.html
│ │ │ │ │ ├── export_bulk_notice.html
│ │ │ │ │ ├── export_download_prepare.html
│ │ │ │ │ ├── export_download_progress.html
│ │ │ │ │ ├── export_list_controller.html
│ │ │ │ │ ├── export_list_create_export_modal.html
│ │ │ │ │ ├── feed_filter_modal.html
│ │ │ │ │ ├── loading_exports.html
│ │ │ │ │ ├── new_customize_export_templates.html
│ │ │ │ │ ├── odata_feed_limit_reached_modal.html
│ │ │ │ │ └── table.html
│ │ │ │ ├── paywall.html
│ │ │ │ └── spec/
│ │ │ │ └── mocha.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ ├── app_with_subcases.json
│ │ │ │ │ ├── app_with_subcases_form.xml
│ │ │ │ │ ├── app_with_subcases_submission.json
│ │ │ │ │ ├── basic_application.json
│ │ │ │ │ ├── basic_case_application.json
│ │ │ │ │ ├── basic_form.xml
│ │ │ │ │ ├── basic_form_version2.xml
│ │ │ │ │ ├── basic_form_version2_delete.xml
│ │ │ │ │ ├── det/
│ │ │ │ │ │ ├── case_export_instance.json
│ │ │ │ │ │ ├── form_export_instance.json
│ │ │ │ │ │ └── form_export_instance_with_repeat.json
│ │ │ │ │ ├── form_with_labels.xml
│ │ │ │ │ ├── multiple_choice_form.xml
│ │ │ │ │ ├── multiple_choice_form_version2.xml
│ │ │ │ │ ├── nested_repeat_form.xml
│ │ │ │ │ ├── parent_child_case_application.json
│ │ │ │ │ ├── question_schema_no_multi.xml
│ │ │ │ │ ├── question_schema_test_app.json
│ │ │ │ │ ├── question_schema_update_form.xml
│ │ │ │ │ ├── repeat_group_form.xml
│ │ │ │ │ ├── repeat_group_form_version2.xml
│ │ │ │ │ └── stock_form.xml
│ │ │ │ ├── test_add_inferred_export_properties.py
│ │ │ │ ├── test_daily_saved_exports.py
│ │ │ │ ├── test_dbaccessors.py
│ │ │ │ ├── test_deid_export.py
│ │ │ │ ├── test_det_schema_generation.py
│ │ │ │ ├── test_esaccessors.py
│ │ │ │ ├── test_export.py
│ │ │ │ ├── test_export_column.py
│ │ │ │ ├── test_export_data_schema.py
│ │ │ │ ├── test_export_filters.py
│ │ │ │ ├── test_export_form_subcases.py
│ │ │ │ ├── test_export_forms.py
│ │ │ │ ├── test_export_instance.py
│ │ │ │ ├── test_export_item.py
│ │ │ │ ├── test_export_list_helper.py
│ │ │ │ ├── test_export_models_new.py
│ │ │ │ ├── test_export_utils.py
│ │ │ │ ├── test_export_views.py
│ │ │ │ ├── test_get_export_file.py
│ │ │ │ ├── test_inferred_schema.py
│ │ │ │ ├── test_new.py
│ │ │ │ ├── test_sms_export.py
│ │ │ │ ├── test_table_configuration.py
│ │ │ │ ├── test_views.py
│ │ │ │ └── util.py
│ │ │ ├── transforms.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views/
│ │ │ ├── __init__.py
│ │ │ ├── download.py
│ │ │ ├── edit.py
│ │ │ ├── list.py
│ │ │ ├── new.py
│ │ │ └── utils.py
│ │ ├── fixtures/
│ │ │ ├── __init__.py
│ │ │ ├── apps.py
│ │ │ ├── dispatcher.py
│ │ │ ├── download.py
│ │ │ ├── exceptions.py
│ │ │ ├── fixturegenerators.py
│ │ │ ├── interface.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_rm_blobdb_domain_fixtures.py
│ │ │ │ ├── 0003_rm_blobdb_domain_fixtures.py
│ │ │ │ ├── 0004_userlookuptablestatus.py
│ │ │ │ ├── 0005_lookuptablemodels.py
│ │ │ │ ├── 0005_sqllookuptablemodels.py
│ │ │ │ ├── 0006_index_row_id.py
│ │ │ │ ├── 0007_db_cascade.py
│ │ │ │ ├── 0008_sqllookuptables.py
│ │ │ │ ├── 0009_remove_lookuptablerowowner_couch_id.py
│ │ │ │ ├── 0010_lookuptable_is_synced.py
│ │ │ │ ├── 0011_lookuptable_last_modified.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── resources/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── v0_1.py
│ │ │ │ └── v0_6.py
│ │ │ ├── static/
│ │ │ │ └── fixtures/
│ │ │ │ └── js/
│ │ │ │ ├── lookup-manage.js
│ │ │ │ └── view-table.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── fixtures/
│ │ │ │ ├── fixtures_base.html
│ │ │ │ ├── manage_tables.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── edit_table_modal.html
│ │ │ │ │ ├── fixture_upload_status.html
│ │ │ │ │ ├── fixture_upload_status_api.txt
│ │ │ │ │ └── modal_edit.html
│ │ │ │ ├── upload_complete.html
│ │ │ │ ├── upload_complete.txt
│ │ │ │ └── view_table.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_download.py
│ │ │ │ ├── test_field_names.py
│ │ │ │ ├── test_fixture_data.py
│ │ │ │ ├── test_location_ownership.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_upload/
│ │ │ │ │ ├── not_excel_file.xlsx
│ │ │ │ │ └── ok.xlsx
│ │ │ │ ├── test_upload.py
│ │ │ │ ├── test_views.py
│ │ │ │ └── test_workbook.py
│ │ │ ├── upload/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── const.py
│ │ │ │ ├── definitions.py
│ │ │ │ ├── failure_messages.py
│ │ │ │ ├── run_upload.py
│ │ │ │ ├── validation.py
│ │ │ │ └── workbook.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── formplayer_api/
│ │ │ ├── __init__.py
│ │ │ ├── clear_user_data.py
│ │ │ ├── const.py
│ │ │ ├── exceptions.py
│ │ │ ├── form_validation.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── clear_formplayer_dbs.py
│ │ │ │ └── prime_formplayer_restores.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_drop_old_tables.py
│ │ │ │ └── __init__.py
│ │ │ ├── smsforms/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── api.py
│ │ │ │ ├── exceptions.py
│ │ │ │ ├── sms.py
│ │ │ │ └── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ └── test_formplayer_interface.py
│ │ │ ├── sync_db.py
│ │ │ ├── tasks.py
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_form_validation.py
│ │ │ │ └── test_prime_restore.py
│ │ │ └── utils.py
│ │ ├── geospatial/
│ │ │ ├── README.md
│ │ │ ├── __init__.py
│ │ │ ├── apps.py
│ │ │ ├── const.py
│ │ │ ├── dispatchers.py
│ │ │ ├── es.py
│ │ │ ├── exceptions.py
│ │ │ ├── filters.py
│ │ │ ├── forms.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── copy_gps_metadata.py
│ │ │ │ ├── index_geolocation_case_properties.py
│ │ │ │ └── index_utils.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_create_geopolygon.py
│ │ │ │ ├── 0002_geoconfig.py
│ │ │ │ ├── 0003_auto_20230908_0927.py
│ │ │ │ ├── 0004_auto_20230920_0821.py
│ │ │ │ ├── 0005_auto_20240202_0807.py
│ │ │ │ ├── 0006_geoconfig_max_cases_per_user_and_more.py
│ │ │ │ ├── 0007_geoconfig_max_case_distance_and_more.py
│ │ │ │ ├── 0008_geoconfig_flag_assigned_cases.py
│ │ │ │ ├── 0009_geoconfig_api_key_cbc_encryption.py
│ │ │ │ ├── 0010_remove_road_network_algorithm.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── reports.py
│ │ │ ├── routing_solvers/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── base.py
│ │ │ │ ├── mapbox_utils.py
│ │ │ │ └── pulp.py
│ │ │ ├── static/
│ │ │ │ └── geospatial/
│ │ │ │ └── js/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── case_grouping_map.js
│ │ │ │ │ ├── case_management.js
│ │ │ │ │ ├── geo_config.js
│ │ │ │ │ ├── gps_capture.js
│ │ │ │ │ └── models.js
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── case_grouping_map.js
│ │ │ │ │ ├── case_management.js
│ │ │ │ │ ├── geo_config.js
│ │ │ │ │ ├── gps_capture.js
│ │ │ │ │ └── models.js
│ │ │ │ └── utils.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── geospatial/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── base_template.html
│ │ │ │ │ ├── case_grouping_map.html
│ │ │ │ │ ├── case_grouping_map_base.html
│ │ │ │ │ ├── case_management.html
│ │ │ │ │ ├── case_management_base.html
│ │ │ │ │ ├── gps_capture.html
│ │ │ │ │ ├── gps_capture_view.html
│ │ │ │ │ └── settings.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── base_template.html
│ │ │ │ │ ├── case_grouping_map.html
│ │ │ │ │ ├── case_grouping_map_base.html
│ │ │ │ │ ├── case_management.html
│ │ │ │ │ ├── case_management_base.html
│ │ │ │ │ ├── gps_capture.html
│ │ │ │ │ ├── gps_capture_view.html
│ │ │ │ │ └── settings.html
│ │ │ │ └── partials/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── delete_saved_area_modal.html
│ │ │ │ │ ├── review_assignment_modal.html
│ │ │ │ │ └── saved_polygon_filter.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── delete_saved_area_modal.html
│ │ │ │ │ ├── review_assignment_modal.html
│ │ │ │ │ └── saved_polygon_filter.html
│ │ │ │ └── index_alert.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_copy_gps_metadata.py
│ │ │ │ ├── test_es.py
│ │ │ │ ├── test_index_geolocation_case_properties.py
│ │ │ │ ├── test_mapbox_optimize.py
│ │ │ │ ├── test_models.py
│ │ │ │ ├── test_pulp.py
│ │ │ │ ├── test_reports.py
│ │ │ │ ├── test_tasks.py
│ │ │ │ ├── test_utils.py
│ │ │ │ └── test_views.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── groups/
│ │ │ ├── __init__.py
│ │ │ ├── _design/
│ │ │ │ └── views/
│ │ │ │ ├── all_groups/
│ │ │ │ │ └── map.js
│ │ │ │ ├── by_name/
│ │ │ │ │ └── map.js
│ │ │ │ └── by_user/
│ │ │ │ └── map.js
│ │ │ ├── dbaccessors.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── groups/
│ │ │ │ └── js/
│ │ │ │ ├── all_groups.js
│ │ │ │ └── group_members.js
│ │ │ ├── templates/
│ │ │ │ └── groups/
│ │ │ │ ├── all_groups.html
│ │ │ │ ├── group_members.html
│ │ │ │ └── partials/
│ │ │ │ ├── case_sharing_upgrade_notice.html
│ │ │ │ └── edit_group_disabled_case_sharing.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_groups.py
│ │ │ │ ├── test_models.py
│ │ │ │ └── test_utils.py
│ │ │ ├── urls.py
│ │ │ └── views.py
│ │ ├── hqadmin/
│ │ │ ├── __init__.py
│ │ │ ├── _design/
│ │ │ │ └── filters/
│ │ │ │ ├── domains_and_doc_types.js
│ │ │ │ └── not_case_form.js
│ │ │ ├── admin.py
│ │ │ ├── app_config.py
│ │ │ ├── corrupt_couch.py
│ │ │ ├── escheck.py
│ │ │ ├── forms.py
│ │ │ ├── history.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── cchq_prbac_bootstrap.py
│ │ │ │ ├── cchq_prbac_grandfather_privs.py
│ │ │ │ ├── cchq_prbac_revoke_privs.py
│ │ │ │ ├── check_case_index_ids.py
│ │ │ │ ├── check_services.py
│ │ │ │ ├── clean_2fa_sessions.py
│ │ │ │ ├── clear_supervisor_confs.py
│ │ │ │ ├── corrupt_couch.py
│ │ │ │ ├── corrupt_couch_nodes.py
│ │ │ │ ├── delete_old_couch_views_from_disk.py
│ │ │ │ ├── delete_related_cases.py
│ │ │ │ ├── deploy_in_progress.py
│ │ │ │ ├── export_domain_forms_raw.py
│ │ │ │ ├── fetch_reconciliation_records.py
│ │ │ │ ├── fix_checkpoint_after_rewind.py
│ │ │ │ ├── fix_checkpoints_from_date.py
│ │ │ │ ├── fix_checkpoints_from_file.py
│ │ │ │ ├── force_web_user_password_reset.py
│ │ │ │ ├── get_download_url.py
│ │ │ │ ├── import_domain_forms_raw.py
│ │ │ │ ├── kill_stale_celery_workers.py
│ │ │ │ ├── mail_admins.py
│ │ │ │ ├── preindex_everything.py
│ │ │ │ ├── prune_couch_views.py
│ │ │ │ ├── prune_elastic_indices.py
│ │ │ │ ├── recent_changes.py
│ │ │ │ ├── record_deploy_success.py
│ │ │ │ ├── republish_doc_changes.py
│ │ │ │ ├── send_email.py
│ │ │ │ ├── shutdown_celery_worker_by_hostname.py
│ │ │ │ ├── stale_data_in_es.py
│ │ │ │ ├── static_analysis.py
│ │ │ │ ├── update_django_locales.py
│ │ │ │ ├── update_site_setup.py
│ │ │ │ └── verify_ssl_connections.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_vcmmigrationaudit.py
│ │ │ │ ├── 0003_auto_20160715_1543.py
│ │ │ │ ├── 0004_auto_20160715_1547.py
│ │ │ │ ├── 0005_auto_20160715_1612.py
│ │ │ │ ├── 0006_esrestorepillowcheckpoints.py
│ │ │ │ ├── 0007_esrestorepillowcheckpoint_datefield.py
│ │ │ │ ├── 0008_delete_vcmmigration.py
│ │ │ │ ├── 0009_auto_20170315_1322.py
│ │ │ │ ├── 0010_sqlhqdeploy.py
│ │ │ │ ├── 0011_alter_hqdeploy_environment.py
│ │ │ │ ├── 0012_alter_hqdeploy_diffurl.py
│ │ │ │ ├── 0013_populate_sqlhqdeploy.py
│ │ │ │ ├── 0014_remove_sqlhqdeploy_couch_id.py
│ │ │ │ ├── 0015_rename_sqlhqdeploy.py
│ │ │ │ ├── 0016_hqdeploy_ordering.py
│ │ │ │ ├── 0017_hqdeploy_commit.py
│ │ │ │ ├── 0018_back_populate_deploy_commit.py
│ │ │ │ ├── 0019_celery_taskmeta_sequence.py
│ │ │ │ ├── 0020_celery_results_sequence.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── pillow_settings.py
│ │ │ ├── reporting/
│ │ │ │ └── __init__.py
│ │ │ ├── reports.py
│ │ │ ├── service_checks.py
│ │ │ ├── signals.py
│ │ │ ├── static/
│ │ │ │ └── hqadmin/
│ │ │ │ └── js/
│ │ │ │ ├── admin_restore.js
│ │ │ │ ├── app_build_timings.js
│ │ │ │ ├── mass_email.js
│ │ │ │ ├── raw_doc.js
│ │ │ │ ├── superuser_management.js
│ │ │ │ └── system_info.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── hqadmin/
│ │ │ │ ├── admin_restore.html
│ │ │ │ ├── app_build_timings.html
│ │ │ │ ├── branches_on_staging.html
│ │ │ │ ├── call_center_ucr_check.html
│ │ │ │ ├── disable_two_factor.html
│ │ │ │ ├── disable_user.html
│ │ │ │ ├── doc_in_es.html
│ │ │ │ ├── email/
│ │ │ │ │ ├── account_disabled_email.html
│ │ │ │ │ ├── error_email.html
│ │ │ │ │ ├── superuser_staff_email.html
│ │ │ │ │ └── two_factor_reset_email.html
│ │ │ │ ├── email_status.html
│ │ │ │ ├── gir_downloader.html
│ │ │ │ ├── global_thresholds.html
│ │ │ │ ├── hqadmin_base_filters.html
│ │ │ │ ├── malt_downloader.html
│ │ │ │ ├── mass_email.html
│ │ │ │ ├── messaging_case_updates.html
│ │ │ │ ├── offboarding_list.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── commit_table.html
│ │ │ │ │ ├── commit_tr.html
│ │ │ │ │ ├── deploy_history.html
│ │ │ │ │ ├── pillow-operation-modal.html
│ │ │ │ │ ├── project_snapshot.html
│ │ │ │ │ └── timing_data_table.html
│ │ │ │ ├── raw_doc.html
│ │ │ │ ├── superuser_management.html
│ │ │ │ ├── system_info.html
│ │ │ │ ├── user_audit_report.html
│ │ │ │ └── web_user_lookup.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ └── restore.xml
│ │ │ │ ├── test_corrupt_couch.py
│ │ │ │ ├── test_prbac.py
│ │ │ │ ├── test_raw_doc.py
│ │ │ │ ├── test_reports.py
│ │ │ │ ├── test_service_checks.py
│ │ │ │ ├── test_stale_data_in_es.py
│ │ │ │ ├── test_user_audit_report.py
│ │ │ │ ├── test_utils.py
│ │ │ │ └── test_views.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views/
│ │ │ ├── __init__.py
│ │ │ ├── data.py
│ │ │ ├── operations.py
│ │ │ ├── reports.py
│ │ │ ├── system.py
│ │ │ ├── users.py
│ │ │ └── utils.py
│ │ ├── hqcase/
│ │ │ ├── __init__.py
│ │ │ ├── analytics.py
│ │ │ ├── api/
│ │ │ │ ├── core.py
│ │ │ │ ├── get_bulk.py
│ │ │ │ ├── get_list.py
│ │ │ │ └── updates.py
│ │ │ ├── bulk.py
│ │ │ ├── case_helper.py
│ │ │ ├── constants.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── initialize_es_indices.py
│ │ │ │ ├── ptop_preindex.py
│ │ │ │ ├── ptop_reindexer_v2.py
│ │ │ │ ├── reindex_sql_forms_in_domain.py
│ │ │ │ ├── reprocess_form_case_es_deletes.py
│ │ │ │ └── reset_case_name.py
│ │ │ ├── static/
│ │ │ │ └── hqcase/
│ │ │ │ └── js/
│ │ │ │ └── explode_cases.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── hqcase/
│ │ │ │ ├── explode_cases.html
│ │ │ │ └── xml/
│ │ │ │ └── case_block.xml
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── test_api_updates.py
│ │ │ │ ├── test_bugs.py
│ │ │ │ ├── test_bulk.py
│ │ │ │ ├── test_case_api_bulk_get.py
│ │ │ │ ├── test_case_api_get.py
│ │ │ │ ├── test_case_api_permissions.py
│ │ │ │ ├── test_case_api_updates.py
│ │ │ │ ├── test_case_copier.py
│ │ │ │ ├── test_case_helper.py
│ │ │ │ ├── test_case_list_api.py
│ │ │ │ ├── test_case_sharing.py
│ │ │ │ ├── test_case_update_api.py
│ │ │ │ ├── test_dbaccessors.py
│ │ │ │ ├── test_explode_cases.py
│ │ │ │ ├── test_external_id_url_encoding.py
│ │ │ │ ├── test_loadtest_users.py
│ │ │ │ ├── test_object_cache.py
│ │ │ │ ├── test_serialization.py
│ │ │ │ └── test_utils.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ └── views.py
│ │ ├── hqmedia/
│ │ │ ├── README.rst
│ │ │ ├── __init__.py
│ │ │ ├── _design/
│ │ │ │ ├── fulltext/
│ │ │ │ │ ├── audio_search/
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── image_search/
│ │ │ │ │ └── index.js
│ │ │ │ └── views/
│ │ │ │ ├── by_domain/
│ │ │ │ │ └── map.js
│ │ │ │ └── by_hash/
│ │ │ │ └── map.js
│ │ │ ├── cache.py
│ │ │ ├── controller.py
│ │ │ ├── exceptions.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ └── __init__.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── static/
│ │ │ │ └── hqmedia/
│ │ │ │ └── js/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── bulk_upload.js
│ │ │ │ │ ├── media_reference_models.js
│ │ │ │ │ ├── references_main.js
│ │ │ │ │ ├── translations_coverage.js
│ │ │ │ │ └── uploaders.js
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── bulk_upload.js
│ │ │ │ │ ├── media_reference_models.js
│ │ │ │ │ ├── references_main.js
│ │ │ │ │ ├── translations_coverage.js
│ │ │ │ │ └── uploaders.js
│ │ │ │ └── manage_paths_main.js
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ └── hqmedia/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── audio_translator.html
│ │ │ │ │ ├── bulk_upload.html
│ │ │ │ │ ├── bulk_upload_status.html
│ │ │ │ │ ├── manage_paths.html
│ │ │ │ │ ├── references.html
│ │ │ │ │ ├── translations_coverage.html
│ │ │ │ │ └── uploader_base.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── audio_translator.html
│ │ │ │ │ ├── bulk_upload.html
│ │ │ │ │ ├── bulk_upload_status.html
│ │ │ │ │ ├── manage_paths.html
│ │ │ │ │ ├── references.html
│ │ │ │ │ ├── translations_coverage.html
│ │ │ │ │ └── uploader_base.html
│ │ │ │ ├── partials/
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── bulk_upload.html
│ │ │ │ │ │ ├── bulk_upload_status.html
│ │ │ │ │ │ ├── manage_paths.html
│ │ │ │ │ │ ├── multimedia_uploader.html
│ │ │ │ │ │ ├── multimedia_zip_notice.html
│ │ │ │ │ │ └── reference_table.html
│ │ │ │ │ └── bootstrap5/
│ │ │ │ │ ├── bulk_upload.html
│ │ │ │ │ ├── bulk_upload_status.html
│ │ │ │ │ ├── manage_paths.html
│ │ │ │ │ ├── multimedia_uploader.html
│ │ │ │ │ ├── multimedia_zip_notice.html
│ │ │ │ │ └── reference_table.html
│ │ │ │ └── uploader/
│ │ │ │ ├── bootstrap3/
│ │ │ │ │ ├── preview_audio_single.html
│ │ │ │ │ ├── preview_image_single.html
│ │ │ │ │ ├── preview_video_single.html
│ │ │ │ │ └── queue_single.html
│ │ │ │ ├── bootstrap5/
│ │ │ │ │ ├── preview_audio_single.html
│ │ │ │ │ ├── preview_image_single.html
│ │ │ │ │ ├── preview_video_single.html
│ │ │ │ │ └── queue_single.html
│ │ │ │ └── errors.html
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── data/
│ │ │ │ │ └── manage-multimedia.json
│ │ │ │ ├── test_audio_translator_files.py
│ │ │ │ ├── test_manage_paths.py
│ │ │ │ ├── test_pillow_library_usage.py
│ │ │ │ └── test_utils.py
│ │ │ ├── urls.py
│ │ │ ├── utils.py
│ │ │ ├── view_helpers.py
│ │ │ └── views.py
│ │ ├── hqwebapp/
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── async_handler.py
│ │ │ ├── context.py
│ │ │ ├── crispy.py
│ │ │ ├── decorators.py
│ │ │ ├── doc_info.py
│ │ │ ├── doc_lookup.py
│ │ │ ├── docs/
│ │ │ │ └── chat_widget_translations.md
│ │ │ ├── encoders.py
│ │ │ ├── exceptions.py
│ │ │ ├── fields.py
│ │ │ ├── forms.py
│ │ │ ├── login_handlers.py
│ │ │ ├── login_utils.py
│ │ │ ├── management/
│ │ │ │ ├── __init__.py
│ │ │ │ └── commands/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── build_bootstrap5_diffs.py
│ │ │ │ ├── complete_bootstrap5_migration.py
│ │ │ │ ├── complete_bootstrap5_report.py
│ │ │ │ ├── copy_required_static_files.py
│ │ │ │ ├── fix_less_imports_collectstatic.py
│ │ │ │ ├── generate_webpack_settings.py
│ │ │ │ ├── generate_widget_translations.py
│ │ │ │ ├── list_waf_allow_patterns.py
│ │ │ │ ├── migrate_app_to_bootstrap5.py
│ │ │ │ ├── resource_static.py
│ │ │ │ ├── show_invalid_bootstrap3_files.py
│ │ │ │ └── update_manifest.py
│ │ │ ├── migrations/
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0002_auto_20171121_1803.py
│ │ │ │ ├── 0003_maintenancealert_domains.py
│ │ │ │ ├── 0004_apikeysettings.py
│ │ │ │ ├── 0005_delete_apikeysettings.py
│ │ │ │ ├── 0006_create_user_access_log.py
│ │ │ │ ├── 0007_user_access_agent.py
│ │ │ │ ├── 0008_hqoauthapplication.py
│ │ │ │ ├── 0009_truncate_authtoken_table.py
│ │ │ │ ├── 0010_maintenancealert_scheduling.py
│ │ │ │ ├── 0011_add_new_columns_and_rename_model.py
│ │ │ │ └── __init__.py
│ │ │ ├── models.py
│ │ │ ├── precompilers.py
│ │ │ ├── session_details_endpoint/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── tests.py
│ │ │ │ └── views.py
│ │ │ ├── signals.py
│ │ │ ├── static/
│ │ │ │ ├── accounting/
│ │ │ │ │ ├── less/
│ │ │ │ │ │ ├── pricing-main.less
│ │ │ │ │ │ └── pricing.less
│ │ │ │ │ └── scss/
│ │ │ │ │ ├── pricing-main.scss
│ │ │ │ │ └── pricing.scss
│ │ │ │ ├── app_manager/
│ │ │ │ │ ├── less/
│ │ │ │ │ │ ├── app_manager-main.less
│ │ │ │ │ │ ├── app_manager.less
│ │ │ │ │ │ ├── case_tile_preview.less
│ │ │ │ │ │ ├── content.less
│ │ │ │ │ │ ├── corehq_overrides.less
│ │ │ │ │ │ ├── deploy.less
│ │ │ │ │ │ ├── diff.less
│ │ │ │ │ │ ├── font/
│ │ │ │ │ │ │ ├── WorkflowFont-Regular.otf
│ │ │ │ │ │ │ └── WorkflowFont.glyphs
│ │ │ │ │ │ ├── font-workflow/
│ │ │ │ │ │ │ ├── core.less
│ │ │ │ │ │ │ ├── icons.less
│ │ │ │ │ │ │ ├── path.less
│ │ │ │ │ │ │ └── variables.less
│ │ │ │ │ │ ├── font-workflow.less
│ │ │ │ │ │ ├── form_editing.less
│ │ │ │ │ │ ├── navigation.less
│ │ │ │ │ │ ├── new_module_modal.less
│ │ │ │ │ │ ├── panel.less
│ │ │ │ │ │ ├── popover.less
│ │ │ │ │ │ ├── preview_app-main.less
│ │ │ │ │ │ ├── preview_app.less
│ │ │ │ │ │ ├── savebtn.less
│ │ │ │ │ │ ├── summary-main.less
│ │ │ │ │ │ ├── summary.less
│ │ │ │ │ │ ├── table.less
│ │ │ │ │ │ ├── tabs.less
│ │ │ │ │ │ └── variables.less
│ │ │ │ │ └── scss/
│ │ │ │ │ ├── app_manager-main.scss
│ │ │ │ │ ├── app_manager.scss
│ │ │ │ │ ├── font/
│ │ │ │ │ │ ├── WorkflowFont-Regular.otf
│ │ │ │ │ │ └── WorkflowFont.glyphs
│ │ │ │ │ ├── font-workflow/
│ │ │ │ │ │ ├── core.scss
│ │ │ │ │ │ ├── icons.scss
│ │ │ │ │ │ ├── path.scss
│ │ │ │ │ │ └── variables.scss
│ │ │ │ │ ├── font-workflow.scss
│ │ │ │ │ ├── includes/
│ │ │ │ │ │ ├── _case_tile_preview.scss
│ │ │ │ │ │ ├── _content.scss
│ │ │ │ │ │ ├── _corehq_overrides.scss
│ │ │ │ │ │ ├── _deploy.scss
│ │ │ │ │ │ ├── _diff.scss
│ │ │ │ │ │ ├── _form_editing.scss
│ │ │ │ │ │ ├── _navigation.scss
│ │ │ │ │ │ ├── _new_module_modal.scss
│ │ │ │ │ │ ├── _panel.scss
│ │ │ │ │ │ ├── _popover.scss
│ │ │ │ │ │ ├── _savebtn.scss
│ │ │ │ │ │ ├── _table.scss
│ │ │ │ │ │ └── _variables.scss
│ │ │ │ │ ├── preview_app-main.scss
│ │ │ │ │ ├── preview_app.scss
│ │ │ │ │ ├── summary-main.scss
│ │ │ │ │ └── summary.scss
│ │ │ │ ├── case_importer/
│ │ │ │ │ └── less/
│ │ │ │ │ └── case_importer.less
│ │ │ │ ├── cloudcare/
│ │ │ │ │ └── scss/
│ │ │ │ │ ├── cloudcare-variables.scss
│ │ │ │ │ ├── corehq_overrides.scss
│ │ │ │ │ ├── debugger/
│ │ │ │ │ │ └── debugger.scss
│ │ │ │ │ ├── formplayer-common/
│ │ │ │ │ │ ├── address.scss
│ │ │ │ │ │ ├── appicon.scss
│ │ │ │ │ │ ├── case.scss
│ │ │ │ │ │ ├── config.scss
│ │ │ │ │ │ ├── form.scss
│ │ │ │ │ │ ├── formnav.scss
│ │ │ │ │ │ ├── grid.scss
│ │ │ │ │ │ ├── markdown-table.scss
│ │ │ │ │ │ ├── mixins.scss
│ │ │ │ │ │ ├── module.scss
│ │ │ │ │ │ ├── navigation.scss
│ │ │ │ │ │ ├── notifications.scss
│ │ │ │ │ │ ├── paginate.scss
│ │ │ │ │ │ ├── query.scss
│ │ │ │ │ │ ├── request.scss
│ │ │ │ │ │ ├── version.scss
│ │ │ │ │ │ └── webforms.scss
│ │ │ │ │ ├── formplayer-common-main.scss
│ │ │ │ │ ├── formplayer-common.scss
│ │ │ │ │ ├── formplayer-webapp/
│ │ │ │ │ │ ├── breadcrumbs.scss
│ │ │ │ │ │ ├── case-tile.scss
│ │ │ │ │ │ ├── content.scss
│ │ │ │ │ │ ├── form.scss
│ │ │ │ │ │ ├── formnav.scss
│ │ │ │ │ │ ├── leaflet.scss
│ │ │ │ │ │ ├── menu.scss
│ │ │ │ │ │ ├── module.scss
│ │ │ │ │ │ ├── navbar.scss
│ │ │ │ │ │ ├── print-general.scss
│ │ │ │ │ │ ├── query.scss
│ │ │ │ │ │ └── version.scss
│ │ │ │ │ ├── formplayer-webapp-main.scss
│ │ │ │ │ └── formplayer-webapp.scss
│ │ │ │ ├── dashboard/
│ │ │ │ │ └── scss/
│ │ │ │ │ ├── dashboard-main.scss
│ │ │ │ │ └── dashboard.scss
│ │ │ │ ├── data_dictionary/
│ │ │ │ │ └── less/
│ │ │ │ │ └── data_dictionary.less
│ │ │ │ ├── hqwebapp/
│ │ │ │ │ ├── css/
│ │ │ │ │ │ ├── proptable.css
│ │ │ │ │ │ └── splits.css
│ │ │ │ │ ├── font/
│ │ │ │ │ │ ├── CommCare HQ Font.json
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ ├── style.scss
│ │ │ │ │ │ └── variables.scss
│ │ │ │ │ ├── js/
│ │ │ │ │ │ ├── 500.js
│ │ │ │ │ │ ├── alpinejs/
│ │ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ │ └── wiggle_button.js
│ │ │ │ │ │ │ ├── directives/
│ │ │ │ │ │ │ │ ├── datepicker.js
│ │ │ │ │ │ │ │ ├── htmx_sortable.js
│ │ │ │ │ │ │ │ ├── report_select2.js
│ │ │ │ │ │ │ │ ├── select2.js
│ │ │ │ │ │ │ │ └── tooltip.js
│ │ │ │ │ │ │ └── stores/
│ │ │ │ │ │ │ └── gtx.js
│ │ │ │ │ │ ├── assert_properties.js
│ │ │ │ │ │ ├── atwho.js
│ │ │ │ │ │ ├── base.js
│ │ │ │ │ │ ├── base_ace.js
│ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ ├── alert_user.js
│ │ │ │ │ │ │ ├── base_main.js
│ │ │ │ │ │ │ ├── commcarehq.js
│ │ │ │ │ │ │ ├── common.js
│ │ │ │ │ │ │ ├── crud_paginated_list.js
│ │ │ │ │ │ │ ├── crud_paginated_list_init.js
│ │ │ │ │ │ │ ├── downgrade_modal.js
│ │ │ │ │ │ │ ├── email-request.js
│ │ │ │ │ │ │ ├── hq.helpers.js
│ │ │ │ │ │ │ ├── inactivity.js
│ │ │ │ │ │ │ ├── knockout_bindings.ko.js
│ │ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ │ ├── prepaid_modal.js
│ │ │ │ │ │ │ ├── sticky_tabs.js
│ │ │ │ │ │ │ ├── validators.ko.js
│ │ │ │ │ │ │ └── widgets.js
│ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ ├── alert_user.js
│ │ │ │ │ │ │ ├── base_main.js
│ │ │ │ │ │ │ ├── commcarehq.js
│ │ │ │ │ │ │ ├── common.js
│ │ │ │ │ │ │ ├── crud_paginated_list.js
│ │ │ │ │ │ │ ├── crud_paginated_list_init.js
│ │ │ │ │ │ │ ├── downgrade_modal.js
│ │ │ │ │ │ │ ├── email-request.js
│ │ │ │ │ │ │ ├── hq.helpers.js
│ │ │ │ │ │ │ ├── inactivity.js
│ │ │ │ │ │ │ ├── knockout_bindings.ko.js
│ │ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ │ ├── prepaid_modal.js
│ │ │ │ │ │ │ ├── sticky_tabs.js
│ │ │ │ │ │ │ ├── validators.ko.js
│ │ │ │ │ │ │ └── widgets.js
│ │ │ │ │ │ ├── bulk_upload_file.js
│ │ │ │ │ │ ├── captcha.js
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ │ └── feedback.js
│ │ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ │ └── feedback.js
│ │ │ │ │ │ │ ├── inline_edit.js
│ │ │ │ │ │ │ ├── pagination.js
│ │ │ │ │ │ │ ├── quill.css
│ │ │ │ │ │ │ ├── rich_text_knockout_bindings.js
│ │ │ │ │ │ │ ├── search_box.js
│ │ │ │ │ │ │ └── select_toggle.js
│ │ │ │ │ │ ├── components.ko.js
│ │ │ │ │ │ ├── constants.js
│ │ │ │ │ │ ├── daterangepicker.config.js
│ │ │ │ │ │ ├── hq_extensions.jquery.js
│ │ │ │ │ │ ├── htmx_and_alpine.js
│ │ │ │ │ │ ├── htmx_base.js
│ │ │ │ │ │ ├── htmx_utils/
│ │ │ │ │ │ │ ├── csrf_token.js
│ │ │ │ │ │ │ ├── errors.js
│ │ │ │ │ │ │ ├── hq_hx_action.js
│ │ │ │ │ │ │ ├── hq_hx_loading.js
│ │ │ │ │ │ │ ├── hq_hx_refresh.js
│ │ │ │ │ │ │ ├── hq_hx_select_all.js
│ │ │ │ │ │ │ ├── htmx_gtx.js
│ │ │ │ │ │ │ └── retry_request.js
│ │ │ │ │ │ ├── initial_page_data.js
│ │ │ │ │ │ ├── key-value-mapping.js
│ │ │ │ │ │ ├── knockout_subscribables.ko.js
│ │ │ │ │ │ ├── layout.js
│ │ │ │ │ │ ├── maintenance_alerts.js
│ │ │ │ │ │ ├── multiselect_utils.js
│ │ │ │ │ │ ├── password_validators.ko.js
│ │ │ │ │ │ ├── privileges.js
│ │ │ │ │ │ ├── select2_handler.js
│ │ │ │ │ │ ├── select2_knockout_bindings.ko.js
│ │ │ │ │ │ ├── select_2_ajax_widget.js
│ │ │ │ │ │ ├── soil.js
│ │ │ │ │ │ ├── sso_inactivity.js
│ │ │ │ │ │ ├── tempus_dominus.js
│ │ │ │ │ │ ├── toggles.js
│ │ │ │ │ │ ├── ui_elements/
│ │ │ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ │ │ ├── ui-element-checkbox.js
│ │ │ │ │ │ │ │ ├── ui-element-input-map.js
│ │ │ │ │ │ │ │ ├── ui-element-input.js
│ │ │ │ │ │ │ │ ├── ui-element-key-val-list.js
│ │ │ │ │ │ │ │ ├── ui-element-key-val-mapping.js
│ │ │ │ │ │ │ │ └── ui-element-select.js
│ │ │ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ │ │ ├── ui-element-checkbox.js
│ │ │ │ │ │ │ │ ├── ui-element-input-map.js
│ │ │ │ │ │ │ │ ├── ui-element-input.js
│ │ │ │ │ │ │ │ ├── ui-element-key-val-list.js
│ │ │ │ │ │ │ │ ├── ui-element-key-val-mapping.js
│ │ │ │ │ │ │ │ └── ui-element-select.js
│ │ │ │ │ │ │ └── ui-element-langcode-button.js
│ │ │ │ │ │ └── utils/
│ │ │ │ │ │ └── email.js
│ │ │ │ │ ├── less/
│ │ │ │ │ │ ├── .gitignore
│ │ │ │ │ │ ├── README.md
│ │ │ │ │ │ ├── _hq/
│ │ │ │ │ │ │ ├── ace_cle.less
│ │ │ │ │ │ │ ├── alerts.less
│ │ │ │ │ │ │ ├── backgrounds.less
│ │ │ │ │ │ │ ├── bootstrap5-temp.less
│ │ │ │ │ │ │ ├── breadcrumbs.less
│ │ │ │ │ │ │ ├── buttons.less
│ │ │ │ │ │ │ ├── datagrid.less
│ │ │ │ │ │ │ ├── datatables.less
│ │ │ │ │ │ │ ├── date_range_picker.less
│ │ │ │ │ │ │ ├── datetimepicker.less
│ │ │ │ │ │ │ ├── dropdowns.less
│ │ │ │ │ │ │ ├── facet.less
│ │ │ │ │ │ │ ├── feedback.less
│ │ │ │ │ │ │ ├── flag_icons.less
│ │ │ │ │ │ │ ├── form_steps.less
│ │ │ │ │ │ │ ├── forms.less
│ │ │ │ │ │ │ ├── helpbubble.less
│ │ │ │ │ │ │ ├── hubspot.less
│ │ │ │ │ │ │ ├── icons.less
│ │ │ │ │ │ │ ├── includes/
│ │ │ │ │ │ │ │ ├── extensions.less
│ │ │ │ │ │ │ │ ├── mixins.less
│ │ │ │ │ │ │ │ └── variables.less
│ │ │ │ │ │ │ ├── inline_edit.less
│ │ │ │ │ │ │ ├── label.less
│ │ │ │ │ │ │ ├── layouts.less
│ │ │ │ │ │ │ ├── list.less
│ │ │ │ │ │ │ ├── mapbox.less
│ │ │ │ │ │ │ ├── modals.less
│ │ │ │ │ │ │ ├── navbar.less
│ │ │ │ │ │ │ ├── navs.less
│ │ │ │ │ │ │ ├── notifications.less
│ │ │ │ │ │ │ ├── ocs_widget.less
│ │ │ │ │ │ │ ├── pagination.less
│ │ │ │ │ │ │ ├── panels.less
│ │ │ │ │ │ │ ├── plan_notice.less
│ │ │ │ │ │ │ ├── popovers.less
│ │ │ │ │ │ │ ├── radio_select.less
│ │ │ │ │ │ │ ├── readable_forms.less
│ │ │ │ │ │ │ ├── report.less
│ │ │ │ │ │ │ ├── select2s.less
│ │ │ │ │ │ │ ├── svg.less
│ │ │ │ │ │ │ ├── tables.less
│ │ │ │ │ │ │ ├── tabs.less
│ │ │ │ │ │ │ ├── typography.less
│ │ │ │ │ │ │ └── utils.less
│ │ │ │ │ │ ├── b5-compatibility.less
│ │ │ │ │ │ ├── bootstrap.less
│ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ └── multiselect/
│ │ │ │ │ │ │ └── multiselect.less
│ │ │ │ │ │ ├── docs-style-imports.less
│ │ │ │ │ │ ├── docs-style.less
│ │ │ │ │ │ ├── mobile/
│ │ │ │ │ │ │ └── c2/
│ │ │ │ │ │ │ ├── footer.less
│ │ │ │ │ │ │ ├── navbar.less
│ │ │ │ │ │ │ ├── reset.less
│ │ │ │ │ │ │ ├── tables.less
│ │ │ │ │ │ │ ├── type.less
│ │ │ │ │ │ │ └── variables.less
│ │ │ │ │ │ ├── style-imports.less
│ │ │ │ │ │ ├── style.less
│ │ │ │ │ │ └── styleguide/
│ │ │ │ │ │ └── palette.less
│ │ │ │ │ ├── scss/
│ │ │ │ │ │ ├── commcarehq/
│ │ │ │ │ │ │ ├── _ace_cle.scss
│ │ │ │ │ │ │ ├── _alert.scss
│ │ │ │ │ │ │ ├── _animations.scss
│ │ │ │ │ │ │ ├── _backgrounds.scss
│ │ │ │ │ │ │ ├── _breadcrumb.scss
│ │ │ │ │ │ │ ├── _buttons.scss
│ │ │ │ │ │ │ ├── _cards.scss
│ │ │ │ │ │ │ ├── _containers.scss
│ │ │ │ │ │ │ ├── _datagrid.scss
│ │ │ │ │ │ │ ├── _datatables.scss
│ │ │ │ │ │ │ ├── _date_range_picker.scss
│ │ │ │ │ │ │ ├── _datetimepicker.scss
│ │ │ │ │ │ │ ├── _dropdown.scss
│ │ │ │ │ │ │ ├── _facet.scss
│ │ │ │ │ │ │ ├── _feedback.scss
│ │ │ │ │ │ │ ├── _flag_icons.scss
│ │ │ │ │ │ │ ├── _form_steps.scss
│ │ │ │ │ │ │ ├── _forms.scss
│ │ │ │ │ │ │ ├── _helpbubble.scss
│ │ │ │ │ │ │ ├── _htmx.scss
│ │ │ │ │ │ │ ├── _hubspot.scss
│ │ │ │ │ │ │ ├── _icons.scss
│ │ │ │ │ │ │ ├── _inline_edit.scss
│ │ │ │ │ │ │ ├── _label.scss
│ │ │ │ │ │ │ ├── _layouts.scss
│ │ │ │ │ │ │ ├── _list.scss
│ │ │ │ │ │ │ ├── _mapbox.scss
│ │ │ │ │ │ │ ├── _mixins.scss
│ │ │ │ │ │ │ ├── _modal.scss
│ │ │ │ │ │ │ ├── _nav.scss
│ │ │ │ │ │ │ ├── _navbar.scss
│ │ │ │ │ │ │ ├── _notifications.scss
│ │ │ │ │ │ │ ├── _ocs_widget.scss
│ │ │ │ │ │ │ ├── _pagination.scss
│ │ │ │ │ │ │ ├── _plan_notice.scss
│ │ │ │ │ │ │ ├── _popover.scss
│ │ │ │ │ │ │ ├── _print.scss
│ │ │ │ │ │ │ ├── _radio_select.scss
│ │ │ │ │ │ │ ├── _readable_forms.scss
│ │ │ │ │ │ │ ├── _report.scss
│ │ │ │ │ │ │ ├── _select2.scss
│ │ │ │ │ │ │ ├── _svg.scss
│ │ │ │ │ │ │ ├── _tables.scss
│ │ │ │ │ │ │ ├── _tabs.scss
│ │ │ │ │ │ │ ├── _type.scss
│ │ │ │ │ │ │ ├── _utils.scss
│ │ │ │ │ │ │ ├── _variables.scss
│ │ │ │ │ │ │ └── _variables_bootstrap3.scss
│ │ │ │ │ │ ├── commcarehq.scss
│ │ │ │ │ │ ├── data_cleaning.scss
│ │ │ │ │ │ └── styleguide.scss
│ │ │ │ │ └── spec/
│ │ │ │ │ ├── assert_properties_spec.js
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── inactivity_spec.js
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ └── widgets_spec.js
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── inactivity_spec.js
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ └── widgets_spec.js
│ │ │ │ │ ├── components/
│ │ │ │ │ │ ├── main.js
│ │ │ │ │ │ └── rich_text_spec.js
│ │ │ │ │ ├── email_validator_spec.js
│ │ │ │ │ └── urllib_spec.js
│ │ │ │ ├── preview_app/
│ │ │ │ │ └── scss/
│ │ │ │ │ ├── preview_app/
│ │ │ │ │ │ ├── appicon.scss
│ │ │ │ │ │ ├── breadcrumbs.scss
│ │ │ │ │ │ ├── case-tile.scss
│ │ │ │ │ │ ├── datepicker.scss
│ │ │ │ │ │ ├── debugger.scss
│ │ │ │ │ │ ├── form.scss
│ │ │ │ │ │ ├── formnav.scss
│ │ │ │ │ │ ├── grid.scss
│ │ │ │ │ │ ├── module.scss
│ │ │ │ │ │ ├── navigation.scss
│ │ │ │ │ │ ├── notifications.scss
│ │ │ │ │ │ ├── scrollable.scss
│ │ │ │ │ │ └── variables.scss
│ │ │ │ │ ├── preview_app-main.scss
│ │ │ │ │ └── preview_app.scss
│ │ │ │ ├── registration/
│ │ │ │ │ ├── less/
│ │ │ │ │ │ ├── register-domain.less
│ │ │ │ │ │ ├── registration-main.less
│ │ │ │ │ │ └── registration.less
│ │ │ │ │ └── scss/
│ │ │ │ │ ├── register-domain.scss
│ │ │ │ │ ├── registration-main.scss
│ │ │ │ │ └── registration.scss
│ │ │ │ ├── registry/
│ │ │ │ │ └── scss/
│ │ │ │ │ ├── light_color_scheme.scss
│ │ │ │ │ └── registry.scss
│ │ │ │ └── reports/
│ │ │ │ ├── less/
│ │ │ │ │ ├── reports.less
│ │ │ │ │ └── sidebar.less
│ │ │ │ └── scss/
│ │ │ │ ├── reports/
│ │ │ │ │ ├── _accordion.scss
│ │ │ │ │ └── _sidebar.scss
│ │ │ │ └── reports.scss
│ │ │ ├── tables/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── columns.py
│ │ │ │ ├── elasticsearch/
│ │ │ │ │ ├── __init__.py
│ │ │ │ │ ├── records.py
│ │ │ │ │ └── tables.py
│ │ │ │ ├── export.py
│ │ │ │ ├── htmx.py
│ │ │ │ ├── pagination.py
│ │ │ │ └── tests/
│ │ │ │ ├── test_columns.py
│ │ │ │ └── test_export.py
│ │ │ ├── tasks.py
│ │ │ ├── templates/
│ │ │ │ ├── 403.html
│ │ │ │ ├── 404.html
│ │ │ │ ├── 500.html
│ │ │ │ ├── apache_license.html
│ │ │ │ ├── bsd_license.html
│ │ │ │ ├── csrf_failure.html
│ │ │ │ ├── error_base.html
│ │ │ │ ├── google9633af922b8b0064.html
│ │ │ │ ├── hq-captcha-field.html
│ │ │ │ ├── hqwebapp/
│ │ │ │ │ ├── base.html
│ │ │ │ │ ├── basic_errors.html
│ │ │ │ │ ├── bootstrap3/
│ │ │ │ │ │ ├── base_navigation.html
│ │ │ │ │ │ ├── base_page.html
│ │ │ │ │ │ ├── base_paginated_crud.html
│ │ │ │ │ │ ├── base_section.html
│ │ │ │ │ │ ├── blank.html
│ │ │ │ │ │ ├── bulk_upload.html
│ │ │ │ │ │ ├── downgrade_modal.html
│ │ │ │ │ │ ├── full_screen.html
│ │ │ │ │ │ ├── iframe_close_window.html
│ │ │ │ │ │ ├── iframe_domain_login.html
│ │ │ │ │ │ ├── iframe_sso_login_pending.html
│ │ │ │ │ │ ├── iframe_sso_login_success.html
│ │ │ │ │ │ ├── oauth_application_registration_form.html
│ │ │ │ │ │ ├── prepaid_modal.html
│ │ │ │ │ │ ├── rollout_revert_modal.html
│ │ │ │ │ │ ├── soil_status_full.html
│ │ │ │ │ │ └── two_column.html
│ │ │ │ │ ├── bootstrap5/
│ │ │ │ │ │ ├── base_navigation.html
│ │ │ │ │ │ ├── base_page.html
│ │ │ │ │ │ ├── base_paginated_crud.html
│ │ │ │ │ │ ├── base_section.html
│ │ │ │ │ │ ├── blank.html
│ │ │ │ │ │ ├── bulk_upload.html
│ │ │ │ │ │ ├── downgrade_modal.html
│ │ │ │ │ │ ├── full_screen.html
│ │ │ │ │ │ ├── iframe_close_window.html
│ │ │ │ │
================================================
FILE CONTENTS
================================================
================================================
FILE: .coderabbit.yaml
================================================
language: en-US
early_access: true
reviews:
high_level_summary: false
review_status: false
poem: false
changed_files_summary: false
abort_on_close: true
instructions: >-
- Follow OWASP recommendations for security best practices.
- Do not implement custom security or cryptographic protocols.
- Authentication and encryption must use publicly documented standards (e.g., OAuth, SAML, AES256).
- Use well-established libraries for security and authentication whenever possible.
- External system integrations should follow best practices for handling secrets.
- Documentation should be close to the relevant code and use reStructuredText with autodoc.
- Developers use tools like `ruff` and `django-extensions` for better development practices.
- Requirements updates should be managed through `pyproject.toml` and `uv.lock` files.
path_instructions:
- path: "**/*.js"
instructions: >-
- Review the javascript code against the google javascript style guide and point out any mismatches.
- User-facing language should be clear.
- All user-facing text must be translated using `gettext`; use `_.template` for variable-containing strings.
- User-facing dates and times must be in a relevant time zone, using `UserTime` or `corehq.util.timezones`.
- Use `<%- ... %>` in Underscore templates to HTML escape values; use `DomPurify` for escaping user input outside templates.
- AJAX requests must show a spinner and have an `error` callback.
- Naming should be consistent, avoiding different capitalizations of the same identifier.
- JavaScript should be modular, with explicit dependencies (except in app manager, reports, and web apps).
- Avoid long parameter lists; prefer kwargs-style objects and use `assert_properties` for validation.
- Ensure JavaScript accessing initial page data waits until the page is fully loaded to prevent race conditions.
- Prefer Knockout over jQuery and avoid mixing the two.
- path: "**/*.py"
instructions: >-
- Review the code following best practises and standards
path_filters: ["**/*.js", "**/*.py", "**/*.md", "**/*.rst", "**/*.html"]
auto_review:
enabled: false
auto_incremental_review: false
drafts: false
================================================
FILE: .coveragerc
================================================
[run]
branch = True
source = corehq
[report]
omit =
*/migrations/*
*/tests/*
*/static/*
*/templates/*
[html]
directory = coverage-report
================================================
FILE: .dockerignore
================================================
**/__pycache__/
sharedfiles/
staticfiles/
*.log
application.properties
formplayer.jar
localsettings.py
================================================
FILE: .editorconfig
================================================
# top-most EditorConfig file
root = true
[*]
# Unix-style newlines with a newline ending every file
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
[*.html]
indent_size = 2
[*.less]
indent_size = 2
[*.json]
indent_size = 2
[*.yml]
indent_size = 2
[*.scss]
indent_size = 2
[Makefile]
indent_style = tab
================================================
FILE: .github/CODEOWNERS
================================================
# https://help.github.com/en/articles/about-code-owners
#
# Contributors can added themselves as "Code Owners" for specific parts of the
# codebase by adding their Github username next to any number of folders or files.
# For pull requests that make changes to these folders or files, code owners are
# automatically added as a "Reviewer" to the pull request and are hence, notified
# about the changes.
# Code Owners can share feedback or concerns over these Pull Requests, though
# it is not mandatory for them to do so.
# Adding yourself as a Code Owner, is also a good way to stay updated with changes
# in code areas you are interested in.
/.github/workflows/ @dimagi/dependency-watchers
/corehq/blobs/ @millerdev
/corehq/ex-submodules/casexml/apps/phone/data_providers/case/livequery.py @millerdev
/corehq/messaging/ @mkangia
/corehq/motech/ @kaapstorm
/corehq/pillows/ @calellowitz @amitphulera
/corehq/sql_db/ @calellowitz
/corehq/tests/ @millerdev
/corehq/util/couch.py @esoergel
/corehq/util/couch_helpers.py @millerdev
/corehq/util/datadog/lockmeter.py @millerdev
/corehq/util/datadog/tests/test_lockmeter.py @millerdev
/corehq/util/pagination.py @millerdev
/corehq/util/timer.py @esoergel
/corehq/warehouse/ @calellowitz
/corehq/warnings.py @millerdev
/corehq/apps/accounting/ @dannyroberts @nospame
/corehq/apps/case_search/ @MartinRiese @esoergel @stephherbers @Robert-Costello
/corehq/apps/change_feed/ @dannyroberts
/corehq/apps/cleanup/management/commands/populate_sql_model_from_couch_model.py @millerdev
/corehq/apps/cloudcare/ @MartinRiese @stephherbers @Robert-Costello @nospame
/corehq/apps/commtrack/ @esoergel
/corehq/apps/data_dictionary/ @esoergel @zandre-eng
/corehq/apps/domain/decorators.py @esoergel
/corehq/apps/domain/auth.py @esoergel
/corehq/apps/domain/views/accounting.py @nospame
/corehq/apps/es/ @esoergel @amitphulera
/corehq/apps/geospatial/ @zandre-eng
/corehq/apps/hqcase/ @esoergel
/corehq/apps/hqwebapp/apps.py @gherceg
/corehq/apps/hqwebapp/two_factor_methods.py @gherceg
/corehq/apps/linked_domain/ @gherceg
/corehq/apps/locations/ @esoergel
/corehq/apps/locations/adjacencylist.py @millerdev
/corehq/apps/locations/resources/v0_6.py @AddisonDunn
/corehq/apps/ota/ @esoergel
/corehq/apps/reports/models.py @AddisonDunn
/corehq/apps/reports/standard/cases/ @esoergel
/corehq/apps/reports/standard/tableau.py @AddisonDunn
/corehq/apps/settings/views.py @gherceg
/corehq/apps/sms*/ @mkangia
/corehq/apps/smsbillables/ @dannyroberts
/corehq/apps/user_importer/ @stephherbers
/corehq/apps/userreports/ @calellowitz
/corehq/apps/userreports/reports/builder/ @kaapstorm
/corehq/apps/users/ @esoergel
/corehq/ex-submodules/dimagi/utils/couch/migration.py @millerdev
/corehq/ex-submodules/pillowtop @dannyroberts @calellowitz
/custom/covid/management/commands/ @stephherbers @AddisonDunn
/docs/js-guide/ @millerdev
/docker/ @millerdev
/package.json @dimagi/dependency-watchers
/pyproject.toml @dimagi/dependency-watchers
/scripts/docker @millerdev
/scripts/test-make-docs.sh @dimagi/dependency-watchers
/scripts/vellum-to-hq @millerdev
/requirements/ @dimagi/dependency-watchers
/testapps/test_pillowtop/ @amitphulera
/testapps/test_elasticsearch/ @amitphulera
/uv.lock @dimagi/dependency-watchers
/yarn.lock @dimagi/dependency-watchers
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: dimagi
================================================
FILE: .github/ISSUE_TEMPLATE/commcare-enhancement-proposal.md
================================================
---
name: CommCare Enhancement Proposal
about: Suggest changes to CommCare
title: "[CEP] title"
labels: CEP
assignees: ''
---
**Abstract**
A short overview of this CEP.
**Motivation**
Clearly explain the motivation for this feature / change and what problems or challenges it addresses.
**Specification**
Describe in as much detail as necessary the proposed changes.
**Impact on users**
Describe how will this change impact end users.
**Impact on hosting**
Describe how will this change impact the hosting and support of CommCare.
**Backwards compatibility**
All CEPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The CEP must explain how the author proposes to deal with these incompatibilities.
**Release Timeline**
If a specific timeline is being targeted for this CEP it should be outlined here including any relevant factors that may impact the timeline.
**Open questions and issues**
While a CEP is in draft, ideas can come up which warrant further discussion. Those ideas should be recorded so people know that they are being thought about but do not have a concrete resolution. This helps make sure all issues required for the CEP to be ready for consideration are complete complete and reduces people duplicating prior discussion.
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
## Product Description
<!-- Where applicable, describe user-facing effects and include screenshots. -->
## Technical Summary
<!--
Provide a link to any tickets, design documents, and/or technical specifications
associated with this change. Describe the rationale and design decisions.
-->
## Feature Flag
<!-- If this is specific to a feature flag, which one? -->
## Safety Assurance
### Safety story
<!--
Describe how you became confident in this change, such as
local testing, why the change is inherently safe, and/or plans to limit the blast radius of a defect.
In particular consider how existing data may be impacted by this change.
-->
### Automated test coverage
<!-- Identify the related test coverage and the tests it would catch -->
### QA Plan
<!--
- Describe QA plan that along with automated test coverages proves this PR is regression free
- Link to QA Ticket
-->
### Migrations
<!-- Delete this section if the PR does not contain any migrations -->
<!-- https://commcare-hq.readthedocs.io/migrations_in_practice.html -->
- [ ] The migrations in this code can be safely applied first independently of the code. Pay particular attention to _backward incompatible_ operations like `RemoveField`, `RenameField`, `RemoveConstraint`, and [others described here](https://github.com/3YOURMIND/django-migration-linter/blob/main/docs/incompatibilities.md) that can cause errors when migrations are applied to a live database.
<!-- Please link to any past code changes that are coordinated with this migration -->
### Rollback instructions
<!--
If this PR follows standards of revertability, check the box below.
Otherwise replace it with detailed instructions or reasons a rollback is impossible.
-->
- [ ] This PR can be reverted after deploy with no further considerations
### Labels & Review
- [ ] Risk label is set correctly
- [ ] The set of people pinged as reviewers is appropriate for the level of risk of the change
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: uv
directory: "/"
schedule:
interval: daily
time: "10:00"
open-pull-requests-limit: 7
labels:
- product/invisible
# Focus on major versions only
# Todo: remove once we're in steady-state on major versions
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-patch"
- "version-update:semver-minor"
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
time: "10:00"
open-pull-requests-limit: 5
labels:
- product/invisible
- dependencies/javascript
# Focus on major versions only
# Todo: remove once we're in steady-state on major versions
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-patch"
- "version-update:semver-minor"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: weekly
labels:
- product/invisible
================================================
FILE: .github/labels.yml
================================================
# See https://github.com/dimagi/label-bot
disabled_actions:
- triage
- review
- lgtm
# Label rules
brace_expansion: true
minus_negate: false
rules:
- labels: ['reindex/migration']
patterns: ['migrations.lock|**/migrations/**|corehq/pillows/mappings/**|**/_design/**|corehq/couchapps/**']
- labels: [ 'Core Compatibility' ]
patterns: ['corehq/apps/app_manager/suite_xml/xml_models.py']
- labels: ['dependencies']
patterns: ['requirements/**|package.json|uv.lock|yarn.lock']
- labels: ['Risk: High']
patterns:
- 'corehq/apps/registration/*.py'
- 'corehq/apps/registration/*.js'
- 'corehq/apps/api/**'
- 'corehq/apps/case_importer/views.py' # bulk case importer API
- 'corehq/form_processor/**'
- 'corehq/pillows/mappings/**'
- 'corehq/apps/hqwebapp/templates/hqwebapp/includes/core_libraries.html'
- 'corehq/apps/hqwebapp/static/hqwebapp/js/bootstrap3/requirejs_config.js'
- 'corehq/apps/domain/decorators.py'
- 'corehq/util/context_processors.py'
- labels: ['Risk: Medium']
patterns:
- 'corehq/apps/cloudcare/**'
- 'corehq/apps/user_importer/**'
- 'corehq/apps/fixtures/**'
- 'corehq/apps/custom_data_fields/**'
- 'corehq/apps/hqwebapp/templates/hqwebapp/base.html'
- 'corehq/apps/hqwebapp/static/hqwebapp/js/components/*.js'
# WIP
wip:
- 'Open for review: do not merge'
- 'awaiting QA'
# Label management
delete_labels: false
colors:
navy: '#0366d6'
dark_orange: '#eb6420'
light_orange: '#fbca04'
royal: '#5319e7'
purple: '#9800c6'
lime: '#75ff68'
dark_green: '#0e8a16'
dark_purple: '#71239b'
sand: '#d38737'
grey: '#cfd2d6'
burnt_orange: '#bc4b29'
green: '#009800'
tan: '#fef2c0'
yellow: '#fcfa4e'
light_yellow: '#fffeb3'
labels:
- name: 'reindex/migration'
color: dark_orange
description: 'Reindex or migration will be required during or before deploy'
- name: 'dependencies'
color: navy
description: 'Pull requests that update a dependency file'
- name: 'dependencies/javascript'
color: navy
description: 'Change in javascript dependency.'
- name: 'awaiting QA'
color: light_orange
description: 'QA in progress. Do not merge'
- name: 'Open for review: do not merge'
color: royal
description: 'A work in progress'
- name: 'product/ab-test'
color: purple
description: 'AB test for product on production devices'
- name: 'QA Passed'
color: green
description: ''
- name: 'QA post deploy'
color: tan
description: 'Change should be re-tested once live'
# labels below are part of the product triage system
- name: 'product/admin'
color: lime
description: 'Change affects admin pages only visible to super users / staff'
- name: 'product/all-users-all-environments'
color: dark_green
description: 'Change impacts all users on all environments'
- name: 'product/custom'
color: dark_purple
description: 'Change will only impact users on a single project'
- name: 'product/feature-flag'
color: sand
description: 'Change will only affect users who have a specific feature flag enabled'
- name: 'product/invisible'
color: grey
description: 'Change has no end-user visible impact'
- name: 'product/prod-india-all-users'
color: burnt_orange
description: 'Change will only be deployed to Dimagi "SaaS" environments'
- name: 'Risk: High'
color: yellow
description: 'Change affects files that have been flagged as high risk.'
- name: 'Risk: Medium'
color: light_yellow
description: 'Change affects files that have been flagged as medium risk.'
================================================
FILE: .github/release.yml
================================================
# .github/release.yml
changelog:
exclude:
authors:
- dependabot
- token-generator-for-cchq
categories:
- title: Product Updates
labels:
- product/all-users-all-environments
- product/prod-india-all-users
- title: Custom Feature Updates
labels:
- product/feature-flag
- product/custom
- title: All other updates
labels:
- "*"
================================================
FILE: .github/workflows/build-static.yml
================================================
name: Build Static Files
on:
push:
branches:
- autostaging
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'yarn'
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: '0.7.2'
- name: Install Python Dependencies
run: |
uv sync --locked --compile-bytecode --no-progress
- name: Install JavaScript Dependencies
run: yarn install --frozen-lockfile
- name: Install SASS
run: |
npm install -g sass
- name: Build Static Files
run: |
source .venv/bin/activate
echo "Starting resource_static..."
./manage.py resource_static
echo "Starting collectstatic..."
./manage.py collectstatic --noinput -v 0
echo "Starting fix_less_imports_collectstatic..."
./manage.py fix_less_imports_collectstatic
echo "Starting compilejsi18n..."
./manage.py compilejsi18n
echo "Starting generate_webpack_settings..."
./manage.py generate_webpack_settings
echo "Starting yarn build..."
yarn build
echo "Starting compress..."
./manage.py compress --force -v 0
echo "Starting Copy Required static files..."
./manage.py copy_required_static_files
- name: Upload Static Files On Push
uses: actions/upload-artifact@v5
with:
name: staticfiles-${{ github.sha }}
path: REQUIRED_STATIC_FILES.zip
retention-days: 90
- name: Generate Static URL
run: |
echo "Static files URL pattern:"
echo "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts/staticfiles-${{ github.sha }}"
================================================
FILE: .github/workflows/codeql-analysis.yml
================================================
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
# We need to run on push too (when things are merged to master) so CodeQL has
# an appropriate base for comparison and can tell when issues are introduced.
push:
branches:
- master
paths-ignore:
- '**/*.md'
- '**/*.rst'
- '**/*.txt'
pull_request:
branches:
- master
paths-ignore:
- '**/*.md'
- '**/*.rst'
- '**/*.txt'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v5
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
config: |
paths-ignore:
# Vellum assets are compiled from another repo that's scanned separately
- 'corehq/apps/app_manager/static/app_manager/js/vellum/'
# Couch views are a special case
- '**/_design/**/*.js'
- 'corehq/couchapps/**/*.js'
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v4
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
================================================
FILE: .github/workflows/dependency-metrics.yml
================================================
name: commcare-hq dependency metrics
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
collect-metrics:
runs-on: ubuntu-latest
env:
REPO: commcare-hq
DATADOG_APP_KEY: ${{ secrets.DATADOG_APP_KEY }}
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: '0.7.2'
- name: install dev-requirements
run: uv sync --all-groups --locked --compile-bytecode --no-progress
- name: run metrics for pip
run: uv run --no-sync metrics pip --send
- name: run metrics for yarn
run: uv run --no-sync metrics yarn --send
================================================
FILE: .github/workflows/docker-image.yml
================================================
name: Build Test Docker Image
on:
push:
branches:
- master
workflow_dispatch:
jobs:
build:
if: ${{ github.secret_source == 'Actions' }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Get Python version
id: vars
# produces a variable like python=py3.13
run: echo "python=py$(cat .python-version)" >> $GITHUB_OUTPUT
- name: Docker login
uses: docker/login-action@v3
with:
username: dimagi
password: ${{ secrets.DOCKERHUB_RW_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push image to Docker Hub
uses: docker/build-push-action@v6
with:
context: .
tags: dimagi/commcarehq-${{ steps.vars.outputs.python }}
push: true
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on:
pull_request:
branches:
- master
permissions:
checks: write
contents: write
jobs:
lint-python:
name: Lint Python
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Check changed py files
id: changed-files
uses: tj-actions/changed-files@v47
with:
files: |
**/*.py
- name: Run ruff linter
if: ${{ steps.changed-files.outputs.all_changed_files }}
uses: astral-sh/ruff-action@v3
with:
src: ${{ steps.changed-files.outputs.all_changed_files }}
# If this is in the same step as python linting the annotations don't show up
lint-javascript:
name: Lint Javascript
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install Node.js dependencies
run: yarn install --frozen-lockfile
- uses: sibiraj-s/action-eslint@v3
with:
token: ${{ secrets.GITHUB_TOKEN }}
annotations: true
================================================
FILE: .github/workflows/rebuild-staging.yml
================================================
name: Rebuild staging branch
on:
workflow_dispatch:
jobs:
rebuild-staging:
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
# Full history needed for branch manipulation
fetch-depth: 0
- uses: astral-sh/setup-uv@v7
- name: Install git-build-branch
run: uv tool install git-build-branch
- name: Configure git
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
- name: Rebuild staging
run: ./scripts/rebuildstaging
================================================
FILE: .github/workflows/required-labels.yml
================================================
name: Labels
on:
pull_request_target: # safe as long as we do not check out and run code from the branch to be merged
branches:
- master
types: [labeled, unlabeled, opened, reopened]
jobs:
check_product_label:
name: 🏷️ Require a product label
runs-on: ubuntu-latest
permissions:
checks: write
steps:
- name: Report product label check
uses: actions/github-script@v7
with:
script: |
const labels = context.payload.pull_request.labels.map(l => l.name);
const hasProductLabel = labels.some(l => l.startsWith('product/'));
await github.rest.checks.create({
owner: context.repo.owner,
repo: context.repo.repo,
name: 'Product label',
head_sha: context.payload.pull_request.head.sha,
status: 'completed',
conclusion: hasProductLabel ? 'success' : 'failure',
output: {
title: hasProductLabel ? 'Product label present' : 'Missing product label',
summary: hasProductLabel
? 'A product/ label is present on this PR.'
: 'This PR requires a product/ label before it can be merged.',
},
});
================================================
FILE: .github/workflows/test-docs.yml
================================================
name: commcare-hq docs
on:
pull_request:
branches:
- master
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
- uses: astral-sh/setup-uv@v7
with:
version: '0.7.2'
- name: Install docs requirements
run: uv sync --group=docs --no-group=sso --locked --compile-bytecode --no-progress
- name: Test docs build
run: |
source .venv/bin/activate
./scripts/test-make-docs.sh
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v5
with:
name: make-docs-artifacts
path: artifacts
if-no-files-found: ignore
retention-days: 7
================================================
FILE: .github/workflows/tests.yml
================================================
name: commcare-hq tests
on:
pull_request:
branches:
- master
- hotfix-deploy
schedule:
# see corehq/apps/hqadmin/management/commands/static_analysis.py
- cron: '47 12 * * *'
workflow_dispatch:
jobs:
tests:
runs-on: ubuntu-22.04
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- {TEST: python, DIVIDED_WE_RUN: '03'}
- {TEST: python, DIVIDED_WE_RUN: '47'}
- {TEST: python, DIVIDED_WE_RUN: '8b'}
- {TEST: python, DIVIDED_WE_RUN: 'cf'}
- {TEST: python-sharded-and-javascript}
env:
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DATADOG_APP_KEY: ${{ secrets.DATADOG_APP_KEY }}
REUSE_DB: true # do not drop databases on completion to save time
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
- name: Docker info
run: |
docker version
docker compose version
- name: Docker login
env:
TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
if: env.TOKEN != ''
uses: docker/login-action@v3
with:
username: dimagi
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Run tests
env:
TEST: ${{ matrix.TEST }}
DIVIDED_WE_RUN: ${{ matrix.DIVIDED_WE_RUN }}
JS_SETUP: yes
KAFKA_HOSTNAME: kafka
STRIPE_PRIVATE_KEY: ${{ secrets.STRIPE_PRIVATE_KEY }}
run: >-
scripts/docker test
--exitfirst
--verbosity=2
--reusedb=1
--no-migrations
--divided-we-run=${{ matrix.DIVIDED_WE_RUN }}
--showlocals
--max-test-time=29
--durations=50
-p no:cacheprovider
- name: "Codecov upload"
env:
TOKEN: ${{ secrets.CODECOV_TOKEN }}
if: env.TOKEN != ''
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.xml
fail_ci_if_error: true
- name: Stop containers
if: always()
run: scripts/docker down
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v5
with:
name: test-artifacts
path: artifacts
if-no-files-found: ignore
retention-days: 7
================================================
FILE: .github/workflows/update-translations.yml
================================================
on:
workflow_dispatch:
schedule:
- cron: '0 6 * * 3' # Every Wed at 06:00 UTC
name: Update transifex translations
jobs:
update_translations:
name: Update transifex translations
runs-on: ubuntu-latest
env:
CCHQ_WITHOUT_SSO: 1
steps:
- uses: actions/checkout@v5
with:
ref: master
submodules: recursive
- uses: astral-sh/setup-uv@v7
with:
version: '0.7.2'
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
id: generate-token
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Install Python requirements
run: |
uv sync --no-group=sso --locked --compile-bytecode --no-progress
uv pip install openai
- name: Install transifex client
run: |
mkdir -p /home/runner/.local/bin
cd /home/runner/.local/bin
curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash
sudo apt-get install -y gettext
- name: Configure transifex client
env:
TRANSIFEX_TOKEN: ${{ secrets.TRANSIFEX_TOKEN }}
run: |
# copy .transifexrc.example to ~/.transifexrc, edited to include token
grep -v '^token =' .transifexrc.example > ~/.transifexrc
echo "token = ${TRANSIFEX_TOKEN}" >> ~/.transifexrc
- name: Run update-translations script
run: |
source .venv/bin/activate
./scripts/update-translations.sh
env:
UPDATE_TRANSLATIONS_SKIP_GIT: 1
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Create Pull Request
# https://github.com/marketplace/actions/create-pull-request
# Pinned to the commit of https://github.com/peter-evans/create-pull-request/releases/tag/v7.0.5
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f
with:
token: ${{ steps.generate-token.outputs.token }}
base: master
branch: create-pull-request/update-translations
branch-suffix: short-commit-hash
commit-message: |
Update translations
title: 'Update Translations'
labels: product/invisible
body: |
GitHub Actions automatically ran update-translations.sh and used [create-pull-request](https://github.com/peter-evans/create-pull-request) to open this pull request.
================================================
FILE: .gitignore
================================================
*.pyc
*.pyo
*~
*.swp
*.swo
/local.ini*
/localsettings*
/loadtest/test_scripts/localsettings.py
!localsettings.example.py
/.claude/*.local.json
.settings/
.project
.pydevproject
.idea
.zed
xforms/
/artifacts/
/data/
submissions/
/formplayer_build
/export/
bad/
*.log*
/CommCare.ja*
startover.sh
tmp.sh
/requirements/local.in
/requirements/local*.txt
/resource_versions.yaml
/loadtest/results/
/loadtest/localsettings.py
/loadtest/config.cfg
/staticfiles/
*.db
\#*\#
.DS_Store
app_builder_live_test/*/
app_builder_live_test/*-performance.txt
dump.rdb
**/less/*.css
/staticmedia*
.transifexrc
python_env
python_env-*
services
KEEP_UNTIL__*
formplayer.properties
application.properties
formplayer.jar
.vscode/*
/.env
/.venv
submodules/formdesigner
submodules/vellum/Vellum
ptop*.txt
ptop*.json
/.coverage
/coverage-report
/coverage-js
# Celery
/celery.db
/celerybeat-schedule*
# Sphinx
docs/_build
# compiled translations
/locale/*/*/*.mo
.grunt
node_modules/
/sharedfiles/
docker/data
*.lock
!couchviews.lock
!migrations.lock
!serializer-pickle-files.lock
!uv.lock
!yarn.lock
fab/config.py
extensions/
# third-party js libraries
corehq/apps/hqwebapp/static/hqwebapp/js/lib/modernizr.js
# direnv
.envrc
.direnv
# webpack build related
/webpack/_build/
# ruff
ruff.toml
CLAUDE.local.md
================================================
FILE: .gitmodules
================================================
[submodule "submodules/commcare-translations"]
path = submodules/commcare-translations
url = https://github.com/dimagi/commcare-translations.git
[submodule "submodules/couchdbkit-aggregate"]
path = submodules/couchdbkit-aggregate
url = https://github.com/dimagi/couchdbkit-aggregate.git
[submodule "submodules/django-digest-src"]
path = submodules/django-digest-src
url = https://github.com/dimagi/django-digest.git
[submodule "submodules/django-no-exceptions"]
path = submodules/django-no-exceptions
url = https://github.com/dimagi/django-no-exceptions.git
[submodule "submodules/langcodes"]
path = submodules/langcodes
url = https://github.com/dimagi/langcodes.git
[submodule "submodules/python-digest"]
path = submodules/python-digest
url = https://github.com/dimagi/python-digest.git
[submodule "submodules/xml2json"]
path = submodules/xml2json
url = https://github.com/dimagi/xml2json.git
================================================
FILE: .isort.cfg
================================================
# https://github.com/timothycrosley/isort/wiki/isort-Settings
[settings]
multi_line_output=3
include_trailing_comma=true
known_django=django
known_third_party=jsonschema
known_exsubmodules=casexml,couchexport,couchforms,dimagi,phonelog,pillow_retry,pillowtop,soil,toggle
known_first_party=corehq,custom
sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,EXSUBMODULES,FIRSTPARTY,LOCALFOLDER
================================================
FILE: .python-version
================================================
3.13
================================================
FILE: .pytype.cfg
================================================
[pytype]
exclude =
**/*_test.py
**/test_*.py
# Space-separated list of files or directories to process.
inputs =
.
# Keep going past errors to analyze as many files as possible.
keep_going = True
jobs = 4
output = .pytype
# Paths to source code directories, separated by ':'.
pythonpath =
.:
submodules:
corehq/ex-submodules:
corehq/_legacy
python_version = 3.7
disable =
pyi-error
================================================
FILE: .readthedocs.yml
================================================
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Doc builds are generated automatically from the master branch
# There is a hidden doc build also generated from the readthedocs branch
# This can be used to test the readthedocs build itself before merging
version: 2
sphinx:
configuration: docs/conf.py
formats: all
build:
os: ubuntu-24.04
apt_packages:
- libmagic1
tools:
python: "3.13"
jobs:
create_environment:
- pip install uv
- >-
UV_PROJECT_ENVIRONMENT=$READTHEDOCS_VIRTUALENV_PATH
uv sync --group=docs --no-group=sso --locked --compile-bytecode --no-progress
install:
- "true"
submodules:
include: all
================================================
FILE: .scss-lint.yml
================================================
scss_files: 'assets/style/**/*.css.scss'
linters:
BorderZero:
enabled: false
Indentation:
severity: warning
width: 2
StringQuotes:
enabled: true
UrlQuotes:
enabled: true
BangFormat:
enabled: true
ColorKeyword:
enabled: false
ColorVariable:
enabled: true
Comment:
enabled: true
DebugStatement:
enabled: true
DeclarationOrder:
enabled: true
DuplicateProperty:
enabled: true
ElsePlacement:
enabled: true
EmptyLineBetweenBlocks:
enabled: true
EmptyRule:
enabled: true
FinalNewline:
enabled: true
HexLength:
style: long
HexValidation:
enabled: true
ImportPath:
enabled: true
LengthVariable:
enabled: false
MergeableSelector:
enabled: true
NameFormat:
enabled: true
NestingDepth:
enabled: true
max_depth: 3
PlaceholderInExtend:
enabled: true
PrivateNamingConvention:
enabled: true
PropertySpelling:
enabled: true
PropertySortOrder:
enabled: true
PropertyCount:
enabled: true
PropertyUnits:
enabled: true
properties:
border: ['px']
margin: ['px', '%']
font-size: ['px', '%', 'em']
line-height: ['px']
PseudoElement:
enabled: true
QualifyingElement:
enabled: false
SelectorDepth:
enabled: true
SelectorFormat:
enabled: true
convention: hyphenated_lowercase
SingleLinePerProperty:
enabled: true
Shorthand:
enabled: true
allowed_shorthands: [1, 2, 3, 4]
SingleLinePerSelector:
enabled: true
SpaceAfterComma:
enabled: true
SpaceAfterComment:
enabled: true
SpaceAfterPropertyColon:
enabled: true
SpaceAfterPropertyName:
enabled: true
================================================
FILE: .transifexrc.example
================================================
[https://www.transifex.com]
api_hostname = https://api.transifex.com
hostname = https://www.transifex.com
rest_hostname = https://rest.api.transifex.com
token =
================================================
FILE: .tx/config
================================================
[main]
host = https://www.transifex.com
[o:dimagi:p:commcare-hq:r:djangopo]
file_filter = locale/<lang>/LC_MESSAGES/django.po
source_file = locale/en/LC_MESSAGES/django.po
source_lang = en
trans.fr = locale/fra/LC_MESSAGES/django.po
trans.es = locale/es/LC_MESSAGES/django.po
trans.hi = locale/hin/LC_MESSAGES/django.po
trans.pt = locale/por/LC_MESSAGES/django.po
trans.sw = locale/sw/LC_MESSAGES/django.po
type = PO
[o:dimagi:p:commcare-hq:r:djangojspo]
file_filter = locale/<lang>/LC_MESSAGES/djangojs.po
source_file = locale/en/LC_MESSAGES/djangojs.po
source_lang = en
trans.fr = locale/fra/LC_MESSAGES/djangojs.po
trans.es = locale/es/LC_MESSAGES/djangojs.po
trans.hi = locale/hin/LC_MESSAGES/djangojs.po
trans.pt = locale/por/LC_MESSAGES/djangojs.po
trans.sw = locale/sw/LC_MESSAGES/djangojs.po
type = PO
================================================
FILE: AGENTS.md
================================================
# Guidelines for AI Agents
This document provides technical information about the CommCare HQ codebase
for AI coding assistants. For coding standards and best practices, see
`CODE_STANDARDS.md`.
## Tech Stack
- Backend: Python, Django
- Python dependency management: uv
- Testing: pytest
- Linting, formatting, & import sorting: Ruff
- Frontend: JavaScript, HTMX, Alpine.js, Knockout.js (legacy), Bootstrap 5
(Bootstrap 3 for legacy code)
- JavaScript bundling & dependency management: Webpack, Yarn
- Databases: PostgreSQL, Elasticsearch, CouchDB (legacy)
- Asynchronous task queue: Celery
- Cache & message broker: Redis
- Stream processing: Kafka
- Version Control: Git
## Architecture
- `corehq/apps/` — primary Django app directory; most feature work lives here
- `corehq/ex-submodules/` — internal packages added to the Python path
- `submodules/` — git submodules also on the Python path
- `localsettings.py` — local dev configuration (copy from `localsettings.example.py`)
- `testsettings.py` — Django settings module used by pytest
## Common Commands
Use command prefix `uv run` to run Python commands in the uv virtualenv.
### Sync Python dev dependencies
```bash
uv sync --compile-bytecode && uv pip install -r requirements/local.txt
```
### Testing
```bash
# Run tests with database reuse for faster execution
uv run pytest --reusedb=1 path/to/test.py
# If tests fail due to schema changes, migrate the test DB:
uv run pytest --reusedb=migrate path/to/test.py
```
Use `pytest-unmagic` for explicit test fixtures (see `CODE_STANDARDS.md`).
Notable markers: `es_test` (Elasticsearch), `sharded` (shard DBs), `slow`.
### Linting
```bash
# Python linting
uv run ruff check path/to/file.py
# JavaScript linting
npx eslint path/to/file.js
```
### Formatting
```bash
# Sort Python imports
uv run ruff check --select I --fix path/to/file.py
# Format Python code
uv run ruff format path/to/file.py
# Format HTML templates
npx prettier --write path/to/template.html
# Format Markdown files
npx prettier --write path/to/file.md
```
Automated formatting and import-sorting changes should be committed
separately from logic changes to keep diffs reviewable.
### JavaScript
```bash
# Watch and rebuild JS during development
yarn dev
# Production build
yarn build
```
Refer to the `docs/js-guide/` directory for the JavaScript Guide
### Frontend Style
Refer to the `corehq/apps/styleguide/` app (run locally at `/a/styleguide/`) for the
UI Style Guide — Bootstrap 5 conventions, button patterns, accessibility requirements,
and HTML/template guidelines for new frontend code.
### Debugging
```bash
# Fetch PR test failures
scripts/pr-failures.sh [pr_number] # uses current branch if omitted
```
## Gotchas
- **migrations.lock** — If you wrote a migration instead of generating one,
run `./manage.py makemigrations --lock-update` to update the lock file.
- **New domain-scoped models** — Any new model with a `domain` field (or
reachable via FK to a domain) must be registered in two places or CI will
fail:
- `corehq/apps/dump_reload/sql/dump.py` — add a
`FilteredModelIteratorBuilder` entry so the model is included in domain
data exports.
- `corehq/apps/domain/deletion.py` — add a `ModelDeletion` entry so the
model is cleaned up when a domain is deleted.
Use `SimpleFilter('domain')` for direct domain fields, or
`SimpleFilter('parent__domain')` for FK traversal.
- **CouchDB is legacy** — Use PostgreSQL for new data models
- **Knockout.js is legacy** — Prefer HTMX or Alpine.js for new frontend code
- **Bootstrap 3 is legacy** — Prefer Bootstrap 5; both coexist in the codebase
## Version Control
- Always commit on a branch, never directly on master.
- When creating a new branch, each author has a prefix they use. Use the
prefix the author has used most on local git branches, or else their
initials (from `git config user.name`).
- Commit work in logical chunks rather than one large commit. Group related
changes together (e.g. a new module with its tests, a migration with its
model changes) so that each commit is self-contained and reviewable.
- When creating PRs, always create them as drafts with the "DON'T REVIEW
YET" label, and always use the GitHub PR template.
- When adding PR descriptions, avoid restating the code diff. Focus on
useful context for the reviewer.
================================================
FILE: CODE_STANDARDS.md
================================================
# Code Standards for CommCare HQ
This document outlines coding standards and best practices for the
CommCare HQ codebase. These guidelines apply when reviewing code,
writing code, or making changes to the project.
## General Principles
- **Clarity, maintainability, and performance** are important.
Prioritize clarity and maintainability higher than performance.
Situations where performance must come at the cost of clarity or
maintainability should be documented using comments.
- Consider both immediate needs and long-term implications.
## Structure
- **Single responsibility**: Keep functions and methods focused on a
single responsibility. Break up longer functions where appropriate.
- **Don't repeat yourself (DRY)**: If you suspect that similar code
might have been needed before, search the codebase for it before
implementing it. If you find functionality similar to what you are
looking for, you may need to move it to keep architectural layers or
Django app dependencies structured well.
## Documentation
- **Comments explain "why" not "what"**: The "what" should be clear from
the code itself. Use comments to explain reasoning that might not be
immediately clear.
- **Docstrings for modules and classes**: Use docstrings to give the
purpose of a module or class. Avoid docstrings on methods or functions
where their purpose is clear from the name.
- **Use reStructuredText format**: Follow reStructuredText conventions
in docstrings.
- **Keep documentation in sync**: Update comments and docstrings when
code changes are made. Keep module README files up to date.
## Testing
- **Coverage**: Changes must be covered by appropriate tests. Tests need
to cover edge cases and failure scenarios.
- **pytest conventions**: Use plain `assert` statements instead of
unittest-style assertion methods. For example, use `assert x == y`
instead of `self.assertEqual(x, y)`, `assert x in y` instead of
`self.assertIn(x, y)`, etc. Use parametrized tests for repetitive
test cases.
- **Explicit fixtures**: Use [pytest-unmagic](https://github.com/dimagi/pytest-unmagic/)
for explicit test fixtures.
- **Test doctests**: Doctests should be tested:
```python
def test_doctests():
results = doctest.testmod(some_module)
assert results.failed == 0
```
- **Run tests before completing**: Run the tests that cover the changes
before considering any changes complete.
## Performance
- **Database queries**: For database operations, queries should be
optimized. In rare instances this could require testing the
performance of the query in an environment similar to production. If
you're writing code and believe this would be beneficial, notify the
developer.
- **Front-end**: For front-end code, be mindful of rendering or loading
concerns.
## Security
- **Prevent vulnerabilities**: Be careful not to introduce security
vulnerabilities such as command injection, XSS, SQL injection, and
other OWASP top 10 vulnerabilities. If you notice insecure code,
immediately fix it.
- **Validate input**: Ensure that user input is properly validated and
sanitized.
- **Implement access controls**: Ensure that permissions and access
controls are properly implemented.
- **Prefer safe actions**: Where a destructive action, like deleting an
instance of an important class, could have strong negative
consequences, rather choose a less permanent action, like disabling
the instance instead.
## Version Control
- **Logical commits**: When changes are ready to be committed, break
them into logical steps to make reviewing easier. Each step should be
staged with a suggested commit message that summarizes the step.
================================================
FILE: CONTRIBUTING.rst
================================================
===========================
Contributing to CommCare HQ
===========================
CommCare HQ is primarily developed by `Dimagi`_, but we welcome contributions.
Code Contributions
------------------
Dimagi tracks many issues internally, but we use github's `issue tracker`_
for public facing issues. Feel free to browse the issues there and tackle
any you feel equipped to do. When you update or add comments to an issue
please mention **@dimagi/dimagi-dev** to send an alert to our internal issue
tracking system.
Please keep in mind that we hold the standard of quality in contributions
to a very high level regardless of source. Contributions which have very
limited scope, come with rigorous tests, and have their purpose outlined
in a Github issue are much more likely to be reviewed for inclusion.
You should ensure the following are true for any PR before it will be
considered for review or re-review
- The code and architecture comply with `Standards and Best Practices`_
- Any UI components comply with the project `Style Guide`_
- Contributions follow any subsystem specific practices (example: `Javascript Guide`_)
- Automated regression and integration tests are passing
- Any automated feedback (label bot, lint bot, etc) is addressed
- Any previous developer feedback is addressed
When opening a PR, please review our `Guide to Authoring Pull Requests`_ and our
`AI Contribution Guidelines`_ if relevant.
You may also be interested in the `Developers category`_ of the `CommCare Forum`_
if you have questions or need feedback.
Useful Tools
------------
Here are some tools widely used by CommCare HQ developers
ruff
Ruff is used for linting, formatting, and sorting imports. It is run on all
PRs automatically. Run ``ruff check path/to/file.py`` to lint,
``ruff format path/to/file.py`` to format, or
``ruff check --select I --fix path/to/file.py`` to sort imports.
Commit formatting and import-sorting changes separately from logic changes.
./manage.py show_urls
Provided by ``django-extensions``, this outputs a list of all URL paths used in the
project. Pipe the output to ``grep`` to easily find what view handles a particular URL.
CommCare Enhancement Proposals
------------------------------
For larger changes or new features we encourage the use of the `CommCare Enhancement Proposal`_
process which gives the team a chance to give feedback on ideas before the code work begins.
.. _CommCare Enhancement Proposal: https://commcare-hq.readthedocs.io/cep.html
Bug Reports
-----------
To file a bug report, please follow the system outlined in our `bug
reports`_ wiki page. You are also welcome to submit an accompanying pull
request if you are able to resolve the issue.
Documentation Contributions
---------------------------
CommCare HQ's technical documentation is available at `Read the Docs`_.
It is written in reStructuredText_.
When documenting features, it is good practice to keep the documentation
close to its code, so that it can easily be found. A good example of
this is the documentation for User Configurable Reporting. The code is
situated in the *corehq/apps/userreports/* directory, and its documentation
is in *corehq/apps/userreports/README.rst*. In general,
*corehq/apps/[myapp]/README.rst* is typically a good starting point for
documentation.
The User Configurable Reporting documentation also shows another good
practice; it uses reStructuredText's autoclass_ directive to include the
documentation written as docstrings in the codebase. This avoids
duplication, and means that when the codebase is changed, the documentation
is updated automatically with it.
Add a file to the *docs/* directory, and a reference to it in
*docs/index.rst* under the ``toctree`` directive, to include it with the
rest of CommCare HQ's documentation. Again, you can refer to *docs/ucr.rst*
to see how that works.
.. _Dimagi: http://www.dimagi.com/
.. _issue tracker: https://github.com/dimagi/commcare-hq/issues
.. _bug reports: https://dimagi.atlassian.net/wiki/spaces/commcarepublic/pages/2143956931/Submit+a+Support+Request
.. _Standards and Best Practices: STANDARDS.rst
.. _Style Guide: https://www.commcarehq.org/styleguide/
.. _Javascript Guide: docs/js-guide/README.rst
.. _Guide to Authoring Pull Requests: https://github.com/dimagi/open-source/blob/master/docs/Writing_PRs.md
.. _Developers category: https://forum.dimagi.com/c/developers
.. _CommCare Forum: https://forum.dimagi.com/
.. _Read the Docs: https://commcare-hq.readthedocs.io/
.. _reStructuredText: https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
.. _autoclass: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
.. _AI Contribution Guidelines: https://github.com/dimagi/open-source/blob/master/docs/ai_contribution_guidelines.md
Updating requirements
---------------------
Python
~~~~~~
To update python requirements, add new requirement(s) to ``pyproject.toml`` or
run ``uv lock --upgrade-package=<package-name>`` to update a specific package.
To upgrade all requirements to their latest allowed version, you can run
``uv lock --upgrade``—this usually results in a large number of upgrades
and is not something we can merge easily, but it is sometimes a useful exploratory first step.
Javascript
~~~~~~~~~~
To update javascript requirements, edit ``package.json``:
* ``dependencies`` for dependencies needed for all environments
* ``devDependencies`` for developer environments only
and run ``yarn install``.
Commit the changes to ``package.json`` along with the updates to ``yarn.lock``.
PR labels
---------
Product labels
~~~~~~~~~~~~~~
All PRs must be tagged with one of the following PR labels. For PRs from external
contributors the onus is on the reviewer to add the labels.
- product/all-users-all-environments
- product/prod-india-all-users
- product/custom
- product/feature-flag
- product/invisible
- product/admin
Label descriptions can be seen on the GitHub `labels`_ page or in the
`.github/labels.yml`_ configuration file.
.. _labels: https://github.com/dimagi/commcare-hq/labels
.. _.github/labels.yml: .github/labels.yml
Reindex / migration
~~~~~~~~~~~~~~~~~~~
Any PR that will require a database migration or some kind of data reindexing to be done
must be labeled with the **reindex/migration** label. This label will get automatically applied
if the PR changes `certain files`_ but it can also be added manually.
It is necessary to add a `RequestReindex`_ Django migration operation, which may
also require adding a new migration file, if the **reindex/migration** label is added to a PR on
account of Couch ``_design`` doc changes (or if a new Elasticsearch index is added, but this
will soon change). The following command will automatically add a migration and update the
Couch views lock file when a reindex is required on account of Couch view changes::
$ ./manage.py makemigrations preindex
A change log entry should be published in `commcare-cloud`_ to alert
operators to run the migration before deploying if it may disrupt the normal deploy cycle (if it
will run for a long time on any environment, for example).
Any PR with this label will fail the `required-labels` check. This is intentional to prevent
premature merging of the PR.
.. _certain files: .github/labels.yml#L12-L13
.. _RequestReindex: corehq/preindex/django_migrations.py
.. _commcare-cloud: https://github.com/dimagi/commcare-cloud/
Risk
~~~~
PRs that touch certain files will be automatically flagged with a "Risk" label,
either medium or high. This includes heavily-used workflows where a bug would have a high impact
and also areas that are technically complex, difficult to roll back, etc.
These PRs will receive extra scrutiny and should have especially solid test coverage and/or
manual testing. Alternatively, the PR description may explain why the PR is not genuinely high risk.
QA / Work in progress
~~~~~~~~~~~~~~~~~~~~~~
PRs that are not ready to be merged can be labeled with one of the following labels:
- awaiting QA
- Open for review: do not merge
As long as either of these labels are present on the PR it will have a pending status.
Reviews with CodeRabbit
~~~~~~~~~~~~~~~~~~~~~~~
.. note:: CodeRabbit is being evaluated as a code review tool for CommCare HQ.
CodeRabbit is an AI-powered code reviewer that delivers context-aware feedback on pull requests within
minutes. It provides a fresh perspective and catches issues that are often missed, enhancing the
overall review quality. A clean CodeRabbit review does not constitute approval allowing the PR to be merged.
Although not required, it's recommended that you use CodeRabbit to review your PRs, ideally before any human-reviewers
do so. A good way to do this is creating a draft PR first, let CodeRabbit review, address the feedback, then
convert it to a regular PR.
CodeRabbit specifies a `list of commands <https://docs.coderabbit.ai/guides/commands/>`_ for managing
the bot’s review workflow. It’s recommended that you read through the page at least once to gain some
perspective on how CodeRabbit functions, but the main commands you’ll probably want to know about are
the following:
- `@coderabbitai review` - Triggers a review from CodeRabbit. This command will do an incremental review of the PR, meaning that CodeRabbit will only review each commit once. This command is suited for most use-cases.
- `@coderabbitai full review` - Triggers a full review again. This command is useful for when major changes require a fresh perspective.
- `@coderabbitai resolve` - This resolves all CodeRabbit comments and is useful for when you want to clean up
the CodeRabbit comments.
================================================
FILE: DEV_FAQ.md
================================================
# Developer FAQ
This is a starting point for troubleshooting issues that frequently occur once your local environment
is set up and you've started working on a significant code change.
## General Advice
- Use the developers section of the [Dimagi Forum](https://forum.dimagi.com/) for help with issues.
- Some features are limited to specific software plans. Use the `make_domain_enterprise_level` command
to mark your local project as a test project and grant access to all features.
## Common Problems
> I created local forms and/or cases, but I don't see them in reports.
ElasticSearch data is out of date. See [ElasticSearch](https://github.com/dimagi/commcare-hq/blob/master/DEV_FAQ.md#elasticsearch) below.
> I created local cases, but some other part of HQ that filters by case type doesn't show my case types.
ElasticSearch data is out of date. See [ElasticSearch](https://github.com/dimagi/commcare-hq/blob/master/DEV_FAQ.md#elasticsearch) below.
> My local downloads/uploads aren't working
Look back over the [celery setup](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP.md#running-commcare-hq).
Many background tasks on HQ will run fine locally, if you don't run celery but do have `CELERY_TASK_ALWAYS_EAGER=True` in local settings, but some do require celery (typically those that have a task and also a polling job, which is how most downloads and uploads work).
> My JS/LESS changes aren't showing up (caching, staticfiles)
Double check that your browser isn't caching. In Chrome, this is Dev Tools > Settings > Preferences > Network and check "Disable cache (while DevTools is open)"
Also double check that you're editing the source file, under `corehq/apps/<app_name>/static`, not a file in `staticfiles`
> Web Apps and/or App Preview aren't working at all
Formplayer isn't running properly. See [Formplayer](https://github.com/dimagi/commcare-hq/blob/master/DEV_FAQ.md#formplayer) below.
> My web server is throwing a slew of web socket errors
It happens, try restarting.
> I'm getting TemplateSyntaxErrors related to Webpack.
Please run `yarn dev`. If this build is failing, and you didn't make any changes to JavaScript files on your branch,
please raise this as an issue.
## Generating Sample Data
### SMS
Check out the `generate_fake_sms_data` command.
### Applications
If you have an application on commcarehq.org, follow [these instructions](https://dimagi.atlassian.net/wiki/spaces/commcarepublic/pages/2143955454/Copy+or+Delete+an+Application) to copy it to your local environment.
There are also [three template apps](https://github.com/dimagi/commcare-hq/tree/master/corehq/apps/app_manager/static/app_manager/template_apps) checked into the codebase.
You can run [load_app_from_slug](https://github.com/dimagi/commcare-hq/blob/6021df8639dc0053c8dbdbb8690993be708776c5/corehq/apps/app_manager/views/apps.py#L510) in a django shell to import one of these apps. Note that you may wish to only run the first few lins of `load_app_from_slug` if you don't care about your app having multimedia.
### Cases
The easiest way to add cases locally is generally to use your local case importer. [Docs](https://dimagi.atlassian.net/wiki/spaces/commcarepublic/pages/2143946828/Importing+Cases+Using+Excel)
for this are extensive, but at its most basic, you can just upload a single-column file with a bunch of case names to create cases.
### Cases + App + Data Dictionary
The `create_case_fixtures` command will add a sample app, a data dictionary, and a set of relevant cases.
## ElasticSearch
ElasticSearch is the data source for reports, exports, and an assortment of parts of other features.
Most issues with data not appearing locally, if you've already done something to create that data, are issues with ElasticSearch.
You can run ElasticSearch continuously, mimicking a production environment, or on an as-needed basis. Either one of these can be simpler,
depending on how much you're working with ES data.
As described in the [dev setup guide](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP.md#running-commcare-hq), you can
use `run_ptop` to keep ES continually up to date.
Alternatively, you can run `ptop_reindexer_v2` as needed to sync individual indexes. `ptop_reindexer_v2` works on one index at a time.
Running it without any arguments will list the available indexes. The indexes you're most likely to use:
+ `sql-form` and `sql-case` populate form-based and case-based reports and exports
+ `user` and `group` may be useful, depending on what you're working on
+ `case_search` populates the Case list Explorer and Explore Case Data reports, as well as the case search/claim feature. For now, this data stands as a duplicate but different format of the origin `case` index and at some point in the future, features that use the legacy `case` index will move to using `case-search`.
+ `sms` populates messaging reports and SMS exports
+ You do **not** care about `case` or `form`, which are only used on legacy domains that store forms and cases in CouchDB.
## Formplayer
You can run formplayer either in Docker or as a standalone service. It's simpler to run via Docker.
If you're doing formplayer development, you'll need to run it as a separate service.
When troubleshooting formplayer in docker, use standard docker commands to view logs: `docker logs <formplayer_container_name>`
Formplayer expects HQ to be running on port 8000. If you run HQ on a different port, you'll need to modify settings and run formplayer outside of docker.
If you run formplayer as a separate service, make sure you're not acciddentally also running it in Docker - HQ's Docker script's `up` command will start it.
If you run into "Unable to connect" formplayer errors, try the following:
- Running formplayer as a standalone service instead of in Docker
- Visiting localhost:8000 instead of 0.0.0.0:8000 when using your locally run HQ app
- If running HQ app on 127.0.0.1:8000 and observe CORS error in browser's network tab while making API request to formplayer via localhost:8080, change HQ app url to use localhost:8000
See the [Formplayer README](https://github.com/dimagi/formplayer/blob/master/README.md)
and [Formplayer setup for HQ](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP.md#formplayer).
================================================
FILE: DEV_SETUP.md
================================================
# Setting up CommCare HQ for Developers
This document describes setting up a development environment for working on
CommCare HQ. Such an environment is not suitable for real projects. Production
environments should be deployed and managed [using
commcare-cloud](https://dimagi.github.io/commcare-cloud/).
These instructions are for Mac or Linux computers. For Windows, consider using
an Ubuntu virtual machine.
Once your environment is up and running, bookmark the [dev FAQ](https://github.com/dimagi/commcare-hq/blob/master/DEV_FAQ.md)
for common issues encountered in day-to-day HQ development.
## (Optional) Copying data from an existing HQ install
If you're setting up HQ on a new computer, you may have an old, functional
environment around. If you don't want to start from scratch, back up your
Postgres and Couch data.
- PostgreSQL
- Create a pg dump. You'll need to verify the host IP address:
```sh
pg_dump -h localhost -U commcarehq commcarehq > /path/to/backup_hq_db.sql
```
- CouchDB
- From a non-Docker install: Copy `/var/lib/couchdb2/`.
- From a Docker install: Copy `~/.local/share/dockerhq/couchdb2`.
- Shared Directory
- If you are following the default instructions, copy the `sharedfiles`
directory from the HQ root folder, otherwise copy the directory referenced
by `SHARED_DRIVE_ROOT` in `localsettings.py`
Save those backups to somewhere you'll be able to access from the new environment.
## Prerequisites
NOTE: Developers on Mac OS have additional prerequisites. See the [Supplementary Guide for Developers on Mac OS](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP_MAC.md).
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
```sh
sudo apt install git
```
- Python dependency managment with `uv`
- There are [several ways to install `uv`](https://docs.astral.sh/uv/getting-started/installation/).
Use whatever method works best for your platform.
- **Linux**
Ubuntu:
```sh
sudo snap install astral-uv
```
- **Mac**:
First install [homebrew](https://brew.sh/). Then use it to install `uv`.
```sh
brew install uv
```
- Requirements of Python libraries, if they aren't already installed. Some of this comes from
[pyenv's recommendations](https://github.com/pyenv/pyenv/wiki#suggested-build-environment)
- **Linux**:
```sh
sudo apt-get update; sudo apt install libncurses-dev libxml2-dev libxmlsec1-dev \
libxmlsec1-openssl libxslt1-dev libpq-dev pkg-config gettext make build-essential \
libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev
```
- **macOS**:
```sh
brew install libmagic libxmlsec1 libxml2 libxslt openssl readline sqlite3 xz zlib tcl-tk
```
- Java (JDK 17)
- **Linux**:
install `openjdk-17-jre` via apt:
```sh
sudo apt install openjdk-17-jre
```
- **macOS**:
See the [Supplementary Guide for Developers on Mac OS](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP_MAC.md)
as this is quite lengthy.
- PostgreSQL
Executing postgres commands (e.g. `psql`, `createdb`, `pg_dump`, etc) requires
installing postgres. These commands are not explicitly necessary, but having
the ability to run them may be useful.
- **Linux** (optional) install the `postgresql-client` package:
```sh
sudo apt install postgresql-client
```
- **macOS** (required) the postgres binaries can be installed via Homebrew:
```sh
brew install postgresql
```
Possible alternative to installing postgres (from [this SO answer](https://stackoverflow.com/a/39800677)).
Do this prior to `uv` commands (outlined later in this doc):
```sh
xcode-select --install
export LDFLAGS="-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib"
```
If you have an M1 chip and are using a Rosetta-based install of Postgres and run into problems with psycopg2, see [this solution](https://github.com/psycopg/psycopg2/issues/1216#issuecomment-767892042).
##### Notes on `xmlsec`
`xmlsec` is a dependency that will require some non-`pip`-installable
packages. The above notes should have covered these requirements for linux and
macOS, but if you are on a different platform or still experiencing issues,
please see [`xmlsec`'s install notes](https://pypi.org/project/xmlsec/).
If you encounter issues installing `xmlsec` on a M1 mac, you can try following a workaround
outlined in the Mac setup [Supplementary Guide](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP_MAC.md).
## Downloading & Running CommCare HQ
### Step 1: Clone this repo
```sh
git clone https://github.com/dimagi/commcare-hq.git
cd commcare-hq
git submodule update --init --recursive
git-hooks/install.sh
```
### Step 2: Install requirements and set up your virtual environment
1. Install the appropriate requirements (**only one is necessary**).
NOTE: If this fails you may need to [install the prerequisite system dependencies](#prerequisites).
**Mac OS:** Issues? See the [Supplementary Guide for Developers on Mac OS](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP_MAC.md).
- Recommended for those developing CommCare HQ
```sh
uv sync --compile-bytecode
```
This will also create a virtual environment in `.venv` if one does not
already exist. To activate the environment:
```sh
source .venv/bin/activate
```
For convenience, you can create an alias to activate virtual environments in
".venv" and "venv" directories. To do that, add the following to your
`.bashrc` or `.zshrc` file:
```sh
alias venv='if [[ -d .venv ]] ; then source .venv/bin/activate ; elif [[ -d venv ]] ; then source venv/bin/activate ; fi'
```
Then you can activate virtual environments with
```sh
venv
```
- Recommended for developers or others with custom requirements: create a
copy of local.txt.sample,
```sh
cp requirements/local.txt.sample requirements/local.txt
```
and follow the instructions in `local.txt` to keep requirements in sync.
The one-liner to keep requirements up to date is:
```sh
uv sync --compile-bytecode && uv pip install -r requirements/local.txt
```
- View pyproject.toml to understand the different dependency groups.
To install a specific group not included in default groups:
```
uv sync --group=docs
```
- If you have ARM64 architecture (Apple M1 chip) and you're having trouble installing ReportLab:
```sh
CFLAGS="-Wno-error=implicit-function-declaration" uv sync --compile-bytecode
```
[Source](https://stackoverflow.com/questions/64871133/reportlab-installation-failed-after-upgrading-to-macos-big-sur)
- If `uv sync` fails with compiler errors, ensure the virtualenv is active
and try:
```sh
CC=gcc LDFLAGS="-L`python -c'import sys; print(sys.base_prefix)'`/lib" uv sync --compile-bytecode
```
`uv` caches compiled dependencies, so this is only necessary when a new
dependency with a compiled binary is installed.
Note that once you're up and running, you'll want to periodically re-run these
steps, and a few others, to keep your environment up to date. Some developers
have found it helpful to automate these tasks. For pulling code, instead of `git pull`,
you can run [this script](https://github.com/dimagi/commcare-hq/blob/master/scripts/update-code.sh)
to update all code, including submodules. [This script](https://github.com/dimagi/commcare-hq/blob/master/scripts/hammer.sh)
will update all code and do a few more tasks like run migrations and update
libraries, so it's good to run once a month or so, or when you pull code and
then immediately hit an error.
### Step 3: Set up `localsettings.py`
First create your `localsettings.py` file:
```sh
cp localsettings.example.py localsettings.py
```
#### Create the shared directory
If you have not modified `SHARED_DRIVE_ROOT`, then run:
```sh
mkdir sharedfiles
```
### Step 4: Set up Docker services
Once you have completed the above steps, you can use Docker to build and run all
of the service containers. There are detailed instructions for setting up Docker
in the [docker folder](docker/README.rst). But the following should cover the
needs of most developers.
1. Install docker packages.
- **Mac**: see [Install Docker Desktop on Mac](https://docs.docker.com/docker-for-mac/install/)
for docker installation and setup.
- **Linux**:
```sh
# install docker
sudo apt install docker.io
# ensure docker is running
systemctl is-active docker || sudo systemctl start docker
# add your user to the `docker` group
sudo adduser $USER docker
# log in as yourself again to activate membership of the "docker" group
su - $USER
# re-activate your virtualenv (presuming uv)
source .venv/bin/activate
```
2. Install `docker compose`
- **Mac**: comes with Docker Desktop
- **Linux**:
```sh
sudo apt install docker-compose-plugin
```
3. Ensure the elasticsearch config files are world-readable (their containers
will fail to start otherwise).
```sh
chmod 0644 ./docker/files/elasticsearch*.yml
```
If the elasticsearch container fails to start, complaining about permissions for `/usr/share/elasticsearch/data`, you'll need to update your local folder's permissions:
```sh
chown -R 1000:root ~/.local/share/dockerhq/elasticsearch6
```
If the above local path does not exist, you can determine where the volume is mounted via:
```sh
docker inspect <elasticsearch6 container id> | grep /usr/share/elasticsearch/data | head -1
```
4. Bring up the docker containers.
In either of the following commands, omit the `-d` option to keep the
containers attached in the foreground.
```sh
./scripts/docker up -d
# Optionally, start only specific containers.
./scripts/docker up -d postgres couch redis elasticsearch6 zookeeper kafka minio formplayer
```
**Mac OS:** You may encounter issues with formplayer and elasticsearch at this stage.
We recommend visiting the Docker section in the [Supplementary Guide](DEV_SETUP_MAC.md#Docker).
5. If you are planning on running Formplayer from a binary or source, stop the
formplayer container to avoid port collisions.
```sh
./scripts/docker stop formplayer
```
### Step 5A: (Optional) Copying data from an existing HQ install
If you previously created backups of another HQ install's data, you can now copy
that to the new install. If not, proceed to Step 5B.
- Postgres
- Make sure Postgres is running:
```sh
./scripts/docker ps
```
- Make sure `psql` is installed: (Ubuntu)
```sh
sudo apt install postgresql postgresql-contrib
```
- Restore the backup:
```sh
psql -U commcarehq -h localhost commcarehq < /path/to/backup_hq_db.sql
```
- CouchDB
- Stop Couch:
```sh
./scripts/docker stop couch
```
- Copy the `couchdb2/` dir to `~/.local/share/dockerhq/couchdb2`.
- Start Couch
```sh
./scripts/docker start couch
```
- Fire up Fauxton to check that the dbs are there: http://localhost:5984/_utils/
- As of CouchDB 3.x, Fauxton is no longer shipped in the container and must
be installed separately:
```sh
npm install -g fauxton
fauxton
```
Open http://localhost:8000 in a browser. Run fauxton with ``-p PORT`` to
use a port other than 8000.
- Shared Directory
- If you are following the default instructions, move/merge the `sharedfiles`
directory into the HQ root, otherwise do so into the `SHARED_DRIVE_ROOT`
directory referenced in `localsettings.py`
### Step 5B: Initial Database Population
Before running any of the commands below, you should have all of the following
running: Postgres, CouchDB, Redis, and Elasticsearch.
The easiest way to do this is using the Docker instructions above.
If you want to use a partitioned database, change
`USE_PARTITIONED_DATABASE = False` to `True` in `localsettings.py`.
You will also need to create the additional databases manually:
```sh
psql -h localhost -p 5432 -U commcarehq
```
(assuming that "commcarehq" is the username in `DATABASES` in
`localsettings.py`). When prompted, use the password associated with the
username, of course.
```sql
commcarehq=# CREATE DATABASE commcarehq_proxy;
CREATE DATABASE
commcarehq=# CREATE DATABASE commcarehq_p1;
CREATE DATABASE
commcarehq=# CREATE DATABASE commcarehq_p2;
CREATE DATABASE
commcarehq=# \q
```
If you are running formplayer through docker, you will need to create the
database for the service
```sql
commcarehq=# CREATE DATABASE formplayer;
CREATE DATABASE
```
Populate your database:
```sh
./manage.py sync_couch_views
./manage.py create_kafka_topics
env CCHQ_IS_FRESH_INSTALL=1 ./manage.py migrate --noinput
```
If you are using a partitioned database, populate the additional
databases too, and configure PL/Proxy:
```sh
env CCHQ_IS_FRESH_INSTALL=1 ./manage.py migrate_multi --noinput
./manage.py configure_pl_proxy_cluster --create_only
```
You should run `./manage.py migrate` frequently, but only use the environment
variable CCHQ_IS_FRESH_INSTALL during your initial setup. It is used to skip a
few tricky migrations that aren't necessary for new installs.
#### Troubleshooting errors from the CouchDB Docker container
If you are seeing errors from the CouchDB Docker container that include
`database_does_not_exist` ... `"_users"`, it could be because CouchDB
is missing its three system databases, `_users`, `_replicator` and
`_global_changes`. The `_global_changes` database is not necessary if
you do not expect to be using the global changes feed. You can use
`curl` to create the databases:
```sh
curl -X PUT http://username:password@127.0.0.1:5984/_users
curl -X PUT http://username:password@127.0.0.1:5984/_replicator
```
where "username" and "password" are the values of "COUCH_USERNAME"
and "COUCH_PASSWORD" given in `COUCH_DATABASES` set in
`dev_settings.py`.
#### Troubleshooting Issues with `sync_couch_views`
**Mac OS M1 Users:** If you see the following error, check the [Supplementary Guide](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP_MAC.md).
```sh
> ImportError: failed to find libmagic. Check your installation
```
#### Troubleshooting Issues with `migrate`
If you have an authentication error running `./manage.py migrate` the first
time, open `pg_hba.conf` (`/etc/postgresql/9.1/main/pg_hba.conf` on Ubuntu)
and change the line "local all all peer" to "local all all md5".
If you have trouble with your first run of `./manage.py sync_couch_views`:
- 401 error related to nonexistent database:
```sh
curl -X PUT http://localhost:5984/commcarehq # create the database
curl -X PUT http://localhost:5984/_config/admins/commcarehq -d '"commcarehq"' . # add admin user
```
- Error stemming from any Python modules: the issue may be that your virtualenv
is relying on the `site-packages` directory of your local Python installation
for some of its requirements. (Creating your virtualenv with the
`--no-site-packages` flag should prevent this, but it seems that it does not
always work). You can check if this is the case by running `uv pip show
{name-of-module-that-is-erroring}`. This will show the location that your
virtualenv is pulling that module from; if the location is somewhere other
than the path to your virtualenv, then something is wrong. The easiest
solution to this is to remove any conflicting modules from the location that
your virtualenv is pulling them from (as long as you use virtualenvs for all
of your Python projects, this won't cause you any issues).
- If you encounter an error stemming from an Incompatible Library Version of
libxml2.2.dylib on Mac OS X, try running the following commands:
```sh
brew link libxml2 --force
brew link libxslt --force
```
- If you encounter an authorization error related to CouchDB, try going to your
`localsettings.py` file and change `COUCH_PASSWORD` to an empty string.
- If you get errors saying "Segmentation fault (core dumped)", with a warning like
"RuntimeWarning: greenlet.greenlet size changed, may indicate binary incompatibility.
Expected 144 from C header, got 152 from PyObject" check that your Python version is correct (3.9).
Alternatively, you can try upgrading `gevent` (`uv pip install --upgrade gevent`) to fix this error
on Python 3.8, but you may run into other issues!
### Step 6: Populate Elasticsearch
To set up elasticsearch indexes run the following:
```sh
./manage.py ptop_preindex [--reset]
```
This will create all the elasticsearch indexes (that don't already exist) and
populate them with any data that's in the database.
### Step 7: Installing JavaScript and Front-End Requirements
#### Install Dart Sass
We are transitioning to using `sass`/`scss` for our stylesheets. In order to compile `*.scss`,
Dart Sass is required.
We recommend using `npm` to install this globally with:
```
npm install -g sass
```
You can also [follow the instructions here](https://sass-lang.com/install) if you encounter issues with this method.
#### Installing Yarn
We use Yarn to manage our JavaScript dependencies. It is able to install older
`bower` dependencies/repositories that we still need and `npm` repositories.
Eventually we will move fully to `npm`, but for now you will need `yarn` to
manage `js` repositories.
In order to download the required JavaScript packages, you'll need to install
`yarn` and run `yarn install`. Follow these steps to install:
1. Follow [these steps](https://classic.yarnpkg.com/en/docs/install)
to install Yarn.
2. Install dependencies with:
```sh
yarn install --frozen-lockfile
```
3. Ensure that `django` translations are compiled for javascript (or it will throw a JS error):
```sh
./manage.py compilejsi18n
```
NOTE: if you are making changes to `package.json`, please run `yarn install`
without the `--frozen-lockfile` flag so that `yarn.lock` will get updated.
##### Troubleshooting Javascript dependency installation
Depending on your operating system, and what version of `nodejs` and `npm` you
have locally, you might run into issues. Here are minimum version requirements
for these packages.
```sh
npm --version
> 11.6.2
node --version
> v24.11.1
```
On a clean Ubuntu 22.04 LTS install, the packaged nodejs version is expected to be v12. The
easiest way to get onto the current nodejs v24 is
```sh
curl -sL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs
```
### Step 8: Configure CSS Precompilers (2 Options)
#### Requirements: Install Dart Sass
At present, we are undergoing a migration from Bootstrap 3 to 5. Bootstrap 3 uses LESS
as its CSS precompiler, and Bootstrap 5 using SASS / SCSS. You will need both installed.
LESS is already taken care of by `package.json` when you run `yarn install`. In order to
compile SASS, we need Dart Sass. There is a `sass` npm package that can be installed globally with
`npm install -g sass`, however this installs the pure javascript version without a binary. For speed in a
development environment, it is recommended to install `sass` with homebrew:
```sh
brew install sass/sass/sass
```
You can also view [alternative installation instructions](https://sass-lang.com/install/) if homebrew doesn't work for you.
#### Option 1: Compile CSS on page-load without compression
This is the setup most developers use. If you don't know which option to use,
use this one. It's the simplest to set up and the least painful way to develop:
just make sure your `localsettings.py` does not contain `COMPRESS_ENABLED` or
`COMPRESS_OFFLINE` settings (or has them both set to `False`).
The disadvantage is that this is a different setup than production, where LESS/SASS
files are compressed.
#### Option 2: Compress OFFLINE, just like production
This mirrors production's setup, but it's really only useful if you're trying to
debug issues that mirror production that's related to staticfiles and
compressor. For all practical uses, please use Option 1 to save yourself the
headache.
Make sure your `localsettings.py` file has the following set:
```python
COMPRESS_ENABLED = True
COMPRESS_OFFLINE = True
```
For all STATICFILES changes (primarily LESS, SASS, and JavaScript), run:
```sh
./manage.py collectstatic
./manage.py compilejsi18n
./manage.py fix_less_imports_collectstatic
./manage.py compress
```
### Step 9: Browser Settings
We recommend disabling the cache. In Chrome, go to **Dev Tools > Settings >
Preferences > Network** and check the following:
- [x] Disable cache (while DevTools is open)
### Step 10: Create a superuser
To be able to use CommCare, you'll want to create a superuser, which you can do by running:
```sh
./manage.py make_superuser <email>
```
This can also be used to promote a user created by signing up to a superuser.
Note that promoting a user to superuser status using this command will also give them the
ability to assign other users as superuser in the in-app Superuser Management page.
### Step 11: Bootstrap a local build
If you are going to build CommCare applications, you should generally ensure that CommCareHQ
has at least one CommCare app version installed and available in the
[build manager](https://github.com/dimagi/commcare-hq/tree/master/corehq/apps/builds#adding-commcare-builds-to-commcare-hq).
The easiest way to do this is to just grab the latest build available.
```sh
./manage.py add_commcare_build --latest
```
### Step 12: Running `yarn dev`
In order to build JavaScript bundles with Webpack, you will need to have `yarn dev`
running in the background. It will watch any existing Webpack Entry Point, aka modules
included on a page using the `js_entry` template tag.
When you add a new entry point (`js_entry` tag), please remember to restart `yarn dev` so
that it can identify the new entry point it needs to watch.
To build Webpack bundles like it's done in production environments, pleas use `yarn build`.
This command does not have a watch functionality, so it needs to be re-run every time you make
changes to javascript files bundled by Webpack.
For more information about JavaScript and Static Files, please see the
[Dimagi JavaScript Guide](https://commcare-hq.readthedocs.io/js-guide/README.html) on Read the Docs.
### Step 13: Running CommCare HQ
Make sure the required services are running (PostgreSQL, Redis, CouchDB, Kafka,
Elasticsearch).
```sh
./manage.py check_services
```
Some of the services listed there aren't necessary for very basic operation, but
it can give you a good idea of what's broken. If you're not running formplayer
in docker, it will of course fail. Don't worry about celery for now.
Then run the django server with the following command:
```sh
./manage.py runserver localhost:8000
```
You should now be able to load CommCare HQ in a browser at [http://localhost:8000](http://localhost:8000).
#### Troubleshooting Javascript Errors
If you can load the page, but either the styling is missing or you get JavaScript console errors trying to create an account,
try running the JavaScript set up steps again. In particular you may need to run:
```sh
yarn install --frozen-lockfile
./manage.py compilejsi18n
./manage.py fix_less_imports_collectstatic
```
See the [Dimagi JavaScript Guide](https://commcare-hq.readthedocs.io/js-guide/README.html) for additional
useful background and tips.
## Running Formplayer and submitting data with Web Apps
Formplayer is a Java service that allows us to use applications on the web instead of on a mobile device.
In `localsettings.py`:
```python
FORMPLAYER_URL = 'http://localhost:8080'
FORMPLAYER_INTERNAL_AUTH_KEY = "secretkey"
LOCAL_APPS += ('django_extensions',)
```
**IMPORTANT:** When running HQ, be sure to use `runserver_plus`
```sh
./manage.py runserver_plus localhost:8000
```
Then you need to have Formplayer running.
### Running `formplayer` Outside of Docker
#### Prerequisites
Before running Formplayer, you need to [initialize the formplayer database](https://github.com/dimagi/formplayer#building-and-running).
The password for the "commcarehq" user is in the localsettings.py file in the
`DATABASES` dictionary.
```sh
createdb formplayer -U commcarehq -h localhost
```
#### Installation
The fastest way to get Formplayer running outside of docker is to download the
`application.properties` and `formplayer.jar` files and run it directly. You may
download and run these in the commcare-hq repo root (these files are excluded
from git in the `.gitignore` file).
```sh
curl https://raw.githubusercontent.com/dimagi/formplayer/master/config/application.example.properties -o application.properties
curl https://s3.amazonaws.com/dimagi-formplayer-jars/latest-successful/formplayer.jar -o formplayer.jar
```
Thereafter, to run Formplayer, navigate to the dir where you installed them
above (probably the repo root), and run:
```sh
java -jar ./formplayer.jar
```
This starts a process in the foreground, so you'll need to keep it open as long
as you plan on using Formplayer.
To keep Formplayer up to date with the version used in production, you can add
the `curl` commands above to your `hammer` command (see [hammer.sh](scripts/hammer.sh)),
or whatever script you use for updating your dev environment.
## Running CommCare HQ's supporting jobs
The following additional processes are required for certain parts of the application to work.
They can each be run in separate terminals:
### Running Pillowtop
Pillowtop is used to keep elasticsearch indices and configurable reports in sync.
> **Note**
> Typically, you won't need to run these in a dev environment unless you are testing them.
> It is simpler to run the 'reindex' command to update ES with local changes when needed.
> See [Populate Elasticsearch](#step-6--populate-elasticsearch)
**Mac OS:** `run_ptop` Will likely not work for you.
See the [Supplementary Guide](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP_MAC.md) for help.
It can be run as follows:
```sh
./manage.py run_ptop --all --processor-chunk-size=1
```
You can also run individual pillows with the following command
(Pillow names can be found in `settings.py`):
```sh
./manage.py run_ptop --pillow-name=CaseSearchToElasticsearchPillow --processor-chunk-size=1
```
Alternatively, you can not run pillowtop and instead manually sync ES indices when needed, by calling the `ptop_reindexer_v2` command.
See the command help for details, but it can be used to sync individual indices like this:
```sh
./manage.py ptop_reindexer_v2 user --reset
```
### Running Celery
Celery is used for background jobs and scheduled tasks.
You can avoid running it by setting `CELERY_TASK_ALWAYS_EAGER=False` in your `localsettings.py`,
though some parts of HQ (especially those involving file uploads and exports) require running it.
This can be done using:
```sh
celery -A corehq worker -l info
```
This will run a worker that will listen to default queue which is named celery.
You may need to add a `-Q` argument based on the queue you want to listen to.
For example, to use case importer with celery locally you need to run:
```sh
celery -A corehq worker -l info -Q case_import_queue
```
You can also run multiple queues on a single worker by passing multiple queue names separated by `,`
```sh
celery -A corehq worker -l info -Q case_import_queue,background_queue
```
If you want to run periodic tasks you would need to start `beat` service along with celery by running
```sh
celery -A corehq beat
```
## Running Formdesigner (Vellum) in Development mode
By default, HQ uses Vellum minified build files to render form-designer. To use
files from Vellum directly, do the following.
- Make Django expect the Vellum source code in the `submodules/formdesigner`
directory instead of using a minified build by enabling Vellum "dev mode" in
`localsettings.py`:
```python
VELLUM_DEBUG = True
```
- Clone (or symlink to repo elsewhere on disk) the Vellum repostory into/at the
`submodules/formdesigner` directory under the commcare-hq repo root:
```sh
# clone directly:
git clone git@github.com:dimagi/Vellum.git ./submodules/formdesigner
# -- OR --
# symlink existing Vellum code at submodules/formdesigner
ln -s absolute/path/to/Vellum ./submodules/formdesigner
```
## Running Tests
To run the standard tests for CommCare HQ, run
```sh
pytest
```
These may not all pass in a local environment. It's often more practical, and
faster, to just run tests for the django app where you're working.
To run a particular test or subset of tests:
```sh
pytest <test/file/path.py>[::<TestClass>[::<test_name>]]
# examples
pytest corehq/apps/app_manager
pytest corehq/apps/app_manager/tests/test_suite.py::SuiteTest
pytest corehq/apps/app_manager/tests/test_suite.py::SuiteTest::test_printing
```
To use the `pdb` debugger in tests, include the `s` flag:
```sh
pytest -s <test/file/path.py>[::<TestClass>[::<test_name>]]
```
If database tests are failing because of a `permission denied` error, give your
Postgres user permissions to create a database.
In the Postgres shell, run the following as a superuser:
```sql
ALTER USER commcarehq CREATEDB;
```
If you are on arm64 architecture using a non-Dimagi Docker Postgres image:
- If you run into a missing "hashlib.control" or "plproxy.control" file while trying to run tests, it is because you are not using the Dimagi Postgres Docker image that includes the pghashlib and plproxy extensions. You will need to change the USE_PARTITIONED_DATABASE variable in your localsettings.py to False so that you won't shard your test database and need the extensions
```
USE_PARTITIONED_DATABASE = False
```
### REUSE DB
To avoid having to run the database setup for each test run you can specify the
`REUSE_DB` environment variable which will use an existing test database if one
exists:
```sh
REUSE_DB=1 pytest corehq/apps/app_manager
```
Or, to drop the current test DB and create a fresh one
```sh
pytest corehq/apps/app_manager --reusedb=reset
```
See `corehq.tests.pytest_plugins.reusedb` ([source](corehq/tests/pytest_plugins/reusedb.py))
for full description of `REUSE_DB` and `--reusedb`.
### Accessing the test shell and database
The `CCHQ_TESTING` environment variable allows you to run management commands in
the context of your test environment rather than your dev environment.
This is most useful for shell or direct database access:
```sh
CCHQ_TESTING=1 ./manage.py dbshell
CCHQ_TESTING=1 ./manage.py shell
```
### Deprecation warnings
Deprecation warnings are converted to errors when running tests unless the
warning has been whitelisted (or unless `PYTHONWARNINGS` is set with a value
that does not convert warnings to errors, more below). The warnings whitelist
can be found in `corehq/warnings.py`.
The `CCHQ_STRICT_WARNINGS` environment variable can be used to convert
non-whitelisted deprecation warnings into errors for all management commands
(in addition to when running tests). It is recommended to add this to your bash
profile or wherever you set environment variables for your shell:
```sh
export CCHQ_STRICT_WARNINGS=1
```
If you don't want strict warnings, but do want to ignore (or perform some other
action on) whitelisted warnings you can use the `CCHQ_WHITELISTED_WARNINGS`
environment variable instead. `CCHQ_WHITELISTED_WARNINGS` accepts any of the
[`PYTHONWARNINGS`](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONWARNINGS)
action values (`ignore`, `default`, `error`, etc).
```sh
export CCHQ_WHITELISTED_WARNINGS=ignore
```
`CCHQ_WHITELISTED_WARNINGS=ignore` is implied when `CCHQ_STRICT_WARNINGS=1` is
set.
If `PYTHONWARNINGS` is set it may override the default behavior of converting
warnings to errors. This allows additional local configuration of personal
whitelist entries, for example. Ensure there is an "error" item as the first
entry in the value to preserve the default behavior of converting warnings to
errors. For example:
```sh
export PYTHONWARNINGS='
error,
ignore:Using or importing the ABCs::kombu.utils.functional,
ignore:Using or importing the ABCs::celery.utils.text,
ignore:the imp module is deprecated::celery.utils.imports,
ignore:unclosed:ResourceWarning'
```
Personal whitelist items may also be added in localsettings.py.
### Running tests by marker
You can run all tests with a certain marker as follows:
```sh
pytest -m MARKER
```
Available markers:
- slow: especially slow tests
- sharded: tests that should get run on the sharded test runner
- es_test: Elasticsearch tests
See https://docs.pytest.org/en/stable/example/markers.html for more details.
### Running on DB tests or Non-DB tests
```sh
# only run tests that extend TestCase
pytest --db=only
# skip all tests that extend TestCase but run everything else
pytest --db=skip
```
### Running only failed tests
See https://docs.pytest.org/en/stable/how-to/cache.html
## Javascript tests
### Setup
Make sure javascript packages are installed with the following. Please see the
section on installing `yarn` above for more details.
It's recommended to install grunt globally (with `yarn`) in order to use grunt
from the command line:
```sh
yarn global add grunt
yarn global add grunt-cli
```
You'll then need to add the yarn bin folder to your path:
```sh
export PATH="$(yarn global bin):$PATH"
```
More information can be found [here](https://classic.yarnpkg.com/en/docs/cli/global/)
In order for the tests to run the **development server needs to be running on port 8000**.
### Running tests from the command line
To run all JavaScript tests in all the apps:
```sh
grunt test
```
To run the JavaScript tests for a particular app run:
```sh
grunt test:<app_name> # (e.g. grunt test:app_manager)
```
To list all the apps available to run:
```sh
grunt list
```
### Running tests from the browser
To run tests from the browser (useful for debugging) visit this url:
```
http://localhost:8000/mocha/<app_name>
```
Occasionally you will see an app specified with a `#`, like `app_manager#b3`.
The string after `#` specifies that the test uses an alternate configuration. To
visit this suite in the browser go to:
```
http://localhost:8000/mocha/<app_name>/<config>
```
For example:
```
http://localhost:8000/mocha/app_manager/b3
```
### Measuring test coverage
To generate a JavaScript test coverage report, ensure the development server is
active on port 8000 and run:
```sh
./scripts/coverage-js.sh
```
This script goes through the steps to prepare a report for test coverage of
JavaScript files _that are touched by tests_, i.e., apps and files with 0% test
coverage will not be shown. A coverage summary is output to the terminal and a
detailed html report is generated at ``coverage-js/index.html``.
================================================
FILE: DEV_SETUP_MAC.md
================================================
# Supplementary Guide for Developers Running CommCare HQ on MacOS
## Prerequisites
- You will need `brew` aka [Homebrew](https://brew.sh) for package management.
- First, install [uv](https://docs.astral.sh/uv/) to manage Python versions and virtualenvs.
```sh
brew install uv
```
To create a new HQ virtual environment, you can do the following:
```sh
uv venv
```
Then to enter the environment:
```sh
source .venv/bin/activate
```
- Java (JDK 17)
We recommend using `sdkman` as a Java environment manager. `jenv` is also an option, though more involved.
- Example setup using `sdkman`:
1. [Install sdkman](https://sdkman.io/install)
On macOS, the default shell (Zsh) and the outdated system Bash can cause the standard installer to fail. Use this command to ensure a compatible installation:
```sh
# Run the installer with Zsh-compatible pattern matching
curl -s "https://get.sdkman.io" | zsh -o NO_NOMATCH
# Initialize SDKMAN! in your current session
source "$HOME/.sdkman/bin/sdkman-init.sh"
```
2. List available java versions to file one that matches Java (JDK 17)
```sh
sdk list java | grep 17
```
Look for Java 17 in the list and install, eg:
```sh
sdk install java 17.0.8-zulu
```
- Example setup using `jenv`:
1. Download and install [Java SE Development Kit 17][oracle_jdk17] from oracle.com downloads page.
2. Install `jenv`
```sh
brew install jenv
```
3. Configure your shell (Bash folks use `~/.bashrc` instead of `~/.zshrc` below):
```sh
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(jenv init -)"' >> ~/.zshrc
```
4. Add JDK 17 to `jenv`:
```sh
jenv add $(/usr/libexec/java_home)
```
5. Verify `jenv` config:
```sh
jenv doctor
```
[oracle_jdk17]: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html
## Issues With `uv sync`
- `psycopg2` may complain
As of Mac OS 11.x Big Sur, the solution for this is:
```sh
brew install libpq --build-from-source
export LDFLAGS="-L/opt/homebrew/opt/libpq/lib"
uv pip install psycopg2-binary
```
Or try: ([reference](https://rogulski.it/blog/install-psycopg2-on-apple-m1/)). Used on Mac OS 12.X Monterey.
```sh
export LDFLAGS="-L/opt/homebrew/opt/openssl@1.1/lib"
export CPPFLAGS="-I/opt/homebrew/opt/openssl@1.1/include"
```
- `uv pip install xmlsec` gives `ImportError`
Due to issues with recent versions of `libxmlsec1` (v1.3 and after) `uv pip install xmlsec` may be broken.
This is a workaround. This solution also assumes your `homebrew` version is greater than `4.0.13`*:
1. run `brew unlink libxmlsec1`
2. overwrite the contents of `/opt/homebrew/opt/libxmlsec1/.brew/libxmlsec1.rb` with
[this formula](https://raw.githubusercontent.com/Homebrew/homebrew-core/7f35e6ede954326a10949891af2dba47bbe1fc17/Formula/libxmlsec1.rb).
3. install that formula (`brew install /opt/homebrew/opt/libxmlsec1/.brew/libxmlsec1.rb`)
4. run `uv pip install xmlsec`
(*)The path to `libxmlsec1.rb` might differ on older versions of homebrew
If it still won't install, this [answer](https://stackoverflow.com/questions/76005401/cant-install-xmlsec-via-pip)
and [thread](https://github.com/xmlsec/python-xmlsec/issues/254) are good starting points for further diagnosing the issue.
### M1 Issues
- `pynacl` will likely install but may throw an error `symbol not found in flat namespace '_ffi_prep_closure'` when attempting to run, particularly when setting up CommCare-Cloud.
This can be fixed by installing a version of `pynacl` specific to the system architecture:
```sh
arch -arm64 uv pip install --upgrade --force-reinstall pynacl
```
## Docker
Docker images that will not run on Mac OS (Intel or M1):
- `formplayer` (See section on Running Formplayer Outside of Docker in the [Main Developer Setup Guide](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP.md))
Docker images that may not run on Mac OS (as of 11.x Big Sur and above):
- `elasticsearch6` (Image is not optimized for arm but can run on apple silicon)
### M1 (OS 11.x and above) Recommended Docker Up Command
```sh
./scripts/docker up -d postgres couch redis zookeeper kafka minio
```
Note: `kafka` will be very cranky on start up. You might have to restart it if you see `kafka` errors.
```sh
./scripts/docker restart kafka
```
### Installing and running Elasticsearch 6.8.23 outside of Docker
First, ensure that you have Java 17 running. `java -version` should output something like `openjdk version "17.0.7" 2023-04-18 LTS"`.
Use `sdkman` or `jenv` to manage your local java versions.
Download the `tar` file for elasticsearch 6.8.23
```sh
curl https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.8.23.tar.gz --output elasticsearch-6.8.23.tar.gz
```
Un-tar and put the folder somewhere you can find it. Take note of that path (`pwd`) and add the following to your `~/.zshrc`:
```sh
tar -xvzf elasticsearch-6.8.23.tar.gz
```
```sh
export PATH="/path/to/elasticsearch-6.8.23/bin:$PATH"
```
NOTE: Make sure that `/path/to` is replaced with the actual path!
You would need to update couple of setting in order to make elasticsearch run on your mac.
Change into elasticsearch directory
```sh
cd /path/to/elasticsearch-6.8.23
```
- In `config/jvm.options`, comment out `10-:-XX:UseAVX=2`
```sh
sed -i '' '/10-:-XX:UseAVX=2/ s/^/# /' config/jvm.options
```
- In `config/elasticsearch.yml`, add xpack.ml.enabled: false
```sh
echo "xpack.ml.enabled: false" | sudo tee -a config/elasticsearch.yml
```
After this you can open a new terminal window and run elasticsearch with `elasticsearch`.
#### Install Elasticsearch plugins
Now that you have Elasticsearch running you will need to install the necessary plugins:
1. Install the plugin
```sh
elasticsearch-plugin install analysis-phonetic
```
(If the `plugin` command is not found you will need to use the full path `<es home>/bin/plugin`).
2. Restart the service
3. Verify the plugin was correctly installed
```sh
curl "localhost:9200/_cat/plugins?s=component&h=component,version"
> analysis-phonetic 6.8.23
```
## Refreshing data in `elasticsearch` manually (alternative to `run_ptop`)
FYI, be sure to check out the [FAQ on elasticsearch](https://github.com/dimagi/commcare-hq/blob/master/DEV_FAQ.md#elasticsearch).
To refresh specific indices in elasticsearch you can do the following...
First make sure everything is up-to-date
```sh
./manage.py ptop_preindex --reset
./manage.py preindex_everything
```
Force a re-index of `forms` and `cases`:
```sh
./manage.py ptop_reindexer_v2 sql-case --reset
./manage.py ptop_reindexer_v2 sql-form --reset
```
For other indices see `./manage.py ptop_reindexer_v2 --help`
================================================
FILE: Dockerfile
================================================
# syntax=docker/dockerfile:1
# This Dockerfile is built as the `dimagi/commcarehq-pyX.Y` image, where X.Y
# is the version in .python-version, and which is used for running tests.
FROM ghcr.io/astral-sh/uv:0.7.17-python3.13-bookworm-slim
LABEL org.opencontainers.image.authors="Dimagi <devops@dimagi.com>"
ENV PYTHONUNBUFFERED=1 \
PYTHONUSERBASE=/vendor \
PATH=/vendor/bin:$PATH \
NODE_VERSION=24.11.1 \
# Compile bytecode during installation to improve startup time. Also
# suppresses a couchdbkit syntax error that happens during bytecode
# compilation.
UV_COMPILE_BYTECODE=1 \
# Copy from the cache instead of linking since it's a mounted volume
UV_LINK_MODE=copy \
UV_PROJECT_ENVIRONMENT=/vendor
RUN mkdir /vendor
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl gnupg \
&& curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
# allows uv to build dependencies; increases image size by 240 MB
build-essential \
bzip2 \
default-jre \
gettext \
git \
google-chrome-stable \
libmagic1 \
libpq5 \
# for xmlsec on Python 3.13
libxml2 libxmlsec1 libxmlsec1-openssl \
make \
&& rm -rf /var/lib/apt/lists/* /src/*.deb
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" \
&& tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.gz"
COPY .python-version pyproject.toml uv.lock /tmp-project/
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --group=test --no-dev --project=/tmp-project --no-install-project \
&& rm -rf /tmp-project
# this keeps the image size down, make sure to set in mocha-headless-chrome options
# executablePath: 'google-chrome-stable'
ENV PUPPETEER_SKIP_DOWNLOAD=true
COPY package.json /vendor/
RUN npm -g install \
yarn \
bower \
grunt-cli \
uglify-js \
puppeteer \
mocha-headless-chrome \
sass \
&& cd /vendor \
&& npm shrinkwrap \
&& yarn global add phantomjs-prebuilt
================================================
FILE: Dockerfile_incl
================================================
# syntax=docker/dockerfile:1
# Dockerfile_incl is built as the `commcarehq_incl` image, and is used
# for running a simple but complete CommCare HQ environment that
# includes Pillowtop and Celery containers.
FROM ghcr.io/astral-sh/uv:0.7.17-python3.13-bookworm-slim
LABEL org.opencontainers.image.authors="Dimagi <devops@dimagi.com>"
ENV PYTHONUNBUFFERED=1 \
PYTHONUSERBASE=/vendor \
PATH=/vendor/bin:$PATH \
NODE_VERSION=24.11.1 \
# Compile bytecode during installation to improve startup time. Also
# suppresses a couchdbkit syntax error that happens during bytecode
# compilation.
UV_COMPILE_BYTECODE=1 \
# Copy from the cache instead of linking since it's a mounted volume
UV_LINK_MODE=copy \
UV_PROJECT_ENVIRONMENT=/vendor
RUN mkdir /vendor
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl gnupg \
&& curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
&& sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
# allows uv to build dependencies; increases image size by 240 MB
build-essential \
bzip2 \
default-jre \
gettext \
git \
google-chrome-stable \
libmagic1 \
libpq5 \
# for xmlsec on Python 3.13
libxml2 libxmlsec1 libxmlsec1-openssl \
make \
&& rm -rf /var/lib/apt/lists/* /src/*.deb
# Deletes all package sources, so don't apt-get install anything after this:
RUN rm -rf /var/lib/apt/lists/* /src/*.deb
# Install Node
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz" && \
tar -xzf "node-v$NODE_VERSION-linux-x64.tar.gz" -C /usr/local --strip-components=1 && \
rm "node-v$NODE_VERSION-linux-x64.tar.gz"
RUN npm -g install \
yarn \
bower \
grunt-cli \
uglify-js
# prefer https for git checkouts made by pip
RUN git config --global url."https://".insteadOf git://
WORKDIR /vendor
# setup venv
COPY .python-version pyproject.toml uv.lock /tmp-project/
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --group=prod --no-dev --project=/tmp-project --no-install-project \
&& rm -rf /tmp-project
RUN mkdir /sharedfiles
RUN groupadd -r cchq && \
useradd -r -m -g cchq cchq && \
chown cchq:cchq /vendor /sharedfiles
USER cchq:cchq
COPY --chown=cchq:cchq package.json /vendor/
RUN npm shrinkwrap && \
yarn global add phantomjs-prebuilt
COPY --chown=cchq:cchq . /vendor/
COPY --chown=cchq:cchq docker/min_settings.py /vendor/localsettings.py
RUN mkdir /vendor/log
RUN python manage.py collectstatic --noinput
================================================
FILE: Gruntfile.js
================================================
/* globals module, process, require */
module.exports = function (grunt) {
var headless = require('mocha-headless-chrome'),
_ = require('lodash'),
fs = require('fs');
// use localhost unless we're running on travis
var BASE_ADDRESS = process.env.WEB_TEST_PORT_8000_TCP_ADDR || 'localhost',
BASE_URL = 'http://' + BASE_ADDRESS + ':8000/mocha/';
/*
* To run all tests:
* grunt test
*
* To run a single test:
* grunt test:<app>
*
* To add a new app to test:
* - Add the app name to this list
* - Create a test runner view at corehq/apps/<app>/templates/<app>/spec/mocha.html
* - Test in the browser at http://localhost:8000/mocha/<app>
*
* To add a new section to an existing app:
* - Add <app>/<section> to this list
* - Create a test runner view at corehq/apps/<app>/templates/<app>/spec/<section>/mocha.html
* - Test in the browser at http://localhost:8000/mocha/<app>/<section>
*/
var apps = [
'app_manager/bootstrap3',
'app_manager/bootstrap5',
'export',
'notifications/bootstrap3',
'notifications/bootstrap5',
'reports_core/choiceListUtils',
'locations',
'userreports/bootstrap3',
'userreports/bootstrap5',
'cloudcare',
'cloudcare/form_entry',
'hqwebapp/bootstrap3',
'hqwebapp/bootstrap5',
'hqwebapp/components',
'case_importer',
];
var extensions = _.split(process.env.JS_TEST_EXTENSIONS || '', ','),
testPaths = _.filter(_.concat(apps, extensions), function (path) { return path !== ''; });
var runTest = function (queuedTests, taskPromise, finishedTests, failures) {
if (finishedTests === undefined) {
finishedTests = [];
failures = {};
}
if (queuedTests.length === 0) {
if (!_.isEmpty(failures)) {
printFailures(failures);
grunt.fail.fatal("Javascript tests failed.");
}
taskPromise();
return;
}
var currentApp = queuedTests[0],
currentTestPath = BASE_URL + currentApp,
testText = "Running Test '" + currentApp + "'",
reporter = grunt.option('verbose') ? 'spec' : 'dot',
runnerOptions = {
file: currentTestPath,
visible: false,
timeout: 120000,
reporter: reporter,
};
// For running in docker
if (process.env.PUPPETEER_SKIP_DOWNLOAD) {
runnerOptions.executablePath = 'google-chrome-stable';
}
grunt.log.writeln("\n");
grunt.log.writeln(testText.bold);
grunt.log.write(currentTestPath.italic.cyan);
headless.runner(runnerOptions).then(function (data) {
if (data.result.failures.length) {
failures[currentApp] = data.result.failures;
}
if (grunt.option('coverage')) {
var coverageDir = './coverage-js/',
filePath = coverageDir + currentApp.replace(/\//g, '-') + '.json';
if (!fs.existsSync(coverageDir)) {
fs.mkdir(coverageDir, error =>
error && grunt.log.write(error));
}
fs.writeFile(filePath, JSON.stringify(data.coverage), { flag: 'w+' }, error =>
error && grunt.log.write(error),
);
}
finishedTests.push(currentApp);
runTest(
_.without(queuedTests, currentApp),
taskPromise,
finishedTests,
failures,
);
});
};
var printFailures = function (failures) {
grunt.log.writeln("\n");
var numFailures = _.flatten(_.values(failures)).length + " test(s) failed.";
grunt.log.writeln(numFailures.bold.red);
_.forIn(failures, function (errors, appName) {
grunt.log.writeln("\n");
var failSummary = " has " + errors.length + " error(s)";
grunt.log.write(appName.bold);
grunt.log.writeln(failSummary.bold.red);
_.each(errors, function (error) {
grunt.log.write("\nERROR: ".bold.red);
grunt.log.writeln(error.fullTitle);
grunt.log.error(error.err.stack);
grunt.log.write("\n");
});
});
};
grunt.task.registerTask(
'test',
'Runs Javascript Tests. Pass in an argument to run a specific test',
function (arg) {
var paths = testPaths;
if (arg) {
paths = [arg];
}
var testStatement = "Running tests: " + paths.join(', ');
grunt.log.writeln(testStatement.bold.green);
runTest(paths, this.async());
},
);
grunt.registerTask('list', 'Lists all available apps to test', function () {
testPaths.forEach(function (app) { console.log('"' + app + '"'); });
});
};
================================================
FILE: LICENSE
================================================
BSD 3-Clause License
Copyright (c) 2009-2020, Dimagi Inc., and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: Makefile
================================================
.PHONY: all requirements upgrade-requirements docs migrations.lock serializer-pickle-files.lock translations
all: requirements serializer-pickle-files.lock translations
requirements:
cd requirements && $(MAKE) requirements
upgrade-requirements:
cd requirements && $(MAKE) upgrade-requirements
docs:
cd docs && $(MAKE) html; cd -
serializer-pickle-files.lock:
./scripts/make-serializer-pickle-files
translations:
./scripts/make-translations.sh
================================================
FILE: README.md
================================================
CommCare HQ
===========
CommCare HQ is a web application for building complex, customizable, frontline worker solutions.
It includes multi-tenant application building, user management, data collection, and reporting.
CommCare HQ apps work with [CommCare mobile](https://github.com/dimagi/commcare-android) and its
bundled [web application platform](https://github.com/dimagi/formplayer).
### Key Components
+ CommCare application builder
+ OpenRosa compliant XForms designer
+ SMS integration
+ Domain/user/mobile worker management
+ XForms data collection
+ Case management
+ Over-the-air (ota) restore of user and cases
+ Integrated web and email reporting
### More Information
+ To try CommCare you can use [this production instance of hosted CommCare](https://www.commcarehq.org/).
+ For setting up a local CommCare HQ developer environment, see [Setting up CommCare HQ for Developers](https://github.com/dimagi/commcare-hq/blob/master/DEV_SETUP.md).
+ For setting up a production CommCare HQ environment, check out [CommCare Cloud](https://commcare-cloud.readthedocs.io/), our toolkit for deploying and maintaining CommCare servers.
+ Additional documentation is available on [ReadTheDocs](https://commcare-hq.readthedocs.io/).
+ We welcome contributions, see [Contributing](CONTRIBUTING.rst) for more.
+ Questions? Contact the CommCare community at our [forum](https://forum.dimagi.com/).
*CommCare is built by [Dimagi](https://dimagi.com/) and our open-source contributors.*
================================================
FILE: STANDARDS.rst
================================================
====================================
Project Standards and Best Practices
====================================
This document is a summation of some of the standards and best practices that are followed in the design and implementation of the project to provide guidance and clarity for `implementers and contributors`_.
These standards are presented for clarity and convenience (to avoid arguments about style or judgement and document reasoning) but they are not intended to be comprehensive. Code reviewers will expect that industry-wide best practices are followed in addition to any specifically outlined concepts which are covered in this document.
========
Security
========
The project's baseline security practices should be based on the latest recommendations from the Open Web Security Project (OWASP), which provides practical and evidence based standards for software implementers. If there is a question in approach on system design and OWASP has a stated position on the best approach, it should be expected that the OWASP sanctioned approach will be adopted.
In addition, the project follows the following specific security practices.
Security Protocols and Cryptography Implementation
--------------------------------------------------
The project strives for a high standard of technical and practical security, but it is not the intended purpose of the project to provide a novel technical approach to security. Since there is an overwhelming consensus from security researchers and practitioners[1]_,[2]_ that it is a harmful practice for software systems to attempt to implement their own unique security or cryptographic protocols, no such implementations will be adopted by the project.
Specifically, components of the project which externally authenticate users or secure data cryptographically will be:
- Based on a publicly documented specification or approach (Basic Authentication, OAuth, SAML, AES256, etc)
- In broad use in modern software with a practical basis of demonstrated success
- Adopted through the use of externally implemented libraries or dependencies whenever practically possible
This expectation does not necessarily extend to code which is required for integrations which authenticate against external systems. Those implementations should always follow this approach when possible, and best practices should be maintained when managing secrets for such components.
.. [1] Jakob Jakobsen and Claudio Orlandi "On the CCA (in)security of MTProto." *Cryptology ePrint Archive, Report 2015/1177*
.. [2] Philipp Jovanovic and Samuel Neves "Dumb Crypto in Smart Grids: Practical Cryptanalysis of the Open Smart Grid Protocol." *Cryptology ePrint Archive, Report 2015/428*
.. _implementers and contributors: CONTRIBUTING.rst
================================================
FILE: codecov.yml
================================================
# https://docs.codecov.com/docs/codecovyml-reference
coverage:
# https://docs.codecov.com/docs/commit-status
status:
project:
default:
informational: true # don't block PRs
patch:
default:
informational: true # don't block PRs
github_checks:
annotations: false
# https://docs.codecov.com/docs/pull-request-comments
comment: false # Disable the PR comment, show only github check
================================================
FILE: corehq/README.rst
================================================
corehq
############################
A few broad areas of functionality are stored directly in this directory.
apps
Most functionality lives in this directory. See README in this directory for details.
blobs
The blob db stores large pieces of binary data. It's where form XML, multimedia, exports, temporary files, etc. are stored.
celery_monitoring
Tools to monitor `Celery <https://docs.celeryproject.org/en/stable/>`_, which we use for async task processing.
couchapps
Certain couch views are stored here, instead of inside the relevant django app, because storing them separately
gives us performance benefits.
dbaccessors
Part of ``couchapps``
ex-submodules
Assorted functionality that used to live in separate repositories. See README in this directory for details.
extensions
Framework for extending HQ functionality in a fork-like way. Used to handle code specific to ICDS-CAS.
form_processor
Code to handle receiving, processing, and storing form submissions from CommCare mobile and Web Apps.
messaging
Code to manage direct-to-user messages in CommCare HQ, most often SMS but also channels like email and
whatsapp. Also see the ``sms`` and ``smsforms`` apps in ``corehq.apps``.
motech
MOTECH is CommCare HQ's integration layer, and allows HQ to forward data to
remote systems' APIs, to import data from them, and to follow workflows for
more complex integrations with systems like OpenMRS and DHIS2.
pillows
HQ-specific mappings that use the ``pillowtop`` framework in ``ex-submodules``.
preindex
Code for handling preindexing, which updates CouchDB views and ElasticSearch indices.
Preindex is run as part of deploy and can also be run ad hoc.
privileges.py
Privileges allow HQ to limit functionality based on software plan.
project_limits
Framework for throttling actions like form submissions on a domain-specific basis.
sql_accessors
Stores custom postgres functions.
sql_db
Code related to partitioning postgres.
sql_proxy_accessors
Stores custom postgres functions that use `PL/Proxy <https://plproxy.github.io/>`_.
sql_proxy_standby_accessors
Stores custom postgres functions relevant to standby servers.
tabs
Menu structure for CommCare HQ.
tests
Contains a few tests for high-level functionality like locks, as well as tooling to run tests with
`pytest <https://docs.pytest.org/en/stable/>`_.
toggles.py
Toggles allow limiting functionality based on user or domain. Also see ``ex-submodules/toggle`` and ``corehq.apps.toggle_ui``.
util
Miscellaneous utilities. Also see ``ex-submodules/dimagi``.
================================================
FILE: corehq/__init__.py
================================================
# This must not import any module that performs app initialization on
# import since it is loaded by manage.py very early during startup as
# a side effect of importing other modules in the package.
#
# Startup logic should be invoked in a Django `AppConfig`, in the
# `main()` method of manage.py, and/or in `corehq.celery` for
# celery processes.
import os
def _get_current_app():
# HACK Celery 4.1 default/current app mechanism is broken when app
# initilaization is not done as an import side-effect. It either
# creates a new app when the current app is first requested (which
# is broken because then tasks get hooked to the wrong app), or
# (with C_STRICT_APP) it unconditionally raises an error when the
# current app is accessed. Why is there no way to configure it to
# raise an error if the current app is accessed before it is set,
# but not thereafter?
#
# Eliminating the thread-local current app hooey should be fine.
# https://github.com/celery/celery/blob/ab3231dea14501c0159d3caa1fcf83689eb6db2d/celery/app/trace.py#L673-L680
app = _state.default_app
if app is None:
raise RuntimeError("""Celery is not initialized.
If you are seeing this error it probably means you have imported
something that depends on the Celery app before initializing it.
Adjust imports and/or INSTALLED_APPS (see corehq.apps.celery).
""")
return app
# Monkey patch Celery. The app will be initialized in
# corehq.apps.celery._init_celery_app during Django setup.
os.environ.setdefault("C_STRICT_APP", "1")
from celery import _state # noqa: E402
if _state.default_app is not None:
# Reset default app, which can be initialized by gevent monkey
# patching, which traverses gc.get_objects() and calls
# isinstance(...) on each object.
_state.set_default_app(None)
assert _state._tls.current_app is None, "Current app already created"
assert hasattr(_state.current_app, "_Proxy__local")
object.__setattr__(_state.current_app, "_Proxy__local", _get_current_app)
_state.get_current_app = _get_current_app
================================================
FILE: corehq/apps/README.rst
================================================
Django Apps in CommCare HQ
##########################
Most CommCare HQ functionality is contained in a django app.
A few areas are contained in ``corehq`` or ``corehq/ex-submodules``,
so for a full overview, check those READMEs as well.
Primary Apps
^^^^^^^^^^^^
These apps are major parts of the system and most have frequent, active development.
accounting
Billing functionality: accounts, subscriptions, software plans, etc.
This includes the UI for internal operations users to modify these objects.
Accessing this UI requires running the ``add_operations_user`` command.
api
APIs to read and write CommCare data for a given project space. Most APIs are externally facing. However, there
are a few that are used internally e.g. for report filters. APIs are built using Tastypie.
app_manager
UI and tooling for configuring and releasing CommCare applications.
Form Builder, for configuring forms themselves, is called here but
is primarily stored in the separate `Vellum <https://github.com/dimagi/Vellum/>`_ repo.
auditcare
A couch-based set of auditing tools. All page views in CommCare HQ are recorded in auditcare.
This backs the User Audit Log report, which allows admins to view a given user's historical actions.
Doing non-user-based queries is prohibitively slow.
celery
A Django app that initializes the default/current Celery app during Django
setup.
cloudcare
Web Apps, a web-based interface for data entry, with essentially the same functionality
as CommCare Mobile, but available via HQ to both web and mobile users. This app contains the HQ
parts of this code, which interfaces with `Formplayer <https://github.com/dimagi/formplayer/>`_
to ultimately run functionality in the `commcare-core <https://github.com/dimagi/commcare-core/>`_
repo, which is shared with CommCare Mobile.
domain
Domains, called "project spaces" in user-facing content, are the core sandboxes of CommCare. Almost
all features and UIs in CommCare are in the context of a domain.
experiments
Framework for comparing old and new code paths.
export
Configurations for exporting project data, typically forms or cases, to an excel download.
hqmedia
Multimedia handling, primarily used in applications.
hqwebapp
Core UI code for the HQ site. Includes things like the standard error pages,
javascript widgets, login views, etc.
locations
Locations provide a hierarchical way to organize CommCare users and data.
ota
Contains a number of the API endpoints used by CommCare mobile including sync / restore, case search, case claim, heartbeat and recovery measures. This is used by both CommCare mobile and formplayer.
reports
Standard, pre-canned reports to view project data: Submission History, User Activity Report, etc.
reports_core
More reporting code that resulted from an attempt to re-architect the report class structure.
sms
Features to send SMS and emails via CommCare HQ. Much of the underlying code is in ``corehq.messaging``.
userreports
User-defined reports. Intertwined with other report-related apps.
users
Users of CommCare HQ and/or CommCare mobile. The primary class dealing with users is ``CouchUser``,
a representation of the user in couch. ``CouchUser`` has the subclasses ``WebUser`` and ``CommCareUser``
to distinguish between admin-type users who primarily use CommCare HQ and data entry users who primarily use
CommCare Mobile. The distinction between web and mobile users is blurry, especially with the advent of
Web Apps for data entry in HQ.
Secondary Apps
^^^^^^^^^^^^^^^^^^^^
These apps are maintained and updated regularly, but are a bit less core than the set above.
case_importer
Bulk import of cases.
custom_data_fields
This allows users to add arbitrary data to mobile users, locations, and products, which can then
be referenced in applications.
data_dictionary
The data dictionary documents a project's data model, specifically, its case types and case properties.
This makes it possible to reference those definitions throughout the app-building process without needing
to repeatedly re-parse it out of the application configuration.
The data dictionary is used partially for project documentation but is also referenced in a few other
parts of HQ: for example, when configuring case properties to be updated in a form, app manager will
pull the properties' descriptions from the data dictionary.
data_interfaces
Functionality for taking a few specific actions on project data, such as reassigning cases in bulk.
Most of this app relies heavily on standard reporting functionality.
enterprise
Enterprise related functionality lives here, with a few exceptions in enterprise permissions functionality in users and linked project spaces.
fixtures
The term "fixtures" comes from the `XML data model <https://github.com/dimagi/commcare-core/wiki/fixtures>`_ used to send custom structured data to the mobile devices (separate from case data). During the sync request with the mobile device, various different fixtures may be sent to the device including lookup tables, locations and mobile reports. This Django app only deals with the "lookup table" fixtures. It provides the UI for creating and editing them and the code to serialize them to XML.
groups
Users can be assigned to groups for the purposes of sharing cases within a group and for reporting purposes.
hqadmin
Internal admin functionality used by the development, support, QA, and product teams.
linked_domain
Functionality to share certain configuration data between domains: apps, lookup tables, report definitions, etc.
Work is done in a primary "upstream" domain, and then that domain's data models are copied to one or more
"downstream" domains. This is most often used to set up a development => production workflow, where changes are made
in the development domain and then pushed to the production domain, where real project data is entered.
Linked domains are also used by certain enterprise-type projects that manage one program across multiple regions
and use a separate downstream domain for each region.
registration
Workflows for creating new accounts on HQ.
reminders
A subset of SMS/messaging, including functionality around incoming SMS keywords. "Reminders" is a leftover term from a previous iteration of the messaging framework.
saved_reports
Functionality to let users save a specific set of report filters and optionally run reports with those filters on a scheduled basis.
toggle_ui
Framework for feature flags, which are used to limit internal features to specific domains and/or users.
translations
Functionality for managing application translations, including integration with Transifex, which is used by a small number of projects.
user_importer
Bulk importing of users.
Tertiary Apps
^^^^^^^^^^^^^
These apps may be useful parts of the system but don't have as much active development as the groups above.
analytics
Integrations with third-party analytics tools such as Google Analytics.
Also contains internal product-focused tools such as AB testing functionality.
builds
Models relating to CommCare Mobile builds, so that app builders can control which mobile version their apps use.
case_search
Models and utils related to searching for cases using Elasticsearch. Used for Case Claim and the Case List Explorer.
dashboard
The tiled UI that acts as the main landing page for HQ.
formplayer_api
Functionality interacting with formplayer, primarily used by SMS surveys.
mobile_auth
Generates the XML needed to authorize mobile users.
notifications
"Banner" notifications used by the support team to notify users of upcoming downtime,
ongoing issues, etc.
receiverwrapper
Contains the API for receiving XML form submissions. This app mostly deals with the interfacing portion of the
API including auth, rate limiting etc. but not the actual data processing which is contained in the
`form_processor` app.
settings
API keys and 2FA functionality.
smsbillables
Billing functionality relating to charging for SMS, allowing us to pass carrier charges on to clients.
smsforms
SMS surveys allow end users to interact with a CommCare form via SMS instead of
via mobile or Web Apps. This is part of ``messaging``.
sso
Features related to Single Sign On.
styleguide
Documentation of best practices for UI development, including live examples of common patterns.
zapier
Integration with `Zapier <https://zapier.com/>`_
Engineering Apps
^^^^^^^^^^^^^^^^
These apps are developer-facing tools.
cachehq
Caching functinality for CouchDB.
change_feed
Infrastructure for propagating changes in primary data stores (couch, postgres) to secondary sources (ElasticSearch).
cleanup
Miscellaneous commands for cleaning up data: deleting duplicate mobile users, deleting couch documents for models that have been moved to postgres, etc.
data_analytics
Internal impact-related metrics.
data_pipeline_audit
Tools used to audit the async data pipeline (change feeds / pillows) to validate the integrity of secondary
sources (mostly Elasticsearch). These tools are not used routinely.
domain_migration_flags
Dynamic flags that are used to indicate when a data migration is taking place for a specific domain. The flags are
checked in various places throughout the code and will restrict access to certain features when enabled. These flags
are set during large data migrations such as moving case & form data from Couch -> SQL, migrating a domain to a
different CommCare instance.
dump_reload
Tools used to dump a domain's data to disk and reload it from disk. This is used to move a domain from one CommCare instance to another e.g. from a managed environment to self hosted environment.
es
Internal APIs for creating and running ElasticSearch queries.
hqcase
Utility functions for handling cases, such as the ability to programmatically submit cases.
mocha
JavaScript testing framework.
Limited-Use and Retired Apps
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
These apps are limited to a small set of clients or on a deprecation path.
appstore
The CommCare Exchange, a deprecated feature that allowed projects to publish their projects in a self-service manner
and download other organizations' projects. This process is now supported internally by the support team. The UI
portions of this app have been removed, but the data models are still necessary for the internal processes.
callcenter
The call center application setting allows an application to reference a mobile user as a case that can be monitored using CommCare. This allows supervisors to view their workforce within CommCare.
casegroups
Functionality around grouping cases in large projects and then taking action on those groups.
commtrack
CommCare Supply, a large and advanced set of functionality for using CommCare in logistics management.
consumption
Part of CommCare Supply.
dropbox
Functionality to allow users to download large HQ files to dropbox instead of their local machines. This is likely being deprecated.
integration
Various integrations with biometrics devices, third-party APIs, etc.
ivr
Functionality to allow users to fill out forms using interactive voice response. Largely deprecated.
products
Part of CommCare Supply.
programs
Part of CommCare Supply.
================================================
FILE: corehq/apps/__init__.py
================================================
================================================
FILE: corehq/apps/accounting/README.md
================================================
### Adding a new Privilege
To add a new `Privilege`
+ Make sure there are no existing Privileges that you can reuse
+ Add the new privilege in appropriate places in `privileges.py` according to software plans
+ Run `python manage.py makemigrations --empty accounting` to create a migration
+ Rename the migration file to something more meaningful. (From Django 1.8+ you can supply a name to the makemigrations command: --name <migration_name>)
+ Add the following operation to the list of operations:
```python
migrations.RunPython(cchq_prbac_bootstrap),
```
This will create a new `Privilege` for you to use. See (Django data migrations)[https://docs.djangoproject.com/en/1.8/topics/migrations/#data-migrations] for more information.
### Removing a deprecated Privilege
To remove an old `Privilege`
+ Remove occurrences of the desired Privilege from all the places.
+ Add the privilege to `cchq_prabac_bootstrap.Command.OLD_PRIVILEGES`
+ Run migration
This will clean up discontinued privileges.
================================================
FILE: corehq/apps/accounting/__init__.py
================================================
================================================
FILE: corehq/apps/accounting/admin.py
================================================
from django.contrib import admin
from .models import DomainUserHistory
@admin.register(DomainUserHistory)
class DomainUserHistoryAdmin(admin.ModelAdmin):
model = DomainUserHistory
list_display = ('domain', 'record_date', 'num_users')
list_filter = ('domain', 'record_date')
================================================
FILE: corehq/apps/accounting/async_handlers.py
================================================
import json
from django.conf import settings
from django.db.models import F, Q
from corehq.apps.accounting.models import (
BillingAccount,
BillingContactInfo,
Feature,
Invoice,
SoftwarePlan,
SoftwarePlanVersion,
SoftwareProductRate,
Subscriber,
Subscription,
CustomerInvoice,
)
from corehq.apps.accounting.utils import (
fmt_feature_rate_dict,
fmt_product_rate_dict,
)
from corehq.apps.domain.models import Domain
from corehq.apps.hqwebapp.async_handler import (
AsyncHandlerError,
BaseAsyncHandler,
)
from corehq.apps.hqwebapp.encoders import LazyEncoder
class BaseRateAsyncHandler(BaseAsyncHandler):
"""
Subclass this for interacting with RatesManager.
"""
allowed_actions = [
'apply',
'create',
]
@property
def name(self):
return self.data.get('name')
@property
def rate_type(self):
return self.data.get('rate_type')
@property
def rate_id(self):
return self.data.get('rate_id')
@property
def create_response(self):
raise NotImplementedError("create_response is required")
@property
def apply_response(self):
raise NotImplementedError("apply_response is required")
class FeatureRateAsyncHandler(BaseRateAsyncHandler):
slug = 'features_handler'
@property
def create_response(self):
if Feature.objects.filter(name=self.name).count() > 0:
raise AsyncHandlerError("Feature '%s' already exists, and likely already "
"in this Software Plan Version." % self.name)
new_feature, _ = Feature.objects.get_or_create(
name=self.name,
feature_type=self.rate_type,
)
return fmt_feature_rate_dict(new_feature)
@property
def apply_response(self):
try:
feature = Feature.objects.get(id=self.rate_id)
return fmt_feature_rate_dict(feature)
except Feature.DoesNotExist:
raise AsyncHandlerError("could not find an existing feature")
class SoftwareProductRateAsyncHandler(BaseRateAsyncHandler):
slug = 'products_handler'
@property
def create_response(self):
if SoftwareProductRate.objects.filter(name=self.name).exists():
raise AsyncHandlerError("Product rate '%s' already exists, and likely already "
"in this Software Plan Version." % self.name)
return fmt_product_rate_dict(self.name)
@property
def apply_response(self):
try:
product_rate = SoftwareProductRate.objects.get(id=self.rate_id)
return fmt_product_rate_dict(product_rate.name)
except SoftwareProductRate.DoesNotExist:
raise AsyncHandlerError("could not find an existing product rate")
class BaseSelect2AsyncHandler(BaseAsyncHandler):
@property
def search_string(self):
return self.data.get('searchString')
@property
def existing(self):
return self.data.getlist('existing[]')
def _fmt_success(self, response):
success = json.dumps({
'results': [{
'id': r[0],
'text': r[1],
} for r in response]
}, cls=LazyEncoder)
return success
class Select2RateAsyncHandler(BaseSelect2AsyncHandler):
"""
Handles the async responses for the select2 widget in the Features & Rates portion
of the SoftwarePlanVersion form.
"""
slug = 'select2_rate'
allowed_actions = [
'select2_feature_id',
'product_rate_id',
]
@property
def select2_feature_id_response(self):
features = Feature.objects
if self.existing:
features = features.exclude(name__in=self.existing)
if self.search_string:
features = features.filter(name__istartswith=self.search_string)
return [(f.id, f.name, f.feature_type) for f in features.all()]
@property
def product_rate_id_response(self):
product_rates = SoftwareProductRate.objects
if self.existing:
product_rates = product_rates.exclude(name__in=self.existing)
if self.search_string:
product_rates = product_rates.filter(name__istartswith=self.search_string)
return [(p.id, p.name) for p in product_rates.all()]
def _fmt_success(self, response):
def _result_from_response(r):
result = {
'id': r[0],
'name': r[1],
'isExisting': True,
}
if len(r) == 3:
result['rate_type'] = r[2]
result['text'] = '%s (%s)' % (r[1], r[2])
else:
result['text'] = '%s' % r[1]
return result
return json.dumps({
'results': [_result_from_response(r) for r in response]
})
class Select2BillingInfoHandler(BaseSelect2AsyncHandler):
slug = 'select2_billing'
allowed_actions = [
'country',
'active_accounts',
'domain',
'account',
'plan_version',
'new_plan_version',
]
@property
def country_response(self):
from django_countries.data import COUNTRIES
countries = sorted(list(COUNTRIES.items()), key=lambda x: x[1].encode('utf-8'))
if self.search_string:
search_string = self.search_string.lower()
return [x for x in countries if x[1].lower().startswith(search_string)]
return countries
@property
def active_accounts_response(self):
accounts = BillingAccount.objects.filter(is_active=True)
if self.search_string:
accounts = accounts.filter(name__icontains=self.search_string)
return [(a.id, a.name) for a in accounts]
@property
def domain_response(self):
domain_names = [domain['key'] for domain in Domain.get_all(include_docs=False)]
if self.search_string:
search_string = self.search_string.lower()
domain_names = [x for x in domain_names if x.lower().startswith(search_string)]
return [(name, name) for name in domain_names]
@property
def account_response(self):
accounts = BillingAccount.objects
if self.search_string:
accounts = accounts.filter(name__icontains=self.search_string)
return [(a.id, a.name) for a in accounts.order_by('name')]
@property
def plan_version_response(self):
edition = self.data.get('additionalData[edition]')
visibility = self.data.get('additionalData[visibility]')
is_most_recent_version = self.data.get('additionalData[most_recent_version]') == 'True'
if is_most_recent_version:
plan_versions = SoftwarePlanVersion.get_most_recent_version(edition, visibility)
else:
plan_versions = SoftwarePlanVersion.objects.all()
if edition:
plan_versions = plan_versions.filter(plan__edition=edition)
if visibility:
plan_versions = plan_versions.filter(plan__visibility=visibility)
if self.search_string:
plan_versions = plan_versions.filter(plan__name__icontains=self.search_string)
return [(p.id, str(p)) for p in plan_versions.order_by('plan__name')]
@property
def new_plan_version_response(self):
current_version = int(self.data.get('additionalData[current_version]'))
plan_versions = [x for x in self.plan_version_response if x[0] != current_version]
return plan_versions
class Select2InvoiceTriggerHandler(BaseSelect2AsyncHandler):
slug = 'select2_billing'
allowed_actions = [
'domain',
]
@property
def domain_response(self):
domain_names = [domain['key'] for domain in Domain.get_all(include_docs=False)]
if self.search_string:
search_string = self.search_string.lower()
domain_names = [x for x in domain_names if search_string in x.lower()]
return [(d, d) for d in domain_names]
class Select2CustomerInvoiceTriggerHandler(BaseSelect2AsyncHandler):
slug = 'select2_billing'
allowed_actions = [
'customer_account',
]
@property
def customer_account_response(self):
accounts = BillingAccount.objects.filter(is_customer_billing_account=True).values_list('name', flat=True)
if self.search_string:
search_string = self.search_string.lower()
accounts = [x for x in accounts if search_string in x.lower()]
return [(n, n) for n in accounts]
class BaseSingleOptionFilterAsyncHandler(BaseAsyncHandler):
@property
def query(self):
raise NotImplementedError("must return a queryset")
@property
def search_string(self):
return self.data.get('q', None)
@property
def page(self):
return int(self.data.get('page', 1))
@property
def paginated_data(self):
start = (self.page - 1) * self.limit
end = self.page * self.limit
return self.query.all()[start:end]
@property
def limit(self):
return self.data.get('limit', 10)
@property
def total(self):
return self.query.count()
@staticmethod
def _fmt_select2_data(data_id, data_text):
return {
'id': data_id,
'text': data_text,
}
def _fmt_success(self, data):
return json.dumps({
'success': True,
'limit': self.limit,
'page': self.page,
'total': self.total,
'items': data,
})
class SubscriberFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
slug = 'subscriber_filter'
allowed_actions = [
'subscriber',
]
@property
def query(self):
query = Subscriber.objects.exclude(domain=None).order_by('domain')
if self.search_string:
query = query.filter(domain__istartswith=self.search_string)
return query
@property
def subscriber_response(self):
return [self._fmt_select2_data(s.domain, s.domain)
for s in self.paginated_data]
class SubscriptionFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
slug = 'subscription_filter'
allowed_actions = [
'contract_id',
]
@property
def query(self):
query = Subscription.visible_objects
if self.action == 'contract_id':
query = query.exclude(
salesforce_contract_id=None
).exclude(salesforce_contract_id='').order_by('salesforce_contract_id')
if self.search_string:
query = query.filter(
salesforce_contract_id__istartswith=self.search_string
)
return query
@property
def contract_id_response(self):
return [self._fmt_select2_data(
s.salesforce_contract_id, s.salesforce_contract_id)
for s in self.paginated_data]
class AccountFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
slug = 'account_filter'
allowed_actions = [
'account_name',
'account_id',
'dimagi_contact',
]
@property
def query(self):
query = BillingAccount.objects.order_by('name')
if self.action == 'account_name' and self.search_string:
query = query.filter(name__icontains=self.search_string)
if self.action == 'account_id':
query = query.exclude(
salesforce_account_id=None
).exclude(
salesforce_account_id=''
).order_by('salesforce_account_id')
if self.search_string:
query = query.filter(
salesforce_account_id__istartswith=self.search_string)
if self.action == 'dimagi_contact':
query = query.exclude(
dimagi_contact=''
).order_by('dimagi_contact')
if self.search_string:
query = query.filter(
dimagi_contact__icontains=self.search_string)
return query
@property
def account_name_response(self):
return [self._fmt_select2_data(a.name, a.name)
for a in self.paginated_data]
@property
def account_id_response(self):
return [self._fmt_select2_data(a.salesforce_account_id,
a.salesforce_account_id)
for a in self.paginated_data]
@property
def dimagi_contact_response(self):
return [self._fmt_select2_data(a.dimagi_contact, a.dimagi_contact)
for a in self.paginated_data]
class BillingContactInfoAsyncHandler(BaseSingleOptionFilterAsyncHandler):
slug = 'billing_contact_filter'
allowed_actions = [
'contact_name'
]
@property
def query(self):
query = BillingContactInfo.objects.exclude(
first_name='', last_name='').order_by('first_name', 'last_name')
if self.search_string:
query = query.filter(
Q(first_name__istartswith=self.search_string)
| Q(last_name__istartswith=self.search_string)
)
return query
@property
def contact_name_response(self):
return [self._fmt_select2_data(c.full_name, c.full_name)
for c in self.paginated_data]
class SoftwarePlanAsyncHandler(BaseSingleOptionFilterAsyncHandler):
slug = 'software_plan_filter'
allowed_actions = [
'name',
]
@property
def query(self):
query = SoftwarePlan.objects.order_by('name')
if self.search_string:
query = query.filter(name__icontains=self.search_string)
return query
@property
def name_response(self):
return [self._fmt_select2_data(p.name, p.name)
for p in self.paginated_data]
class BaseInvoiceNumberAsyncHandler(BaseSingleOptionFilterAsyncHandler):
invoice_class = None
@property
def query(self):
query = self.invoice_class.objects.annotate(
number_on_invoice=F('id') + settings.INVOICE_STARTING_NUMBER
).order_by('number_on_invoice')
if self.search_string:
query = query.filter(number_on_invoice__startswith=self.search_string)
return query
@property
def base_response(self):
return [
self._fmt_select2_data(str(p.id), str(p.number_on_invoice))
for p in self.paginated_data
]
class InvoiceNumberAsyncHandler(BaseInvoiceNumberAsyncHandler):
slug = 'invoice_number_filter'
allowed_actions = [
'invoice_number',
]
invoice_class = Invoice
@property
def invoice_number_response(self):
return self.base_response
class CustomerInvoiceNumberAsyncHandler(BaseInvoiceNumberAsyncHandler):
slug = 'customer_invoice_number_filter'
allowed_actions = [
'customer_invoice_number',
]
invoice_class = CustomerInvoice
@property
def customer_invoice_number_response(self):
return self.base_response
class InvoiceBalanceAsyncHandler(BaseSingleOptionFilterAsyncHandler):
slug = 'invoice_balance_filter'
allowed_actions = [
'invoice_balance'
]
@property
def query(self):
query = Invoice.objects.order_by('balance')
if self.search_string:
query = query.filter(balance__startswith=self.search_string)
return query
@property
def invoice_balance_response(self):
return [
self._fmt_select2_data(str(p.balance), str(p.balance))
for p in self.paginated_data
]
class DomainFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
slug = 'domain_filter'
allowed_actions = [
'domain_name',
]
@property
def query(self):
db = Domain.get_db()
search_params = {
'reduce': False,
'limit': 20
}
if self.search_string:
# Search by string range: https://docs.couchdb.org/en/latest/ddocs/views/collation.html#string-ranges
search_params['startkey'] = self.search_string
search_params['endkey'] = "{}\ufff0".format(self.search_string)
query = db.view('domain/domains', **search_params)
return query
@property
def domain_name_response(self):
return [self._fmt_select2_data(p['key'], p['key']) for p in self.paginated_data]
================================================
FILE: corehq/apps/accounting/automated_reports.py
================================================
import datetime
import io
from decimal import Decimal
from django.conf import settings
from django.template.loader import render_to_string
from corehq.apps.accounting.models import Subscription, CreditLine
from corehq.apps.accounting.utils import quantize_accounting_decimal
from corehq.util.log import send_HTML_email
from couchexport.export import export_from_tables
from couchexport.models import Format
class CreditsAutomatedReport(object):
"""
This report gets sent to the finance team to determine how many credits
are still active on subscriptions and billing accounts on HQ.
But why base the report on 'yesterday'?
It is much more difficult to trigger a report on the last day of the month
(which changes) vs. the first day of the month.
Since this report is generally run of the first hour on the first day
of the month, the report is really about the previous day's credits.
"""
def send_report(self, recipient):
yesterday = datetime.date.today() - datetime.timedelta(days=1)
yesterday_string = yesterday.strftime("%d %b %Y")
table = self._generate_report_table()
file_to_attach = io.BytesIO()
export_from_tables(
[[yesterday_string, table]],
file_to_attach,
Format.XLS_2007
)
email_context = {
'date_of_report': yesterday_string,
}
email_content = render_to_string(
'accounting/email/credits_on_hq.html', email_context)
email_content_plaintext = render_to_string(
'accounting/email/credits_on_hq.txt', email_context)
format_dict = Format.FORMAT_DICT[Format.XLS_2007]
file_attachment = {
'title': 'Credits_on_hq_{}_{}.{}'.format(
yesterday.isoformat(),
settings.SERVER_ENVIRONMENT,
format_dict['extension'],
),
'mimetype': format_dict['mimetype'],
'file_obj': file_to_attach,
}
from_email = "Dimagi Finance <{}>".format(settings.DEFAULT_FROM_EMAIL)
send_HTML_email(
"{} Credits on HQ {}".format(
yesterday_string,
settings.SERVER_ENVIRONMENT,
),
recipient,
email_content,
email_from=from_email,
text_content=email_content_plaintext,
file_attachments=[file_attachment],
)
def _generate_report_table(self):
table = [[
"Project",
"Edition",
"General Credits / Credits Remaining for Subscription",
"Feature / Product Credits for Subscription",
"Account Name",
"Billing Account-level Credits",
"Billing Account-level Feature / Product Credits",
"Product/Implementation?",
]]
for subscription in Subscription.visible_and_suppressed_objects.filter(
is_active=True):
domain = subscription.subscriber.domain
plan_edition = subscription.plan_version.plan.edition
credit_info = self._get_credit_info(subscription)
general_credit = (credit_info['general_credit']['amount']
if credit_info['general_credit']
else "")
feature_credit = (credit_info['feature_credit']['amount']
if credit_info['feature_credit']
else "")
account_credit = (credit_info['account_general_credit']['amount']
if credit_info['account_general_credit']['amount']
else "")
account_feature_credit = (credit_info['account_feature_credit']['amount']
if credit_info['account_feature_credit']['amount']
else "")
if any([c not in ["", "0.00"] for c in [
general_credit,
feature_credit,
account_credit,
account_feature_credit,
]]):
table.append([
domain,
plan_edition,
general_credit,
feature_credit,
subscription.account.name if subscription.account else "",
account_credit,
account_feature_credit,
subscription.service_type,
])
return table
def _get_credit_info(self, subscription):
return {
'general_credit': self._fmt_credit(self._credit_grand_total(
CreditLine.get_credits_by_subscription_and_features(
subscription
)
)),
'feature_credit': self._fmt_credit(self._credit_grand_total(
CreditLine.get_non_general_credits_by_subscription(subscription)
)),
'account_general_credit': self._fmt_credit(self._credit_grand_total(
CreditLine.get_credits_for_account(
subscription.account
) if subscription.account else None
)),
'account_feature_credit': self._fmt_credit(self._credit_grand_total(
CreditLine.get_non_general_credits_for_account(subscription.account)
)),
}
@staticmethod
def _credit_grand_total(credit_lines):
return sum([c.balance for c in credit_lines]) if credit_lines else Decimal('0.00')
@staticmethod
def _fmt_credit(credit_amount=None):
if credit_amount is None:
return {
'amount': "--",
}
return {
'amount': quantize_accounting_decimal(credit_amount),
'is_visible': credit_amount != Decimal('0.0'),
}
================================================
FILE: corehq/apps/accounting/bootstrap/__init__.py
================================================
================================================
FILE: corehq/apps/accounting/bootstrap/config/__init__.py
================================================
================================================
FILE: corehq/apps/accounting/bootstrap/config/annual_plans_may_2024.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.STANDARD, False, False, True): {
'role': 'standard_plan_v1',
'product_rate_monthly_fee': Decimal('250.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=125, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
},
},
(SoftwarePlanEdition.PRO, False, False, True): {
'role': 'pro_plan_v1',
'product_rate_monthly_fee': Decimal('500.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=250, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
},
},
(SoftwarePlanEdition.ADVANCED, False, False, True): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1000.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=500, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/enterprise.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import (
UNLIMITED_FEATURE_USAGE,
FeatureType,
SoftwarePlanEdition,
)
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.ENTERPRISE, False, False): {
'role': 'enterprise_plan_v0',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=UNLIMITED_FEATURE_USAGE, per_excess_fee=Decimal('0.00')),
FeatureType.SMS: dict(monthly_limit=UNLIMITED_FEATURE_USAGE),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/form_submitting_mobile_worker_feature_rate.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType
BOOTSTRAP_CONFIG = {
"feature_rates": {
FeatureType.FORM_SUBMITTING_MOBILE_WORKER: dict(monthly_limit=2000, per_excess_fee=Decimal('3.00'))
}
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/new_plans_dec_2019.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.PAUSED, False, False): {
'role': 'paused_plan_v0',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=0, per_excess_fee=Decimal('0.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
(SoftwarePlanEdition.FREE, False, False): {
'role': 'community_plan_v2',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=5, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
(SoftwarePlanEdition.STANDARD, False, False): {
'role': 'standard_plan_v1',
'product_rate_monthly_fee': Decimal('300.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=125, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
},
},
(SoftwarePlanEdition.PRO, False, False): {
'role': 'pro_plan_v1',
'product_rate_monthly_fee': Decimal('600.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=250, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
},
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/remove_free_50_sms_sep_2023.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.STANDARD, False, False): {
'role': 'standard_plan_v1',
'product_rate_monthly_fee': Decimal('300.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=125, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
},
},
(SoftwarePlanEdition.PRO, False, False): {
'role': 'pro_plan_v1',
'product_rate_monthly_fee': Decimal('600.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=250, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
},
},
(SoftwarePlanEdition.ADVANCED, False, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1200.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=500, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/report_builder_v0.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.STANDARD, False, True): {
'role': 'standard_plan_report_builder_v0',
'product_rate_monthly_fee': Decimal('100.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=50, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.PRO, False, True): {
'role': 'pro_plan_report_builder_v0',
'product_rate_monthly_fee': Decimal('500.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=250, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.ADVANCED, False, True): {
'role': 'advanced_plan_report_builder_v0',
'product_rate_monthly_fee': Decimal('1000.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=500, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/resellers_and_managed_hosting.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.MANAGED_HOSTING, False, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1000.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=0, per_excess_fee=Decimal('1.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
(SoftwarePlanEdition.RESELLER, False, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1000.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=10, per_excess_fee=Decimal('1.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/standard_pricing_march_2018.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.STANDARD, False, False): {
'role': 'standard_plan_v0',
'product_rate_monthly_fee': Decimal('250.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=50, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/standard_update_april_2025.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
# pay monthly
(SoftwarePlanEdition.STANDARD, False, False, False): {
'role': 'standard_plan_v2',
'product_rate_monthly_fee': Decimal('120.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=50, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
# pay annually
(SoftwarePlanEdition.STANDARD, False, False, True): {
'role': 'standard_plan_v2',
'product_rate_monthly_fee': Decimal('100.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=50, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/standard_user_limit_march_2018.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.STANDARD, False, False): {
'role': 'standard_plan_v0',
'product_rate_monthly_fee': Decimal('250.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=125, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/standard_user_limit_october_2018.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.STANDARD, False, False): {
'role': 'standard_plan_v0',
'product_rate_monthly_fee': Decimal('300.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=125, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
}
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/testing.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import (
UNLIMITED_FEATURE_USAGE,
FeatureType,
SoftwarePlanEdition,
)
BOOTSTRAP_CONFIG_TESTING = {
(SoftwarePlanEdition.FREE, False, False, False): {
'role': 'community_plan_v1',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=2, per_excess_fee=Decimal('1.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
(SoftwarePlanEdition.STANDARD, False, False, False): {
'role': 'standard_plan_v0',
'product_rate_monthly_fee': Decimal('300.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=4, per_excess_fee=Decimal('1.00')),
FeatureType.SMS: dict(monthly_limit=3),
FeatureType.WEB_USER: dict(monthly_limit=10, per_excess_fee=Decimal('10.00')),
}
},
(SoftwarePlanEdition.PRO, False, False, False): {
'role': 'pro_plan_v1',
'product_rate_monthly_fee': Decimal('600.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=6, per_excess_fee=Decimal('1.00')),
FeatureType.SMS: dict(monthly_limit=5),
}
},
(SoftwarePlanEdition.ADVANCED, False, False, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1200.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=8, per_excess_fee=Decimal('1.00')),
FeatureType.SMS: dict(monthly_limit=7),
}
},
(SoftwarePlanEdition.ADVANCED, True, False, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=2, per_excess_fee=Decimal('1.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
(SoftwarePlanEdition.ENTERPRISE, False, False, False): {
'role': 'enterprise_plan_v0',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=UNLIMITED_FEATURE_USAGE, per_excess_fee=Decimal('0.00')),
FeatureType.SMS: dict(monthly_limit=UNLIMITED_FEATURE_USAGE),
}
},
(SoftwarePlanEdition.STANDARD, False, False, True): {
'role': 'standard_plan_v1',
'product_rate_monthly_fee': Decimal('250.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=125, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
},
},
(SoftwarePlanEdition.PRO, False, False, True): {
'role': 'pro_plan_v1',
'product_rate_monthly_fee': Decimal('500.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=250, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
},
},
(SoftwarePlanEdition.ADVANCED, False, False, True): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1000.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=500, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/user_buckets_august_2018.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import (
UNLIMITED_FEATURE_USAGE,
FeatureType,
SoftwarePlanEdition,
)
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.FREE, False, False): {
'role': 'community_plan_v1',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=10, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
(SoftwarePlanEdition.STANDARD, False, False): {
'role': 'standard_plan_v0',
'product_rate_monthly_fee': Decimal('300.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=50, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.PRO, False, False): {
'role': 'pro_plan_v0',
'product_rate_monthly_fee': Decimal('600.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=250, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.ADVANCED, False, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1200.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=500, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.ADVANCED, True, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=UNLIMITED_FEATURE_USAGE, per_excess_fee=Decimal('0.00')),
FeatureType.SMS: dict(monthly_limit=UNLIMITED_FEATURE_USAGE),
}
}
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/user_buckets_jan_2017.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition
BOOTSTRAP_CONFIG = {
(SoftwarePlanEdition.FREE, False, False): {
'role': 'community_plan_v1',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=10, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
},
(SoftwarePlanEdition.STANDARD, False, False): {
'role': 'standard_plan_v0',
'product_rate_monthly_fee': Decimal('100.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=50, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.PRO, False, False): {
'role': 'pro_plan_v0',
'product_rate_monthly_fee': Decimal('500.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=250, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.ADVANCED, False, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('1000.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=500, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=50),
}
},
(SoftwarePlanEdition.ADVANCED, True, False): {
'role': 'advanced_plan_v0',
'product_rate_monthly_fee': Decimal('0.00'),
'feature_rates': {
FeatureType.USER: dict(monthly_limit=10, per_excess_fee=Decimal('2.00')),
FeatureType.SMS: dict(monthly_limit=0),
}
}
}
================================================
FILE: corehq/apps/accounting/bootstrap/config/web_user_feature_rate.py
================================================
from decimal import Decimal
from corehq.apps.accounting.models import FeatureType
BOOTSTRAP_CONFIG = {
"feature_rates": {
FeatureType.WEB_USER: dict(monthly_limit=10, per_excess_fee=Decimal('10.00'))
}
}
================================================
FILE: corehq/apps/accounting/bootstrap/features.py
================================================
"""
These are the feature allocations for all tiers of our Software Plan Editions
available for self-service.
"""
from corehq import privileges
paused_v0 = []
# FREE PLANS
# Grandfathered Free Plans created prior to August 2018
community_v0 = [
privileges.PROJECT_ACCESS,
privileges.EXCEL_DASHBOARD,
privileges.DAILY_SAVED_EXPORT,
privileges.CASE_SHARING_GROUPS,
privileges.CHILD_CASES,
privileges.DATA_FORWARDING,
]
# Grandfathered Free Plans created prior to Dec 18, 2019
community_v1 = [
privileges.PROJECT_ACCESS,
privileges.CASE_SHARING_GROUPS,
privileges.CHILD_CASES,
privileges.DATA_FORWARDING,
]
# Current Free Plan
community_v2 = [
privileges.PROJECT_ACCESS,
privileges.LOGIN_AS,
]
# STANDARD PLANS
# Grandfathered Standard Plans created prior to Dec 18, 2019
standard_v0 = community_v0 + [
privileges.API_ACCESS,
privileges.LOOKUP_TABLES,
privileges.OUTBOUND_SMS,
privileges.REMINDERS_FRAMEWORK,
privileges.CUSTOM_SMS_GATEWAY,
privileges.ROLE_BASED_ACCESS,
privileges.BULK_USER_MANAGEMENT,
privileges.BULK_CASE_MANAGEMENT,
privileges.ALLOW_EXCESS_USERS,
privileges.LOCATIONS,
privileges.USERCASE,
privileges.ZAPIER_INTEGRATION,
privileges.LOGIN_AS,
privileges.PRACTICE_MOBILE_WORKERS,
]
# Grandfathered Standard Plans created prior to Apr 22, 2025
standard_v1 = community_v2 + [
privileges.LOOKUP_TABLES,
privileges.ROLE_BASED_ACCESS,
privileges.OUTBOUND_SMS,
privileges.REMINDERS_FRAMEWORK,
privileges.CUSTOM_SMS_GATEWAY,
privileges.BULK_CASE_MANAGEMENT,
privileges.BULK_USER_MANAGEMENT,
privileges.ALLOW_EXCESS_USERS,
privileges.USERCASE,
privileges.EXCEL_DASHBOARD,
privileges.DAILY_SAVED_EXPORT,
privileges.ZAPIER_INTEGRATION,
privileges.PRACTICE_MOBILE_WORKERS,
privileges.FORM_LINK_WORKFLOW,
privileges.PHONE_APK_HEARTBEAT,
privileges.FORM_CASE_IDS_CASE_IMPORTER,
privileges.EXPORT_MULTISORT,
]
# Current Standard Plan
standard_v2 = community_v2 + [
privileges.LOOKUP_TABLES,
privileges.ROLE_BASED_ACCESS,
privileges.OUTBOUND_SMS,
privileges.REMINDERS_FRAMEWORK,
privileges.CUSTOM_SMS_GATEWAY,
privileges.BULK_CASE_MANAGEMENT,
privileges.BULK_USER_MANAGEMENT,
privileges.ALLOW_EXCESS_USERS,
privileges.EXCEL_DASHBOARD,
privileges.DAILY_SAVED_EXPORT,
privileges.PRACTICE_MOBILE_WORKERS,
privileges.FORM_LINK_WORKFLOW,
privileges.PHONE_APK_HEARTBEAT,
privileges.FORM_CASE_IDS_CASE_IMPORTER,
privileges.EXPORT_MULTISORT,
privileges.TWO_STAGE_MOBILE_WORKER_ACCOUNT_CREATION,
]
# PRO PLANS
# Grandfathered Pro Plans created prior to Dec 18, 2019
pro_v0 = standard_v0 + [
privileges.CLOUDCARE,
privileges.CUSTOM_REPORTS,
privileges.INBOUND_SMS,
privileges.HIPAA_COMPLIANCE_ASSURANCE,
privileges.DEIDENTIFIED_DATA,
privileges.REPORT_BUILDER,
privileges.DATA_CLEANUP,
privileges.TEMPLATED_INTENTS,
privileges.RESTRICT_ACCESS_BY_LOCATION,
privileges.REPORT_BUILDER_5,
]
# Current Pro Plan
pro_v1 = standard_v2 + [
privileges.DATA_FORWARDING,
privileges.API_ACCESS,
privileges.CUSTOM_REPORTS,
privileges.REPORT_BUILDER,
privileges.REPORT_BUILDER_5,
privileges.DATA_CLEANUP,
privileges.TEMPLATED_INTENTS,
privileges.CASE_SHARING_GROUPS,
privileges.CHILD_CASES,
privileges.LITE_RELEASE_MANAGEMENT,
privileges.LOADTEST_USERS,
privileges.DATA_FILE_DOWNLOAD,
privileges.REGEX_FIELD_VALIDATION,
privileges.EXPORT_OWNERSHIP,
privileges.CASE_LIST_EXPLORER,
privileges.CASE_DEDUPE,
privileges.SHOW_ENABLE_ALL_ADD_ONS,
privileges.USERCASE,
privileges.ZAPIER_INTEGRATION,
]
# ADVANCED PLANS
# Current Advanced Plan
advanced_v0 = pro_v1 + [
privileges.CLOUDCARE,
privileges.ACTIVE_DATA_MANAGEMENT,
privileges.CUSTOM_BRANDING,
privileges.RESTRICT_ACCESS_BY_LOCATION,
privileges.INBOUND_SMS,
privileges.DEIDENTIFIED_DATA,
privileges.HIPAA_COMPLIANCE_ASSURANCE,
privileges.COMMCARE_LOGO_UPLOADER,
privileges.LOCATIONS,
privileges.CUSTOM_INTENTS,
privileges.BUILD_PROFILES,
privileges.ADVANCED_DOMAIN_SECURITY,
privileges.ODATA_FEED,
privileges.APP_USER_PROFILES,
privileges.VIEW_APP_DIFF,
privileges.LOCATION_SAFE_CASE_IMPORTS,
privileges.FILTERED_BULK_USER_DOWNLOAD,
privileges.DATA_DICTIONARY,
privileges.CASE_COPY,
privileges.CUSTOM_DOMAIN_ALERTS,
privileges.APP_DEPENDENCIES,
privileges.BULK_DATA_EDITING,
privileges.DATA_DICT_TYPES,
privileges.GEOJSON_EXPORT,
privileges.CUSTOM_ICON_BADGES,
privileges.LOCATION_COLUMNS_IN_USER_LAST_ACTIVITY_REPORT,
]
enterprise_v0 = advanced_v0 + [
privileges.GEOCODER,
privileges.DEFAULT_EXPORT_SETTINGS,
privileges.RELEASE_MANAGEMENT,
]
================================================
FILE: corehq/apps/accounting/bootstrap/utils.py
================================================
from collections import namedtuple
from corehq.apps.accounting.const import COMMCARE_PRODUCT_TYPE, DIMAGI_ONLY
from corehq.apps.accounting.models import (
FeatureType,
SoftwarePlanEdition,
SoftwarePlanVisibility,
)
from corehq.apps.accounting.utils import (
log_accounting_error,
log_accounting_info,
)
def ensure_plans(config, verbose, apps):
DefaultProductPlan = apps.get_model('accounting', 'DefaultProductPlan')
SoftwarePlan = apps.get_model('accounting', 'SoftwarePlan')
Role = apps.get_model('django_prbac', 'Role')
PlanKey = namedtuple('PlanKey', ['edition', 'is_trial', 'is_report_builder_enabled', 'is_annual_plan'],
defaults=('is_annual_plan', None))
for plan_key, plan_deets in config.items():
plan_key = PlanKey(*plan_key)
try:
role = _ensure_role(plan_deets['role'], apps)
except Role.DoesNotExist:
return
product, product_rate = _ensure_product_rate(
plan_deets['product_rate_monthly_fee'], plan_key.edition,
verbose=verbose, apps=apps,
)
features = _ensure_features(plan_deets['feature_rates'], plan_key.edition, verbose, apps)
feature_rates = ensure_feature_rates(plan_deets['feature_rates'], features, verbose=verbose, apps=apps)
software_plan = _ensure_software_plan(plan_key, product, product_rate, verbose, apps)
_ensure_software_plan_version(role, software_plan, product_rate, feature_rates, apps)
_ensure_default_product_plan(plan_key, software_plan, verbose, apps)
_clear_cache(SoftwarePlan.objects.all(), DefaultProductPlan.objects.all())
def _ensure_role(role_slug, apps):
Role = apps.get_model('django_prbac', 'Role')
try:
role = Role.objects.get(slug=role_slug)
except Role.DoesNotExist:
log_accounting_error(
f"Could not find the role '{role_slug}'. Did you forget to run cchq_prbac_bootstrap?"
)
log_accounting_error("Aborting. You should figure this out.")
raise
return role
def _ensure_product_rate(monthly_fee, edition, verbose, apps):
"""
Ensures that all the necessary SoftwareProductRates are created for the plan.
"""
if verbose:
log_accounting_info('Ensuring Product Rates')
SoftwareProductRate = apps.get_model('accounting', 'SoftwareProductRate')
product_name = f"{COMMCARE_PRODUCT_TYPE} {edition}"
if edition == SoftwarePlanEdition.ENTERPRISE:
product_name = f"{DIMAGI_ONLY} {product_name}"
product_rate = SoftwareProductRate(monthly_fee=monthly_fee)
try:
product = _get_software_product(product_name, verbose, apps)
product_rate.product = product
except LookupError:
product = None
product_rate.name = product_name
product_rate.save()
if verbose:
log_accounting_info(f"Corresponding product rate of {product_rate.monthly_fee} created.")
return product, product_rate
def _get_software_product(product_name, verbose, apps):
# SoftwareProduct no longer exists but is retained here to avoid breaking old migrations
SoftwareProduct = apps.get_model('accounting', 'SoftwareProduct')
product = SoftwareProduct(name=product_name, product_type=COMMCARE_PRODUCT_TYPE)
try:
product = SoftwareProduct.objects.get(name=product.name)
if verbose:
log_accounting_info(
f"Product '{product.name}' already exists. Using existing product to add rate."
)
except SoftwareProduct.DoesNotExist:
if verbose:
log_accounting_info(f"Creating Product: {product}")
product.save()
return product
def _ensure_features(feature_rates, edition, verbose, apps):
"""
Ensures that all the Features necessary for the plans are created.
"""
Feature = apps.get_model('accounting', 'Feature')
if verbose:
log_accounting_info(f"Ensuring Features for plan: {edition}")
features = []
for feature_type in feature_rates.keys():
if feature_type in FeatureType.EDITIONED_FEATURES:
feature_name = f"{feature_type} {edition}"
if edition == SoftwarePlanEdition.ENTERPRISE:
feature_name = f"Dimagi Only {feature_name}"
else:
feature_name = feature_type
try:
feature = Feature.objects.get(name=feature_name)
if verbose:
log_accounting_info(
f"Feature '{feature.name}' already exists. Using existing feature to add rate."
)
except Feature.DoesNotExist:
feature = Feature.objects.create(name=feature_name, feature_type=feature_type)
if verbose:
log_accounting_info(f"Creating Feature: {feature}")
features.append(feature)
return features
def ensure_feature_rates(feature_rates, features, verbose, apps):
"""
Ensures that all the FeatureRates necessary for the plans are created.
"""
FeatureRate = apps.get_model('accounting', 'FeatureRate')
if verbose:
log_accounting_info('Ensuring Feature Rates')
db_feature_rates = []
for feature in features:
# web user feature rate is not included in all plans
# if current plan doesn't have web user feature rate, skip
try:
feature_rate = FeatureRate(**feature_rates[feature.feature_type])
except KeyError:
continue
feature_rate.feature = feature
if verbose:
log_accounting_info(f"Creating rate for feature '{feature.name}': {feature_rate}")
db_feature_rates.append(feature_rate)
return db_feature_rates
def _ensure_software_plan(plan_key, product, product_rate, verbose, apps):
SoftwarePlan = apps.get_model('accounting', 'SoftwarePlan')
plan_name = _software_plan_name(plan_key, product, product_rate)
try:
software_plan = SoftwarePlan.objects.get(name=plan_name)
if verbose:
log_accounting_info(
f"Plan '{software_plan.name}' already exists. Using existing plan to add version."
)
except SoftwarePlan.DoesNotExist:
plan_opts = {
'name': plan_name,
'edition': plan_key.edition,
'visibility': (SoftwarePlanVisibility.INTERNAL
if plan_key.edition == SoftwarePlanEdition.ENTERPRISE
else SoftwarePlanVisibility.PUBLIC),
}
if plan_key.is_annual_plan is not None:
plan_opts['is_annual_plan'] = plan_key.is_annual_plan
software_plan = SoftwarePlan.objects.create(**plan_opts)
if verbose:
log_accounting_info(f"Creating Software Plan: {software_plan.name}")
return software_plan
def _software_plan_name(plan_key, product, product_rate):
name_parts = [
product_rate.name if product is None else product.name,
(" Trial" if plan_key.is_trial else " Edition") if product is None else "",
]
if plan_key.edition in SoftwarePlanEdition.SELF_RENEWABLE_EDITIONS:
name_parts.extend([
" - Pay Annually" if plan_key.is_annual_plan else " - Pay Monthly",
" - Report Builder (5 Reports)" if plan_key.is_report_builder_enabled else "",
])
return "".join(name_parts)
def _ensure_software_plan_version(role, software_plan, product_rate, feature_rates, apps):
SoftwarePlanVersion = apps.get_model('accounting', 'SoftwarePlanVersion')
software_plan_version = SoftwarePlanVersion(role=role, plan=software_plan, product_rate=product_rate)
software_plan_version.save()
for feature_rate in feature_rates:
feature_rate.save()
software_plan_version.feature_rates.add(feature_rate)
software_plan_version.save()
return software_plan_version
def _ensure_default_product_plan(plan_key, software_plan, verbose, apps):
DefaultProductPlan = apps.get_model('accounting', 'DefaultProductPlan')
plan_opts = {
'edition': plan_key.edition,
'is_trial': plan_key.is_trial,
'is_report_builder_enabled': plan_key.is_report_builder_enabled,
}
if plan_key.is_annual_plan is not None:
plan_opts['is_annual_plan'] = plan_key.is_annual_plan
try:
default_product_plan = DefaultProductPlan.objects.get(**plan_opts)
if verbose:
log_accounting_info(
f"Default for edition '{default_product_plan.edition}' "
f"with is_trial='{plan_key.is_trial}' "
f"and is_annual_plan='{plan_key.is_annual_plan}' already exists."
)
except DefaultProductPlan.DoesNotExist:
default_product_plan = DefaultProductPlan(**plan_opts)
finally:
default_product_plan.plan = software_plan
default_product_plan.save()
if verbose:
log_accounting_info(
f"Setting plan as default for edition '{default_product_plan.edition}' "
f"with is_trial='{plan_key.is_trial}' "
f"and is_annual_plan='{plan_key.is_annual_plan}'."
)
return default_product_plan
def _clear_cache(software_plans, default_plans):
from corehq.apps.accounting.models import SoftwarePlan, DefaultProductPlan
for software_plan in software_plans:
SoftwarePlan.get_version.clear(software_plan)
for plan in default_plans:
DefaultProductPlan.get_default_plan_version.clear(
DefaultProductPlan, plan.edition, plan.is_trial, plan.is_report_builder_enabled,
)
================================================
FILE: corehq/apps/accounting/const.py
================================================
EXCHANGE_RATE_DECIMAL_PLACES = 9
SMALL_INVOICE_THRESHOLD = 1
OVERDUE_INVOICE_LIMIT_DAYS = 10
DAYS_PAST_DUE_TO_TRIGGER_DOWNGRADE = OVERDUE_INVOICE_LIMIT_DAYS + 1
DAYS_PAST_DUE_TO_TRIGGER_DOWNGRADE_WARNING = OVERDUE_INVOICE_LIMIT_DAYS - 2
DAYS_PAST_DUE_TO_TRIGGER_OVERDUE_NOTICE = OVERDUE_INVOICE_LIMIT_DAYS - 7
DAYS_BEFORE_DUE_TO_TRIGGER_REMINDER = 1
PAY_ANNUALLY_SUBSCRIPTION_MONTHS = 12
SUBSCRIPTION_PREPAY_MIN_DAYS_UNTIL_DUE = 15
COMMCARE_PRODUCT_TYPE
Showing preview only (665K chars total). Download the full file or copy to clipboard to get everything.
gitextract_3yjwnnlc/ ├── .coderabbit.yaml ├── .coveragerc ├── .dockerignore ├── .editorconfig ├── .github/ │ ├── CODEOWNERS │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ └── commcare-enhancement-proposal.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ ├── labels.yml │ ├── release.yml │ └── workflows/ │ ├── build-static.yml │ ├── codeql-analysis.yml │ ├── dependency-metrics.yml │ ├── docker-image.yml │ ├── lint.yml │ ├── rebuild-staging.yml │ ├── required-labels.yml │ ├── test-docs.yml │ ├── tests.yml │ └── update-translations.yml ├── .gitignore ├── .gitmodules ├── .isort.cfg ├── .python-version ├── .pytype.cfg ├── .readthedocs.yml ├── .scss-lint.yml ├── .transifexrc.example ├── .tx/ │ └── config ├── AGENTS.md ├── CODE_STANDARDS.md ├── CONTRIBUTING.rst ├── DEV_FAQ.md ├── DEV_SETUP.md ├── DEV_SETUP_MAC.md ├── Dockerfile ├── Dockerfile_incl ├── Gruntfile.js ├── LICENSE ├── Makefile ├── README.md ├── STANDARDS.rst ├── codecov.yml ├── corehq/ │ ├── README.rst │ ├── __init__.py │ ├── apps/ │ │ ├── README.rst │ │ ├── __init__.py │ │ ├── accounting/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── async_handlers.py │ │ │ ├── automated_reports.py │ │ │ ├── bootstrap/ │ │ │ │ ├── __init__.py │ │ │ │ ├── config/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── annual_plans_may_2024.py │ │ │ │ │ ├── enterprise.py │ │ │ │ │ ├── form_submitting_mobile_worker_feature_rate.py │ │ │ │ │ ├── new_plans_dec_2019.py │ │ │ │ │ ├── remove_free_50_sms_sep_2023.py │ │ │ │ │ ├── report_builder_v0.py │ │ │ │ │ ├── resellers_and_managed_hosting.py │ │ │ │ │ ├── standard_pricing_march_2018.py │ │ │ │ │ ├── standard_update_april_2025.py │ │ │ │ │ ├── standard_user_limit_march_2018.py │ │ │ │ │ ├── standard_user_limit_october_2018.py │ │ │ │ │ ├── testing.py │ │ │ │ │ ├── user_buckets_august_2018.py │ │ │ │ │ ├── user_buckets_jan_2017.py │ │ │ │ │ └── web_user_feature_rate.py │ │ │ │ ├── features.py │ │ │ │ └── utils.py │ │ │ ├── const.py │ │ │ ├── decorators.py │ │ │ ├── dispatcher.py │ │ │ ├── emails.py │ │ │ ├── exceptions.py │ │ │ ├── filters.py │ │ │ ├── forms.py │ │ │ ├── interface.py │ │ │ ├── invoice_pdf.py │ │ │ ├── invoicing.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── add_operations_user.py │ │ │ │ ├── change_role_for_software_plan_version.py │ │ │ │ ├── create_test_pdf_templates.py │ │ │ │ ├── find_inactive_custom_modules.py │ │ │ │ ├── get_minimum_features_by_domain.py │ │ │ │ ├── get_partner_domain_user_history.py │ │ │ │ ├── list_customer_billing_account_software_plan.py │ │ │ │ ├── list_prepayments_by_year.py │ │ │ │ └── make_domain_enterprise_level.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_squashed_0052_ensure_report_builder_plans.py │ │ │ │ ├── 0002_auto_20170222_2008.py │ │ │ │ ├── 0003_auto_20170328_2102.py │ │ │ │ ├── 0004_auto_20170404_0028.py │ │ │ │ ├── 0005_automatic_downgrade_adjustment_method.py │ │ │ │ ├── 0006_unique_active_domain_subscription.py │ │ │ │ ├── 0007_practice_mobile_workers.py │ │ │ │ ├── 0008_update_report_builder_included_feature_numbers.py │ │ │ │ ├── 0009_make_billingaccount_name_unique.py │ │ │ │ ├── 0010_remove_softwareproduct_product_type.py │ │ │ │ ├── 0011_remove_softwareproduct.py │ │ │ │ ├── 0012_replace__product_type__with__is_product.py │ │ │ │ ├── 0013_subscription_dates_check.py │ │ │ │ ├── 0014_paymentmethod__web_user__nonnullable.py │ │ │ │ ├── 0015_grandfather_login_as.py │ │ │ │ ├── 0016_grandfather_reportbuilder_5_pro.py │ │ │ │ ├── 0017_nonnullable_char_fields.py │ │ │ │ ├── 0018_alter_nonnullable_char_fields.py │ │ │ │ ├── 0019_standard_pricing_march_2018.py │ │ │ │ ├── 0020_payment_method__unique_together.py │ │ │ │ ├── 0021_standard_user_limit_march_2018.py │ │ │ │ ├── 0022_add__skip_auto_downgrade_reason.py │ │ │ │ ├── 0023_auto_20180501_1813.py │ │ │ │ ├── 0024_unique__transaction_id.py │ │ │ │ ├── 0025_auto_20180508_1952.py │ │ │ │ ├── 0026_auto_20180508_1956.py │ │ │ │ ├── 0027_auto_20180509_1857.py │ │ │ │ ├── 0028_auto_20180604_1757.py │ │ │ │ ├── 0029_auto_20180605_1826.py │ │ │ │ ├── 0030_softwareplan_max_domains.py │ │ │ │ ├── 0031_billingaccount_billing_admin_emails.py │ │ │ │ ├── 0032_billingaccount_invoicing_plan.py │ │ │ │ ├── 0032_customerinvoice_squashed_0036_customerbillingrecord.py │ │ │ │ ├── 0033_auto_20180709_1837.py │ │ │ │ ├── 0034_merge_20180711_1828.py │ │ │ │ ├── 0034_remove_subscription_date_delay_invoicing.py │ │ │ │ ├── 0035_enterprise_settings.py │ │ │ │ ├── 0035_merge_20180711_2039.py │ │ │ │ ├── 0036_domainuserhistory.py │ │ │ │ ├── 0037_merge_20180807_0915.py │ │ │ │ ├── 0038_remove_billingaccount_restrict_signup_email.py │ │ │ │ ├── 0039_auto_20180828_2258.py │ │ │ │ ├── 0040_auto_20181002_1721.py │ │ │ │ ├── 0041_auto_20190130_1709.py │ │ │ │ ├── 0042_domain_user_history__unique__and__nonnullable.py │ │ │ │ ├── 0043_grandfather_case_privs.py │ │ │ │ ├── 0044_grandfather_odata_privs.py │ │ │ │ ├── 0045_grandfather_data_forwarding_privs.py │ │ │ │ ├── 0046_new_plans.py │ │ │ │ ├── 0047_invoice_communication.py │ │ │ │ ├── 0048_friendly_writeoff.py │ │ │ │ ├── 0049_auto_20200924_1753.py │ │ │ │ ├── 0050_app_user_profiles.py │ │ │ │ ├── 0051_hubspot_restrictions.py │ │ │ │ ├── 0052_geocoder_permissions.py │ │ │ │ ├── 0053_app_user_profiles_advanced.py │ │ │ │ ├── 0054_default_export_settings.py │ │ │ │ ├── 0055_linked_projects.py │ │ │ │ ├── 0056_add_release_management.py │ │ │ │ ├── 0057_add_sms_report_toggle.py │ │ │ │ ├── 0058_delete_linked_projects_role.py │ │ │ │ ├── 0059_add_lite_release_management_priv.py │ │ │ │ ├── 0060_add_loadtest_users_priv.py │ │ │ │ ├── 0061_remove_enterprise_v1.py │ │ │ │ ├── 0062_add_release_management_to_enterprise.py │ │ │ │ ├── 0063_replace_linked_projects_ff_with_erm.py │ │ │ │ ├── 0064_add_form_link_workflow_priv.py │ │ │ │ ├── 0065_phone_apk_heartbeat_privs.py │ │ │ │ ├── 0066_data_file_download_priv.py │ │ │ │ ├── 0067_add_view_app_diff_priv.py │ │ │ │ ├── 0068_regex_field_validation_privilege.py │ │ │ │ ├── 0069_location_safe_case_imports_priv.py │ │ │ │ ├── 0070_form_case_ids_case_importer_priv.py │ │ │ │ ├── 0071_add_billingaccountwebuserhistory.py │ │ │ │ ├── 0072_export_multisort_priv.py │ │ │ │ ├── 0073_export_ownership_priv.py │ │ │ │ ├── 0074_filtered_bulk_user_download_priv.py │ │ │ │ ├── 0075_application_error_report_priv.py │ │ │ │ ├── 0076_location_owner_in_report_builder_priv.py │ │ │ │ ├── 0077_case_list_explorer_priv.py │ │ │ │ ├── 0078_revert_location_owner_in_report_builder_priv.py │ │ │ │ ├── 0079_add_web_user_feature.py │ │ │ │ ├── 0080_add_web_user_feature_in_other_models.py │ │ │ │ ├── 0081_billingaccount_bill_web_user.py │ │ │ │ ├── 0082_application_error_report_priv.py │ │ │ │ ├── 0083_data_dictionary_priv.py │ │ │ │ ├── 0084_copy_cases_priv.py │ │ │ │ ├── 0085_remove_free_50_sms.py │ │ │ │ ├── 0086_add_duplicate_invoice_id_to_invoice_model.py │ │ │ │ ├── 0087_invoice_unique_constraints.py │ │ │ │ ├── 0088_add_new_softwareplan_visibility.py │ │ │ │ ├── 0089_dedupe_priv.py │ │ │ │ ├── 0090_custom_domain_alerts_priv.py │ │ │ │ ├── 0091_remove_custom_banner_alerts_feature_flag.py │ │ │ │ ├── 0092_revert_application_error_report_priv.py │ │ │ │ ├── 0093_defaultproductplan_is_annual_plan.py │ │ │ │ ├── 0094_add_annual_softwareplans.py │ │ │ │ ├── 0095_update_softwareplan_visibilities.py │ │ │ │ ├── 0096_formsubmittingmobileworkerhistory_and_featuretype_choice.py │ │ │ │ ├── 0097_add_form_submitting_mobile_worker_feature.py │ │ │ │ ├── 0098_app_dependencies_priv.py │ │ │ │ ├── 0099_data_cleaning_priv.py │ │ │ │ ├── 0100_alter_customerinvoicecommunicationhistory_communication_type_and_more.py │ │ │ │ ├── 0101_update_standard_plan_pricing_users_and_privs.py │ │ │ │ ├── 0102_alter_defaultproductplan_edition_and_more.py │ │ │ │ ├── 0103_bulk_data_cleaning_priv.py │ │ │ │ ├── 0104_fix_priv_community_rename.py │ │ │ │ ├── 0105_alter_billingcontactinfo_city_and_more.py │ │ │ │ ├── 0106_alter_billingcontactinfo_company_name.py │ │ │ │ ├── 0107_two_stage_mobile_worker_creation_priv.py │ │ │ │ ├── 0108_subscription_auto_renew_and_more.py │ │ │ │ ├── 0109_enable_all_add_ons_priv.py │ │ │ │ ├── 0110_alter_customerinvoicecommunicationhistory_communication_type_and_more.py │ │ │ │ ├── 0111_rename_is_auto_invoiceable_billingaccount_require_auto_pay.py │ │ │ │ ├── 0112_data_dict_types_priv.py │ │ │ │ ├── 0113_geojson_export_priv.py │ │ │ │ ├── 0114_custom_icon_badges_priv.py │ │ │ │ ├── 0115_loc_cols_in_user_last_activity_priv.py │ │ │ │ ├── 0116_creditadjustment_payment_type.py │ │ │ │ └── __init__.py │ │ │ ├── mixins.py │ │ │ ├── models.py │ │ │ ├── payment_handlers.py │ │ │ ├── signals.py │ │ │ ├── static/ │ │ │ │ └── accounting/ │ │ │ │ └── js/ │ │ │ │ ├── base_subscriptions_main.js │ │ │ │ ├── billing_account_form.js │ │ │ │ ├── confirm_plan.js │ │ │ │ ├── credits.js │ │ │ │ ├── credits_tab.js │ │ │ │ ├── payment_method_handler.js │ │ │ │ ├── pricing_table.js │ │ │ │ ├── renew_plan_selection.js │ │ │ │ ├── report_filter_actions.js │ │ │ │ ├── software_plan_version_handler.js │ │ │ │ ├── stripe.js │ │ │ │ ├── subscriptions_main.js │ │ │ │ └── widgets.js │ │ │ ├── subscription_changes.py │ │ │ ├── task_utils.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── accounting/ │ │ │ │ ├── accounting_admins.html │ │ │ │ ├── accounts.html │ │ │ │ ├── accounts_base.html │ │ │ │ ├── email/ │ │ │ │ │ ├── autopay_card_removed.html │ │ │ │ │ ├── autopay_failed.html │ │ │ │ │ ├── autopay_failed.txt │ │ │ │ │ ├── bookkeeper.html │ │ │ │ │ ├── bookkeeper.txt │ │ │ │ │ ├── bulk_payment_receipt.html │ │ │ │ │ ├── bulk_payment_receipt.txt │ │ │ │ │ ├── credit_receipt.html │ │ │ │ │ ├── credit_receipt.txt │ │ │ │ │ ├── credits_on_hq.html │ │ │ │ │ ├── credits_on_hq.txt │ │ │ │ │ ├── customer_invoice.html │ │ │ │ │ ├── customer_invoice.txt │ │ │ │ │ ├── digest.html │ │ │ │ │ ├── digest.txt │ │ │ │ │ ├── downgrade.html │ │ │ │ │ ├── downgrade.txt │ │ │ │ │ ├── downgrade_warning.html │ │ │ │ │ ├── downgrade_warning.txt │ │ │ │ │ ├── invoice.html │ │ │ │ │ ├── invoice.txt │ │ │ │ │ ├── invoice_autopay_setup.html │ │ │ │ │ ├── invoice_autopayment.html │ │ │ │ │ ├── invoice_autopayment.txt │ │ │ │ │ ├── invoice_contracted.html │ │ │ │ │ ├── invoice_contracted.txt │ │ │ │ │ ├── invoice_receipt.html │ │ │ │ │ ├── invoice_receipt.txt │ │ │ │ │ ├── invoice_reminder.html │ │ │ │ │ ├── invoice_reminder.txt │ │ │ │ │ ├── overdue_notice.html │ │ │ │ │ ├── overdue_notice.txt │ │ │ │ │ ├── pay_annually_unpaid.html │ │ │ │ │ ├── pay_annually_unpaid.txt │ │ │ │ │ ├── sales_request.html │ │ │ │ │ ├── subscription_change.html │ │ │ │ │ ├── subscription_change.txt │ │ │ │ │ ├── subscription_ending.html │ │ │ │ │ ├── subscription_ending.txt │ │ │ │ │ ├── subscription_ending_reminder_dimagi.html │ │ │ │ │ ├── subscription_ending_reminder_dimagi.txt │ │ │ │ │ ├── subscription_renewal_reminder.html │ │ │ │ │ ├── subscription_renewal_reminder.txt │ │ │ │ │ ├── subscription_renewed.html │ │ │ │ │ ├── subscription_renewed.txt │ │ │ │ │ ├── wire_invoice.html │ │ │ │ │ └── wire_invoice.txt │ │ │ │ ├── invoice.html │ │ │ │ ├── invoice_list.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── add_new_item_button.html │ │ │ │ │ ├── adjust_balance.html │ │ │ │ │ ├── anchor_tag.html │ │ │ │ │ ├── confirm_pause_summary.html │ │ │ │ │ ├── confirm_plan_summary.html │ │ │ │ │ ├── credits_tab.html │ │ │ │ │ ├── customer_plan_version_tools.html │ │ │ │ │ ├── downgrade_messages.html │ │ │ │ │ ├── invoice_table.html │ │ │ │ │ ├── renew_plan_selection.html │ │ │ │ │ ├── stripe_card_ko_template.html │ │ │ │ │ ├── subscriptions_tab.html │ │ │ │ │ └── version_summary_tab.html │ │ │ │ ├── plan_version.html │ │ │ │ ├── plans.html │ │ │ │ ├── plans_base.html │ │ │ │ ├── report_filter_actions.html │ │ │ │ ├── subscriptions.html │ │ │ │ ├── subscriptions_base.html │ │ │ │ ├── test_reminder_emails.html │ │ │ │ ├── trigger_accounting_tests.html │ │ │ │ ├── trigger_bookkeeper.html │ │ │ │ ├── trigger_customer_invoice.html │ │ │ │ └── trigger_invoice.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base_tests.py │ │ │ │ ├── data/ │ │ │ │ │ ├── app-commcare-icon-build.json │ │ │ │ │ └── app-commcare-icon-standard.json │ │ │ │ ├── generator.py │ │ │ │ ├── test_admin_software_plans.py │ │ │ │ ├── test_autopay.py │ │ │ │ ├── test_autopay_with_api.py │ │ │ │ ├── test_card_utils.py │ │ │ │ ├── test_change_role_for_software_plan_version.py │ │ │ │ ├── test_credit_lines.py │ │ │ │ ├── test_customer_invoicing.py │ │ │ │ ├── test_domain_user_history.py │ │ │ │ ├── test_emails.py │ │ │ │ ├── test_ensure_plans.py │ │ │ │ ├── test_enterprise_mode.py │ │ │ │ ├── test_forms.py │ │ │ │ ├── test_invoice_line_items.py │ │ │ │ ├── test_invoice_pdf.py │ │ │ │ ├── test_invoicing.py │ │ │ │ ├── test_migrations.py │ │ │ │ ├── test_model_validation.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_new_domain_subscription.py │ │ │ │ ├── test_race_condition_is_prevented.py │ │ │ │ ├── test_renew_subscription.py │ │ │ │ ├── test_revoke_grants.py │ │ │ │ ├── test_software_plan_version_filter.py │ │ │ │ ├── test_stripe_payment.py │ │ │ │ ├── test_stripe_payment_method_with_api.py │ │ │ │ ├── test_stripe_utils_with_api.py │ │ │ │ ├── test_subscription_changes.py │ │ │ │ ├── test_subscription_permissions_changes.py │ │ │ │ ├── test_task_utils.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_unpaid_invoice.py │ │ │ │ ├── test_utils.py │ │ │ │ ├── test_wire_invoice.py │ │ │ │ └── utils.py │ │ │ ├── urls.py │ │ │ ├── usage.py │ │ │ ├── user_text.py │ │ │ ├── utils/ │ │ │ │ ├── __init__.py │ │ │ │ ├── account.py │ │ │ │ ├── cards.py │ │ │ │ ├── invoicing.py │ │ │ │ ├── software_plans.py │ │ │ │ ├── stripe.py │ │ │ │ ├── subscription.py │ │ │ │ └── unpaid_invoice.py │ │ │ └── views.py │ │ ├── analytics/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── ab_tests.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── audit_user_in_hubspot.py │ │ │ │ ├── blocked_hubspot_users_summary.py │ │ │ │ ├── list_blocked_from_hubspot.py │ │ │ │ ├── manually_cleanup_blocked_hubspot_contacts.py │ │ │ │ └── update_hubspot_properties.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_data_point_unique_constraint.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── signals.py │ │ │ ├── static/ │ │ │ │ └── analytix/ │ │ │ │ └── js/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── cta_forms.js │ │ │ │ │ └── hubspot.js │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── cta_forms.js │ │ │ │ │ └── hubspot.js │ │ │ │ ├── google.js │ │ │ │ ├── gtx.js │ │ │ │ ├── initial.js │ │ │ │ ├── logging.js │ │ │ │ ├── noopMetrics.js │ │ │ │ └── utils.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── analytics/ │ │ │ │ ├── email/ │ │ │ │ │ ├── partner_analytics_report.html │ │ │ │ │ └── partner_analytics_report.txt │ │ │ │ ├── forms/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ └── hubspot_cta_form.html │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ └── hubspot_cta_form.html │ │ │ │ ├── google.html │ │ │ │ └── initial/ │ │ │ │ ├── all.html │ │ │ │ ├── global.html │ │ │ │ ├── google.html │ │ │ │ ├── gtm.html │ │ │ │ └── hubspot.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_hubspot.py │ │ │ │ ├── test_partner_analytics_utils.py │ │ │ │ ├── test_subscription_properties.py │ │ │ │ ├── test_tasks.py │ │ │ │ └── test_utils.py │ │ │ ├── urls.py │ │ │ ├── utils/ │ │ │ │ ├── __init__.py │ │ │ │ ├── hubspot.py │ │ │ │ └── partner_analytics.py │ │ │ └── views.py │ │ ├── api/ │ │ │ ├── __init__.py │ │ │ ├── accounting.py │ │ │ ├── cors.py │ │ │ ├── decorators.py │ │ │ ├── domain_metadata.py │ │ │ ├── es.py │ │ │ ├── exceptions.py │ │ │ ├── fields.py │ │ │ ├── keyset_paginator.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_alter_permissions.py │ │ │ │ ├── 0003_populate_apiuser.py │ │ │ │ ├── 0004_rename_apiuser.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── object_fetch_api.py │ │ │ ├── odata/ │ │ │ │ ├── __init__.py │ │ │ │ ├── serializers.py │ │ │ │ ├── tests/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── data/ │ │ │ │ │ │ ├── populated_case_odata_metadata_document_from_config.xml │ │ │ │ │ │ └── populated_form_odata_metadata_document_from_config.xml │ │ │ │ │ ├── test_auth.py │ │ │ │ │ ├── test_feed.py │ │ │ │ │ ├── test_metadata.py │ │ │ │ │ ├── test_models.py │ │ │ │ │ ├── test_serializers.py │ │ │ │ │ ├── test_service.py │ │ │ │ │ └── utils.py │ │ │ │ ├── urls.py │ │ │ │ ├── utils.py │ │ │ │ └── views.py │ │ │ ├── query_adapters.py │ │ │ ├── resources/ │ │ │ │ ├── __init__.py │ │ │ │ ├── auth.py │ │ │ │ ├── messaging_event/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── filters.py │ │ │ │ │ ├── pagination.py │ │ │ │ │ ├── serializers.py │ │ │ │ │ ├── utils.py │ │ │ │ │ └── view.py │ │ │ │ ├── meta.py │ │ │ │ ├── pagination.py │ │ │ │ ├── serializers.py │ │ │ │ ├── v0_1.py │ │ │ │ ├── v0_3.py │ │ │ │ ├── v0_4.py │ │ │ │ ├── v0_5.py │ │ │ │ └── v1_0.py │ │ │ ├── serializers.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── api/ │ │ │ │ └── odata_metadata.xml │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── app_resources.py │ │ │ │ ├── audit_resources.py │ │ │ │ ├── case_resources.py │ │ │ │ ├── core_api.py │ │ │ │ ├── form_resources.py │ │ │ │ ├── group_resources.py │ │ │ │ ├── internal_resources.py │ │ │ │ ├── keyset_paginator_tests.py │ │ │ │ ├── lookup_table_resources.py │ │ │ │ ├── test_auth.py │ │ │ │ ├── test_det_export_resource.py │ │ │ │ ├── test_es.py │ │ │ │ ├── test_messaging_event_api.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_ucr_resources.py │ │ │ │ ├── test_user_resources.py │ │ │ │ ├── test_user_updates.py │ │ │ │ ├── test_validation.py │ │ │ │ └── utils.py │ │ │ ├── urls.py │ │ │ ├── user_updates.py │ │ │ ├── util.py │ │ │ └── validation.py │ │ ├── app_execution/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── api.py │ │ │ ├── apps.py │ │ │ ├── const.py │ │ │ ├── data_model/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── expectations.py │ │ │ │ └── steps.py │ │ │ ├── db_accessors.py │ │ │ ├── exceptions.py │ │ │ ├── forms.py │ │ │ ├── har_parser.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ └── clean_har_file.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_alter_appworkflowconfig_unique_together.py │ │ │ │ ├── 0003_appexecutionlog_updated.py │ │ │ │ ├── 0004_alter_appworkflowconfig_notification_emails_and_more.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── app_execution/ │ │ │ │ └── js/ │ │ │ │ ├── workflow_charts.js │ │ │ │ └── workflow_logs.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── app_execution/ │ │ │ │ ├── components/ │ │ │ │ │ ├── logs.html │ │ │ │ │ └── title_bar.html │ │ │ │ ├── workflow_form.html │ │ │ │ ├── workflow_list.html │ │ │ │ ├── workflow_log.html │ │ │ │ ├── workflow_log_list.html │ │ │ │ └── workflow_run.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ ├── case_list.json │ │ │ │ │ ├── case_list_action.json │ │ │ │ │ ├── reg_form.json │ │ │ │ │ ├── search_again.json │ │ │ │ │ ├── split_case_search_search.json │ │ │ │ │ └── split_case_search_select.json │ │ │ │ ├── mock_formplayer.py │ │ │ │ ├── response_factory.py │ │ │ │ ├── test_dsl.py │ │ │ │ ├── test_execution.py │ │ │ │ ├── test_expect_xpath.py │ │ │ │ ├── test_expectations.py │ │ │ │ ├── test_har_extract.py │ │ │ │ ├── test_question_value.py │ │ │ │ ├── test_steps.py │ │ │ │ └── utils.py │ │ │ ├── urls.py │ │ │ ├── views.py │ │ │ └── workflow_dsl.py │ │ ├── app_manager/ │ │ │ ├── __init__.py │ │ │ ├── _design/ │ │ │ │ ├── filters/ │ │ │ │ │ └── all_apps.js │ │ │ │ └── views/ │ │ │ │ ├── applications/ │ │ │ │ │ └── map.js │ │ │ │ ├── applications_brief/ │ │ │ │ │ └── map.js │ │ │ │ ├── builds_by_date/ │ │ │ │ │ ├── map.js │ │ │ │ │ └── reduce.js │ │ │ │ └── saved_app/ │ │ │ │ └── map.js │ │ │ ├── add_ons.py │ │ │ ├── analytics.py │ │ │ ├── app_schemas/ │ │ │ │ ├── __init__.py │ │ │ │ ├── app_case_metadata.py │ │ │ │ ├── case_properties.py │ │ │ │ ├── casedb_schema.py │ │ │ │ ├── form_metadata.py │ │ │ │ ├── session_schema.py │ │ │ │ └── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_app_diffs.py │ │ │ │ ├── test_case_meta.py │ │ │ │ ├── test_case_properties.py │ │ │ │ ├── test_form_metadata.py │ │ │ │ └── test_schema.py │ │ │ ├── app_strings.py │ │ │ ├── commcare_settings.py │ │ │ ├── const.py │ │ │ ├── current_builds.py │ │ │ ├── dbaccessors.py │ │ │ ├── decorators.py │ │ │ ├── detail_screen.py │ │ │ ├── download_urls.py │ │ │ ├── exceptions.py │ │ │ ├── feature_support.py │ │ │ ├── fields.py │ │ │ ├── fixtures/ │ │ │ │ ├── __init__.py │ │ │ │ └── mobile_ucr.py │ │ │ ├── forms.py │ │ │ ├── helpers/ │ │ │ │ ├── __init__.py │ │ │ │ └── validators.py │ │ │ ├── id_strings.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── _form_iterator.py │ │ │ │ ├── add_case_tile_template_field.py │ │ │ │ ├── add_resource_overrides.py │ │ │ │ ├── applications_with_add_ons.py │ │ │ │ ├── audit_case_search_config.py │ │ │ │ ├── audit_form_questions.py │ │ │ │ ├── benchmark_build_times.py │ │ │ │ ├── benchmark_direct_ccz.py │ │ │ │ ├── check_for_form_entities.py │ │ │ │ ├── check_for_xss_attempts.py │ │ │ │ ├── delete_case_list_custom_variables_xml.py │ │ │ │ ├── download_app_forms.py │ │ │ │ ├── fix_app_docs_with_empty_keys.py │ │ │ │ ├── fix_broken_blob_references.py │ │ │ │ ├── helpers.py │ │ │ │ ├── import_app.py │ │ │ │ ├── migrate_address_popup.py │ │ │ │ ├── migrate_case_list_custom_variables.py │ │ │ │ ├── overwrite_app.py │ │ │ │ └── upload_app_forms.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_linked_app_domain.py │ │ │ │ ├── 0002_latestenabledbuildprofiles.py │ │ │ │ ├── 0003_auto_20190326_0853.py │ │ │ │ ├── 0004_latestenabledbuildprofiles_active.py │ │ │ │ ├── 0005_latestenabledbuildprofiles_domain.py │ │ │ │ ├── 0006_multi_master_linked_apps.py │ │ │ │ ├── 0007_add_linked_app_fields_to_es.py │ │ │ │ ├── 0008_remove_uses_master_app_form_ids.py │ │ │ │ ├── 0009_add_sqlglobalappconfig.py │ │ │ │ ├── 0009_resourceoverride.py │ │ │ │ ├── 0010_merge_20191211_1921.py │ │ │ │ ├── 0010_run_add_resource_overrides.py │ │ │ │ ├── 0010_sqlglobalappconfig.py │ │ │ │ ├── 0011_merge_20191212_0106.py │ │ │ │ ├── 0011_merge_20191212_0936.py │ │ │ │ ├── 0012_merge_20191217_0605.py │ │ │ │ ├── 0013_rename_sqlglobalappconfig.py │ │ │ │ ├── 0014_create_exchangeapplication.py │ │ │ │ ├── 0015_alter_exchangeapplication.py │ │ │ │ ├── 0016_alter_exchangeapplication.py │ │ │ │ ├── 0017_migrate_case_search_relevant.py │ │ │ │ ├── 0018_migrate_case_search_labels.py │ │ │ │ ├── 0019_exchangeapplication_required_privileges.py │ │ │ │ ├── 0020_exchangeapplication_allow_blank_privilege.py │ │ │ │ ├── 0021_migrate_case_search_itemset_ids.py │ │ │ │ ├── 0022_migrate_to_conditional_case_update.py │ │ │ │ ├── 0023_applicationreleaselog.py │ │ │ │ ├── 0024_applicationreleaselog_info.py │ │ │ │ ├── 0025_migrate_to_search_filter_flag.py │ │ │ │ ├── 0026_conditional_case_update_deleted_apps.py │ │ │ │ ├── 0027_add_case_tile_template_field.py │ │ │ │ ├── 0028_case_list_custom_variable_xml_to_dict.py │ │ │ │ ├── 0029_delete_case_list_custom_variable_xml.py │ │ │ │ ├── 0030_credentialapplication.py │ │ │ │ ├── 0031_delete_latestenabledbuildprofiles.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── remote_app.py │ │ │ ├── signals.py │ │ │ ├── static/ │ │ │ │ └── app_manager/ │ │ │ │ ├── css/ │ │ │ │ │ └── diff.css │ │ │ │ ├── js/ │ │ │ │ │ ├── app_exchange.js │ │ │ │ │ ├── app_manager_utils.js │ │ │ │ │ ├── app_view.js │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── add_ons.js │ │ │ │ │ │ ├── app_manager.js │ │ │ │ │ │ ├── app_manager_media.js │ │ │ │ │ │ ├── download_async_modal.js │ │ │ │ │ │ ├── download_index_main.js │ │ │ │ │ │ ├── manage_releases_by_location.js │ │ │ │ │ │ ├── multimedia_size_util.js │ │ │ │ │ │ ├── nav_menu_media.js │ │ │ │ │ │ ├── nav_menu_media_common.js │ │ │ │ │ │ ├── preview_app.js │ │ │ │ │ │ ├── source_files.js │ │ │ │ │ │ ├── supported_languages.js │ │ │ │ │ │ └── visit_scheduler.js │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── add_ons.js │ │ │ │ │ │ ├── app_manager.js │ │ │ │ │ │ ├── app_manager_media.js │ │ │ │ │ │ ├── download_async_modal.js │ │ │ │ │ │ ├── download_index_main.js │ │ │ │ │ │ ├── manage_releases_by_location.js │ │ │ │ │ │ ├── multimedia_size_util.js │ │ │ │ │ │ ├── nav_menu_media.js │ │ │ │ │ │ ├── nav_menu_media_common.js │ │ │ │ │ │ ├── preview_app.js │ │ │ │ │ │ ├── source_files.js │ │ │ │ │ │ ├── supported_languages.js │ │ │ │ │ │ └── visit_scheduler.js │ │ │ │ │ ├── case_config_utils.js │ │ │ │ │ ├── custom_assertions.js │ │ │ │ │ ├── custom_icon.js │ │ │ │ │ ├── details/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── case_list_callout.js │ │ │ │ │ │ │ ├── column.js │ │ │ │ │ │ │ ├── detail_tab_nodeset.js │ │ │ │ │ │ │ ├── graph_config.js │ │ │ │ │ │ │ ├── screen.js │ │ │ │ │ │ │ ├── screen_config.js │ │ │ │ │ │ │ └── sort_rows.js │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ ├── case_list_callout.js │ │ │ │ │ │ │ ├── column.js │ │ │ │ │ │ │ ├── detail_tab_nodeset.js │ │ │ │ │ │ │ ├── graph_config.js │ │ │ │ │ │ │ ├── screen.js │ │ │ │ │ │ │ ├── screen_config.js │ │ │ │ │ │ │ └── sort_rows.js │ │ │ │ │ │ ├── case_claim.js │ │ │ │ │ │ ├── filter.js │ │ │ │ │ │ ├── fixture_select.js │ │ │ │ │ │ ├── parent_select.js │ │ │ │ │ │ └── utils.js │ │ │ │ │ ├── forms/ │ │ │ │ │ │ ├── advanced/ │ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ │ ├── actions.js │ │ │ │ │ │ │ │ └── case_config_ui.js │ │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ │ ├── actions.js │ │ │ │ │ │ │ │ └── case_config_ui.js │ │ │ │ │ │ │ └── case_properties.js │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── case_config_ui.js │ │ │ │ │ │ │ ├── form_designer.js │ │ │ │ │ │ │ ├── form_view.js │ │ │ │ │ │ │ └── form_workflow.js │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ ├── case_config_ui.js │ │ │ │ │ │ │ ├── form_designer.js │ │ │ │ │ │ │ ├── form_view.js │ │ │ │ │ │ │ └── form_workflow.js │ │ │ │ │ │ ├── case_knockout_bindings.js │ │ │ │ │ │ ├── copy_form_to_app.js │ │ │ │ │ │ ├── custom_instances.js │ │ │ │ │ │ ├── edit_form_details.js │ │ │ │ │ │ ├── form_action_diffs.js │ │ │ │ │ │ └── form_designer_analytics.js │ │ │ │ │ ├── menu.js │ │ │ │ │ ├── modules/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── module_view.js │ │ │ │ │ │ │ ├── module_view_report.js │ │ │ │ │ │ │ └── report_module.js │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ ├── module_view.js │ │ │ │ │ │ │ ├── module_view_report.js │ │ │ │ │ │ │ └── report_module.js │ │ │ │ │ │ ├── case_list_setting.js │ │ │ │ │ │ └── shadow_module_settings.js │ │ │ │ │ ├── releases/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── app_view_release_manager.js │ │ │ │ │ │ │ ├── language_profiles.js │ │ │ │ │ │ │ ├── releases.js │ │ │ │ │ │ │ └── update_prompt.js │ │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ │ ├── app_view_release_manager.js │ │ │ │ │ │ ├── language_profiles.js │ │ │ │ │ │ ├── releases.js │ │ │ │ │ │ └── update_prompt.js │ │ │ │ │ ├── section_changer.js │ │ │ │ │ ├── settings/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── app_logos.js │ │ │ │ │ │ │ └── commcare_settings.js │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ ├── app_logos.js │ │ │ │ │ │ │ └── commcare_settings.js │ │ │ │ │ │ └── translations.js │ │ │ │ │ ├── summary/ │ │ │ │ │ │ ├── case_summary.js │ │ │ │ │ │ ├── form_diff.js │ │ │ │ │ │ ├── form_models.js │ │ │ │ │ │ ├── form_summary.js │ │ │ │ │ │ ├── models.js │ │ │ │ │ │ └── utils.js │ │ │ │ │ ├── vellum/ │ │ │ │ │ │ ├── 1307.js │ │ │ │ │ │ ├── 150.js │ │ │ │ │ │ ├── 1664.js │ │ │ │ │ │ ├── 1879.js │ │ │ │ │ │ ├── 2229.js │ │ │ │ │ │ ├── 2773.js │ │ │ │ │ │ ├── 2979.js │ │ │ │ │ │ ├── 3333.js │ │ │ │ │ │ ├── 3534.js │ │ │ │ │ │ ├── 3576.js │ │ │ │ │ │ ├── 358.js │ │ │ │ │ │ ├── 3588.js │ │ │ │ │ │ ├── 3606.js │ │ │ │ │ │ ├── 3720.js │ │ │ │ │ │ ├── 375.js │ │ │ │ │ │ ├── 395.js │ │ │ │ │ │ ├── 3984.js │ │ │ │ │ │ ├── 4002.js │ │ │ │ │ │ ├── 4054.js │ │ │ │ │ │ ├── 4141.js │ │ │ │ │ │ ├── 4194.js │ │ │ │ │ │ ├── 4334.js │ │ │ │ │ │ ├── 4437.js │ │ │ │ │ │ ├── 4518.js │ │ │ │ │ │ ├── 4580.js │ │ │ │ │ │ ├── 4636.js │ │ │ │ │ │ ├── 4730.js │ │ │ │ │ │ ├── 477.js │ │ │ │ │ │ ├── 5051.js │ │ │ │ │ │ ├── 5319.js │ │ │ │ │ │ ├── 5455.js │ │ │ │ │ │ ├── 5460.js │ │ │ │ │ │ ├── 5498.js │ │ │ │ │ │ ├── 571.js │ │ │ │ │ │ ├── 576.js │ │ │ │ │ │ ├── 6077.js │ │ │ │ │ │ ├── 6129.js │ │ │ │ │ │ ├── 6367.js │ │ │ │ │ │ ├── 6414.js │ │ │ │ │ │ ├── 6794.js │ │ │ │ │ │ ├── 6940.js │ │ │ │ │ │ ├── 711.js │ │ │ │ │ │ ├── 711.js.LICENSE.txt │ │ │ │ │ │ ├── 7574.js │ │ │ │ │ │ ├── 7828.js │ │ │ │ │ │ ├── 8324.js │ │ │ │ │ │ ├── 8324.js.LICENSE.txt │ │ │ │ │ │ ├── 8454.js │ │ │ │ │ │ ├── 8985.js │ │ │ │ │ │ ├── 9084.js │ │ │ │ │ │ ├── 9084.js.LICENSE.txt │ │ │ │ │ │ ├── 9928.js │ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ │ ├── SaveButton.js │ │ │ │ │ │ │ └── diff_match_patch.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ ├── main.js.LICENSE.txt │ │ │ │ │ │ ├── manifest.txt │ │ │ │ │ │ ├── tests.js │ │ │ │ │ │ ├── tests.js.LICENSE.txt │ │ │ │ │ │ └── version.txt │ │ │ │ │ ├── widgets.js │ │ │ │ │ └── xpathValidator.js │ │ │ │ ├── json/ │ │ │ │ │ ├── case-reserved-words.json │ │ │ │ │ ├── commcare-app-settings.yml │ │ │ │ │ ├── commcare-profile-settings.yml │ │ │ │ │ ├── commcare-settings-layout.yml │ │ │ │ │ └── vellum-app-callout-templates.yml │ │ │ │ ├── spec/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── download_async_modal_spec.js │ │ │ │ │ │ ├── form_workflow_spec.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ └── releases_spec.js │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ ├── download_async_modal_spec.js │ │ │ │ │ ├── form_workflow_spec.js │ │ │ │ │ ├── main.js │ │ │ │ │ └── releases_spec.js │ │ │ │ └── template_apps/ │ │ │ │ ├── agriculture/ │ │ │ │ │ ├── app.json │ │ │ │ │ └── multimedia.json │ │ │ │ ├── health/ │ │ │ │ │ ├── app.json │ │ │ │ │ └── multimedia.json │ │ │ │ ├── wash/ │ │ │ │ │ ├── app.json │ │ │ │ │ └── multimedia.json │ │ │ │ └── wash_before_cond_case_update/ │ │ │ │ └── app.json │ │ │ ├── static_strings.py │ │ │ ├── suite_xml/ │ │ │ │ ├── __init__.py │ │ │ │ ├── case_tile_templates/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── icon_text_grid.json │ │ │ │ │ ├── icon_text_grid.xml │ │ │ │ │ ├── person_simple.json │ │ │ │ │ └── person_simple.xml │ │ │ │ ├── const.py │ │ │ │ ├── contributors.py │ │ │ │ ├── features/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── case_tiles.py │ │ │ │ │ ├── mobile_ucr.py │ │ │ │ │ └── scheduler.py │ │ │ │ ├── generator.py │ │ │ │ ├── post_process/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── endpoints.py │ │ │ │ │ ├── instances.py │ │ │ │ │ ├── menu.py │ │ │ │ │ ├── remote_requests.py │ │ │ │ │ ├── resources.py │ │ │ │ │ └── workflow.py │ │ │ │ ├── sections/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── details.py │ │ │ │ │ ├── entries.py │ │ │ │ │ ├── fixtures.py │ │ │ │ │ ├── menus.py │ │ │ │ │ └── resources.py │ │ │ │ ├── utils.py │ │ │ │ └── xml_models.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── app_manager/ │ │ │ │ ├── CommCare.jad │ │ │ │ ├── app_exchange.html │ │ │ │ ├── app_view_release_manager.html │ │ │ │ ├── app_view_settings.html │ │ │ │ ├── base_summary.html │ │ │ │ ├── blank_form.xml │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── app_diff.html │ │ │ │ │ ├── app_view.html │ │ │ │ │ ├── apps_base.html │ │ │ │ │ ├── download_index.html │ │ │ │ │ ├── form_designer.html │ │ │ │ │ ├── form_view.html │ │ │ │ │ ├── module_view.html │ │ │ │ │ ├── module_view_advanced.html │ │ │ │ │ ├── module_view_report.html │ │ │ │ │ ├── no_longer_supported.html │ │ │ │ │ ├── odk_install.html │ │ │ │ │ └── source_files.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── app_diff.html │ │ │ │ │ ├── app_view.html │ │ │ │ │ ├── apps_base.html │ │ │ │ │ ├── download_index.html │ │ │ │ │ ├── form_designer.html │ │ │ │ │ ├── form_view.html │ │ │ │ │ ├── module_view.html │ │ │ │ │ ├── module_view_advanced.html │ │ │ │ │ ├── module_view_report.html │ │ │ │ │ ├── no_longer_supported.html │ │ │ │ │ ├── odk_install.html │ │ │ │ │ └── source_files.html │ │ │ │ ├── case_summary.html │ │ │ │ ├── default_followup_form.xml │ │ │ │ ├── form_summary.html │ │ │ │ ├── form_summary_base.html │ │ │ │ ├── form_summary_diff.html │ │ │ │ ├── languages.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── add_item_templates.html │ │ │ │ │ │ ├── add_new_module_modal.html │ │ │ │ │ │ ├── app_summary_button.html │ │ │ │ │ │ ├── appnav_menu_header.html │ │ │ │ │ │ ├── appnav_menu_langs.html │ │ │ │ │ │ ├── apps_stylesheets.html │ │ │ │ │ │ ├── build_errors.html │ │ │ │ │ │ ├── build_profiles_select.html │ │ │ │ │ │ ├── case_list_session_endpoint.html │ │ │ │ │ │ ├── confirm_delete_app.html │ │ │ │ │ │ ├── custom_icon.html │ │ │ │ │ │ ├── define_case_type_modal.html │ │ │ │ │ │ ├── download_async_modal.html │ │ │ │ │ │ ├── form_session_endpoint.html │ │ │ │ │ │ ├── function_datum_endpoints.html │ │ │ │ │ │ ├── load_save_questions.html │ │ │ │ │ │ ├── module_session_endpoint.html │ │ │ │ │ │ ├── nav_menu_media.html │ │ │ │ │ │ ├── nav_menu_media_single_type.html │ │ │ │ │ │ ├── toggle_diff_modal.html │ │ │ │ │ │ └── undo_delete_app.html │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── add_item_templates.html │ │ │ │ │ │ ├── add_new_module_modal.html │ │ │ │ │ │ ├── app_summary_button.html │ │ │ │ │ │ ├── appnav_menu_header.html │ │ │ │ │ │ ├── appnav_menu_langs.html │ │ │ │ │ │ ├── apps_stylesheets.html │ │ │ │ │ │ ├── build_errors.html │ │ │ │ │ │ ├── build_profiles_select.html │ │ │ │ │ │ ├── case_list_session_endpoint.html │ │ │ │ │ │ ├── confirm_delete_app.html │ │ │ │ │ │ ├── custom_icon.html │ │ │ │ │ │ ├── define_case_type_modal.html │ │ │ │ │ │ ├── download_async_modal.html │ │ │ │ │ │ ├── form_session_endpoint.html │ │ │ │ │ │ ├── function_datum_endpoints.html │ │ │ │ │ │ ├── load_save_questions.html │ │ │ │ │ │ ├── module_session_endpoint.html │ │ │ │ │ │ ├── nav_menu_media.html │ │ │ │ │ │ ├── nav_menu_media_single_type.html │ │ │ │ │ │ ├── toggle_diff_modal.html │ │ │ │ │ │ └── undo_delete_app.html │ │ │ │ │ ├── bulk_upload_app_translations.html │ │ │ │ │ ├── case_summary_case_details.html │ │ │ │ │ ├── form_error_message.html │ │ │ │ │ ├── form_summary_header.html │ │ │ │ │ ├── forms/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── case_config.html │ │ │ │ │ │ │ ├── case_config_advanced.html │ │ │ │ │ │ │ ├── case_config_ko_templates.html │ │ │ │ │ │ │ ├── custom_assertions.html │ │ │ │ │ │ │ ├── custom_instances.html │ │ │ │ │ │ │ ├── form_filter.html │ │ │ │ │ │ │ ├── form_gps_capture.html │ │ │ │ │ │ │ ├── form_tab_advanced.html │ │ │ │ │ │ │ ├── form_tab_settings.html │ │ │ │ │ │ │ ├── form_tab_visit_scheduler.html │ │ │ │ │ │ │ ├── form_view_modals.html │ │ │ │ │ │ │ ├── form_workflow.html │ │ │ │ │ │ │ ├── release_notes_setting.html │ │ │ │ │ │ │ ├── shadow_parent_select.html │ │ │ │ │ │ │ └── usercase_config.html │ │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ │ ├── case_config.html │ │ │ │ │ │ ├── case_config_advanced.html │ │ │ │ │ │ ├── case_config_ko_templates.html │ │ │ │ │ │ ├── custom_assertions.html │ │ │ │ │ │ ├── custom_instances.html │ │ │ │ │ │ ├── form_filter.html │ │ │ │ │ │ ├── form_gps_capture.html │ │ │ │ │ │ ├── form_tab_advanced.html │ │ │ │ │ │ ├── form_tab_settings.html │ │ │ │ │ │ ├── form_tab_visit_scheduler.html │ │ │ │ │ │ ├── form_view_modals.html │ │ │ │ │ │ ├── form_workflow.html │ │ │ │ │ │ ├── release_notes_setting.html │ │ │ │ │ │ ├── shadow_parent_select.html │ │ │ │ │ │ └── usercase_config.html │ │ │ │ │ ├── menu/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── appnav_menu.html │ │ │ │ │ │ │ ├── form_link.html │ │ │ │ │ │ │ └── module_link.html │ │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ │ ├── appnav_menu.html │ │ │ │ │ │ ├── form_link.html │ │ │ │ │ │ └── module_link.html │ │ │ │ │ ├── mobile_ucr_v1_warning.html │ │ │ │ │ ├── module_options/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── module_option_advanced.html │ │ │ │ │ │ │ ├── module_option_biometrics.html │ │ │ │ │ │ │ ├── module_option_button_base.html │ │ │ │ │ │ │ ├── module_option_case.html │ │ │ │ │ │ │ ├── module_option_report.html │ │ │ │ │ │ │ ├── module_option_shadow.html │ │ │ │ │ │ │ ├── module_option_shadow_v1.html │ │ │ │ │ │ │ ├── module_option_survey.html │ │ │ │ │ │ │ └── module_option_training.html │ │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ │ ├── module_option_advanced.html │ │ │ │ │ │ ├── module_option_biometrics.html │ │ │ │ │ │ ├── module_option_button_base.html │ │ │ │ │ │ ├── module_option_case.html │ │ │ │ │ │ ├── module_option_report.html │ │ │ │ │ │ ├── module_option_shadow.html │ │ │ │ │ │ ├── module_option_shadow_v1.html │ │ │ │ │ │ ├── module_option_survey.html │ │ │ │ │ │ └── module_option_training.html │ │ │ │ │ ├── modules/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── add_property_button.html │ │ │ │ │ │ │ ├── case_detail.html │ │ │ │ │ │ │ ├── case_list.html │ │ │ │ │ │ │ ├── case_list_filtering.html │ │ │ │ │ │ │ ├── case_list_form_setting.html │ │ │ │ │ │ │ ├── case_list_lazy_loading.html │ │ │ │ │ │ │ ├── case_list_lookup.html │ │ │ │ │ │ │ ├── case_list_missing_warning.html │ │ │ │ │ │ │ ├── case_list_module_overwrite.html │ │ │ │ │ │ │ ├── case_list_multi_select.html │ │ │ │ │ │ │ ├── case_list_properties.html │ │ │ │ │ │ │ ├── case_list_setting.html │ │ │ │ │ │ │ ├── case_search_properties.html │ │ │ │ │ │ │ ├── case_search_property.html │ │ │ │ │ │ │ ├── case_tile_preview.html │ │ │ │ │ │ │ ├── case_tile_templates.html │ │ │ │ │ │ │ ├── custom_detail_variables.html │ │ │ │ │ │ │ ├── enable_schedule.html │ │ │ │ │ │ │ ├── filter_configs.html │ │ │ │ │ │ │ ├── fixture_case_selection.html │ │ │ │ │ │ │ ├── graph_configuration_modal.html │ │ │ │ │ │ │ ├── mobile_report_configs.html │ │ │ │ │ │ │ ├── module_actions.html │ │ │ │ │ │ │ ├── module_filter.html │ │ │ │ │ │ │ ├── module_view_case_type.html │ │ │ │ │ │ │ ├── module_view_heading.html │ │ │ │ │ │ │ ├── module_view_settings.html │ │ │ │ │ │ │ ├── report_context_tile.html │ │ │ │ │ │ │ └── style_configuration_modal.html │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ ├── add_property_button.html │ │ │ │ │ │ │ ├── case_detail.html │ │ │ │ │ │ │ ├── case_list.html │ │ │ │ │ │ │ ├── case_list_filtering.html │ │ │ │ │ │ │ ├── case_list_form_setting.html │ │ │ │ │ │ │ ├── case_list_lazy_loading.html │ │ │ │ │ │ │ ├── case_list_lookup.html │ │ │ │ │ │ │ ├── case_list_missing_warning.html │ │ │ │ │ │ │ ├── case_list_module_overwrite.html │ │ │ │ │ │ │ ├── case_list_multi_select.html │ │ │ │ │ │ │ ├── case_list_properties.html │ │ │ │ │ │ │ ├── case_list_setting.html │ │ │ │ │ │ │ ├── case_search_properties.html │ │ │ │ │ │ │ ├── case_search_property.html │ │ │ │ │ │ │ ├── case_tile_preview.html │ │ │ │ │ │ │ ├── case_tile_templates.html │ │ │ │ │ │ │ ├── custom_detail_variables.html │ │ │ │ │ │ │ ├── enable_schedule.html │ │ │ │ │ │ │ ├── filter_configs.html │ │ │ │ │ │ │ ├── fixture_case_selection.html │ │ │ │ │ │ │ ├── graph_configuration_modal.html │ │ │ │ │ │ │ ├── mobile_report_configs.html │ │ │ │ │ │ │ ├── module_actions.html │ │ │ │ │ │ │ ├── module_filter.html │ │ │ │ │ │ │ ├── module_view_case_type.html │ │ │ │ │ │ │ ├── module_view_heading.html │ │ │ │ │ │ │ ├── module_view_settings.html │ │ │ │ │ │ │ ├── report_context_tile.html │ │ │ │ │ │ │ └── style_configuration_modal.html │ │ │ │ │ │ └── graph_configs.html │ │ │ │ │ ├── multimedia_sizes.html │ │ │ │ │ ├── preview_app.html │ │ │ │ │ ├── releases/ │ │ │ │ │ │ ├── app_release_logs.html │ │ │ │ │ │ ├── build_profiles.html │ │ │ │ │ │ ├── releases.html │ │ │ │ │ │ ├── releases_deploy_modal.html │ │ │ │ │ │ ├── releases_deploy_modal_sms.html │ │ │ │ │ │ └── releases_table.html │ │ │ │ │ ├── settings/ │ │ │ │ │ │ ├── add_ons.html │ │ │ │ │ │ ├── app_settings.html │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ └── supported_languages.html │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ └── supported_languages.html │ │ │ │ │ │ ├── commcare_settings.html │ │ │ │ │ │ └── multimedia_ajax.html │ │ │ │ │ ├── vellum_case_management_warning.html │ │ │ │ │ └── view_submissions_button.html │ │ │ │ ├── profile.xml │ │ │ │ ├── simprints_enrolment_form.xml │ │ │ │ ├── simprints_followup_form.xml │ │ │ │ └── spec/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ └── mocha.html │ │ │ │ └── bootstrap5/ │ │ │ │ └── mocha.html │ │ │ ├── templatetags/ │ │ │ │ ├── __init__.py │ │ │ │ ├── app_manager_extras.py │ │ │ │ ├── url_extras.py │ │ │ │ └── xforms_extras.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── app_factory.py │ │ │ │ ├── data/ │ │ │ │ │ ├── bad_case_tile_config.json │ │ │ │ │ ├── case_in_form.xml │ │ │ │ │ ├── case_list_form/ │ │ │ │ │ │ ├── case-list-form-advanced-autoload.xml │ │ │ │ │ │ ├── case-list-form-advanced.xml │ │ │ │ │ │ ├── case-list-form-suite-form-nav-entry.xml │ │ │ │ │ │ ├── case-list-form-suite-multiple-references.xml │ │ │ │ │ │ ├── case-list-form-suite-no-media-partial.xml │ │ │ │ │ │ ├── case-list-form-suite-parent-child-advanced.xml │ │ │ │ │ │ ├── case-list-form-suite-parent-child-basic.xml │ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-advanced-rename-var.xml │ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-advanced.xml │ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-basic.xml │ │ │ │ │ │ ├── case-list-form-suite-parent-child-submodule-mixed.xml │ │ │ │ │ │ ├── case-list-form-suite-usercase.xml │ │ │ │ │ │ ├── case-list-form-suite.xml │ │ │ │ │ │ ├── case_list_form_advanced_form.xml │ │ │ │ │ │ ├── case_list_form_basic_form.xml │ │ │ │ │ │ ├── case_list_form_end_of_form_case_list.xml │ │ │ │ │ │ ├── case_list_form_end_of_form_case_list_clmi_only.xml │ │ │ │ │ │ ├── case_list_form_reg_form_creates_child_case.xml │ │ │ │ │ │ ├── source_requires_case_target_doesnt.xml │ │ │ │ │ │ └── target_module_different_datums.xml │ │ │ │ │ ├── case_meta/ │ │ │ │ │ │ └── standard_questions.xml │ │ │ │ │ ├── cyclical-app.json │ │ │ │ │ ├── days_ago_migration.json │ │ │ │ │ ├── days_ago_suite.xml │ │ │ │ │ ├── duplicate_text_questions.xml │ │ │ │ │ ├── extension_case/ │ │ │ │ │ │ ├── case_index_relationship_question.xml │ │ │ │ │ │ ├── open_subcase.xml │ │ │ │ │ │ ├── open_subcase_child.xml │ │ │ │ │ │ ├── original.xml │ │ │ │ │ │ ├── preload_host_case.xml │ │ │ │ │ │ ├── preload_host_case_adv.xml │ │ │ │ │ │ ├── update_host_case.xml │ │ │ │ │ │ └── update_host_case_adv.xml │ │ │ │ │ ├── fixtures/ │ │ │ │ │ │ ├── expected_v1_report.xml │ │ │ │ │ │ ├── expected_v2_report.xml │ │ │ │ │ │ └── expected_v2_report_total.xml │ │ │ │ │ ├── form_preparation_v2/ │ │ │ │ │ │ ├── attachment.xml │ │ │ │ │ │ ├── close_case.xml │ │ │ │ │ │ ├── complex-case-sharing.json │ │ │ │ │ │ ├── complex-case-sharing.xml │ │ │ │ │ │ ├── gps.json │ │ │ │ │ │ ├── gps_no_question.xml │ │ │ │ │ │ ├── gps_no_question_auto.xml │ │ │ │ │ │ ├── gps_with_question.xml │ │ │ │ │ │ ├── gps_with_question_auto.xml │ │ │ │ │ │ ├── ignore_retain.xml │ │ │ │ │ │ ├── ignore_retain_stripped.xml │ │ │ │ │ │ ├── missing_instances.xml │ │ │ │ │ │ ├── multi_no_actions.xml │ │ │ │ │ │ ├── multi_open_update_case.xml │ │ │ │ │ │ ├── multiple_subcase_repeat.json │ │ │ │ │ │ ├── multiple_subcase_repeat.xml │ │ │ │ │ │ ├── no_actions.xml │ │ │ │ │ │ ├── normal_suite.xml │ │ │ │ │ │ ├── open_case.xml │ │ │ │ │ │ ├── open_case_external_id.xml │ │ │ │ │ │ ├── open_update_case.xml │ │ │ │ │ │ ├── subcase-parent-ref.json │ │ │ │ │ │ ├── subcase-parent-ref.xml │ │ │ │ │ │ ├── subcase-repeat-sharing.xml │ │ │ │ │ │ ├── subcase-repeat.json │ │ │ │ │ │ ├── subcase-repeat.xml │ │ │ │ │ │ ├── subcase_repeat_mixed_form_post.xml │ │ │ │ │ │ ├── subcase_repeat_mixed_form_pre.xml │ │ │ │ │ │ ├── task_mode_update_preload_case.xml │ │ │ │ │ │ ├── update_attachment_case.xml │ │ │ │ │ │ ├── update_case.xml │ │ │ │ │ │ ├── update_parent_case.xml │ │ │ │ │ │ └── update_preload_case.xml │ │ │ │ │ ├── form_preparation_v2_advanced/ │ │ │ │ │ │ ├── attachment.xml │ │ │ │ │ │ ├── child_module_adjusted_case_id_advanced.xml │ │ │ │ │ │ ├── child_module_adjusted_case_id_basic.xml │ │ │ │ │ │ ├── close_case.xml │ │ │ │ │ │ ├── extension-case.xml │ │ │ │ │ │ ├── no_actions.xml │ │ │ │ │ │ ├── open_case.xml │ │ │ │ │ │ ├── open_close_case.xml │ │ │ │ │ │ ├── open_update_case.xml │ │ │ │ │ │ ├── schedule.xml │ │ │ │ │ │ ├── subcase-open.xml │ │ │ │ │ │ ├── subcase-repeat-multiple.xml │ │ │ │ │ │ ├── subcase-repeat-sharing.xml │ │ │ │ │ │ ├── subcase-repeat.xml │ │ │ │ │ │ ├── subcase.xml │ │ │ │ │ │ ├── subcase_original.xml │ │ │ │ │ │ ├── update_attachment_case.xml │ │ │ │ │ │ ├── update_case.xml │ │ │ │ │ │ ├── update_parent_case.xml │ │ │ │ │ │ ├── update_preload_case.xml │ │ │ │ │ │ └── update_preload_case_multiple.xml │ │ │ │ │ ├── form_with_fixtures.xml │ │ │ │ │ ├── form_with_repeats.xml │ │ │ │ │ ├── form_workflow/ │ │ │ │ │ │ ├── form_link_basic_form.xml │ │ │ │ │ │ ├── form_link_child_modules.xml │ │ │ │ │ │ ├── form_link_create_update_case.xml │ │ │ │ │ │ ├── form_link_enikshay.xml │ │ │ │ │ │ ├── form_link_followup_module.xml │ │ │ │ │ │ ├── form_link_multiple.xml │ │ │ │ │ │ ├── form_link_registration_module.xml │ │ │ │ │ │ ├── form_link_submodule.xml │ │ │ │ │ │ ├── form_link_tdh.xml │ │ │ │ │ │ ├── form_link_tdh_with_fallback_module.xml │ │ │ │ │ │ ├── form_link_tdh_with_fallback_previous.xml │ │ │ │ │ │ ├── form_link_tdh_with_fallback_root.xml │ │ │ │ │ │ ├── form_link_update_case.xml │ │ │ │ │ │ ├── suite-workflow-module-in-root.xml │ │ │ │ │ │ ├── suite-workflow-module.xml │ │ │ │ │ │ ├── suite-workflow-previous.xml │ │ │ │ │ │ └── suite-workflow-root.xml │ │ │ │ │ ├── itext_form.xml │ │ │ │ │ ├── itext_form_normalized.xml │ │ │ │ │ ├── label_form.xml │ │ │ │ │ ├── list_apps/ │ │ │ │ │ │ └── list_apps.xml │ │ │ │ │ ├── one_question_two_images.xml │ │ │ │ │ ├── original_form.xml │ │ │ │ │ ├── save_to_case_in_groups.xml │ │ │ │ │ ├── session_endpoint_remote_request.xml │ │ │ │ │ ├── session_endpoint_remote_request_multi_select.xml │ │ │ │ │ ├── subcase-details.json │ │ │ │ │ ├── suite/ │ │ │ │ │ │ ├── advanced_module_parent.xml │ │ │ │ │ │ ├── advanced_module_parent_filters.xml │ │ │ │ │ │ ├── advanced_module_parent_null_relationship.xml │ │ │ │ │ │ ├── advanced_submodule_xform.xml │ │ │ │ │ │ ├── app.json │ │ │ │ │ │ ├── app_attached_image.json │ │ │ │ │ │ ├── app_audio_format.json │ │ │ │ │ │ ├── app_case_detail_instances.json │ │ │ │ │ │ ├── app_case_detail_tabs.json │ │ │ │ │ │ ├── app_case_detail_tabs_with_nodesets.json │ │ │ │ │ │ ├── app_case_sharing.json │ │ │ │ │ │ ├── app_case_tiles.json │ │ │ │ │ │ ├── app_fixture_graphing.json │ │ │ │ │ │ ├── app_graphing.json │ │ │ │ │ │ ├── app_no_case_sharing.json │ │ │ │ │ │ ├── app_video_inline.json │ │ │ │ │ │ ├── autoload_supplypoint.xml │ │ │ │ │ │ ├── basic_submodule_xform.xml │ │ │ │ │ │ ├── call-center.json │ │ │ │ │ │ ├── call-center.xml │ │ │ │ │ │ ├── case-search-again-with-action.xml │ │ │ │ │ │ ├── case-search-again-with-localized-action.xml │ │ │ │ │ │ ├── case-search-with-action.xml │ │ │ │ │ │ ├── case-search-with-localized-action.xml │ │ │ │ │ │ ├── case-tile-case-detail-tabs.xml │ │ │ │ │ │ ├── case-tile-case-detail.xml │ │ │ │ │ │ ├── case_list_media_suite.xml │ │ │ │ │ │ ├── case_tile_pulldown_session.xml │ │ │ │ │ │ ├── case_tile_template_format.xml │ │ │ │ │ │ ├── child-module-entry-datums-added-advanced.xml │ │ │ │ │ │ ├── child-module-entry-datums-added-basic.xml │ │ │ │ │ │ ├── child-module-entry-datums-added-usercase.xml │ │ │ │ │ │ ├── child-module-entry-datums-parent-select-other-same-case-type.xml │ │ │ │ │ │ ├── child-module-entry-datums-parent-select-other.xml │ │ │ │ │ │ ├── child-module-entry-datums.xml │ │ │ │ │ │ ├── child-module-form-workflow-previous.xml │ │ │ │ │ │ ├── child-module-grandchild-case.xml │ │ │ │ │ │ ├── child-module-preload-parent-ref.xml │ │ │ │ │ │ ├── child-module-rename-session-vars.xml │ │ │ │ │ │ ├── child-module-with-parent-select-and-case-list.xml │ │ │ │ │ │ ├── child-module-with-parent-select-entry-datums-added.xml │ │ │ │ │ │ ├── custom-parent-ref.xml │ │ │ │ │ │ ├── fixture-to-case-selection-localization.xml │ │ │ │ │ │ ├── fixture-to-case-selection-parent-child.xml │ │ │ │ │ │ ├── fixture-to-case-selection-with-form-filtering.xml │ │ │ │ │ │ ├── fixture-to-case-selection.xml │ │ │ │ │ │ ├── form_media_suite.xml │ │ │ │ │ │ ├── form_media_suite_all.xml │ │ │ │ │ │ ├── form_media_suite_en.xml │ │ │ │ │ │ ├── form_media_suite_hin.xml │ │ │ │ │ │ ├── form_with_media_refs.xml │ │ │ │ │ │ ├── load_case_from_custom_fixture_instance.xml │ │ │ │ │ │ ├── load_case_from_custom_fixture_session.xml │ │ │ │ │ │ ├── load_case_from_fixture_arbitrary_datum.xml │ │ │ │ │ │ ├── load_case_from_fixture_instance.xml │ │ │ │ │ │ ├── load_case_from_fixture_session.xml │ │ │ │ │ │ ├── load_case_from_report_fixture_instance.xml │ │ │ │ │ │ ├── load_case_from_report_fixture_session.xml │ │ │ │ │ │ ├── load_from_fixture_autoselect_session.xml │ │ │ │ │ │ ├── load_from_fixture_instance.xml │ │ │ │ │ │ ├── load_from_fixture_session.xml │ │ │ │ │ │ ├── media-suite-lazy-false.xml │ │ │ │ │ │ ├── media-suite-lazy-true.xml │ │ │ │ │ │ ├── module-filter-user-entry.xml │ │ │ │ │ │ ├── module-filter-user.xml │ │ │ │ │ │ ├── module-filter.xml │ │ │ │ │ │ ├── multi-sort.json │ │ │ │ │ │ ├── multi-sort.xml │ │ │ │ │ │ ├── multi_select_case_list/ │ │ │ │ │ │ │ └── basic_remote_request.xml │ │ │ │ │ │ ├── normal-suite.xml │ │ │ │ │ │ ├── open_case_and_subcase.xml │ │ │ │ │ │ ├── owner-name.json │ │ │ │ │ │ ├── owner-name.xml │ │ │ │ │ │ ├── parent_null_relationship_same_type.xml │ │ │ │ │ │ ├── remote_request.xml │ │ │ │ │ │ ├── reports_module_data_detail-translated-mixed.xml │ │ │ │ │ │ ├── reports_module_data_detail-translated-simple.xml │ │ │ │ │ │ ├── reports_module_data_detail-translated.xml │ │ │ │ │ │ ├── reports_module_data_detail.xml │ │ │ │ │ │ ├── reports_module_data_entry.xml │ │ │ │ │ │ ├── reports_module_data_entry_mobile_ucr_v1.xml │ │ │ │ │ │ ├── reports_module_menu.xml │ │ │ │ │ │ ├── reports_module_menu_multimedia.xml │ │ │ │ │ │ ├── reports_module_select_detail.xml │ │ │ │ │ │ ├── reports_module_summary_detail_hide_data_table.xml │ │ │ │ │ │ ├── reports_module_summary_detail_use_localized_description.xml │ │ │ │ │ │ ├── reports_module_summary_detail_use_xpath_description.xml │ │ │ │ │ │ ├── schedule-entry.xml │ │ │ │ │ │ ├── schedule-fixture.xml │ │ │ │ │ │ ├── search_command_detail.xml │ │ │ │ │ │ ├── search_config_blacklisted_owners.xml │ │ │ │ │ │ ├── search_config_default_only.xml │ │ │ │ │ │ ├── shadow_module.json │ │ │ │ │ │ ├── shadow_module.xml │ │ │ │ │ │ ├── shadow_module_cases.json │ │ │ │ │ │ ├── shadow_module_cases.xml │ │ │ │ │ │ ├── shadow_module_families_source_child.xml │ │ │ │ │ │ ├── shadow_module_families_source_parent.xml │ │ │ │ │ │ ├── shadow_module_families_source_parent_forms_only.xml │ │ │ │ │ │ ├── shadow_module_families_source_parent_forms_only_v1.xml │ │ │ │ │ │ ├── shadow_module_families_source_parent_v1.xml │ │ │ │ │ │ ├── shadow_module_forms_only.json │ │ │ │ │ │ ├── shadow_module_forms_only.xml │ │ │ │ │ │ ├── shadow_module_source_parent_v1_v2.xml │ │ │ │ │ │ ├── smart_link_remote_request.xml │ │ │ │ │ │ ├── sort-cache-search.xml │ │ │ │ │ │ ├── sort-cache.xml │ │ │ │ │ │ ├── sort-only-value.json │ │ │ │ │ │ ├── sort-only-value.xml │ │ │ │ │ │ ├── suite-advanced-autoselect-case-mobile.xml │ │ │ │ │ │ ├── suite-advanced-autoselect-case.xml │ │ │ │ │ │ ├── suite-advanced-autoselect-fixture.xml │ │ │ │ │ │ ├── suite-advanced-autoselect-raw.xml │ │ │ │ │ │ ├── suite-advanced-autoselect-user.xml │ │ │ │ │ │ ├── suite-advanced-autoselect-usercase.xml │ │ │ │ │ │ ├── suite-advanced-autoselect-with-filter.xml │ │ │ │ │ │ ├── suite-advanced-details.xml │ │ │ │ │ │ ├── suite-advanced-filter.xml │ │ │ │ │ │ ├── suite-advanced.json │ │ │ │ │ │ ├── suite-advanced.xml │ │ │ │ │ │ ├── suite-attached-image.xml │ │ │ │ │ │ ├── suite-case-detail-instances.xml │ │ │ │ │ │ ├── suite-case-detail-tabs-with-nodesets-for-sorting-search-only.xml │ │ │ │ │ │ ├── suite-case-detail-tabs-with-nodesets.xml │ │ │ │ │ │ ├── suite-case-detail-tabs.xml │ │ │ │ │ │ ├── suite-case-sharing.xml │ │ │ │ │ │ ├── suite-case-tiles.xml │ │ │ │ │ │ ├── suite-fixture-graphing.xml │ │ │ │ │ │ ├── suite-graphing.xml │ │ │ │ │ │ ├── suite-no-case-sharing.xml │ │ │ │ │ │ ├── task-mode-suite.xml │ │ │ │ │ │ ├── tiered-select-3.json │ │ │ │ │ │ ├── tiered-select-3.xml │ │ │ │ │ │ ├── tiered-select.json │ │ │ │ │ │ ├── tiered-select.xml │ │ │ │ │ │ ├── update_case_and_subcase.xml │ │ │ │ │ │ └── usercase_entry.xml │ │ │ │ │ ├── suite_inline_search/ │ │ │ │ │ │ ├── detail_tabs.xml │ │ │ │ │ │ └── shadow_module_entry.xml │ │ │ │ │ ├── suite_registry/ │ │ │ │ │ │ ├── detail_tabs.xml │ │ │ │ │ │ ├── form_link_followup_module_registry.xml │ │ │ │ │ │ ├── shadow_module_entry.xml │ │ │ │ │ │ └── shadow_module_remote_request.xml │ │ │ │ │ ├── unicode_error_form.xhtml │ │ │ │ │ ├── very_simple_form.xml │ │ │ │ │ ├── xform_builder/ │ │ │ │ │ │ ├── data_types.xml │ │ │ │ │ │ ├── group.xml │ │ │ │ │ │ ├── question_params.xml │ │ │ │ │ │ ├── repeat_group.xml │ │ │ │ │ │ ├── select1_question.xml │ │ │ │ │ │ ├── select_question.xml │ │ │ │ │ │ ├── unicode_translation.xml │ │ │ │ │ │ └── xform_title.xml │ │ │ │ │ ├── xform_test/ │ │ │ │ │ │ └── MySuperSpecialForm.xml │ │ │ │ │ ├── yesno.json │ │ │ │ │ ├── yesno_default_app_strings.txt │ │ │ │ │ ├── yesno_en_app_strings.txt │ │ │ │ │ └── yesno_es_app_strings.txt │ │ │ │ ├── mocks/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── mobile_ucr.py │ │ │ │ ├── templatetags/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── test_xforms_extras.py │ │ │ │ ├── test_advanced_suite.py │ │ │ │ ├── test_analytics.py │ │ │ │ ├── test_app_import_api.py │ │ │ │ ├── test_app_strings.py │ │ │ │ ├── test_app_view_context.py │ │ │ │ ├── test_apps.py │ │ │ │ ├── test_build_errors.py │ │ │ │ ├── test_build_errors_inline_search.py │ │ │ │ ├── test_bulk_ui_translation.py │ │ │ │ ├── test_case_detail_distance.py │ │ │ │ ├── test_case_list_form.py │ │ │ │ ├── test_case_list_lookup.py │ │ │ │ ├── test_case_refs_from_fb.py │ │ │ │ ├── test_ccz.py │ │ │ │ ├── test_child_module.py │ │ │ │ ├── test_commcare_settings.py │ │ │ │ ├── test_commcare_versioning.py │ │ │ │ ├── test_copy_form.py │ │ │ │ ├── test_days_ago_migration.py │ │ │ │ ├── test_dbaccessors.py │ │ │ │ ├── test_delete_case_list_custom_variables_xml.py │ │ │ │ ├── test_exceptions.py │ │ │ │ ├── test_filters.py │ │ │ │ ├── test_fix_app_docs_with_empty_keys.py │ │ │ │ ├── test_form_actions_diff.py │ │ │ │ ├── test_form_preparation_v2.py │ │ │ │ ├── test_form_versioning.py │ │ │ │ ├── test_form_workflow.py │ │ │ │ ├── test_form_workflow_multiple_case_types.py │ │ │ │ ├── test_form_xmlns.py │ │ │ │ ├── test_generators.py │ │ │ │ ├── test_get_questions.py │ │ │ │ ├── test_grid_menus.py │ │ │ │ ├── test_handle_shadow_child_modules.py │ │ │ │ ├── test_id_strings.py │ │ │ │ ├── test_list_apps_view.py │ │ │ │ ├── test_location_xpath.py │ │ │ │ ├── test_media_suite.py │ │ │ │ ├── test_migrate_address_popup.py │ │ │ │ ├── test_migrate_case_list_custom_variables.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_modules.py │ │ │ │ ├── test_practice_user_config.py │ │ │ │ ├── test_profile.py │ │ │ │ ├── test_release_notes_form.py │ │ │ │ ├── test_repeater.py │ │ │ │ ├── test_report_config.py │ │ │ │ ├── test_report_fixtures_provider.py │ │ │ │ ├── test_schedule.py │ │ │ │ ├── test_shadow_forms.py │ │ │ │ ├── test_shadow_modules.py │ │ │ │ ├── test_suite.py │ │ │ │ ├── test_suite_assertions.py │ │ │ │ ├── test_suite_case_tiles.py │ │ │ │ ├── test_suite_case_tiles_grouping.py │ │ │ │ ├── test_suite_commtrack.py │ │ │ │ ├── test_suite_custom_case_tiles.py │ │ │ │ ├── test_suite_detail_tabs.py │ │ │ │ ├── test_suite_filters.py │ │ │ │ ├── test_suite_fixture_to_case_selection.py │ │ │ │ ├── test_suite_form_filter_errors.py │ │ │ │ ├── test_suite_formats.py │ │ │ │ ├── test_suite_graphing.py │ │ │ │ ├── test_suite_inline_search.py │ │ │ │ ├── test_suite_instances.py │ │ │ │ ├── test_suite_multi_select_case_list.py │ │ │ │ ├── test_suite_regex.py │ │ │ │ ├── test_suite_registry_search.py │ │ │ │ ├── test_suite_remote_request.py │ │ │ │ ├── test_suite_resource_overrides.py │ │ │ │ ├── test_suite_session_endpoints.py │ │ │ │ ├── test_suite_shadow_module.py │ │ │ │ ├── test_suite_sorting.py │ │ │ │ ├── test_suite_split_screen_case_search.py │ │ │ │ ├── test_suite_subcases.py │ │ │ │ ├── test_suite_training_module.py │ │ │ │ ├── test_suite_usercase.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_translation_file_paths.py │ │ │ │ ├── test_util.py │ │ │ │ ├── test_views.py │ │ │ │ ├── test_xform.py │ │ │ │ ├── test_xform_builder.py │ │ │ │ ├── test_xform_parsing.py │ │ │ │ ├── test_xml_parsing.py │ │ │ │ ├── test_xpath.py │ │ │ │ ├── util.py │ │ │ │ └── views/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_apply_patch.py │ │ │ │ ├── test_get_form_datums.py │ │ │ │ ├── test_paginate_releases.py │ │ │ │ ├── test_release_build.py │ │ │ │ └── test_session_endpoint_utils.py │ │ │ ├── ui_translations/ │ │ │ │ ├── __init__.py │ │ │ │ ├── commcare_versioning.py │ │ │ │ └── ui_translations.py │ │ │ ├── urls.py │ │ │ ├── util.py │ │ │ ├── view_helpers.py │ │ │ ├── views/ │ │ │ │ ├── __init__.py │ │ │ │ ├── app_import_api.py │ │ │ │ ├── app_summary.py │ │ │ │ ├── apps.py │ │ │ │ ├── cli.py │ │ │ │ ├── download.py │ │ │ │ ├── formdesigner.py │ │ │ │ ├── forms.py │ │ │ │ ├── media_utils.py │ │ │ │ ├── modules.py │ │ │ │ ├── multimedia.py │ │ │ │ ├── phone.py │ │ │ │ ├── releases.py │ │ │ │ ├── schedules.py │ │ │ │ ├── settings.py │ │ │ │ ├── utils.py │ │ │ │ └── view_generic.py │ │ │ ├── xform.py │ │ │ ├── xform_builder.py │ │ │ ├── xpath.py │ │ │ └── xpath_validator/ │ │ │ ├── __init__.py │ │ │ ├── config.py │ │ │ ├── exceptions.py │ │ │ ├── nodejs/ │ │ │ │ ├── xpathConfig.js │ │ │ │ └── xpathValidator.js │ │ │ ├── tests.py │ │ │ └── wrapper.py │ │ ├── appstore/ │ │ │ ├── __init__.py │ │ │ ├── exceptions.py │ │ │ ├── management/ │ │ │ │ └── commands/ │ │ │ │ └── export_exchange.py │ │ │ └── models.py │ │ ├── auditcare/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── decorators/ │ │ │ │ └── __init__.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── export_audit_data.py │ │ │ │ ├── export_domain_logins.py │ │ │ │ ├── gdpr_scrub_user_auditcare.py │ │ │ │ ├── generate_request_report.py │ │ │ │ └── resolve_urls.py │ │ │ ├── middleware.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_sqlmodels.py │ │ │ │ ├── 0002_uniques.py │ │ │ │ ├── 0003_truncatechars.py │ │ │ │ ├── 0004_add_couch_id.py │ │ │ │ ├── 0005_auditcaremigrationmeta.py │ │ │ │ ├── 0006_auto_20210811_1215.py │ │ │ │ ├── 0007_delete_auditcaremigrationmeta.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ └── auditcare_migration.py │ │ │ │ ├── test_auth.py │ │ │ │ ├── test_export.py │ │ │ │ ├── test_gdpr_scrub_user_auditcare.py │ │ │ │ ├── test_middleware.py │ │ │ │ ├── test_models.py │ │ │ │ └── testutils.py │ │ │ ├── urls.py │ │ │ ├── utils/ │ │ │ │ ├── __init__.py │ │ │ │ ├── export.py │ │ │ │ ├── resolver.py │ │ │ │ └── show_urls.py │ │ │ └── views.py │ │ ├── builds/ │ │ │ ├── README.rst │ │ │ ├── __init__.py │ │ │ ├── _design/ │ │ │ │ ├── filters/ │ │ │ │ │ └── all_builds.js │ │ │ │ └── views/ │ │ │ │ └── all/ │ │ │ │ ├── map.js │ │ │ │ └── reduce.js │ │ │ ├── fixtures.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ └── add_commcare_build.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── builds/ │ │ │ │ └── js/ │ │ │ │ └── edit_builds.js │ │ │ ├── templates/ │ │ │ │ └── builds/ │ │ │ │ ├── all.html │ │ │ │ └── edit_menu.html │ │ │ ├── tests/ │ │ │ │ └── test_utils.py │ │ │ ├── tests.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── cachehq/ │ │ │ ├── __init__.py │ │ │ ├── cachemodels.py │ │ │ ├── invalidate.py │ │ │ ├── mixins.py │ │ │ ├── models.py │ │ │ ├── signals.py │ │ │ └── tests.py │ │ ├── callcenter/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── app_parser.py │ │ │ ├── checks.py │ │ │ ├── const.py │ │ │ ├── data_source.py │ │ │ ├── data_sources/ │ │ │ │ ├── call_center_case_actions.json │ │ │ │ ├── call_center_cases.json │ │ │ │ └── call_center_forms.json │ │ │ ├── fixturegenerators.py │ │ │ ├── indicator_sets.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ └── rebuild_call_center_datasources.py │ │ │ ├── models.py │ │ │ ├── queries.py │ │ │ ├── sync_usercase.py │ │ │ ├── tasks.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ └── form_template.xml │ │ │ │ ├── sql_fixture.py │ │ │ │ ├── test_datasources.py │ │ │ │ ├── test_indicator_fixture.py │ │ │ │ ├── test_indicators.py │ │ │ │ ├── test_location_owners.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_owner_options_view.py │ │ │ │ ├── test_sync_usercases_tasks.py │ │ │ │ ├── test_use_fixtures_configuration.py │ │ │ │ └── test_utils.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── campaign/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── apps.py │ │ │ ├── const.py │ │ │ ├── forms.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_dashboardmap_dashboardreport.py │ │ │ │ ├── 0003_add_dashboard_widget_fields.py │ │ │ │ ├── 0004_gauge.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── services.py │ │ │ ├── static/ │ │ │ │ └── campaign/ │ │ │ │ └── js/ │ │ │ │ └── dashboard.js │ │ │ ├── templates/ │ │ │ │ └── campaign/ │ │ │ │ ├── dashboard.html │ │ │ │ └── partials/ │ │ │ │ ├── add_widget_button.html │ │ │ │ ├── case_properties_dropdown.html │ │ │ │ ├── dashboard_gauge.html │ │ │ │ ├── dashboard_map.html │ │ │ │ ├── delete_widget_button.html │ │ │ │ ├── delete_widget_confirmation_modal.html │ │ │ │ ├── edit_widget_button.html │ │ │ │ ├── widget_form.html │ │ │ │ └── widget_modal.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_services.py │ │ │ │ └── test_views.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── case_importer/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── base.py │ │ │ ├── const.py │ │ │ ├── do_import.py │ │ │ ├── exceptions.py │ │ │ ├── extension_points.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── import_cases.py │ │ │ │ ├── terminate_case_import.py │ │ │ │ └── update_case_property.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_auto_20161206_1937.py │ │ │ │ ├── 0003_caseuploadrecord_couch_user_id.py │ │ │ │ ├── 0004_caseuploadrecord_case_type.py │ │ │ │ ├── 0005_caseuploadfilemeta.py │ │ │ │ ├── 0006_caseuploadrecord_upload_file_meta.py │ │ │ │ ├── 0007_auto_20161209_2004.py │ │ │ │ ├── 0008_caseuploadrecord_task_status_json.py │ │ │ │ ├── 0009_caseuploadrecord_comment.py │ │ │ │ ├── 0010_caseuploadformrecord.py │ │ │ │ ├── 0011_update_blob_paths.py │ │ │ │ ├── 0012_auto_20190405_1747.py │ │ │ │ ├── 0013_make_duplicates_errors.py │ │ │ │ ├── 0014_rename_caseuploadrecord_domain_created_case_import_domain_21a470_idx.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── case_importer/ │ │ │ │ ├── js/ │ │ │ │ │ ├── excel_fields.js │ │ │ │ │ ├── import_history.js │ │ │ │ │ └── main.js │ │ │ │ └── spec/ │ │ │ │ ├── excel_fields_spec.js │ │ │ │ └── main.js │ │ │ ├── suggested_fields.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── case_importer/ │ │ │ │ ├── excel_config.html │ │ │ │ ├── excel_fields.html │ │ │ │ ├── import_cases.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── excel_field_rows.html │ │ │ │ │ ├── help_message.html │ │ │ │ │ ├── ko_import_history.html │ │ │ │ │ └── ko_import_status.html │ │ │ │ └── spec/ │ │ │ │ └── mocha.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_importer.py │ │ │ │ ├── test_importer_config_compat.py │ │ │ │ ├── test_suggested_fields.py │ │ │ │ └── test_util.py │ │ │ ├── tracking/ │ │ │ │ ├── __init__.py │ │ │ │ ├── analytics.py │ │ │ │ ├── case_upload_tracker.py │ │ │ │ ├── dbaccessors.py │ │ │ │ ├── filestorage.py │ │ │ │ ├── jsmodels.py │ │ │ │ ├── models.py │ │ │ │ ├── permissions.py │ │ │ │ ├── task_status.py │ │ │ │ ├── tests/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── test_dbaccessors.py │ │ │ │ │ └── test_filestorage.py │ │ │ │ └── views.py │ │ │ ├── urls.py │ │ │ ├── util.py │ │ │ └── views.py │ │ ├── case_search/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── const.py │ │ │ ├── dsl_utils.py │ │ │ ├── exceptions.py │ │ │ ├── filter_dsl.py │ │ │ ├── fixtures.py │ │ │ ├── forms.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_auto_20161114_1901.py │ │ │ │ ├── 0003_casesearchqueryaddition.py │ │ │ │ ├── 0004_auto_20170518_2018.py │ │ │ │ ├── 0005_migrate_json_config.py │ │ │ │ ├── 0006_remove_casesearchconfig__config.py │ │ │ │ ├── 0007_auto_20170522_1506.py │ │ │ │ ├── 0008_auto_20180119_1716.py │ │ │ │ ├── 0009_delete_casesearchqueryaddition.py │ │ │ │ ├── 0010_casesearchconfig_synchronous_web_apps.py │ │ │ │ ├── 0011_domainsnotincasesearchindex.py │ │ │ │ ├── 0012_casesearchconfig_sync_cases_on_form_entry.py │ │ │ │ ├── 0013_casesearchconfig_fuzzy_prefix_length.py │ │ │ │ ├── 0014_casesearchconfig_index_name.py │ │ │ │ ├── 0015_csqlfixtureexpression_csqlfixtureexpressionlog.py │ │ │ │ ├── 0016_csqlfixtureexpression_user_data_criteria.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── case_search/ │ │ │ │ └── js/ │ │ │ │ ├── case_search.js │ │ │ │ └── profile_case_search.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── case_search/ │ │ │ │ ├── case_search.html │ │ │ │ ├── csql_expression_form.html │ │ │ │ ├── csql_fixture_configuration.html │ │ │ │ ├── csql_modal_form.html │ │ │ │ ├── csql_user_data_criteria_fields.html │ │ │ │ └── profile_case_search.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_ancestor_functions.py │ │ │ │ ├── test_case_search_endpoint.py │ │ │ │ ├── test_case_search_es_queries.py │ │ │ │ ├── test_case_search_filters.py │ │ │ │ ├── test_case_search_registry.py │ │ │ │ ├── test_filter_dsl.py │ │ │ │ ├── test_fixtures.py │ │ │ │ ├── test_get_related_cases.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_subcase_query_parser.py │ │ │ │ ├── test_utils.py │ │ │ │ ├── test_value_functions.py │ │ │ │ ├── test_views.py │ │ │ │ ├── test_xpath_functions.py │ │ │ │ └── utils.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ ├── views.py │ │ │ └── xpath_functions/ │ │ │ ├── __init__.py │ │ │ ├── ancestor_functions.py │ │ │ ├── comparison.py │ │ │ ├── query_functions.py │ │ │ ├── subcase_functions.py │ │ │ ├── utils.py │ │ │ └── value_functions.py │ │ ├── casegroups/ │ │ │ ├── __init__.py │ │ │ ├── dbaccessors.py │ │ │ ├── models.py │ │ │ └── tests.py │ │ ├── celery/ │ │ │ ├── __init__.py │ │ │ ├── analytics.py │ │ │ ├── durable.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ └── requeue_task_records.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_taskrecord.py │ │ │ │ ├── 0002_taskrecord_kombu_serialization.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── periodic.py │ │ │ ├── serial.py │ │ │ ├── shared_task.py │ │ │ └── tests/ │ │ │ ├── __init__.py │ │ │ ├── test_celery_app.py │ │ │ ├── test_durable_task.py │ │ │ └── test_requeue_task_records.py │ │ ├── change_feed/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── connection.py │ │ │ ├── consumer/ │ │ │ │ ├── __init__.py │ │ │ │ └── feed.py │ │ │ ├── data_sources.py │ │ │ ├── document_types.py │ │ │ ├── exceptions.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── create_kafka_topics.py │ │ │ │ ├── list_kafka_topics.py │ │ │ │ ├── pillow_topic_assignments.py │ │ │ │ └── validate_kafka_pillow_checkpoints.py │ │ │ ├── pillow.py │ │ │ ├── producer.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_connection.py │ │ │ │ ├── test_data_sources.py │ │ │ │ ├── test_document_types.py │ │ │ │ ├── test_kafka_change_feed.py │ │ │ │ ├── test_pillow.py │ │ │ │ ├── test_topics.py │ │ │ │ └── utils.py │ │ │ └── topics.py │ │ ├── cleanup/ │ │ │ ├── __init__.py │ │ │ ├── dbaccessors.py │ │ │ ├── deletable_doc_types.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── couch_integrity/ │ │ │ │ │ └── basic.json │ │ │ │ ├── couch_integrity.py │ │ │ │ ├── delete_case_indices.py │ │ │ │ ├── delete_couch_dbs.py │ │ │ │ ├── delete_doc_conflicts.py │ │ │ │ ├── delete_duplicate_users.py │ │ │ │ ├── delete_es_docs_for_domain.py │ │ │ │ ├── delete_forms.py │ │ │ │ ├── evaluate_couch_model_for_sql.py │ │ │ │ ├── fire_repeaters.py │ │ │ │ ├── get_doc_info.py │ │ │ │ ├── multi_populate.py │ │ │ │ ├── pillow_reset.py │ │ │ │ ├── populate_sql_model_from_couch_model.py │ │ │ │ ├── purge_docs.py │ │ │ │ ├── rebuild_cases.py │ │ │ │ ├── remove_deleted_cases_from_es.py │ │ │ │ ├── reprocess_error_forms.py │ │ │ │ ├── reprocess_incomplete_submissions.py │ │ │ │ ├── republish_forms_rebuild_cases.py │ │ │ │ ├── templates/ │ │ │ │ │ ├── couch_model_additions.j2 │ │ │ │ │ ├── migration_attr_equality_test.j2 │ │ │ │ │ ├── populate_command.j2 │ │ │ │ │ └── sql_model.j2 │ │ │ │ ├── undelete_docs.py │ │ │ │ └── undo_uuid_clash.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_convert_change_feed_checkpoint_to_sql.py │ │ │ │ ├── 0002_convert_mc_checkpoint_to_sql.py │ │ │ │ ├── 0003_convert_fluff_checkpoints_to_sql.py │ │ │ │ ├── 0004_convert_ucr_checkpoints_to_sql.py │ │ │ │ ├── 0005_convert_mvp_checkpoints_to_sql.py │ │ │ │ ├── 0006_convert_report_es_checkpoints_to_sql.py │ │ │ │ ├── 0007_convert_es_checkpoints_to_sql.py │ │ │ │ ├── 0008_convert_sofabed_checkpoints_to_sql.py │ │ │ │ ├── 0009_convert_final_checkpoints_to_sql.py │ │ │ │ ├── 0010_rename_default_change_feed_checkpoint.py │ │ │ │ ├── 0011_merge_couch_sql_pillows.py │ │ │ │ ├── 0012_add_es_index_to_checkpoint_ids.py │ │ │ │ ├── 0013_migrate_kafka_checkpoint_format.py │ │ │ │ ├── 0014_deletedcouchdoc.py │ │ │ │ ├── 0015_deletedcouchdoc_unique_id_and_type.py │ │ │ │ ├── 0016_add_deletedsqldoc.py │ │ │ │ ├── 0017_delete_oauth_integrations_models.py │ │ │ │ ├── 0018_delete_ewsghana_models.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── tasks.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_dbaccessors.py │ │ │ │ ├── test_delete_es_docs_for_domain.py │ │ │ │ ├── test_populate_sql_model_from_couch_model.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_utils.py │ │ │ │ └── util.py │ │ │ └── utils.py │ │ ├── cloudcare/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── api.py │ │ │ ├── const.py │ │ │ ├── dbaccessors.py │ │ │ ├── decorators.py │ │ │ ├── esaccessors.py │ │ │ ├── exceptions.py │ │ │ ├── middleware.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_sqlapplicationaccess.py │ │ │ │ ├── 0003_rename_sqlapplicationaccess.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── cloudcare/ │ │ │ │ └── js/ │ │ │ │ ├── config.js │ │ │ │ ├── debugger/ │ │ │ │ │ └── debugger.js │ │ │ │ ├── form_entry/ │ │ │ │ │ ├── const.js │ │ │ │ │ ├── entries.js │ │ │ │ │ ├── errors.js │ │ │ │ │ ├── form_ui.js │ │ │ │ │ ├── spec/ │ │ │ │ │ │ ├── entries_spec.js │ │ │ │ │ │ ├── fixtures.js │ │ │ │ │ │ ├── form_ui_spec.js │ │ │ │ │ │ ├── integration_spec.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ ├── utils_spec.js │ │ │ │ │ │ └── web_form_session_spec.js │ │ │ │ │ ├── task_queue.js │ │ │ │ │ ├── utils.js │ │ │ │ │ └── web_form_session.js │ │ │ │ ├── formplayer/ │ │ │ │ │ ├── app.js │ │ │ │ │ ├── apps/ │ │ │ │ │ │ ├── api.js │ │ │ │ │ │ ├── collections.js │ │ │ │ │ │ ├── controller.js │ │ │ │ │ │ ├── models.js │ │ │ │ │ │ └── views.js │ │ │ │ │ ├── constants.js │ │ │ │ │ ├── hq_events.js │ │ │ │ │ ├── layout/ │ │ │ │ │ │ └── views/ │ │ │ │ │ │ ├── progress_bar.js │ │ │ │ │ │ └── settings.js │ │ │ │ │ ├── main.js │ │ │ │ │ ├── menus/ │ │ │ │ │ │ ├── api.js │ │ │ │ │ │ ├── collections.js │ │ │ │ │ │ ├── controller.js │ │ │ │ │ │ ├── utils.js │ │ │ │ │ │ ├── views/ │ │ │ │ │ │ │ └── query.js │ │ │ │ │ │ └── views.js │ │ │ │ │ ├── middleware.js │ │ │ │ │ ├── router.js │ │ │ │ │ ├── sessions/ │ │ │ │ │ │ ├── api.js │ │ │ │ │ │ ├── collections.js │ │ │ │ │ │ ├── controller.js │ │ │ │ │ │ ├── models.js │ │ │ │ │ │ └── views.js │ │ │ │ │ ├── spec/ │ │ │ │ │ │ ├── case_list_pagination_spec.js │ │ │ │ │ │ ├── debugger_spec.js │ │ │ │ │ │ ├── fake_formplayer.js │ │ │ │ │ │ ├── fixtures/ │ │ │ │ │ │ │ ├── case_grid_list.js │ │ │ │ │ │ │ ├── case_list.js │ │ │ │ │ │ │ ├── case_tile_list.js │ │ │ │ │ │ │ ├── menu_list.js │ │ │ │ │ │ │ └── split_screen_case_list.js │ │ │ │ │ │ ├── hq_events_spec.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ ├── menu_list_spec.js │ │ │ │ │ │ ├── menu_utils_spec.js │ │ │ │ │ │ ├── query_spec.js │ │ │ │ │ │ ├── session_middleware_spec.js │ │ │ │ │ │ ├── split_screen_case_search_spec.js │ │ │ │ │ │ ├── user_spec.js │ │ │ │ │ │ └── utils_spec.js │ │ │ │ │ ├── users/ │ │ │ │ │ │ ├── collections.js │ │ │ │ │ │ ├── controller.js │ │ │ │ │ │ ├── models.js │ │ │ │ │ │ ├── utils.js │ │ │ │ │ │ └── views.js │ │ │ │ │ └── utils/ │ │ │ │ │ ├── calendar-picker-translations.js │ │ │ │ │ └── utils.js │ │ │ │ ├── gtx.js │ │ │ │ ├── markdown.js │ │ │ │ ├── preview_app/ │ │ │ │ │ ├── dragscroll.js │ │ │ │ │ ├── main.js │ │ │ │ │ └── preview_app.js │ │ │ │ ├── sentry.js │ │ │ │ ├── spec/ │ │ │ │ │ ├── markdown_spec.js │ │ │ │ │ └── utils_spec.js │ │ │ │ └── utils.js │ │ │ ├── templates/ │ │ │ │ └── cloudcare/ │ │ │ │ ├── block_preview_app.html │ │ │ │ ├── block_web_apps.html │ │ │ │ ├── config.html │ │ │ │ ├── formplayer_home.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── all_templates.html │ │ │ │ │ ├── case_detail.html │ │ │ │ │ ├── case_list/ │ │ │ │ │ │ ├── cell_container_style.html │ │ │ │ │ │ ├── cell_grid_style.html │ │ │ │ │ │ ├── cell_layout_style.html │ │ │ │ │ │ ├── config.html │ │ │ │ │ │ ├── config_modal.html │ │ │ │ │ │ ├── detail.html │ │ │ │ │ │ ├── item.html │ │ │ │ │ │ ├── list.html │ │ │ │ │ │ ├── menu_header.html │ │ │ │ │ │ ├── multi_select_continue_button.html │ │ │ │ │ │ ├── no_items_text.html │ │ │ │ │ │ ├── search_controls.html │ │ │ │ │ │ ├── show_map_button.html │ │ │ │ │ │ ├── table_container.html │ │ │ │ │ │ ├── tile_grouped_item.html │ │ │ │ │ │ └── tile_item.html │ │ │ │ │ ├── confirmation_modal.html │ │ │ │ │ ├── debugger.html │ │ │ │ │ ├── dependencies.html │ │ │ │ │ ├── form_entry/ │ │ │ │ │ │ ├── entry_address.html │ │ │ │ │ │ ├── entry_blank.html │ │ │ │ │ │ ├── entry_button.html │ │ │ │ │ │ ├── entry_choice_label.html │ │ │ │ │ │ ├── entry_datetime.html │ │ │ │ │ │ ├── entry_dropdown.html │ │ │ │ │ │ ├── entry_ethiopian_date.html │ │ │ │ │ │ ├── entry_file.html │ │ │ │ │ │ ├── entry_geo.html │ │ │ │ │ │ ├── entry_multidropdown.html │ │ │ │ │ │ ├── entry_numeric.html │ │ │ │ │ │ ├── entry_password.html │ │ │ │ │ │ ├── entry_select.html │ │ │ │ │ │ ├── entry_signature.html │ │ │ │ │ │ ├── entry_text.html │ │ │ │ │ │ ├── entry_unsupported.html │ │ │ │ │ │ ├── form.html │ │ │ │ │ │ ├── form_navigation.html │ │ │ │ │ │ ├── grouped_element_tile_row.html │ │ │ │ │ │ ├── help_multimedia.html │ │ │ │ │ │ ├── multimedia.html │ │ │ │ │ │ ├── question.html │ │ │ │ │ │ └── sub_group.html │ │ │ │ │ ├── grid_view/ │ │ │ │ │ │ ├── grid.html │ │ │ │ │ │ ├── row.html │ │ │ │ │ │ └── single_app.html │ │ │ │ │ ├── menu/ │ │ │ │ │ │ ├── audio.html │ │ │ │ │ │ ├── breadcrumbs.html │ │ │ │ │ │ ├── dropdown.html │ │ │ │ │ │ ├── grid.html │ │ │ │ │ │ ├── grid_item.html │ │ │ │ │ │ ├── list.html │ │ │ │ │ │ ├── persistent_menu.html │ │ │ │ │ │ └── row.html │ │ │ │ │ ├── new_app_version_modal.html │ │ │ │ │ ├── pagination.html │ │ │ │ │ ├── progress.html │ │ │ │ │ ├── query/ │ │ │ │ │ │ ├── group.html │ │ │ │ │ │ ├── item.html │ │ │ │ │ │ └── list.html │ │ │ │ │ ├── sessions/ │ │ │ │ │ │ ├── item.html │ │ │ │ │ │ └── list.html │ │ │ │ │ ├── settings_view.html │ │ │ │ │ └── users/ │ │ │ │ │ ├── restore_as.html │ │ │ │ │ ├── restore_as_banner.html │ │ │ │ │ ├── user_data.html │ │ │ │ │ └── user_row.html │ │ │ │ ├── preview_app.html │ │ │ │ ├── preview_app_base.html │ │ │ │ ├── spec/ │ │ │ │ │ ├── form_entry/ │ │ │ │ │ │ └── mocha.html │ │ │ │ │ └── mocha.html │ │ │ │ └── web_apps_disabled.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_api.py │ │ │ │ ├── test_doc_tests.py │ │ │ │ ├── test_esaccessors.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_session.py │ │ │ │ └── test_utils.py │ │ │ ├── touchforms_api.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── commtrack/ │ │ │ ├── __init__.py │ │ │ ├── const.py │ │ │ ├── consumption.py │ │ │ ├── exceptions.py │ │ │ ├── fixtures.py │ │ │ ├── forms.py │ │ │ ├── helpers.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ └── __init__.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_stockstate_last_modified_form_id.py │ │ │ │ ├── 0003_create_config_models.py │ │ │ │ ├── 0004_update_overstock_threshold.py │ │ │ │ ├── 0005_populate_config_models.py │ │ │ │ ├── 0006_remove_sqlcommtrackconfig_couch_id.py │ │ │ │ ├── 0007_rename_sql_models.py │ │ │ │ ├── 0008_delete_stockstate.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── processing.py │ │ │ ├── resources/ │ │ │ │ ├── __init__.py │ │ │ │ └── v0_1.py │ │ │ ├── sms.py │ │ │ ├── static/ │ │ │ │ └── commtrack/ │ │ │ │ └── js/ │ │ │ │ ├── base_list_view_model.js │ │ │ │ ├── products_and_programs_main.js │ │ │ │ └── sms.js │ │ │ ├── templates/ │ │ │ │ └── commtrack/ │ │ │ │ └── manage/ │ │ │ │ ├── default_consumption.html │ │ │ │ └── partials/ │ │ │ │ └── pagination.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── balances.py │ │ │ │ │ └── xml/ │ │ │ │ │ └── device_log.xml │ │ │ │ ├── test_dbaccessors.py │ │ │ │ ├── test_fixture.py │ │ │ │ ├── test_locations.py │ │ │ │ ├── test_products.py │ │ │ │ ├── test_programs.py │ │ │ │ ├── test_rebuild.py │ │ │ │ ├── test_settings.py │ │ │ │ ├── test_sms_reporting.py │ │ │ │ ├── test_stock_report.py │ │ │ │ ├── test_supply_points.py │ │ │ │ ├── test_utils.py │ │ │ │ ├── test_xml.py │ │ │ │ └── util.py │ │ │ ├── urls.py │ │ │ ├── util.py │ │ │ ├── views.py │ │ │ └── xmlutil.py │ │ ├── consumption/ │ │ │ ├── __init__.py │ │ │ ├── const.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_populate_sqldefaultconsumption.py │ │ │ │ ├── 0003_remove_sqldefaultconsumption_couch_id.py │ │ │ │ ├── 0004_rename_sqldefaultconsumption.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── shortcuts.py │ │ │ └── tests.py │ │ ├── custom_data_fields/ │ │ │ ├── README.rst │ │ │ ├── __init__.py │ │ │ ├── edit_entity.py │ │ │ ├── edit_model.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_populate_customdatafieldsdefinition.py │ │ │ │ ├── 0003_remove_sqlcustomdatafieldsdefinition_couch_id.py │ │ │ │ ├── 0004_rename_tables.py │ │ │ │ ├── 0005_customdatafieldsprofile.py │ │ │ │ ├── 0006_auto_20200924_1753.py │ │ │ │ ├── 0007_custom_data_fields_erm_support.py │ │ │ │ ├── 0008_custom_data_fields_upstream_ids.py │ │ │ │ ├── 0009_field_required_for.py │ │ │ │ ├── 0010_customdatafieldsdefinition_profile_required_for_user_type.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── custom_data_fields/ │ │ │ │ └── js/ │ │ │ │ └── custom_data_fields.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── custom_data_fields/ │ │ │ │ ├── custom_data_fields.html │ │ │ │ └── partials/ │ │ │ │ ├── fields_tab.html │ │ │ │ ├── modal_delete.html │ │ │ │ ├── modal_edit.html │ │ │ │ ├── profiles_tab.html │ │ │ │ └── purge_existing_fields.html │ │ │ └── tests/ │ │ │ ├── __init__.py │ │ │ ├── test_edit_model.py │ │ │ ├── test_fields.py │ │ │ ├── test_form.py │ │ │ ├── test_models.py │ │ │ ├── test_profiles.py │ │ │ └── test_verification.py │ │ ├── dashboard/ │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── dashboard/ │ │ │ │ └── js/ │ │ │ │ └── dashboard.js │ │ │ ├── templates/ │ │ │ │ └── dashboard/ │ │ │ │ └── base.html │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── data_analytics/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── const.py │ │ │ ├── daily_calcs.py │ │ │ ├── esaccessors.py │ │ │ ├── feature_calcs.py │ │ │ ├── gir_generator.py │ │ │ ├── malt_generator.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── add_to_malt_table.py │ │ │ │ ├── collect_feature_metrics.py │ │ │ │ ├── update_gir_table.py │ │ │ │ └── update_malt_table.py │ │ │ ├── metric_registry.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_squashed_0004_auto_20150810_1710.py │ │ │ │ ├── 0002_maltrow_threshold.py │ │ │ │ ├── 0003_auto_20160205_0927.py │ │ │ │ ├── 0004_experienced_threshold.py │ │ │ │ ├── 0005_girrow.py │ │ │ │ ├── 0006_unique_girrow.py │ │ │ │ ├── 0007_auto_20160819_1423.py │ │ │ │ ├── 0008_auto_20161114_1903.py │ │ │ │ ├── 0009_remove_girrow_wam.py │ │ │ │ ├── 0010_maltrow_last_run_date.py │ │ │ │ ├── 0011_domainmetrics.py │ │ │ │ ├── 0012_update_domainmetrics_fields.py │ │ │ │ ├── 0013_domainmetrics_bulk_case_editing_sessions_and_more.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── tasks.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_app_feature_metrics.py │ │ │ │ ├── test_collect_feature_metrics_command.py │ │ │ │ ├── test_data_export_feature_metrics.py │ │ │ │ ├── test_esaccessors.py │ │ │ │ ├── test_gir.py │ │ │ │ ├── test_malt_generator.py │ │ │ │ ├── test_metric_registry.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_user_security_feature_metrics.py │ │ │ │ ├── test_util.py │ │ │ │ └── utils.py │ │ │ └── util.py │ │ ├── data_cleaning/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── columns.py │ │ │ ├── decorators.py │ │ │ ├── exceptions.py │ │ │ ├── filters.py │ │ │ ├── forms/ │ │ │ │ ├── __init__.py │ │ │ │ ├── bulk_edit.py │ │ │ │ ├── columns.py │ │ │ │ ├── filters.py │ │ │ │ └── start_session.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── dc_create_test_app.py │ │ │ │ ├── dc_create_test_data.py │ │ │ │ ├── dc_create_test_users.py │ │ │ │ └── utils/ │ │ │ │ ├── __init__.py │ │ │ │ ├── apps/ │ │ │ │ │ └── plant_care_app.json │ │ │ │ ├── fake_data_users.py │ │ │ │ ├── fake_plant_data.py │ │ │ │ ├── input_validation.py │ │ │ │ └── issues.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_update_fields_and_ordering.py │ │ │ │ ├── 0003_column_filter_unique_ids_match_type_updates.py │ │ │ │ ├── 0004_alter_bulkeditcolumn_data_type_and_more.py │ │ │ │ ├── 0005_rename_bulkeditcolumnfilter_bulkeditfilter.py │ │ │ │ ├── 0006_alter_bulkeditchange_action_type.py │ │ │ │ ├── 0007_alter_bulkeditrecord_doc_id_and_more.py │ │ │ │ └── __init__.py │ │ │ ├── models/ │ │ │ │ ├── __init__.py │ │ │ │ ├── change.py │ │ │ │ ├── column.py │ │ │ │ ├── filters.py │ │ │ │ ├── record.py │ │ │ │ ├── session.py │ │ │ │ └── types.py │ │ │ ├── records.py │ │ │ ├── static/ │ │ │ │ └── data_cleaning/ │ │ │ │ └── js/ │ │ │ │ ├── bulk_edit_main.js │ │ │ │ ├── bulk_edit_session.js │ │ │ │ └── directives/ │ │ │ │ └── dynamic_options_select2.js │ │ │ ├── tables.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── data_cleaning/ │ │ │ │ ├── bulk_edit_main.html │ │ │ │ ├── bulk_edit_session.html │ │ │ │ ├── columns/ │ │ │ │ │ ├── column_editable.html │ │ │ │ │ ├── column_main.html │ │ │ │ │ ├── column_system.html │ │ │ │ │ ├── selection.html │ │ │ │ │ ├── selection_header.html │ │ │ │ │ ├── selection_read_only.html │ │ │ │ │ ├── task_form_ids.html │ │ │ │ │ ├── task_status.html │ │ │ │ │ └── values/ │ │ │ │ │ ├── empty.html │ │ │ │ │ ├── null.html │ │ │ │ │ └── text.html │ │ │ │ ├── filters/ │ │ │ │ │ ├── formatted_value.html │ │ │ │ │ └── pinned/ │ │ │ │ │ ├── base.html │ │ │ │ │ ├── multi_option.html │ │ │ │ │ └── single_option.html │ │ │ │ ├── forms/ │ │ │ │ │ ├── edit_selected_records_form.html │ │ │ │ │ ├── manage_columns_form.html │ │ │ │ │ ├── manage_filters_form.html │ │ │ │ │ ├── partials/ │ │ │ │ │ │ ├── active_session_exists.html │ │ │ │ │ │ ├── column_list_item.html │ │ │ │ │ │ └── filter_list_item.html │ │ │ │ │ └── pinned_filter_form.html │ │ │ │ ├── modals/ │ │ │ │ │ ├── base_confirm.html │ │ │ │ │ ├── confirm_apply.html │ │ │ │ │ ├── confirm_clear.html │ │ │ │ │ ├── confirm_select_all.html │ │ │ │ │ ├── confirm_undo.html │ │ │ │ │ └── select_all_not_possible.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── button_bar.html │ │ │ │ │ ├── data_type_icon.html │ │ │ │ │ ├── loading_indicator.html │ │ │ │ │ └── offcanvas.html │ │ │ │ ├── status/ │ │ │ │ │ ├── base_modal_body.html │ │ │ │ │ ├── complete.html │ │ │ │ │ ├── in_progress.html │ │ │ │ │ ├── modal.html │ │ │ │ │ ├── partial/ │ │ │ │ │ │ └── task_progress_bar.html │ │ │ │ │ └── previous_session.html │ │ │ │ ├── summary/ │ │ │ │ │ ├── apply_changes.html │ │ │ │ │ ├── clear_changes.html │ │ │ │ │ ├── partial/ │ │ │ │ │ │ ├── change_detail.html │ │ │ │ │ │ └── change_history.html │ │ │ │ │ └── undo_changes.html │ │ │ │ └── tables/ │ │ │ │ ├── bulk_edit_session.html │ │ │ │ └── recent_sessions.html │ │ │ ├── templatetags/ │ │ │ │ ├── __init__.py │ │ │ │ └── data_cleaning.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── mixins.py │ │ │ │ ├── test_add_column_form.py │ │ │ │ ├── test_add_filter_form.py │ │ │ │ ├── test_bulk_edit_form.py │ │ │ │ ├── test_changes.py │ │ │ │ ├── test_filter_outputs.py │ │ │ │ ├── test_filters.py │ │ │ │ ├── test_records.py │ │ │ │ ├── test_session.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_utils_cases.py │ │ │ │ └── test_views.py │ │ │ ├── urls.py │ │ │ ├── utils/ │ │ │ │ ├── __init__.py │ │ │ │ ├── cases.py │ │ │ │ └── decorators.py │ │ │ └── views/ │ │ │ ├── __init__.py │ │ │ ├── bulk_edit.py │ │ │ ├── columns.py │ │ │ ├── filters.py │ │ │ ├── main.py │ │ │ ├── mixins.py │ │ │ ├── start.py │ │ │ ├── status.py │ │ │ ├── summary.py │ │ │ └── tables.py │ │ ├── data_dictionary/ │ │ │ ├── __init__.py │ │ │ ├── bulk.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── generate_data_dictionary.py │ │ │ │ └── refresh_data_dictionary.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_squashed_0002_auto_20161116_2209.py │ │ │ │ ├── 0002_auto_20161118_1537.py │ │ │ │ ├── 0003_auto_20161128_2047.py │ │ │ │ ├── 0004_auto_20161130_2125.py │ │ │ │ ├── 0005_casetype_fully_generated.py │ │ │ │ ├── 0006_caseproperty_group.py │ │ │ │ ├── 0007_property_type_choices.py │ │ │ │ ├── 0008_casepropertyallowedvalue.py │ │ │ │ ├── 0009_caseproperty_label.py │ │ │ │ ├── 0010_caseproperty_index.py │ │ │ │ ├── 0011_casepropertygroup.py │ │ │ │ ├── 0012_populate_case_property_groups.py │ │ │ │ ├── 0013_auto_20230529_1614.py │ │ │ │ ├── 0014_auto_20230705_2007.py │ │ │ │ ├── 0015_casetype_is_deprecated.py │ │ │ │ ├── 0016_remove_case_property_group_and_rename_group_obj_caseproperty_group.py │ │ │ │ ├── 0017_refresh_data_dictionary.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── data_dictionary/ │ │ │ │ └── js/ │ │ │ │ ├── data_dictionary.js │ │ │ │ └── partials/ │ │ │ │ └── case_property_warning.js │ │ │ ├── templates/ │ │ │ │ └── data_dictionary/ │ │ │ │ ├── base.html │ │ │ │ └── partials/ │ │ │ │ ├── case_property_warning.html │ │ │ │ ├── confirmation_modals.html │ │ │ │ └── valid_values_th_content.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ ├── broken_data_dictionary.xlsx │ │ │ │ │ └── clean_data_dictionary.xlsx │ │ │ │ ├── test_import_export.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_util.py │ │ │ │ ├── test_views.py │ │ │ │ └── utils.py │ │ │ ├── urls.py │ │ │ ├── util.py │ │ │ └── views.py │ │ ├── data_interfaces/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── app_config.py │ │ │ ├── deduplication.py │ │ │ ├── dispatcher.py │ │ │ ├── forms.py │ │ │ ├── interfaces.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── get_case_rule_submissions.py │ │ │ │ └── undo_case_rule_submissions.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_remove_exists_option.py │ │ │ │ ├── 0003_update__automaticupdaterulecriteria__match_type__choices.py │ │ │ │ ├── 0004_optional_modified_date_and_prop_type_choices.py │ │ │ │ ├── 0005_remove_match_type_choices.py │ │ │ │ ├── 0006_case_rule_refactor.py │ │ │ │ ├── 0007_logging_models.py │ │ │ │ ├── 0008_update_case_rulesubmission.py │ │ │ │ ├── 0009_scheduling_integration.py │ │ │ │ ├── 0010_automaticupdaterule_workflow.py │ │ │ │ ├── 0011_domaincaserulerun_num_creates.py │ │ │ │ ├── 0012_createscheduleinstanceactiondefinition_reset_case_property_name.py │ │ │ │ ├── 0013_createscheduleinstanceactiondefinition_scheduler_module_info.py │ │ │ │ ├── 0014_createscheduleinstanceactiondefinition_start_date_case_property.py │ │ │ │ ├── 0015_automaticupdaterule_locked_for_editing.py │ │ │ │ ├── 0016_createscheduleinstanceactiondefinition_specific_start_date.py │ │ │ │ ├── 0017_alter_domaincaserulerun.py │ │ │ │ ├── 0018_check_for_rule_migration.py │ │ │ │ ├── 0019_remove_old_rule_models.py │ │ │ │ ├── 0020_make_migrated_nullable.py │ │ │ │ ├── 0021_remove_automaticupdaterule_migrated.py │ │ │ │ ├── 0022_domaincaserulerun_case_type.py │ │ │ │ ├── 0023_auto_20210914_1726.py │ │ │ │ ├── 0024_add_automaticupdaterule_upstream_id.py │ │ │ │ ├── 0025_domaincaserulerun_num_errors.py │ │ │ │ ├── 0026_automaticupdaterule_criteria_operator.py │ │ │ │ ├── 0027_auto_20220511_2017.py │ │ │ │ ├── 0028_auto_20220420_1301.py │ │ │ │ ├── 0029_locationfilterdefinition_include_child_locations.py │ │ │ │ ├── 0030_add_workflow_choices.py │ │ │ │ ├── 0031_add_domaincaserulerun_status_choices.py │ │ │ │ ├── 0032_bootstrap_audit_events_for_update_rules.py │ │ │ │ ├── 0033_automaticupdaterule_deleted_on.py │ │ │ │ ├── 0034_case_name_actions.py │ │ │ │ ├── 0035_add_case_duplicate_new.py │ │ │ │ ├── 0036_backfill_dedupe_match_values.py │ │ │ │ ├── 0037_add_dedupe_update_toggle.py │ │ │ │ ├── 0038_alter_caseduplicate_potential_duplicates.py │ │ │ │ ├── 0039_rename_caserulesubmission_domain_created_on_data_interf_domain_718740_idx_and_more.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── pillow.py │ │ │ ├── signals.py │ │ │ ├── static/ │ │ │ │ └── data_interfaces/ │ │ │ │ ├── js/ │ │ │ │ │ ├── archive_forms.js │ │ │ │ │ ├── auto_update_rules.js │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── case_dedupe_main.js │ │ │ │ │ │ ├── case_management.js │ │ │ │ │ │ ├── deduplication_rules.js │ │ │ │ │ │ └── manage_case_groups.js │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── case_dedupe_main.js │ │ │ │ │ │ ├── case_management.js │ │ │ │ │ │ ├── deduplication_rules.js │ │ │ │ │ │ └── manage_case_groups.js │ │ │ │ │ ├── case_property_input.js │ │ │ │ │ ├── case_rule_actions.js │ │ │ │ │ ├── case_rule_criteria.js │ │ │ │ │ ├── case_rule_main.js │ │ │ │ │ ├── find_by_id.js │ │ │ │ │ └── make_read_only.js │ │ │ │ └── xlsx/ │ │ │ │ └── cases_bulk_example.xlsx │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── data_interfaces/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── auto_update_rules.html │ │ │ │ │ ├── case_rule.html │ │ │ │ │ ├── edit_deduplication_rule.html │ │ │ │ │ ├── find_by_id.html │ │ │ │ │ ├── list_case_groups.html │ │ │ │ │ ├── list_deduplication_rules.html │ │ │ │ │ └── manage_case_groups.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── auto_update_rules.html │ │ │ │ │ ├── case_rule.html │ │ │ │ │ ├── edit_deduplication_rule.html │ │ │ │ │ ├── find_by_id.html │ │ │ │ │ ├── list_case_groups.html │ │ │ │ │ ├── list_deduplication_rules.html │ │ │ │ │ └── manage_case_groups.html │ │ │ │ ├── interfaces/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── archive_forms.html │ │ │ │ │ │ └── case_management.html │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ ├── archive_forms.html │ │ │ │ │ └── case_management.html │ │ │ │ └── partials/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── auto_update_rule_list.html │ │ │ │ │ ├── auto_update_rule_run_history.html │ │ │ │ │ ├── case_rule_actions.html │ │ │ │ │ ├── case_rule_criteria.html │ │ │ │ │ ├── find_by_id_form.html │ │ │ │ │ └── modal_edit.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── auto_update_rule_list.html │ │ │ │ │ ├── auto_update_rule_run_history.html │ │ │ │ │ ├── case_rule_actions.html │ │ │ │ │ ├── case_rule_criteria.html │ │ │ │ │ ├── find_by_id_form.html │ │ │ │ │ └── modal_edit.html │ │ │ │ ├── case_copy_complete_email.html │ │ │ │ ├── case_copy_status.html │ │ │ │ ├── case_reassign_complete_email.html │ │ │ │ ├── case_reassign_status.html │ │ │ │ └── xform_management_status.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── deduplication_helpers.py │ │ │ │ ├── files/ │ │ │ │ │ ├── basic_forms_bulk.xlsx │ │ │ │ │ ├── missing_forms_bulk.xlsx │ │ │ │ │ └── wrong_file.xyz │ │ │ │ ├── test_auto_case_updates.py │ │ │ │ ├── test_auto_update_rules.py │ │ │ │ ├── test_case_deduplication.py │ │ │ │ ├── test_deduplication.py │ │ │ │ ├── test_deduplication_rules.py │ │ │ │ ├── test_form_validation.py │ │ │ │ ├── test_forms.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_pillow.py │ │ │ │ ├── test_scheduling_integration.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_utils.py │ │ │ │ ├── test_views.py │ │ │ │ ├── test_xform_management.py │ │ │ │ └── util.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── data_pipeline_audit/ │ │ │ ├── __init__.py │ │ │ ├── dbacessors.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── compare_doc_counts.py │ │ │ │ ├── compare_doc_ids.py │ │ │ │ └── find_sql_forms_not_in_es.py │ │ │ ├── tools.py │ │ │ └── utils.py │ │ ├── domain/ │ │ │ ├── __init__.py │ │ │ ├── _design/ │ │ │ │ ├── filters/ │ │ │ │ │ ├── all_docs.js │ │ │ │ │ ├── all_domains.js │ │ │ │ │ └── domains_inclusive.js │ │ │ │ ├── fulltext/ │ │ │ │ │ └── snapshot_search/ │ │ │ │ │ └── index.js │ │ │ │ └── views/ │ │ │ │ ├── by_status/ │ │ │ │ │ ├── map.js │ │ │ │ │ └── reduce.js │ │ │ │ ├── copied_from_snapshot/ │ │ │ │ │ └── map.js │ │ │ │ ├── deleted_domains/ │ │ │ │ │ ├── map.js │ │ │ │ │ └── reduce.js │ │ │ │ ├── domains/ │ │ │ │ │ ├── map.js │ │ │ │ │ └── reduce.js │ │ │ │ ├── not_snapshots/ │ │ │ │ │ └── map.js │ │ │ │ ├── published_snapshots/ │ │ │ │ │ └── map.js │ │ │ │ └── snapshots/ │ │ │ │ ├── map.js │ │ │ │ └── reduce.js │ │ │ ├── admin.py │ │ │ ├── auth.py │ │ │ ├── calculations.py │ │ │ ├── dbaccessors.py │ │ │ ├── decorators.py │ │ │ ├── deletion.py │ │ │ ├── exceptions.py │ │ │ ├── extension_points.py │ │ │ ├── forms.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── bootstrap_app.py │ │ │ │ ├── calculate_physical_size.py │ │ │ │ ├── delete_domain.py │ │ │ │ ├── disable_toggles_for_deleted_domains.py │ │ │ │ ├── first_superuser.py │ │ │ │ ├── hard_delete_forms_and_cases_in_domain.py │ │ │ │ ├── make_superuser.py │ │ │ │ ├── redirect_url.py │ │ │ │ ├── remove_duplicate_domains.py │ │ │ │ ├── reset_case_search_toggles.py │ │ │ │ ├── tests/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── test_make_superuser.py │ │ │ │ └── update_case_search_toggles.py │ │ │ ├── middleware.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_auto_20171020_1428.py │ │ │ │ ├── 0003_auto_20180525_1551.py │ │ │ │ ├── 0004_domainauditrecordentry.py │ │ │ │ ├── 0005_ga_opt_out.py │ │ │ │ ├── 0006_fix_domain_es_docs.py │ │ │ │ ├── 0007_auto_20200924_1753.py │ │ │ │ ├── 0008_use_livequery.py │ │ │ │ ├── 0009_restrict_mob_access_from_FF.py │ │ │ │ ├── 0010_projectlimit.py │ │ │ │ ├── 0011_alloweducrexpressionsettings.py │ │ │ │ ├── 0012_operatorcalllimitsettings.py │ │ │ │ ├── 0013_accountconfirmationsettings_squashed_0016_alter_smsaccountconfirmationsettings_project_name.py │ │ │ │ ├── 0014_appreleasemodesetting.py │ │ │ │ ├── 0015_delete_projectlimit.py │ │ │ │ ├── 0016_rename_superuserprojectentryrecord_domain_username_domain_supe_domain_c3d32e_idx.py │ │ │ │ ├── 0017_appmanagerdomainsettings.py │ │ │ │ ├── 0018_enable_all_add_ons.py │ │ │ │ ├── 0019_remove_usercase_enabled_field.py │ │ │ │ ├── 0020_delete_transferdomainrequest.py │ │ │ │ ├── 0021_delete_smsaccountconfirmationsettings.py │ │ │ │ ├── __init__.py │ │ │ │ └── sql_templates/ │ │ │ │ └── update_tables1.sql │ │ │ ├── models.py │ │ │ ├── project_access/ │ │ │ │ ├── __init__.py │ │ │ │ ├── middleware.py │ │ │ │ └── models.py │ │ │ ├── shortcuts.py │ │ │ ├── signals.py │ │ │ ├── static/ │ │ │ │ └── domain/ │ │ │ │ ├── js/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── billing_statements.js │ │ │ │ │ │ ├── info_basic.js │ │ │ │ │ │ ├── internal_subscription_management.js │ │ │ │ │ │ └── toggles.js │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── billing_statements.js │ │ │ │ │ │ ├── info_basic.js │ │ │ │ │ │ ├── internal_subscription_management.js │ │ │ │ │ │ └── toggles.js │ │ │ │ │ ├── case_search.js │ │ │ │ │ ├── commtrack_settings.js │ │ │ │ │ ├── confirm_billing_info.js │ │ │ │ │ ├── current_subscription.js │ │ │ │ │ ├── feature_previews.js │ │ │ │ │ ├── internal_calculations.js │ │ │ │ │ ├── internal_settings.js │ │ │ │ │ ├── ip_access_config.js │ │ │ │ │ ├── manage_alerts.js │ │ │ │ │ ├── my_project_settings.js │ │ │ │ │ ├── new_stripe_card_manager.js │ │ │ │ │ ├── select.js │ │ │ │ │ └── update_billing_contact_info.js │ │ │ │ └── json/ │ │ │ │ └── stop_words.yml │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ ├── domain/ │ │ │ │ │ ├── admin/ │ │ │ │ │ │ ├── application_credentials.html │ │ │ │ │ │ ├── case_search.html │ │ │ │ │ │ ├── commtrack_settings.html │ │ │ │ │ │ ├── edit_alert.html │ │ │ │ │ │ ├── feature_previews.html │ │ │ │ │ │ ├── flags_and_privileges.html │ │ │ │ │ │ ├── global_sms_rates.html │ │ │ │ │ │ ├── info_basic.html │ │ │ │ │ │ ├── ip_access_config.html │ │ │ │ │ │ ├── location_fixture.html │ │ │ │ │ │ ├── manage_alerts.html │ │ │ │ │ │ ├── my_project_settings.html │ │ │ │ │ │ ├── partials/ │ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ │ └── project_limits_table.html │ │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ │ └── project_limits_table.html │ │ │ │ │ │ │ ├── case_search_templates.html │ │ │ │ │ │ │ ├── commtrack_action_table.html │ │ │ │ │ │ │ └── ocs_chatbot_enable_modal.html │ │ │ │ │ │ ├── project_limits.html │ │ │ │ │ │ ├── project_privacy.html │ │ │ │ │ │ ├── recovery_measures_history.html │ │ │ │ │ │ ├── sms_rates.html │ │ │ │ │ │ └── sms_settings.html │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── base_change_plan.html │ │ │ │ │ │ ├── billing_statements.html │ │ │ │ │ │ ├── confirm_plan.html │ │ │ │ │ │ ├── confirm_subscription_renewal.html │ │ │ │ │ │ ├── current_subscription.html │ │ │ │ │ │ ├── data_migration_in_progress.html │ │ │ │ │ │ ├── deactivated_notice.html │ │ │ │ │ │ ├── insufficient_privilege_notification.html │ │ │ │ │ │ ├── internal_subscription_management.html │ │ │ │ │ │ ├── manage_releases_by_location.html │ │ │ │ │ │ ├── renew_plan.html │ │ │ │ │ │ ├── select.html │ │ │ │ │ │ ├── select_plan.html │ │ │ │ │ │ ├── selected_plan_contact.html │ │ │ │ │ │ └── tombstone_management.html │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── base_change_plan.html │ │ │ │ │ │ ├── billing_statements.html │ │ │ │ │ │ ├── confirm_plan.html │ │ │ │ │ │ ├── confirm_subscription_renewal.html │ │ │ │ │ │ ├── current_subscription.html │ │ │ │ │ │ ├── data_migration_in_progress.html │ │ │ │ │ │ ├── deactivated_notice.html │ │ │ │ │ │ ├── insufficient_privilege_notification.html │ │ │ │ │ │ ├── internal_subscription_management.html │ │ │ │ │ │ ├── manage_releases_by_location.html │ │ │ │ │ │ ├── renew_plan.html │ │ │ │ │ │ ├── select.html │ │ │ │ │ │ ├── select_plan.html │ │ │ │ │ │ ├── selected_plan_contact.html │ │ │ │ │ │ └── tombstone_management.html │ │ │ │ │ ├── confirm_billing_info.html │ │ │ │ │ ├── email/ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ └── domain_invite.html │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ └── domain_invite.html │ │ │ │ │ │ ├── domain_invite.txt │ │ │ │ │ │ ├── domain_request_approval.html │ │ │ │ │ │ ├── domain_request_approval.txt │ │ │ │ │ │ ├── invite_confirmation.html │ │ │ │ │ │ ├── invite_confirmation.txt │ │ │ │ │ │ ├── password_reset_confirmation.html │ │ │ │ │ │ ├── password_reset_confirmation.txt │ │ │ │ │ │ ├── self_starter.html │ │ │ │ │ │ ├── self_starter.txt │ │ │ │ │ │ ├── support_handoff.html │ │ │ │ │ │ └── support_handoff.txt │ │ │ │ │ ├── import_app_from_another_server_main.html │ │ │ │ │ ├── internal_calculations.html │ │ │ │ │ ├── internal_settings.html │ │ │ │ │ ├── partials/ │ │ │ │ │ │ ├── app_list.html │ │ │ │ │ │ ├── autopay_terms.html │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── auto_renew_modal.html │ │ │ │ │ │ │ ├── license_explanations.html │ │ │ │ │ │ │ ├── new_stripe_card_template.html │ │ │ │ │ │ │ └── payment_modal.html │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ ├── auto_renew_modal.html │ │ │ │ │ │ │ ├── license_explanations.html │ │ │ │ │ │ │ ├── new_stripe_card_template.html │ │ │ │ │ │ │ ├── payment_modal.html │ │ │ │ │ │ │ └── restrictive_license.html │ │ │ │ │ │ ├── disable_auto_renew.html │ │ │ │ │ │ ├── enable_auto_renew.html │ │ │ │ │ │ ├── how_to_download_app_json.html │ │ │ │ │ │ ├── how_to_import_multimedia.html │ │ │ │ │ │ ├── how_to_start_with_import_app_feature.html │ │ │ │ │ │ ├── new_stripe_card_alpinejs.html │ │ │ │ │ │ ├── payment_methods.html │ │ │ │ │ │ └── select_autopay_method.html │ │ │ │ │ └── update_billing_contact_info.html │ │ │ │ ├── error.html │ │ │ │ └── login_and_password/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── login.html │ │ │ │ │ └── password_change_done.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── login.html │ │ │ │ │ └── password_change_done.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── login_full.html │ │ │ │ │ │ └── server_location_select.html │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── login_full.html │ │ │ │ │ │ └── server_location_select.html │ │ │ │ │ └── password_reset_form_only.html │ │ │ │ ├── password_reset_complete.html │ │ │ │ ├── password_reset_confirm.html │ │ │ │ ├── password_reset_done.html │ │ │ │ ├── password_reset_email.html │ │ │ │ ├── password_reset_form.html │ │ │ │ └── two_factor/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── _wizard_actions.html │ │ │ │ │ └── _wizard_forms.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── _wizard_actions.html │ │ │ │ │ └── _wizard_forms.html │ │ │ │ ├── core/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── backup_tokens.html │ │ │ │ │ │ ├── login.html │ │ │ │ │ │ ├── login_form.html │ │ │ │ │ │ ├── phone_register.html │ │ │ │ │ │ ├── setup.html │ │ │ │ │ │ └── setup_complete.html │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ ├── backup_tokens.html │ │ │ │ │ ├── login.html │ │ │ │ │ ├── login_form.html │ │ │ │ │ ├── otp_required.html │ │ │ │ │ ├── phone_register.html │ │ │ │ │ ├── setup.html │ │ │ │ │ └── setup_complete.html │ │ │ │ └── profile/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── disable.html │ │ │ │ │ └── profile.html │ │ │ │ └── bootstrap5/ │ │ │ │ ├── disable.html │ │ │ │ └── profile.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_api_key_auth.py │ │ │ │ ├── test_auth.py │ │ │ │ ├── test_auth_decorators.py │ │ │ │ ├── test_auth_type.py │ │ │ │ ├── test_dbaccessors.py │ │ │ │ ├── test_delete_domain.py │ │ │ │ ├── test_deletion_models.py │ │ │ │ ├── test_domain_calculated_properties.py │ │ │ │ ├── test_domain_name_generation.py │ │ │ │ ├── test_forms.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_oauth_superuser_access.py │ │ │ │ ├── test_password_reset.py │ │ │ │ ├── test_password_strength.py │ │ │ │ ├── test_redirect_url.py │ │ │ │ ├── test_remove_duplicate_domains.py │ │ │ │ ├── test_two_factor_check.py │ │ │ │ ├── test_utils.py │ │ │ │ └── test_views.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views/ │ │ │ ├── __init__.py │ │ │ ├── accounting.py │ │ │ ├── base.py │ │ │ ├── feedback.py │ │ │ ├── fixtures.py │ │ │ ├── import_apps.py │ │ │ ├── internal.py │ │ │ ├── releases.py │ │ │ ├── settings.py │ │ │ ├── sms.py │ │ │ └── tombstone.py │ │ ├── domain_migration_flags/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── api.py │ │ │ ├── exceptions.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_migrate_data_from_tzmigration.py │ │ │ │ ├── 0003_add_migration_dates.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ └── tests/ │ │ │ ├── __init__.py │ │ │ └── test_domain_migration_progress.py │ │ ├── dropbox/ │ │ │ ├── __init__.py │ │ │ ├── decorators.py │ │ │ ├── exceptions.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ └── upload_file_to_dropbox.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── dropbox/ │ │ │ │ └── emails/ │ │ │ │ ├── upload_error.html │ │ │ │ ├── upload_error.txt │ │ │ │ ├── upload_success.html │ │ │ │ └── upload_success.txt │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_dropbox_upload_helper.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── dump_reload/ │ │ │ ├── __init__.py │ │ │ ├── const.py │ │ │ ├── couch/ │ │ │ │ ├── __init__.py │ │ │ │ ├── dump.py │ │ │ │ ├── id_providers.py │ │ │ │ └── load.py │ │ │ ├── exceptions.py │ │ │ ├── interface.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── dump_case_data.py │ │ │ │ ├── dump_domain_data.py │ │ │ │ ├── dump_domain_data_raw.py │ │ │ │ ├── load_domain_data.py │ │ │ │ └── print_domain_stats.py │ │ │ ├── sql/ │ │ │ │ ├── __init__.py │ │ │ │ ├── dump.py │ │ │ │ ├── filters.py │ │ │ │ ├── load.py │ │ │ │ └── serialization.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ └── old-model-names.json │ │ │ │ ├── test_couch_dump_load.py │ │ │ │ ├── test_dump_models.py │ │ │ │ ├── test_serialization.py │ │ │ │ ├── test_slugs.py │ │ │ │ ├── test_sql_data_loader.py │ │ │ │ ├── test_sql_dump_load.py │ │ │ │ └── test_sql_filters.py │ │ │ └── util.py │ │ ├── email/ │ │ │ ├── __init__.py │ │ │ ├── forms.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_emailsettings_return_path_email.py │ │ │ │ ├── 0003_emailsettings_password_cbc.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── email/ │ │ │ │ └── js/ │ │ │ │ └── email_settings.js │ │ │ ├── templates/ │ │ │ │ └── email/ │ │ │ │ └── email_settings.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_email_form.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── enterprise/ │ │ │ ├── __init__.py │ │ │ ├── api/ │ │ │ │ ├── __init__.py │ │ │ │ ├── api.py │ │ │ │ └── resources.py │ │ │ ├── decorators.py │ │ │ ├── dispatcher.py │ │ │ ├── enterprise.py │ │ │ ├── exceptions.py │ │ │ ├── filters.py │ │ │ ├── forms.py │ │ │ ├── interface.py │ │ │ ├── iterators.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ └── report_on_enterprise_domain.py │ │ │ ├── metric_events.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_enterprisepermissions_account_unique.py │ │ │ │ ├── 0003_enterprisepermissions_modify_account.py │ │ │ │ ├── 0004_enterprisemobileworkersettings.py │ │ │ │ └── __init__.py │ │ │ ├── mixins.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── enterprise/ │ │ │ │ └── js/ │ │ │ │ ├── enterprise_settings.js │ │ │ │ └── project_dashboard.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── enterprise/ │ │ │ │ ├── enterprise_permissions.html │ │ │ │ ├── enterprise_settings.html │ │ │ │ ├── manage_mobile_workers.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── date_range_modal.html │ │ │ │ │ ├── enterprise_permissions_table.html │ │ │ │ │ └── project_tile.html │ │ │ │ └── project_dashboard.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── api/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── test_resources.py │ │ │ │ ├── test_apis.py │ │ │ │ ├── test_decorators.py │ │ │ │ ├── test_enterprise.py │ │ │ │ ├── test_enterprise_mobile_worker_settings.py │ │ │ │ ├── test_enterprise_tasks.py │ │ │ │ ├── test_forms.py │ │ │ │ ├── test_interface.py │ │ │ │ ├── test_iterators.py │ │ │ │ ├── test_permissions.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_views.py │ │ │ │ └── utils.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── es/ │ │ │ ├── README.rst │ │ │ ├── REINDEX_PROCESS.md │ │ │ ├── __init__.py │ │ │ ├── aggregations.py │ │ │ ├── app_config.py │ │ │ ├── apps.py │ │ │ ├── case_search.py │ │ │ ├── case_search_sub.py │ │ │ ├── cases.py │ │ │ ├── client.py │ │ │ ├── const.py │ │ │ ├── domains.py │ │ │ ├── es_query.py │ │ │ ├── exceptions.py │ │ │ ├── fake/ │ │ │ │ ├── __init__.py │ │ │ │ ├── es_query_fake.py │ │ │ │ ├── forms_fake.py │ │ │ │ ├── groups_fake.py │ │ │ │ └── users_fake.py │ │ │ ├── filters.py │ │ │ ├── forms.py │ │ │ ├── groups.py │ │ │ ├── index/ │ │ │ │ ├── __init__.py │ │ │ │ ├── analysis.py │ │ │ │ └── settings.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── elastic_sync_multiplexed.py │ │ │ │ ├── ensure_indices_reindexed.py │ │ │ │ ├── es_version_for_index.py │ │ │ │ ├── make_elastic_migration.py │ │ │ │ ├── resave_failed_forms_and_cases.py │ │ │ │ ├── restore_es_snapshot.py │ │ │ │ ├── verify_reindex.py │ │ │ │ └── wipe_es.py │ │ │ ├── mappings/ │ │ │ │ ├── __init__.py │ │ │ │ ├── app_mapping.py │ │ │ │ ├── case_mapping.py │ │ │ │ ├── case_search_mapping.py │ │ │ │ ├── const.py │ │ │ │ ├── domain_mapping.py │ │ │ │ ├── group_mapping.py │ │ │ │ ├── sms_mapping.py │ │ │ │ ├── tests/ │ │ │ │ │ └── __init__.py │ │ │ │ ├── user_mapping.py │ │ │ │ └── xform_mapping.py │ │ │ ├── migration_operations.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_bootstrap_es_indexes.py │ │ │ │ ├── 0002_add_tombstones.py │ │ │ │ ├── 0003_add_assigned_location_ids.py │ │ │ │ ├── 0004_make_new_indexes.py │ │ │ │ ├── 0005_add_epoch_as_valid_date_to_forms.py │ │ │ │ ├── 0006_verify_es2_indices_reindexed.py │ │ │ │ ├── 0007_init_indices_for_fresh_es_5.py │ │ │ │ ├── 0008_add_doc_id_to_all_mappings.py │ │ │ │ ├── 0009_add_indices_for_reindex_in_es5.py │ │ │ │ ├── 0010_delete_reverted_indices.py │ │ │ │ ├── 0011_add_indices_for_es5_reindex.py │ │ │ │ ├── 0012_add_new_index_for_bha.py │ │ │ │ ├── 0013_add_last_modifed.py │ │ │ │ ├── 0014_enable_slowlogs.py │ │ │ │ ├── 0015_add_user_domain_memberships.py │ │ │ │ ├── 0016_add_new_index_for_cc_perf.py │ │ │ │ ├── 0017_add_is_account_confirmed.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── profiling.py │ │ │ ├── queries.py │ │ │ ├── sms.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── index/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── test_analysis.py │ │ │ │ │ └── test_settings.py │ │ │ │ ├── test_aggregations.py │ │ │ │ ├── test_app_adapter.py │ │ │ │ ├── test_case_adapter.py │ │ │ │ ├── test_case_search_adapter.py │ │ │ │ ├── test_case_search_es.py │ │ │ │ ├── test_client.py │ │ │ │ ├── test_command_make_elastic_migration.py │ │ │ │ ├── test_domain_adapter.py │ │ │ │ ├── test_elastic_sync_multiplexed_command.py │ │ │ │ ├── test_ensure_indices_reindexed.py │ │ │ │ ├── test_esquery.py │ │ │ │ ├── test_esqueryset.py │ │ │ │ ├── test_filters.py │ │ │ │ ├── test_form_adapter.py │ │ │ │ ├── test_migration_operations.py │ │ │ │ ├── test_queries.py │ │ │ │ ├── test_sms.py │ │ │ │ ├── test_sorting.py │ │ │ │ ├── test_test_utils.py │ │ │ │ ├── test_user_adapter.py │ │ │ │ ├── test_users_es.py │ │ │ │ ├── test_utils.py │ │ │ │ ├── test_verify_reindex.py │ │ │ │ └── utils.py │ │ │ ├── transient_util.py │ │ │ ├── users.py │ │ │ └── utils.py │ │ ├── events/ │ │ │ ├── __init__.py │ │ │ └── migrations/ │ │ │ ├── 0001_add_events_model.py │ │ │ ├── 0002_attendancetrackingconfig.py │ │ │ ├── 0003_event_attendance_taker_ids.py │ │ │ ├── 0004_event_id_case_id.py │ │ │ ├── 0005_rename_alter_event__attendance_taker_ids.py │ │ │ ├── 0006_remove_end_date_constraint.py │ │ │ ├── 0007_alter_event_attendee_list_status.py │ │ │ ├── 0008_alter_event__case_id.py │ │ │ ├── 0009_attendeemodel.py │ │ │ ├── 0010_event_location.py │ │ │ └── __init__.py │ │ ├── experiments/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── experiment.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ └── tests/ │ │ │ ├── __init__.py │ │ │ ├── test_enabler.py │ │ │ └── test_experiment.py │ │ ├── export/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── const.py │ │ │ ├── dbaccessors.py │ │ │ ├── det/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── exceptions.py │ │ │ │ └── schema_generator.py │ │ │ ├── esaccessors.py │ │ │ ├── exceptions.py │ │ │ ├── export.py │ │ │ ├── filters.py │ │ │ ├── forms.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── deid_export.py │ │ │ │ ├── delete_exports.py │ │ │ │ ├── download_saved_export.py │ │ │ │ ├── process_skipped_pages.py │ │ │ │ ├── rebuild_export.py │ │ │ │ ├── update_export_with_newest_data.py │ │ │ │ └── upload_saved_export.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_datafile.py │ │ │ │ ├── 0003_emailexportwhendonerequest.py │ │ │ │ ├── 0004_datafile_delete_after.py │ │ │ │ ├── 0005_datafile_blobmeta.py │ │ │ │ ├── 0006_delete_dailysavedexportnotification.py │ │ │ │ ├── 0007_auto_20190906_0149.py │ │ │ │ ├── 0008_auto_20190906_2008.py │ │ │ │ ├── 0009_incrementalexport_incrementalexportcheckpoint.py │ │ │ │ ├── 0010_defaultexportsettings.py │ │ │ │ ├── 0011_defaultexportsettings_usecouchfiletypes.py │ │ │ │ ├── 0012_defaultexportsettings_remove_duplicates_option.py │ │ │ │ ├── 0013_rm_incrementalexport.py │ │ │ │ ├── 0014_deidhash.py │ │ │ │ └── __init__.py │ │ │ ├── models/ │ │ │ │ ├── __init__.py │ │ │ │ ├── deid_export.py │ │ │ │ ├── export_settings.py │ │ │ │ └── new.py │ │ │ ├── multiprocess.py │ │ │ ├── static/ │ │ │ │ └── export/ │ │ │ │ ├── js/ │ │ │ │ │ ├── const.js │ │ │ │ │ ├── create_export.js │ │ │ │ │ ├── customize_export_new.js │ │ │ │ │ ├── datasource_export.js │ │ │ │ │ ├── download_data_files.js │ │ │ │ │ ├── download_export.js │ │ │ │ │ ├── export_list.js │ │ │ │ │ ├── export_list_main.js │ │ │ │ │ ├── models.js │ │ │ │ │ └── utils.js │ │ │ │ └── spec/ │ │ │ │ ├── ExportColumn.spec.js │ │ │ │ ├── ExportInstance.spec.js │ │ │ │ ├── Exports.Utils.spec.js │ │ │ │ ├── data/ │ │ │ │ │ └── export_instances.js │ │ │ │ └── main.js │ │ │ ├── system_properties.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── export/ │ │ │ │ ├── commcare_analytics.html │ │ │ │ ├── customize_export_new.html │ │ │ │ ├── datasource_export_view.html │ │ │ │ ├── dialogs/ │ │ │ │ │ ├── bulk_delete_custom_export_dialog.html │ │ │ │ │ ├── delete_custom_export_dialog.html │ │ │ │ │ ├── process_deleted_questions.html │ │ │ │ │ └── process_deprecated_properties.html │ │ │ │ ├── download_data_files.html │ │ │ │ ├── download_export.html │ │ │ │ ├── export_list.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── customize_export_header.html │ │ │ │ │ ├── delete_bulk_notice.html │ │ │ │ │ ├── export_bulk_notice.html │ │ │ │ │ ├── export_download_prepare.html │ │ │ │ │ ├── export_download_progress.html │ │ │ │ │ ├── export_list_controller.html │ │ │ │ │ ├── export_list_create_export_modal.html │ │ │ │ │ ├── feed_filter_modal.html │ │ │ │ │ ├── loading_exports.html │ │ │ │ │ ├── new_customize_export_templates.html │ │ │ │ │ ├── odata_feed_limit_reached_modal.html │ │ │ │ │ └── table.html │ │ │ │ ├── paywall.html │ │ │ │ └── spec/ │ │ │ │ └── mocha.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ ├── app_with_subcases.json │ │ │ │ │ ├── app_with_subcases_form.xml │ │ │ │ │ ├── app_with_subcases_submission.json │ │ │ │ │ ├── basic_application.json │ │ │ │ │ ├── basic_case_application.json │ │ │ │ │ ├── basic_form.xml │ │ │ │ │ ├── basic_form_version2.xml │ │ │ │ │ ├── basic_form_version2_delete.xml │ │ │ │ │ ├── det/ │ │ │ │ │ │ ├── case_export_instance.json │ │ │ │ │ │ ├── form_export_instance.json │ │ │ │ │ │ └── form_export_instance_with_repeat.json │ │ │ │ │ ├── form_with_labels.xml │ │ │ │ │ ├── multiple_choice_form.xml │ │ │ │ │ ├── multiple_choice_form_version2.xml │ │ │ │ │ ├── nested_repeat_form.xml │ │ │ │ │ ├── parent_child_case_application.json │ │ │ │ │ ├── question_schema_no_multi.xml │ │ │ │ │ ├── question_schema_test_app.json │ │ │ │ │ ├── question_schema_update_form.xml │ │ │ │ │ ├── repeat_group_form.xml │ │ │ │ │ ├── repeat_group_form_version2.xml │ │ │ │ │ └── stock_form.xml │ │ │ │ ├── test_add_inferred_export_properties.py │ │ │ │ ├── test_daily_saved_exports.py │ │ │ │ ├── test_dbaccessors.py │ │ │ │ ├── test_deid_export.py │ │ │ │ ├── test_det_schema_generation.py │ │ │ │ ├── test_esaccessors.py │ │ │ │ ├── test_export.py │ │ │ │ ├── test_export_column.py │ │ │ │ ├── test_export_data_schema.py │ │ │ │ ├── test_export_filters.py │ │ │ │ ├── test_export_form_subcases.py │ │ │ │ ├── test_export_forms.py │ │ │ │ ├── test_export_instance.py │ │ │ │ ├── test_export_item.py │ │ │ │ ├── test_export_list_helper.py │ │ │ │ ├── test_export_models_new.py │ │ │ │ ├── test_export_utils.py │ │ │ │ ├── test_export_views.py │ │ │ │ ├── test_get_export_file.py │ │ │ │ ├── test_inferred_schema.py │ │ │ │ ├── test_new.py │ │ │ │ ├── test_sms_export.py │ │ │ │ ├── test_table_configuration.py │ │ │ │ ├── test_views.py │ │ │ │ └── util.py │ │ │ ├── transforms.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views/ │ │ │ ├── __init__.py │ │ │ ├── download.py │ │ │ ├── edit.py │ │ │ ├── list.py │ │ │ ├── new.py │ │ │ └── utils.py │ │ ├── fixtures/ │ │ │ ├── __init__.py │ │ │ ├── apps.py │ │ │ ├── dispatcher.py │ │ │ ├── download.py │ │ │ ├── exceptions.py │ │ │ ├── fixturegenerators.py │ │ │ ├── interface.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_rm_blobdb_domain_fixtures.py │ │ │ │ ├── 0003_rm_blobdb_domain_fixtures.py │ │ │ │ ├── 0004_userlookuptablestatus.py │ │ │ │ ├── 0005_lookuptablemodels.py │ │ │ │ ├── 0005_sqllookuptablemodels.py │ │ │ │ ├── 0006_index_row_id.py │ │ │ │ ├── 0007_db_cascade.py │ │ │ │ ├── 0008_sqllookuptables.py │ │ │ │ ├── 0009_remove_lookuptablerowowner_couch_id.py │ │ │ │ ├── 0010_lookuptable_is_synced.py │ │ │ │ ├── 0011_lookuptable_last_modified.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── resources/ │ │ │ │ ├── __init__.py │ │ │ │ ├── v0_1.py │ │ │ │ └── v0_6.py │ │ │ ├── static/ │ │ │ │ └── fixtures/ │ │ │ │ └── js/ │ │ │ │ ├── lookup-manage.js │ │ │ │ └── view-table.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── fixtures/ │ │ │ │ ├── fixtures_base.html │ │ │ │ ├── manage_tables.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── edit_table_modal.html │ │ │ │ │ ├── fixture_upload_status.html │ │ │ │ │ ├── fixture_upload_status_api.txt │ │ │ │ │ └── modal_edit.html │ │ │ │ ├── upload_complete.html │ │ │ │ ├── upload_complete.txt │ │ │ │ └── view_table.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_download.py │ │ │ │ ├── test_field_names.py │ │ │ │ ├── test_fixture_data.py │ │ │ │ ├── test_location_ownership.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_upload/ │ │ │ │ │ ├── not_excel_file.xlsx │ │ │ │ │ └── ok.xlsx │ │ │ │ ├── test_upload.py │ │ │ │ ├── test_views.py │ │ │ │ └── test_workbook.py │ │ │ ├── upload/ │ │ │ │ ├── __init__.py │ │ │ │ ├── const.py │ │ │ │ ├── definitions.py │ │ │ │ ├── failure_messages.py │ │ │ │ ├── run_upload.py │ │ │ │ ├── validation.py │ │ │ │ └── workbook.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── formplayer_api/ │ │ │ ├── __init__.py │ │ │ ├── clear_user_data.py │ │ │ ├── const.py │ │ │ ├── exceptions.py │ │ │ ├── form_validation.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── clear_formplayer_dbs.py │ │ │ │ └── prime_formplayer_restores.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_drop_old_tables.py │ │ │ │ └── __init__.py │ │ │ ├── smsforms/ │ │ │ │ ├── __init__.py │ │ │ │ ├── api.py │ │ │ │ ├── exceptions.py │ │ │ │ ├── sms.py │ │ │ │ └── tests/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_formplayer_interface.py │ │ │ ├── sync_db.py │ │ │ ├── tasks.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_form_validation.py │ │ │ │ └── test_prime_restore.py │ │ │ └── utils.py │ │ ├── geospatial/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── apps.py │ │ │ ├── const.py │ │ │ ├── dispatchers.py │ │ │ ├── es.py │ │ │ ├── exceptions.py │ │ │ ├── filters.py │ │ │ ├── forms.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── copy_gps_metadata.py │ │ │ │ ├── index_geolocation_case_properties.py │ │ │ │ └── index_utils.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_create_geopolygon.py │ │ │ │ ├── 0002_geoconfig.py │ │ │ │ ├── 0003_auto_20230908_0927.py │ │ │ │ ├── 0004_auto_20230920_0821.py │ │ │ │ ├── 0005_auto_20240202_0807.py │ │ │ │ ├── 0006_geoconfig_max_cases_per_user_and_more.py │ │ │ │ ├── 0007_geoconfig_max_case_distance_and_more.py │ │ │ │ ├── 0008_geoconfig_flag_assigned_cases.py │ │ │ │ ├── 0009_geoconfig_api_key_cbc_encryption.py │ │ │ │ ├── 0010_remove_road_network_algorithm.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── reports.py │ │ │ ├── routing_solvers/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── mapbox_utils.py │ │ │ │ └── pulp.py │ │ │ ├── static/ │ │ │ │ └── geospatial/ │ │ │ │ └── js/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── case_grouping_map.js │ │ │ │ │ ├── case_management.js │ │ │ │ │ ├── geo_config.js │ │ │ │ │ ├── gps_capture.js │ │ │ │ │ └── models.js │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── case_grouping_map.js │ │ │ │ │ ├── case_management.js │ │ │ │ │ ├── geo_config.js │ │ │ │ │ ├── gps_capture.js │ │ │ │ │ └── models.js │ │ │ │ └── utils.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── geospatial/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── base_template.html │ │ │ │ │ ├── case_grouping_map.html │ │ │ │ │ ├── case_grouping_map_base.html │ │ │ │ │ ├── case_management.html │ │ │ │ │ ├── case_management_base.html │ │ │ │ │ ├── gps_capture.html │ │ │ │ │ ├── gps_capture_view.html │ │ │ │ │ └── settings.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── base_template.html │ │ │ │ │ ├── case_grouping_map.html │ │ │ │ │ ├── case_grouping_map_base.html │ │ │ │ │ ├── case_management.html │ │ │ │ │ ├── case_management_base.html │ │ │ │ │ ├── gps_capture.html │ │ │ │ │ ├── gps_capture_view.html │ │ │ │ │ └── settings.html │ │ │ │ └── partials/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── delete_saved_area_modal.html │ │ │ │ │ ├── review_assignment_modal.html │ │ │ │ │ └── saved_polygon_filter.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── delete_saved_area_modal.html │ │ │ │ │ ├── review_assignment_modal.html │ │ │ │ │ └── saved_polygon_filter.html │ │ │ │ └── index_alert.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_copy_gps_metadata.py │ │ │ │ ├── test_es.py │ │ │ │ ├── test_index_geolocation_case_properties.py │ │ │ │ ├── test_mapbox_optimize.py │ │ │ │ ├── test_models.py │ │ │ │ ├── test_pulp.py │ │ │ │ ├── test_reports.py │ │ │ │ ├── test_tasks.py │ │ │ │ ├── test_utils.py │ │ │ │ └── test_views.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── groups/ │ │ │ ├── __init__.py │ │ │ ├── _design/ │ │ │ │ └── views/ │ │ │ │ ├── all_groups/ │ │ │ │ │ └── map.js │ │ │ │ ├── by_name/ │ │ │ │ │ └── map.js │ │ │ │ └── by_user/ │ │ │ │ └── map.js │ │ │ ├── dbaccessors.py │ │ │ ├── exceptions.py │ │ │ ├── fields.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── groups/ │ │ │ │ └── js/ │ │ │ │ ├── all_groups.js │ │ │ │ └── group_members.js │ │ │ ├── templates/ │ │ │ │ └── groups/ │ │ │ │ ├── all_groups.html │ │ │ │ ├── group_members.html │ │ │ │ └── partials/ │ │ │ │ ├── case_sharing_upgrade_notice.html │ │ │ │ └── edit_group_disabled_case_sharing.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_groups.py │ │ │ │ ├── test_models.py │ │ │ │ └── test_utils.py │ │ │ ├── urls.py │ │ │ └── views.py │ │ ├── hqadmin/ │ │ │ ├── __init__.py │ │ │ ├── _design/ │ │ │ │ └── filters/ │ │ │ │ ├── domains_and_doc_types.js │ │ │ │ └── not_case_form.js │ │ │ ├── admin.py │ │ │ ├── app_config.py │ │ │ ├── corrupt_couch.py │ │ │ ├── escheck.py │ │ │ ├── forms.py │ │ │ ├── history.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── cchq_prbac_bootstrap.py │ │ │ │ ├── cchq_prbac_grandfather_privs.py │ │ │ │ ├── cchq_prbac_revoke_privs.py │ │ │ │ ├── check_case_index_ids.py │ │ │ │ ├── check_services.py │ │ │ │ ├── clean_2fa_sessions.py │ │ │ │ ├── clear_supervisor_confs.py │ │ │ │ ├── corrupt_couch.py │ │ │ │ ├── corrupt_couch_nodes.py │ │ │ │ ├── delete_old_couch_views_from_disk.py │ │ │ │ ├── delete_related_cases.py │ │ │ │ ├── deploy_in_progress.py │ │ │ │ ├── export_domain_forms_raw.py │ │ │ │ ├── fetch_reconciliation_records.py │ │ │ │ ├── fix_checkpoint_after_rewind.py │ │ │ │ ├── fix_checkpoints_from_date.py │ │ │ │ ├── fix_checkpoints_from_file.py │ │ │ │ ├── force_web_user_password_reset.py │ │ │ │ ├── get_download_url.py │ │ │ │ ├── import_domain_forms_raw.py │ │ │ │ ├── kill_stale_celery_workers.py │ │ │ │ ├── mail_admins.py │ │ │ │ ├── preindex_everything.py │ │ │ │ ├── prune_couch_views.py │ │ │ │ ├── prune_elastic_indices.py │ │ │ │ ├── recent_changes.py │ │ │ │ ├── record_deploy_success.py │ │ │ │ ├── republish_doc_changes.py │ │ │ │ ├── send_email.py │ │ │ │ ├── shutdown_celery_worker_by_hostname.py │ │ │ │ ├── stale_data_in_es.py │ │ │ │ ├── static_analysis.py │ │ │ │ ├── update_django_locales.py │ │ │ │ ├── update_site_setup.py │ │ │ │ └── verify_ssl_connections.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_vcmmigrationaudit.py │ │ │ │ ├── 0003_auto_20160715_1543.py │ │ │ │ ├── 0004_auto_20160715_1547.py │ │ │ │ ├── 0005_auto_20160715_1612.py │ │ │ │ ├── 0006_esrestorepillowcheckpoints.py │ │ │ │ ├── 0007_esrestorepillowcheckpoint_datefield.py │ │ │ │ ├── 0008_delete_vcmmigration.py │ │ │ │ ├── 0009_auto_20170315_1322.py │ │ │ │ ├── 0010_sqlhqdeploy.py │ │ │ │ ├── 0011_alter_hqdeploy_environment.py │ │ │ │ ├── 0012_alter_hqdeploy_diffurl.py │ │ │ │ ├── 0013_populate_sqlhqdeploy.py │ │ │ │ ├── 0014_remove_sqlhqdeploy_couch_id.py │ │ │ │ ├── 0015_rename_sqlhqdeploy.py │ │ │ │ ├── 0016_hqdeploy_ordering.py │ │ │ │ ├── 0017_hqdeploy_commit.py │ │ │ │ ├── 0018_back_populate_deploy_commit.py │ │ │ │ ├── 0019_celery_taskmeta_sequence.py │ │ │ │ ├── 0020_celery_results_sequence.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── pillow_settings.py │ │ │ ├── reporting/ │ │ │ │ └── __init__.py │ │ │ ├── reports.py │ │ │ ├── service_checks.py │ │ │ ├── signals.py │ │ │ ├── static/ │ │ │ │ └── hqadmin/ │ │ │ │ └── js/ │ │ │ │ ├── admin_restore.js │ │ │ │ ├── app_build_timings.js │ │ │ │ ├── mass_email.js │ │ │ │ ├── raw_doc.js │ │ │ │ ├── superuser_management.js │ │ │ │ └── system_info.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── hqadmin/ │ │ │ │ ├── admin_restore.html │ │ │ │ ├── app_build_timings.html │ │ │ │ ├── branches_on_staging.html │ │ │ │ ├── call_center_ucr_check.html │ │ │ │ ├── disable_two_factor.html │ │ │ │ ├── disable_user.html │ │ │ │ ├── doc_in_es.html │ │ │ │ ├── email/ │ │ │ │ │ ├── account_disabled_email.html │ │ │ │ │ ├── error_email.html │ │ │ │ │ ├── superuser_staff_email.html │ │ │ │ │ └── two_factor_reset_email.html │ │ │ │ ├── email_status.html │ │ │ │ ├── gir_downloader.html │ │ │ │ ├── global_thresholds.html │ │ │ │ ├── hqadmin_base_filters.html │ │ │ │ ├── malt_downloader.html │ │ │ │ ├── mass_email.html │ │ │ │ ├── messaging_case_updates.html │ │ │ │ ├── offboarding_list.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── commit_table.html │ │ │ │ │ ├── commit_tr.html │ │ │ │ │ ├── deploy_history.html │ │ │ │ │ ├── pillow-operation-modal.html │ │ │ │ │ ├── project_snapshot.html │ │ │ │ │ └── timing_data_table.html │ │ │ │ ├── raw_doc.html │ │ │ │ ├── superuser_management.html │ │ │ │ ├── system_info.html │ │ │ │ ├── user_audit_report.html │ │ │ │ └── web_user_lookup.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ └── restore.xml │ │ │ │ ├── test_corrupt_couch.py │ │ │ │ ├── test_prbac.py │ │ │ │ ├── test_raw_doc.py │ │ │ │ ├── test_reports.py │ │ │ │ ├── test_service_checks.py │ │ │ │ ├── test_stale_data_in_es.py │ │ │ │ ├── test_user_audit_report.py │ │ │ │ ├── test_utils.py │ │ │ │ └── test_views.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views/ │ │ │ ├── __init__.py │ │ │ ├── data.py │ │ │ ├── operations.py │ │ │ ├── reports.py │ │ │ ├── system.py │ │ │ ├── users.py │ │ │ └── utils.py │ │ ├── hqcase/ │ │ │ ├── __init__.py │ │ │ ├── analytics.py │ │ │ ├── api/ │ │ │ │ ├── core.py │ │ │ │ ├── get_bulk.py │ │ │ │ ├── get_list.py │ │ │ │ └── updates.py │ │ │ ├── bulk.py │ │ │ ├── case_helper.py │ │ │ ├── constants.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── initialize_es_indices.py │ │ │ │ ├── ptop_preindex.py │ │ │ │ ├── ptop_reindexer_v2.py │ │ │ │ ├── reindex_sql_forms_in_domain.py │ │ │ │ ├── reprocess_form_case_es_deletes.py │ │ │ │ └── reset_case_name.py │ │ │ ├── static/ │ │ │ │ └── hqcase/ │ │ │ │ └── js/ │ │ │ │ └── explode_cases.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── hqcase/ │ │ │ │ ├── explode_cases.html │ │ │ │ └── xml/ │ │ │ │ └── case_block.xml │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── test_api_updates.py │ │ │ │ ├── test_bugs.py │ │ │ │ ├── test_bulk.py │ │ │ │ ├── test_case_api_bulk_get.py │ │ │ │ ├── test_case_api_get.py │ │ │ │ ├── test_case_api_permissions.py │ │ │ │ ├── test_case_api_updates.py │ │ │ │ ├── test_case_copier.py │ │ │ │ ├── test_case_helper.py │ │ │ │ ├── test_case_list_api.py │ │ │ │ ├── test_case_sharing.py │ │ │ │ ├── test_case_update_api.py │ │ │ │ ├── test_dbaccessors.py │ │ │ │ ├── test_explode_cases.py │ │ │ │ ├── test_external_id_url_encoding.py │ │ │ │ ├── test_loadtest_users.py │ │ │ │ ├── test_object_cache.py │ │ │ │ ├── test_serialization.py │ │ │ │ └── test_utils.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ └── views.py │ │ ├── hqmedia/ │ │ │ ├── README.rst │ │ │ ├── __init__.py │ │ │ ├── _design/ │ │ │ │ ├── fulltext/ │ │ │ │ │ ├── audio_search/ │ │ │ │ │ │ └── index.js │ │ │ │ │ └── image_search/ │ │ │ │ │ └── index.js │ │ │ │ └── views/ │ │ │ │ ├── by_domain/ │ │ │ │ │ └── map.js │ │ │ │ └── by_hash/ │ │ │ │ └── map.js │ │ │ ├── cache.py │ │ │ ├── controller.py │ │ │ ├── exceptions.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ └── __init__.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── static/ │ │ │ │ └── hqmedia/ │ │ │ │ └── js/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── bulk_upload.js │ │ │ │ │ ├── media_reference_models.js │ │ │ │ │ ├── references_main.js │ │ │ │ │ ├── translations_coverage.js │ │ │ │ │ └── uploaders.js │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── bulk_upload.js │ │ │ │ │ ├── media_reference_models.js │ │ │ │ │ ├── references_main.js │ │ │ │ │ ├── translations_coverage.js │ │ │ │ │ └── uploaders.js │ │ │ │ └── manage_paths_main.js │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ └── hqmedia/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── audio_translator.html │ │ │ │ │ ├── bulk_upload.html │ │ │ │ │ ├── bulk_upload_status.html │ │ │ │ │ ├── manage_paths.html │ │ │ │ │ ├── references.html │ │ │ │ │ ├── translations_coverage.html │ │ │ │ │ └── uploader_base.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── audio_translator.html │ │ │ │ │ ├── bulk_upload.html │ │ │ │ │ ├── bulk_upload_status.html │ │ │ │ │ ├── manage_paths.html │ │ │ │ │ ├── references.html │ │ │ │ │ ├── translations_coverage.html │ │ │ │ │ └── uploader_base.html │ │ │ │ ├── partials/ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── bulk_upload.html │ │ │ │ │ │ ├── bulk_upload_status.html │ │ │ │ │ │ ├── manage_paths.html │ │ │ │ │ │ ├── multimedia_uploader.html │ │ │ │ │ │ ├── multimedia_zip_notice.html │ │ │ │ │ │ └── reference_table.html │ │ │ │ │ └── bootstrap5/ │ │ │ │ │ ├── bulk_upload.html │ │ │ │ │ ├── bulk_upload_status.html │ │ │ │ │ ├── manage_paths.html │ │ │ │ │ ├── multimedia_uploader.html │ │ │ │ │ ├── multimedia_zip_notice.html │ │ │ │ │ └── reference_table.html │ │ │ │ └── uploader/ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ ├── preview_audio_single.html │ │ │ │ │ ├── preview_image_single.html │ │ │ │ │ ├── preview_video_single.html │ │ │ │ │ └── queue_single.html │ │ │ │ ├── bootstrap5/ │ │ │ │ │ ├── preview_audio_single.html │ │ │ │ │ ├── preview_image_single.html │ │ │ │ │ ├── preview_video_single.html │ │ │ │ │ └── queue_single.html │ │ │ │ └── errors.html │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ ├── data/ │ │ │ │ │ └── manage-multimedia.json │ │ │ │ ├── test_audio_translator_files.py │ │ │ │ ├── test_manage_paths.py │ │ │ │ ├── test_pillow_library_usage.py │ │ │ │ └── test_utils.py │ │ │ ├── urls.py │ │ │ ├── utils.py │ │ │ ├── view_helpers.py │ │ │ └── views.py │ │ ├── hqwebapp/ │ │ │ ├── __init__.py │ │ │ ├── admin.py │ │ │ ├── apps.py │ │ │ ├── async_handler.py │ │ │ ├── context.py │ │ │ ├── crispy.py │ │ │ ├── decorators.py │ │ │ ├── doc_info.py │ │ │ ├── doc_lookup.py │ │ │ ├── docs/ │ │ │ │ └── chat_widget_translations.md │ │ │ ├── encoders.py │ │ │ ├── exceptions.py │ │ │ ├── fields.py │ │ │ ├── forms.py │ │ │ ├── login_handlers.py │ │ │ ├── login_utils.py │ │ │ ├── management/ │ │ │ │ ├── __init__.py │ │ │ │ └── commands/ │ │ │ │ ├── __init__.py │ │ │ │ ├── build_bootstrap5_diffs.py │ │ │ │ ├── complete_bootstrap5_migration.py │ │ │ │ ├── complete_bootstrap5_report.py │ │ │ │ ├── copy_required_static_files.py │ │ │ │ ├── fix_less_imports_collectstatic.py │ │ │ │ ├── generate_webpack_settings.py │ │ │ │ ├── generate_widget_translations.py │ │ │ │ ├── list_waf_allow_patterns.py │ │ │ │ ├── migrate_app_to_bootstrap5.py │ │ │ │ ├── resource_static.py │ │ │ │ ├── show_invalid_bootstrap3_files.py │ │ │ │ └── update_manifest.py │ │ │ ├── migrations/ │ │ │ │ ├── 0001_initial.py │ │ │ │ ├── 0002_auto_20171121_1803.py │ │ │ │ ├── 0003_maintenancealert_domains.py │ │ │ │ ├── 0004_apikeysettings.py │ │ │ │ ├── 0005_delete_apikeysettings.py │ │ │ │ ├── 0006_create_user_access_log.py │ │ │ │ ├── 0007_user_access_agent.py │ │ │ │ ├── 0008_hqoauthapplication.py │ │ │ │ ├── 0009_truncate_authtoken_table.py │ │ │ │ ├── 0010_maintenancealert_scheduling.py │ │ │ │ ├── 0011_add_new_columns_and_rename_model.py │ │ │ │ └── __init__.py │ │ │ ├── models.py │ │ │ ├── precompilers.py │ │ │ ├── session_details_endpoint/ │ │ │ │ ├── __init__.py │ │ │ │ ├── tests.py │ │ │ │ └── views.py │ │ │ ├── signals.py │ │ │ ├── static/ │ │ │ │ ├── accounting/ │ │ │ │ │ ├── less/ │ │ │ │ │ │ ├── pricing-main.less │ │ │ │ │ │ └── pricing.less │ │ │ │ │ └── scss/ │ │ │ │ │ ├── pricing-main.scss │ │ │ │ │ └── pricing.scss │ │ │ │ ├── app_manager/ │ │ │ │ │ ├── less/ │ │ │ │ │ │ ├── app_manager-main.less │ │ │ │ │ │ ├── app_manager.less │ │ │ │ │ │ ├── case_tile_preview.less │ │ │ │ │ │ ├── content.less │ │ │ │ │ │ ├── corehq_overrides.less │ │ │ │ │ │ ├── deploy.less │ │ │ │ │ │ ├── diff.less │ │ │ │ │ │ ├── font/ │ │ │ │ │ │ │ ├── WorkflowFont-Regular.otf │ │ │ │ │ │ │ └── WorkflowFont.glyphs │ │ │ │ │ │ ├── font-workflow/ │ │ │ │ │ │ │ ├── core.less │ │ │ │ │ │ │ ├── icons.less │ │ │ │ │ │ │ ├── path.less │ │ │ │ │ │ │ └── variables.less │ │ │ │ │ │ ├── font-workflow.less │ │ │ │ │ │ ├── form_editing.less │ │ │ │ │ │ ├── navigation.less │ │ │ │ │ │ ├── new_module_modal.less │ │ │ │ │ │ ├── panel.less │ │ │ │ │ │ ├── popover.less │ │ │ │ │ │ ├── preview_app-main.less │ │ │ │ │ │ ├── preview_app.less │ │ │ │ │ │ ├── savebtn.less │ │ │ │ │ │ ├── summary-main.less │ │ │ │ │ │ ├── summary.less │ │ │ │ │ │ ├── table.less │ │ │ │ │ │ ├── tabs.less │ │ │ │ │ │ └── variables.less │ │ │ │ │ └── scss/ │ │ │ │ │ ├── app_manager-main.scss │ │ │ │ │ ├── app_manager.scss │ │ │ │ │ ├── font/ │ │ │ │ │ │ ├── WorkflowFont-Regular.otf │ │ │ │ │ │ └── WorkflowFont.glyphs │ │ │ │ │ ├── font-workflow/ │ │ │ │ │ │ ├── core.scss │ │ │ │ │ │ ├── icons.scss │ │ │ │ │ │ ├── path.scss │ │ │ │ │ │ └── variables.scss │ │ │ │ │ ├── font-workflow.scss │ │ │ │ │ ├── includes/ │ │ │ │ │ │ ├── _case_tile_preview.scss │ │ │ │ │ │ ├── _content.scss │ │ │ │ │ │ ├── _corehq_overrides.scss │ │ │ │ │ │ ├── _deploy.scss │ │ │ │ │ │ ├── _diff.scss │ │ │ │ │ │ ├── _form_editing.scss │ │ │ │ │ │ ├── _navigation.scss │ │ │ │ │ │ ├── _new_module_modal.scss │ │ │ │ │ │ ├── _panel.scss │ │ │ │ │ │ ├── _popover.scss │ │ │ │ │ │ ├── _savebtn.scss │ │ │ │ │ │ ├── _table.scss │ │ │ │ │ │ └── _variables.scss │ │ │ │ │ ├── preview_app-main.scss │ │ │ │ │ ├── preview_app.scss │ │ │ │ │ ├── summary-main.scss │ │ │ │ │ └── summary.scss │ │ │ │ ├── case_importer/ │ │ │ │ │ └── less/ │ │ │ │ │ └── case_importer.less │ │ │ │ ├── cloudcare/ │ │ │ │ │ └── scss/ │ │ │ │ │ ├── cloudcare-variables.scss │ │ │ │ │ ├── corehq_overrides.scss │ │ │ │ │ ├── debugger/ │ │ │ │ │ │ └── debugger.scss │ │ │ │ │ ├── formplayer-common/ │ │ │ │ │ │ ├── address.scss │ │ │ │ │ │ ├── appicon.scss │ │ │ │ │ │ ├── case.scss │ │ │ │ │ │ ├── config.scss │ │ │ │ │ │ ├── form.scss │ │ │ │ │ │ ├── formnav.scss │ │ │ │ │ │ ├── grid.scss │ │ │ │ │ │ ├── markdown-table.scss │ │ │ │ │ │ ├── mixins.scss │ │ │ │ │ │ ├── module.scss │ │ │ │ │ │ ├── navigation.scss │ │ │ │ │ │ ├── notifications.scss │ │ │ │ │ │ ├── paginate.scss │ │ │ │ │ │ ├── query.scss │ │ │ │ │ │ ├── request.scss │ │ │ │ │ │ ├── version.scss │ │ │ │ │ │ └── webforms.scss │ │ │ │ │ ├── formplayer-common-main.scss │ │ │ │ │ ├── formplayer-common.scss │ │ │ │ │ ├── formplayer-webapp/ │ │ │ │ │ │ ├── breadcrumbs.scss │ │ │ │ │ │ ├── case-tile.scss │ │ │ │ │ │ ├── content.scss │ │ │ │ │ │ ├── form.scss │ │ │ │ │ │ ├── formnav.scss │ │ │ │ │ │ ├── leaflet.scss │ │ │ │ │ │ ├── menu.scss │ │ │ │ │ │ ├── module.scss │ │ │ │ │ │ ├── navbar.scss │ │ │ │ │ │ ├── print-general.scss │ │ │ │ │ │ ├── query.scss │ │ │ │ │ │ └── version.scss │ │ │ │ │ ├── formplayer-webapp-main.scss │ │ │ │ │ └── formplayer-webapp.scss │ │ │ │ ├── dashboard/ │ │ │ │ │ └── scss/ │ │ │ │ │ ├── dashboard-main.scss │ │ │ │ │ └── dashboard.scss │ │ │ │ ├── data_dictionary/ │ │ │ │ │ └── less/ │ │ │ │ │ └── data_dictionary.less │ │ │ │ ├── hqwebapp/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ ├── proptable.css │ │ │ │ │ │ └── splits.css │ │ │ │ │ ├── font/ │ │ │ │ │ │ ├── CommCare HQ Font.json │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── style.scss │ │ │ │ │ │ └── variables.scss │ │ │ │ │ ├── js/ │ │ │ │ │ │ ├── 500.js │ │ │ │ │ │ ├── alpinejs/ │ │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ │ └── wiggle_button.js │ │ │ │ │ │ │ ├── directives/ │ │ │ │ │ │ │ │ ├── datepicker.js │ │ │ │ │ │ │ │ ├── htmx_sortable.js │ │ │ │ │ │ │ │ ├── report_select2.js │ │ │ │ │ │ │ │ ├── select2.js │ │ │ │ │ │ │ │ └── tooltip.js │ │ │ │ │ │ │ └── stores/ │ │ │ │ │ │ │ └── gtx.js │ │ │ │ │ │ ├── assert_properties.js │ │ │ │ │ │ ├── atwho.js │ │ │ │ │ │ ├── base.js │ │ │ │ │ │ ├── base_ace.js │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ ├── alert_user.js │ │ │ │ │ │ │ ├── base_main.js │ │ │ │ │ │ │ ├── commcarehq.js │ │ │ │ │ │ │ ├── common.js │ │ │ │ │ │ │ ├── crud_paginated_list.js │ │ │ │ │ │ │ ├── crud_paginated_list_init.js │ │ │ │ │ │ │ ├── downgrade_modal.js │ │ │ │ │ │ │ ├── email-request.js │ │ │ │ │ │ │ ├── hq.helpers.js │ │ │ │ │ │ │ ├── inactivity.js │ │ │ │ │ │ │ ├── knockout_bindings.ko.js │ │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ │ ├── prepaid_modal.js │ │ │ │ │ │ │ ├── sticky_tabs.js │ │ │ │ │ │ │ ├── validators.ko.js │ │ │ │ │ │ │ └── widgets.js │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ ├── alert_user.js │ │ │ │ │ │ │ ├── base_main.js │ │ │ │ │ │ │ ├── commcarehq.js │ │ │ │ │ │ │ ├── common.js │ │ │ │ │ │ │ ├── crud_paginated_list.js │ │ │ │ │ │ │ ├── crud_paginated_list_init.js │ │ │ │ │ │ │ ├── downgrade_modal.js │ │ │ │ │ │ │ ├── email-request.js │ │ │ │ │ │ │ ├── hq.helpers.js │ │ │ │ │ │ │ ├── inactivity.js │ │ │ │ │ │ │ ├── knockout_bindings.ko.js │ │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ │ ├── prepaid_modal.js │ │ │ │ │ │ │ ├── sticky_tabs.js │ │ │ │ │ │ │ ├── validators.ko.js │ │ │ │ │ │ │ └── widgets.js │ │ │ │ │ │ ├── bulk_upload_file.js │ │ │ │ │ │ ├── captcha.js │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ │ └── feedback.js │ │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ │ └── feedback.js │ │ │ │ │ │ │ ├── inline_edit.js │ │ │ │ │ │ │ ├── pagination.js │ │ │ │ │ │ │ ├── quill.css │ │ │ │ │ │ │ ├── rich_text_knockout_bindings.js │ │ │ │ │ │ │ ├── search_box.js │ │ │ │ │ │ │ └── select_toggle.js │ │ │ │ │ │ ├── components.ko.js │ │ │ │ │ │ ├── constants.js │ │ │ │ │ │ ├── daterangepicker.config.js │ │ │ │ │ │ ├── hq_extensions.jquery.js │ │ │ │ │ │ ├── htmx_and_alpine.js │ │ │ │ │ │ ├── htmx_base.js │ │ │ │ │ │ ├── htmx_utils/ │ │ │ │ │ │ │ ├── csrf_token.js │ │ │ │ │ │ │ ├── errors.js │ │ │ │ │ │ │ ├── hq_hx_action.js │ │ │ │ │ │ │ ├── hq_hx_loading.js │ │ │ │ │ │ │ ├── hq_hx_refresh.js │ │ │ │ │ │ │ ├── hq_hx_select_all.js │ │ │ │ │ │ │ ├── htmx_gtx.js │ │ │ │ │ │ │ └── retry_request.js │ │ │ │ │ │ ├── initial_page_data.js │ │ │ │ │ │ ├── key-value-mapping.js │ │ │ │ │ │ ├── knockout_subscribables.ko.js │ │ │ │ │ │ ├── layout.js │ │ │ │ │ │ ├── maintenance_alerts.js │ │ │ │ │ │ ├── multiselect_utils.js │ │ │ │ │ │ ├── password_validators.ko.js │ │ │ │ │ │ ├── privileges.js │ │ │ │ │ │ ├── select2_handler.js │ │ │ │ │ │ ├── select2_knockout_bindings.ko.js │ │ │ │ │ │ ├── select_2_ajax_widget.js │ │ │ │ │ │ ├── soil.js │ │ │ │ │ │ ├── sso_inactivity.js │ │ │ │ │ │ ├── tempus_dominus.js │ │ │ │ │ │ ├── toggles.js │ │ │ │ │ │ ├── ui_elements/ │ │ │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ │ │ ├── ui-element-checkbox.js │ │ │ │ │ │ │ │ ├── ui-element-input-map.js │ │ │ │ │ │ │ │ ├── ui-element-input.js │ │ │ │ │ │ │ │ ├── ui-element-key-val-list.js │ │ │ │ │ │ │ │ ├── ui-element-key-val-mapping.js │ │ │ │ │ │ │ │ └── ui-element-select.js │ │ │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ │ │ ├── ui-element-checkbox.js │ │ │ │ │ │ │ │ ├── ui-element-input-map.js │ │ │ │ │ │ │ │ ├── ui-element-input.js │ │ │ │ │ │ │ │ ├── ui-element-key-val-list.js │ │ │ │ │ │ │ │ ├── ui-element-key-val-mapping.js │ │ │ │ │ │ │ │ └── ui-element-select.js │ │ │ │ │ │ │ └── ui-element-langcode-button.js │ │ │ │ │ │ └── utils/ │ │ │ │ │ │ └── email.js │ │ │ │ │ ├── less/ │ │ │ │ │ │ ├── .gitignore │ │ │ │ │ │ ├── README.md │ │ │ │ │ │ ├── _hq/ │ │ │ │ │ │ │ ├── ace_cle.less │ │ │ │ │ │ │ ├── alerts.less │ │ │ │ │ │ │ ├── backgrounds.less │ │ │ │ │ │ │ ├── bootstrap5-temp.less │ │ │ │ │ │ │ ├── breadcrumbs.less │ │ │ │ │ │ │ ├── buttons.less │ │ │ │ │ │ │ ├── datagrid.less │ │ │ │ │ │ │ ├── datatables.less │ │ │ │ │ │ │ ├── date_range_picker.less │ │ │ │ │ │ │ ├── datetimepicker.less │ │ │ │ │ │ │ ├── dropdowns.less │ │ │ │ │ │ │ ├── facet.less │ │ │ │ │ │ │ ├── feedback.less │ │ │ │ │ │ │ ├── flag_icons.less │ │ │ │ │ │ │ ├── form_steps.less │ │ │ │ │ │ │ ├── forms.less │ │ │ │ │ │ │ ├── helpbubble.less │ │ │ │ │ │ │ ├── hubspot.less │ │ │ │ │ │ │ ├── icons.less │ │ │ │ │ │ │ ├── includes/ │ │ │ │ │ │ │ │ ├── extensions.less │ │ │ │ │ │ │ │ ├── mixins.less │ │ │ │ │ │ │ │ └── variables.less │ │ │ │ │ │ │ ├── inline_edit.less │ │ │ │ │ │ │ ├── label.less │ │ │ │ │ │ │ ├── layouts.less │ │ │ │ │ │ │ ├── list.less │ │ │ │ │ │ │ ├── mapbox.less │ │ │ │ │ │ │ ├── modals.less │ │ │ │ │ │ │ ├── navbar.less │ │ │ │ │ │ │ ├── navs.less │ │ │ │ │ │ │ ├── notifications.less │ │ │ │ │ │ │ ├── ocs_widget.less │ │ │ │ │ │ │ ├── pagination.less │ │ │ │ │ │ │ ├── panels.less │ │ │ │ │ │ │ ├── plan_notice.less │ │ │ │ │ │ │ ├── popovers.less │ │ │ │ │ │ │ ├── radio_select.less │ │ │ │ │ │ │ ├── readable_forms.less │ │ │ │ │ │ │ ├── report.less │ │ │ │ │ │ │ ├── select2s.less │ │ │ │ │ │ │ ├── svg.less │ │ │ │ │ │ │ ├── tables.less │ │ │ │ │ │ │ ├── tabs.less │ │ │ │ │ │ │ ├── typography.less │ │ │ │ │ │ │ └── utils.less │ │ │ │ │ │ ├── b5-compatibility.less │ │ │ │ │ │ ├── bootstrap.less │ │ │ │ │ │ ├── components/ │ │ │ │ │ │ │ └── multiselect/ │ │ │ │ │ │ │ └── multiselect.less │ │ │ │ │ │ ├── docs-style-imports.less │ │ │ │ │ │ ├── docs-style.less │ │ │ │ │ │ ├── mobile/ │ │ │ │ │ │ │ └── c2/ │ │ │ │ │ │ │ ├── footer.less │ │ │ │ │ │ │ ├── navbar.less │ │ │ │ │ │ │ ├── reset.less │ │ │ │ │ │ │ ├── tables.less │ │ │ │ │ │ │ ├── type.less │ │ │ │ │ │ │ └── variables.less │ │ │ │ │ │ ├── style-imports.less │ │ │ │ │ │ ├── style.less │ │ │ │ │ │ └── styleguide/ │ │ │ │ │ │ └── palette.less │ │ │ │ │ ├── scss/ │ │ │ │ │ │ ├── commcarehq/ │ │ │ │ │ │ │ ├── _ace_cle.scss │ │ │ │ │ │ │ ├── _alert.scss │ │ │ │ │ │ │ ├── _animations.scss │ │ │ │ │ │ │ ├── _backgrounds.scss │ │ │ │ │ │ │ ├── _breadcrumb.scss │ │ │ │ │ │ │ ├── _buttons.scss │ │ │ │ │ │ │ ├── _cards.scss │ │ │ │ │ │ │ ├── _containers.scss │ │ │ │ │ │ │ ├── _datagrid.scss │ │ │ │ │ │ │ ├── _datatables.scss │ │ │ │ │ │ │ ├── _date_range_picker.scss │ │ │ │ │ │ │ ├── _datetimepicker.scss │ │ │ │ │ │ │ ├── _dropdown.scss │ │ │ │ │ │ │ ├── _facet.scss │ │ │ │ │ │ │ ├── _feedback.scss │ │ │ │ │ │ │ ├── _flag_icons.scss │ │ │ │ │ │ │ ├── _form_steps.scss │ │ │ │ │ │ │ ├── _forms.scss │ │ │ │ │ │ │ ├── _helpbubble.scss │ │ │ │ │ │ │ ├── _htmx.scss │ │ │ │ │ │ │ ├── _hubspot.scss │ │ │ │ │ │ │ ├── _icons.scss │ │ │ │ │ │ │ ├── _inline_edit.scss │ │ │ │ │ │ │ ├── _label.scss │ │ │ │ │ │ │ ├── _layouts.scss │ │ │ │ │ │ │ ├── _list.scss │ │ │ │ │ │ │ ├── _mapbox.scss │ │ │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ │ │ ├── _modal.scss │ │ │ │ │ │ │ ├── _nav.scss │ │ │ │ │ │ │ ├── _navbar.scss │ │ │ │ │ │ │ ├── _notifications.scss │ │ │ │ │ │ │ ├── _ocs_widget.scss │ │ │ │ │ │ │ ├── _pagination.scss │ │ │ │ │ │ │ ├── _plan_notice.scss │ │ │ │ │ │ │ ├── _popover.scss │ │ │ │ │ │ │ ├── _print.scss │ │ │ │ │ │ │ ├── _radio_select.scss │ │ │ │ │ │ │ ├── _readable_forms.scss │ │ │ │ │ │ │ ├── _report.scss │ │ │ │ │ │ │ ├── _select2.scss │ │ │ │ │ │ │ ├── _svg.scss │ │ │ │ │ │ │ ├── _tables.scss │ │ │ │ │ │ │ ├── _tabs.scss │ │ │ │ │ │ │ ├── _type.scss │ │ │ │ │ │ │ ├── _utils.scss │ │ │ │ │ │ │ ├── _variables.scss │ │ │ │ │ │ │ └── _variables_bootstrap3.scss │ │ │ │ │ │ ├── commcarehq.scss │ │ │ │ │ │ ├── data_cleaning.scss │ │ │ │ │ │ └── styleguide.scss │ │ │ │ │ └── spec/ │ │ │ │ │ ├── assert_properties_spec.js │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── inactivity_spec.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ └── widgets_spec.js │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── inactivity_spec.js │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ └── widgets_spec.js │ │ │ │ │ ├── components/ │ │ │ │ │ │ ├── main.js │ │ │ │ │ │ └── rich_text_spec.js │ │ │ │ │ ├── email_validator_spec.js │ │ │ │ │ └── urllib_spec.js │ │ │ │ ├── preview_app/ │ │ │ │ │ └── scss/ │ │ │ │ │ ├── preview_app/ │ │ │ │ │ │ ├── appicon.scss │ │ │ │ │ │ ├── breadcrumbs.scss │ │ │ │ │ │ ├── case-tile.scss │ │ │ │ │ │ ├── datepicker.scss │ │ │ │ │ │ ├── debugger.scss │ │ │ │ │ │ ├── form.scss │ │ │ │ │ │ ├── formnav.scss │ │ │ │ │ │ ├── grid.scss │ │ │ │ │ │ ├── module.scss │ │ │ │ │ │ ├── navigation.scss │ │ │ │ │ │ ├── notifications.scss │ │ │ │ │ │ ├── scrollable.scss │ │ │ │ │ │ └── variables.scss │ │ │ │ │ ├── preview_app-main.scss │ │ │ │ │ └── preview_app.scss │ │ │ │ ├── registration/ │ │ │ │ │ ├── less/ │ │ │ │ │ │ ├── register-domain.less │ │ │ │ │ │ ├── registration-main.less │ │ │ │ │ │ └── registration.less │ │ │ │ │ └── scss/ │ │ │ │ │ ├── register-domain.scss │ │ │ │ │ ├── registration-main.scss │ │ │ │ │ └── registration.scss │ │ │ │ ├── registry/ │ │ │ │ │ └── scss/ │ │ │ │ │ ├── light_color_scheme.scss │ │ │ │ │ └── registry.scss │ │ │ │ └── reports/ │ │ │ │ ├── less/ │ │ │ │ │ ├── reports.less │ │ │ │ │ └── sidebar.less │ │ │ │ └── scss/ │ │ │ │ ├── reports/ │ │ │ │ │ ├── _accordion.scss │ │ │ │ │ └── _sidebar.scss │ │ │ │ └── reports.scss │ │ │ ├── tables/ │ │ │ │ ├── __init__.py │ │ │ │ ├── columns.py │ │ │ │ ├── elasticsearch/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── records.py │ │ │ │ │ └── tables.py │ │ │ │ ├── export.py │ │ │ │ ├── htmx.py │ │ │ │ ├── pagination.py │ │ │ │ └── tests/ │ │ │ │ ├── test_columns.py │ │ │ │ └── test_export.py │ │ │ ├── tasks.py │ │ │ ├── templates/ │ │ │ │ ├── 403.html │ │ │ │ ├── 404.html │ │ │ │ ├── 500.html │ │ │ │ ├── apache_license.html │ │ │ │ ├── bsd_license.html │ │ │ │ ├── csrf_failure.html │ │ │ │ ├── error_base.html │ │ │ │ ├── google9633af922b8b0064.html │ │ │ │ ├── hq-captcha-field.html │ │ │ │ ├── hqwebapp/ │ │ │ │ │ ├── base.html │ │ │ │ │ ├── basic_errors.html │ │ │ │ │ ├── bootstrap3/ │ │ │ │ │ │ ├── base_navigation.html │ │ │ │ │ │ ├── base_page.html │ │ │ │ │ │ ├── base_paginated_crud.html │ │ │ │ │ │ ├── base_section.html │ │ │ │ │ │ ├── blank.html │ │ │ │ │ │ ├── bulk_upload.html │ │ │ │ │ │ ├── downgrade_modal.html │ │ │ │ │ │ ├── full_screen.html │ │ │ │ │ │ ├── iframe_close_window.html │ │ │ │ │ │ ├── iframe_domain_login.html │ │ │ │ │ │ ├── iframe_sso_login_pending.html │ │ │ │ │ │ ├── iframe_sso_login_success.html │ │ │ │ │ │ ├── oauth_application_registration_form.html │ │ │ │ │ │ ├── prepaid_modal.html │ │ │ │ │ │ ├── rollout_revert_modal.html │ │ │ │ │ │ ├── soil_status_full.html │ │ │ │ │ │ └── two_column.html │ │ │ │ │ ├── bootstrap5/ │ │ │ │ │ │ ├── base_navigation.html │ │ │ │ │ │ ├── base_page.html │ │ │ │ │ │ ├── base_paginated_crud.html │ │ │ │ │ │ ├── base_section.html │ │ │ │ │ │ ├── blank.html │ │ │ │ │ │ ├── bulk_upload.html │ │ │ │ │ │ ├── downgrade_modal.html │ │ │ │ │ │ ├── full_screen.html │ │ │ │ │ │ ├── iframe_close_window.html │ │ │ │ │ │ ├── iframe_domain_login.html │ │ │ │ │ │ ├── iframe_sso_login_pending.html │ │ │ │ │ │ ├──
Showing preview only (4,713K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (52825 symbols across 4663 files)
FILE: corehq/__init__.py
function _get_current_app (line 11) | def _get_current_app():
FILE: corehq/apps/accounting/admin.py
class DomainUserHistoryAdmin (line 7) | class DomainUserHistoryAdmin(admin.ModelAdmin):
FILE: corehq/apps/accounting/async_handlers.py
class BaseRateAsyncHandler (line 30) | class BaseRateAsyncHandler(BaseAsyncHandler):
method name (line 40) | def name(self):
method rate_type (line 44) | def rate_type(self):
method rate_id (line 48) | def rate_id(self):
method create_response (line 52) | def create_response(self):
method apply_response (line 56) | def apply_response(self):
class FeatureRateAsyncHandler (line 60) | class FeatureRateAsyncHandler(BaseRateAsyncHandler):
method create_response (line 64) | def create_response(self):
method apply_response (line 75) | def apply_response(self):
class SoftwareProductRateAsyncHandler (line 83) | class SoftwareProductRateAsyncHandler(BaseRateAsyncHandler):
method create_response (line 87) | def create_response(self):
method apply_response (line 94) | def apply_response(self):
class BaseSelect2AsyncHandler (line 102) | class BaseSelect2AsyncHandler(BaseAsyncHandler):
method search_string (line 105) | def search_string(self):
method existing (line 109) | def existing(self):
method _fmt_success (line 112) | def _fmt_success(self, response):
class Select2RateAsyncHandler (line 122) | class Select2RateAsyncHandler(BaseSelect2AsyncHandler):
method select2_feature_id_response (line 134) | def select2_feature_id_response(self):
method product_rate_id_response (line 143) | def product_rate_id_response(self):
method _fmt_success (line 151) | def _fmt_success(self, response):
class Select2BillingInfoHandler (line 170) | class Select2BillingInfoHandler(BaseSelect2AsyncHandler):
method country_response (line 182) | def country_response(self):
method active_accounts_response (line 191) | def active_accounts_response(self):
method domain_response (line 198) | def domain_response(self):
method account_response (line 206) | def account_response(self):
method plan_version_response (line 213) | def plan_version_response(self):
method new_plan_version_response (line 232) | def new_plan_version_response(self):
class Select2InvoiceTriggerHandler (line 238) | class Select2InvoiceTriggerHandler(BaseSelect2AsyncHandler):
method domain_response (line 245) | def domain_response(self):
class Select2CustomerInvoiceTriggerHandler (line 253) | class Select2CustomerInvoiceTriggerHandler(BaseSelect2AsyncHandler):
method customer_account_response (line 260) | def customer_account_response(self):
class BaseSingleOptionFilterAsyncHandler (line 268) | class BaseSingleOptionFilterAsyncHandler(BaseAsyncHandler):
method query (line 271) | def query(self):
method search_string (line 275) | def search_string(self):
method page (line 279) | def page(self):
method paginated_data (line 283) | def paginated_data(self):
method limit (line 289) | def limit(self):
method total (line 293) | def total(self):
method _fmt_select2_data (line 297) | def _fmt_select2_data(data_id, data_text):
method _fmt_success (line 303) | def _fmt_success(self, data):
class SubscriberFilterAsyncHandler (line 313) | class SubscriberFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 320) | def query(self):
method subscriber_response (line 327) | def subscriber_response(self):
class SubscriptionFilterAsyncHandler (line 332) | class SubscriptionFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 339) | def query(self):
method contract_id_response (line 352) | def contract_id_response(self):
class AccountFilterAsyncHandler (line 358) | class AccountFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 367) | def query(self):
method account_name_response (line 394) | def account_name_response(self):
method account_id_response (line 399) | def account_id_response(self):
method dimagi_contact_response (line 405) | def dimagi_contact_response(self):
class BillingContactInfoAsyncHandler (line 410) | class BillingContactInfoAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 417) | def query(self):
method contact_name_response (line 428) | def contact_name_response(self):
class SoftwarePlanAsyncHandler (line 433) | class SoftwarePlanAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 440) | def query(self):
method name_response (line 447) | def name_response(self):
class BaseInvoiceNumberAsyncHandler (line 452) | class BaseInvoiceNumberAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 456) | def query(self):
method base_response (line 465) | def base_response(self):
class InvoiceNumberAsyncHandler (line 472) | class InvoiceNumberAsyncHandler(BaseInvoiceNumberAsyncHandler):
method invoice_number_response (line 480) | def invoice_number_response(self):
class CustomerInvoiceNumberAsyncHandler (line 484) | class CustomerInvoiceNumberAsyncHandler(BaseInvoiceNumberAsyncHandler):
method customer_invoice_number_response (line 492) | def customer_invoice_number_response(self):
class InvoiceBalanceAsyncHandler (line 496) | class InvoiceBalanceAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 503) | def query(self):
method invoice_balance_response (line 510) | def invoice_balance_response(self):
class DomainFilterAsyncHandler (line 517) | class DomainFilterAsyncHandler(BaseSingleOptionFilterAsyncHandler):
method query (line 524) | def query(self):
method domain_name_response (line 541) | def domain_name_response(self):
FILE: corehq/apps/accounting/automated_reports.py
class CreditsAutomatedReport (line 15) | class CreditsAutomatedReport(object):
method send_report (line 27) | def send_report(self, recipient):
method _generate_report_table (line 71) | def _generate_report_table(self):
method _get_credit_info (line 124) | def _get_credit_info(self, subscription):
method _credit_grand_total (line 145) | def _credit_grand_total(credit_lines):
method _fmt_credit (line 149) | def _fmt_credit(credit_amount=None):
FILE: corehq/apps/accounting/bootstrap/utils.py
function ensure_plans (line 15) | def ensure_plans(config, verbose, apps):
function _ensure_role (line 43) | def _ensure_role(role_slug, apps):
function _ensure_product_rate (line 56) | def _ensure_product_rate(monthly_fee, edition, verbose, apps):
function _get_software_product (line 84) | def _get_software_product(product_name, verbose, apps):
function _ensure_features (line 101) | def _ensure_features(feature_rates, edition, verbose, apps):
function ensure_feature_rates (line 132) | def ensure_feature_rates(feature_rates, features, verbose, apps):
function _ensure_software_plan (line 156) | def _ensure_software_plan(plan_key, product, product_rate, verbose, apps):
function _software_plan_name (line 181) | def _software_plan_name(plan_key, product, product_rate):
function _ensure_software_plan_version (line 194) | def _ensure_software_plan_version(role, software_plan, product_rate, fea...
function _ensure_default_product_plan (line 207) | def _ensure_default_product_plan(plan_key, software_plan, verbose, apps):
function _clear_cache (line 239) | def _clear_cache(software_plans, default_plans):
FILE: corehq/apps/accounting/decorators.py
function requires_privilege_with_fallback (line 14) | def requires_privilege_with_fallback(slug, **assignment):
function requires_privilege_plaintext_response (line 62) | def requires_privilege_plaintext_response(slug,
function requires_privilege_json_response (line 85) | def requires_privilege_json_response(slug, http_status_code=None,
function requires_privilege_for_commcare_user (line 121) | def requires_privilege_for_commcare_user(slug, **assignment):
function always_allow_project_access (line 139) | def always_allow_project_access(view_func):
FILE: corehq/apps/accounting/dispatcher.py
class AccountingAdminInterfaceDispatcher (line 10) | class AccountingAdminInterfaceDispatcher(ReportDispatcher):
method dispatch (line 16) | def dispatch(self, request, *args, **kwargs):
FILE: corehq/apps/accounting/emails.py
class SubjectTemplate (line 28) | class SubjectTemplate:
function send_subscription_change_alert (line 34) | def send_subscription_change_alert(domain, new_subscription, old_subscri...
function send_subscription_renewal_alert (line 74) | def send_subscription_renewal_alert(domain, new_subscription, old_subscr...
function send_self_start_subscription_alert (line 79) | def send_self_start_subscription_alert(domain, new_subscription, old_sub...
function send_flagged_pay_annually_subscription_alert (line 84) | def send_flagged_pay_annually_subscription_alert(subscription, current_i...
function send_subscription_renewed_email (line 114) | def send_subscription_renewed_email(subscription):
function send_renewal_reminder_email (line 148) | def send_renewal_reminder_email(subscription, days_left):
function send_subscription_ending_email (line 154) | def send_subscription_ending_email(subscription, days_left):
function _send_subscription_ending_reminder_email (line 160) | def _send_subscription_ending_reminder_email(subscription, days_left, te...
function _get_reminder_email_contacts (line 185) | def _get_reminder_email_contacts(subscription, domain):
function _ending_reminder_context (line 217) | def _ending_reminder_context(subscription, days_left):
function send_dimagi_contact_ending_reminder_email (line 258) | def send_dimagi_contact_ending_reminder_email(subscription):
function _dimagi_ending_reminder_subject (line 275) | def _dimagi_ending_reminder_subject(subscription):
function _dimagi_ending_reminder_context (line 286) | def _dimagi_ending_reminder_context(subscription):
FILE: corehq/apps/accounting/exceptions.py
class AccountingError (line 1) | class AccountingError(Exception):
class LineItemError (line 5) | class LineItemError(Exception):
class InvoiceError (line 9) | class InvoiceError(Exception):
class InvoiceAlreadyCreatedError (line 13) | class InvoiceAlreadyCreatedError(Exception):
class CreditLineError (line 17) | class CreditLineError(Exception):
class SubscriptionAdjustmentError (line 21) | class SubscriptionAdjustmentError(Exception):
class SubscriptionChangeError (line 25) | class SubscriptionChangeError(Exception):
class NewSubscriptionError (line 29) | class NewSubscriptionError(Exception):
class InvoiceEmailThrottledError (line 33) | class InvoiceEmailThrottledError(Exception):
class SubscriptionReminderError (line 37) | class SubscriptionReminderError(Exception):
class SubscriptionRenewalError (line 41) | class SubscriptionRenewalError(Exception):
class PaymentRequestError (line 45) | class PaymentRequestError(Exception):
class PaymentHandlerError (line 49) | class PaymentHandlerError(Exception):
class CreateAccountingAdminError (line 53) | class CreateAccountingAdminError(Exception):
class ProductPlanNotFoundError (line 57) | class ProductPlanNotFoundError(Exception):
class NoActiveSubscriptionError (line 61) | class NoActiveSubscriptionError(Exception):
class MultipleActiveSubscriptionsError (line 65) | class MultipleActiveSubscriptionsError(Exception):
class ActiveSubscriptionWithoutDomain (line 69) | class ActiveSubscriptionWithoutDomain(Exception):
class CreditLineBalanceMismatchError (line 73) | class CreditLineBalanceMismatchError(Exception):
class AccountingCommunicationError (line 77) | class AccountingCommunicationError(Exception):
class SubscriptionTaskError (line 81) | class SubscriptionTaskError(Exception):
FILE: corehq/apps/accounting/filters.py
class BaseAccountingSingleOptionFilter (line 41) | class BaseAccountingSingleOptionFilter(BaseSingleOptionFilter):
method pagination_source (line 45) | def pagination_source(self):
class AccountTypeFilter (line 50) | class AccountTypeFilter(BaseSingleOptionFilter):
class NameFilter (line 57) | class NameFilter(BaseAccountingSingleOptionFilter):
class DomainFilter (line 65) | class DomainFilter(BaseAccountingSingleOptionFilter):
class CreditAdjustmentReasonFilter (line 73) | class CreditAdjustmentReasonFilter(BaseSingleOptionFilter):
class IdPServiceTypeFilter (line 80) | class IdPServiceTypeFilter(BaseSingleOptionFilter):
class CreditAdjustmentLinkFilter (line 87) | class CreditAdjustmentLinkFilter(BaseSingleOptionFilter):
function clean_options (line 97) | def clean_options(options):
class SalesforceAccountIDFilter (line 101) | class SalesforceAccountIDFilter(BaseAccountingSingleOptionFilter):
class SubscriberFilter (line 109) | class SubscriberFilter(BaseAccountingSingleOptionFilter):
class SalesforceContractIDFilter (line 117) | class SalesforceContractIDFilter(BaseAccountingSingleOptionFilter):
class ActiveStatusFilter (line 125) | class ActiveStatusFilter(BaseSingleOptionFilter):
class CustomerAccountFilter (line 137) | class CustomerAccountFilter(BaseSingleOptionFilter):
class DimagiContactFilter (line 149) | class DimagiContactFilter(BaseAccountingSingleOptionFilter):
class EntryPointFilter (line 157) | class EntryPointFilter(BaseSingleOptionFilter):
class DoNotInvoiceFilter (line 164) | class DoNotInvoiceFilter(BaseSingleOptionFilter):
class TrialStatusFilter (line 177) | class TrialStatusFilter(BaseSingleOptionFilter):
class SubscriptionTypeFilter (line 189) | class SubscriptionTypeFilter(BaseSingleOptionFilter):
class ProBonoStatusFilter (line 196) | class ProBonoStatusFilter(BaseSingleOptionFilter):
class IsHiddenFilter (line 203) | class IsHiddenFilter(BaseSingleOptionFilter):
class CreatedSubAdjMethodFilter (line 215) | class CreatedSubAdjMethodFilter(BaseSingleOptionFilter):
class DateRangeFilter (line 233) | class DateRangeFilter(BaseReportFilter):
method filter_context (line 241) | def filter_context(self):
method get_date_str (line 247) | def get_date_str(cls, request, date_type):
method get_date (line 251) | def get_date(cls, request, date_type):
method get_start_date (line 268) | def get_start_date(cls, request):
method get_end_date (line 272) | def get_end_date(cls, request):
method datespan (line 276) | def datespan(self):
method shared_pagination_GET_params (line 287) | def shared_pagination_GET_params(cls, request):
class OptionalFilterMixin (line 294) | class OptionalFilterMixin(object):
method use_filter (line 297) | def use_filter(cls, request):
method optional_filter_slug (line 301) | def optional_filter_slug(cls):
method optional_filter_string_value (line 305) | def optional_filter_string_value(cls, request):
class OptionalDateRangeFilter (line 309) | class OptionalDateRangeFilter(DateRangeFilter, OptionalFilterMixin):
method filter_context (line 313) | def filter_context(self):
class DateFilter (line 321) | class DateFilter(OptionalDateRangeFilter):
class DateCreatedFilter (line 326) | class DateCreatedFilter(OptionalDateRangeFilter):
class StartDateFilter (line 331) | class StartDateFilter(OptionalDateRangeFilter):
class EndDateFilter (line 336) | class EndDateFilter(OptionalDateRangeFilter):
class OptionalMonthYearFilter (line 341) | class OptionalMonthYearFilter(BaseReportFilter, OptionalFilterMixin):
method filter_context (line 345) | def filter_context(self):
method get_value (line 356) | def get_value(cls, request, domain):
method months (line 366) | def months(cls):
method selected_period (line 375) | def selected_period(self):
class StatementPeriodFilter (line 389) | class StatementPeriodFilter(OptionalMonthYearFilter):
method selected_period (line 393) | def selected_period(self):
class DueDatePeriodFilter (line 410) | class DueDatePeriodFilter(OptionalMonthYearFilter):
class SoftwarePlanNameFilter (line 415) | class SoftwarePlanNameFilter(BaseAccountingSingleOptionFilter):
class SoftwarePlanEditionFilter (line 423) | class SoftwarePlanEditionFilter(BaseSingleOptionFilter):
class SoftwarePlanVisibilityFilter (line 430) | class SoftwarePlanVisibilityFilter(BaseSingleOptionFilter):
class InvoiceNumberFilter (line 437) | class InvoiceNumberFilter(BaseAccountingSingleOptionFilter):
class CustomerInvoiceNumberFilter (line 445) | class CustomerInvoiceNumberFilter(BaseAccountingSingleOptionFilter):
class InvoiceBalanceFilter (line 453) | class InvoiceBalanceFilter(BaseAccountingSingleOptionFilter):
class PaymentStatusFilter (line 461) | class PaymentStatusFilter(BaseSingleOptionFilter):
class BillingContactFilter (line 473) | class BillingContactFilter(BaseAccountingSingleOptionFilter):
class PaymentTransactionIdFilter (line 481) | class PaymentTransactionIdFilter(BaseSimpleFilter):
FILE: corehq/apps/accounting/forms.py
class BillingAccountBasicForm (line 96) | class BillingAccountBasicForm(forms.Form):
method __init__ (line 182) | def __init__(self, account, *args, **kwargs):
method clean_name (line 347) | def clean_name(self):
method clean_email_list (line 357) | def clean_email_list(self):
method clean_enterprise_admin_emails (line 360) | def clean_enterprise_admin_emails(self):
method clean_enterprise_restricted_signup_domains (line 363) | def clean_enterprise_restricted_signup_domains(self):
method clean_active_accounts (line 381) | def clean_active_accounts(self):
method create_account (line 400) | def create_account(self):
method update_basic_info (line 425) | def update_basic_info(self, account):
class BillingAccountContactForm (line 460) | class BillingAccountContactForm(forms.ModelForm):
class Meta (line 464) | class Meta(object):
method __init__ (line 480) | def __init__(self, account, *args, **kwargs):
class SubscriptionForm (line 521) | class SubscriptionForm(forms.Form):
method __init__ (line 612) | def __init__(self, subscription, account_id, web_user, *args, **kwargs):
method create_subscription (line 803) | def create_subscription(self):
method update_subscription (line 816) | def update_subscription(self):
method shared_keywords (line 832) | def shared_keywords(self):
method clean_active_accounts (line 851) | def clean_active_accounts(self):
method clean_domain (line 865) | def clean_domain(self):
method clean (line 873) | def clean(self):
class ChangeSubscriptionForm (line 914) | class ChangeSubscriptionForm(forms.Form):
method __init__ (line 955) | def __init__(self, subscription, web_user, *args, **kwargs):
method change_subscription (line 999) | def change_subscription(self):
class BulkUpgradeToLatestVersionForm (line 1013) | class BulkUpgradeToLatestVersionForm(forms.Form):
method __init__ (line 1020) | def __init__(self, old_plan_version, web_user, *args, **kwargs):
method upgrade_subscriptions (line 1044) | def upgrade_subscriptions(self):
class CreditForm (line 1052) | class CreditForm(forms.Form):
method __init__ (line 1067) | def __init__(self, account, subscription, *args, **kwargs):
method clean_amount (line 1097) | def clean_amount(self):
method adjust_credit (line 1110) | def adjust_credit(self, web_user=None):
class RemoveAutopayForm (line 1129) | class RemoveAutopayForm(forms.Form):
method __init__ (line 1133) | def __init__(self, account, *args, **kwargs):
method remove_autopay_user_from_account (line 1155) | def remove_autopay_user_from_account(self):
class CancelForm (line 1161) | class CancelForm(forms.Form):
method __init__ (line 1167) | def __init__(self, subscription, *args, **kwargs):
class SuppressSubscriptionForm (line 1193) | class SuppressSubscriptionForm(forms.Form):
method __init__ (line 1197) | def __init__(self, subscription, *args, **kwargs):
method clean (line 1235) | def clean(self):
class PlanInformationForm (line 1252) | class PlanInformationForm(forms.Form):
method __init__ (line 1261) | def __init__(self, plan, *args, **kwargs):
method clean_name (line 1304) | def clean_name(self):
method create_plan (line 1313) | def create_plan(self):
method update_plan (line 1332) | def update_plan(self, request, plan):
class SoftwarePlanVersionForm (line 1347) | class SoftwarePlanVersionForm(forms.Form):
method __init__ (line 1425) | def __init__(self, plan, plan_version, admin_web_user, *args, **kwargs):
method available_privileges (line 1610) | def available_privileges(self):
method existing_roles (line 1616) | def existing_roles(self):
method feature_rates_dict (line 1634) | def feature_rates_dict(self):
method product_rates_dict (line 1644) | def product_rates_dict(self):
method role_dict (line 1654) | def role_dict(self):
method current_features_to_rates (line 1671) | def current_features_to_rates(self):
method _get_errors_from_subform (line 1678) | def _get_errors_from_subform(form_name, subform):
method _retrieve_feature_rate (line 1688) | def _retrieve_feature_rate(self, rate_form):
method _retrieve_product_rate (line 1710) | def _retrieve_product_rate(self, rate_form):
method clean_feature_rates (line 1728) | def clean_feature_rates(self):
method clean_product_rates (line 1771) | def clean_product_rates(self):
method clean_create_new_role (line 1793) | def clean_create_new_role(self):
method clean_role_slug (line 1799) | def clean_role_slug(self):
method clean_new_role_slug (line 1805) | def clean_new_role_slug(self):
method clean_new_role_name (line 1815) | def clean_new_role_name(self):
method save (line 1822) | def save(self, request):
class FeatureRateForm (line 1875) | class FeatureRateForm(forms.ModelForm):
class Meta (line 1889) | class Meta(object):
method __init__ (line 1893) | def __init__(self, data=None, *args, **kwargs):
method is_new (line 1918) | def is_new(self):
method get_instance (line 1921) | def get_instance(self, feature):
class ProductRateForm (line 1927) | class ProductRateForm(forms.ModelForm):
class Meta (line 1942) | class Meta(object):
method __init__ (line 1946) | def __init__(self, data=None, *args, **kwargs):
method is_new (line 1960) | def is_new(self):
method get_instance (line 1963) | def get_instance(self):
class PlanContactForm (line 1967) | class PlanContactForm(forms.Form):
method __init__ (line 1981) | def __init__(self, domain, web_user, back_button=('Back', None), data=...
method send_message (line 2007) | def send_message(self, request_type):
class TriggerInvoiceForm (line 2030) | class TriggerInvoiceForm(forms.Form):
method __init__ (line 2047) | def __init__(self, *args, **kwargs):
method trigger_invoice (line 2103) | def trigger_invoice(self):
method clean_previous_invoices (line 2130) | def clean_previous_invoices(invoice_start, invoice_end, domain_name):
method _overwrite_user_history (line 2151) | def _overwrite_user_history(history_cls, domain, num_users, invoice_st...
method clean (line 2165) | def clean(self):
class TriggerCustomerInvoiceForm (line 2174) | class TriggerCustomerInvoiceForm(forms.Form):
method __init__ (line 2179) | def __init__(self, *args, **kwargs):
method trigger_customer_invoice (line 2211) | def trigger_customer_invoice(self):
method clean_previous_invoices (line 2231) | def clean_previous_invoices(invoice_start, invoice_end, account):
method clean (line 2252) | def clean(self):
method get_invoice_dates (line 2259) | def get_invoice_dates(self, account, year, month):
class TriggerBookkeeperEmailForm (line 2289) | class TriggerBookkeeperEmailForm(forms.Form):
method __init__ (line 2294) | def __init__(self, *args, **kwargs):
method clean_emails (line 2326) | def clean_emails(self):
method trigger_email (line 2329) | def trigger_email(self):
class TestReminderEmailFrom (line 2338) | class TestReminderEmailFrom(forms.Form):
method __init__ (line 2351) | def __init__(self, *args, **kwargs):
method send_emails (line 2381) | def send_emails(self):
class AdjustBalanceForm (line 2392) | class AdjustBalanceForm(forms.Form):
method __init__ (line 2427) | def __init__(self, invoice, *args, **kwargs):
method amount (line 2489) | def amount(self):
method adjust_balance (line 2500) | def adjust_balance(self, web_user=None):
class InvoiceInfoForm (line 2565) | class InvoiceInfoForm(forms.Form):
method __init__ (line 2572) | def __init__(self, invoice, *args, **kwargs):
class ResendEmailForm (line 2646) | class ResendEmailForm(forms.Form):
method __init__ (line 2654) | def __init__(self, invoice, *args, **kwargs):
method clean_additional_recipients (line 2688) | def clean_additional_recipients(self):
method resend_email (line 2694) | def resend_email(self):
class SuppressInvoiceForm (line 2706) | class SuppressInvoiceForm(forms.Form):
method __init__ (line 2710) | def __init__(self, invoice, *args, **kwargs):
method suppress_invoice (line 2737) | def suppress_invoice(self):
class HideInvoiceForm (line 2742) | class HideInvoiceForm(forms.Form):
method __init__ (line 2746) | def __init__(self, invoice, *args, **kwargs):
method hide_invoice (line 2773) | def hide_invoice(self):
class CreateAdminForm (line 2778) | class CreateAdminForm(forms.Form):
method __init__ (line 2783) | def __init__(self, *args, **kwargs):
method add_admin_user (line 2804) | def add_admin_user(self):
class TriggerDowngradeForm (line 2835) | class TriggerDowngradeForm(forms.Form):
method __init__ (line 2838) | def __init__(self, *args, **kwargs):
class TriggerAutopaymentsForm (line 2865) | class TriggerAutopaymentsForm(forms.Form):
method __init__ (line 2868) | def __init__(self, *args, **kwargs):
class TriggerAutoRenewalForm (line 2895) | class TriggerAutoRenewalForm(forms.Form):
method __init__ (line 2898) | def __init__(self, *args, **kwargs):
class TriggerRemovedSsoUserAutoDeactivationForm (line 2925) | class TriggerRemovedSsoUserAutoDeactivationForm(forms.Form):
method __init__ (line 2927) | def __init__(self, *args, **kwargs):
FILE: corehq/apps/accounting/interface.py
function invoice_column_cell (line 82) | def invoice_column_cell(invoice):
function customer_invoice_cell (line 93) | def customer_invoice_cell(invoice):
function invoice_cost_cell (line 104) | def invoice_cost_cell(invoice):
class AddItemInterface (line 115) | class AddItemInterface(GenericTabularReport):
method template_context (line 124) | def template_context(self):
method report_context (line 133) | def report_context(self):
class AccountingInterface (line 141) | class AccountingInterface(AddItemInterface):
method new_item_view (line 162) | def new_item_view(self):
method headers (line 167) | def headers(self):
method rows (line 179) | def rows(self):
method _accounts (line 194) | def _accounts(self):
class SubscriptionInterface (line 241) | class SubscriptionInterface(AddItemInterface):
method new_item_view (line 266) | def new_item_view(self):
method headers (line 271) | def headers(self):
method rows (line 290) | def rows(self):
method _subscriptions (line 329) | def _subscriptions(self):
class SoftwarePlanInterface (line 400) | class SoftwarePlanInterface(AddItemInterface):
method new_item_view (line 416) | def new_item_view(self):
method headers (line 421) | def headers(self):
method rows (line 431) | def rows(self):
method _plans (line 447) | def _plans(self):
function get_exportable_column (line 469) | def get_exportable_column(amount):
function get_subtotal_and_deduction (line 476) | def get_subtotal_and_deduction(line_items):
class InvoiceInterfaceBase (line 485) | class InvoiceInterfaceBase(GenericTabularReport):
method filter_by_subscription (line 493) | def filter_by_subscription(self, subscription):
class WireInvoiceInterface (line 497) | class WireInvoiceInterface(InvoiceInterfaceBase):
method headers (line 510) | def headers(self):
method rows (line 538) | def rows(self):
method _invoices (line 589) | def _invoices(self):
method email_response (line 630) | def email_response(self):
class InvoiceInterface (line 641) | class InvoiceInterface(InvoiceInterfaceBase):
method headers (line 663) | def headers(self):
method rows (line 705) | def rows(self):
method _invoices (line 799) | def _invoices(self):
method adjust_balance_forms (line 892) | def adjust_balance_forms(self):
method report_context (line 896) | def report_context(self):
method adjust_balance_form (line 906) | def adjust_balance_form(self):
method view_response (line 914) | def view_response(self):
method email_response (line 923) | def email_response(self):
class CustomerInvoiceInterface (line 934) | class CustomerInvoiceInterface(InvoiceInterfaceBase):
method headers (line 954) | def headers(self):
method rows (line 993) | def rows(self):
method _invoices (line 1076) | def _invoices(self):
method adjust_balance_forms (line 1151) | def adjust_balance_forms(self):
method report_context (line 1155) | def report_context(self):
method adjust_balance_form (line 1165) | def adjust_balance_form(self):
method view_response (line 1173) | def view_response(self):
method email_response (line 1182) | def email_response(self):
function _get_domain_from_payment_record (line 1193) | def _get_domain_from_payment_record(payment_record):
class PaymentRecordInterface (line 1207) | class PaymentRecordInterface(GenericTabularReport):
method headers (line 1227) | def headers(self):
method rows (line 1238) | def rows(self):
method _payment_records (line 1272) | def _payment_records(self):
class SubscriptionAdjustmentInterface (line 1300) | class SubscriptionAdjustmentInterface(GenericTabularReport):
method headers (line 1317) | def headers(self):
method rows (line 1331) | def rows(self):
method _subscription_adjustments (line 1355) | def _subscription_adjustments(self):
class CreditAdjustmentInterface (line 1371) | class CreditAdjustmentInterface(GenericTabularReport):
method headers (line 1393) | def headers(self):
method rows (line 1421) | def rows(self):
method _credit_adjustments (line 1484) | def _credit_adjustments(self):
FILE: corehq/apps/accounting/invoice_pdf.py
function prepend_newline_if_not_empty (line 19) | def prepend_newline_if_not_empty(string):
class PdfLineItem (line 28) | class PdfLineItem(object):
method __init__ (line 30) | def __init__(self, description, quantity, unit_cost, subtotal, credits...
class Address (line 39) | class Address(object):
method __init__ (line 41) | def __init__(
method __str__ (line 67) | def __str__(self):
method mailing_address (line 74) | def mailing_address(self):
method contact_info (line 90) | def contact_info(self):
function inches (line 105) | def inches(num_inches):
function midpoint (line 109) | def midpoint(x1, x2):
function item_height (line 113) | def item_height(item, width):
class InvoiceTemplate (line 119) | class InvoiceTemplate(object):
method __init__ (line 135) | def __init__(self, filename,
method add_item (line 183) | def add_item(self, description, quantity, unit_cost, subtotal, credits...
method get_pdf (line 187) | def get_pdf(self):
method draw_customer_invoice (line 201) | def draw_customer_invoice(self, items, items_to_draw):
method draw_logo (line 211) | def draw_logo(self):
method draw_text (line 216) | def draw_text(self, string, x, y):
method draw_from_address (line 223) | def draw_from_address(self):
method draw_to_address (line 238) | def draw_to_address(self):
method draw_project_name (line 274) | def draw_project_name(self):
method draw_account_name (line 303) | def draw_account_name(self):
method draw_statement_period (line 332) | def draw_statement_period(self):
method draw_invoice_label (line 353) | def draw_invoice_label(self):
method draw_details (line 358) | def draw_details(self):
method draw_header (line 414) | def draw_header(self):
method draw_table (line 427) | def draw_table(self, items):
method draw_footer (line 517) | def draw_footer(self):
method _add_flywire_footer_item (line 539) | def _add_flywire_footer_item(self, items, text_style):
method _add_credit_card_footer_item (line 549) | def _add_credit_card_footer_item(self, items, text_style):
method _add_ach_and_wire_footer_items (line 563) | def _add_ach_and_wire_footer_items(self, items, text_style):
method draw_table_with_header_and_footer (line 604) | def draw_table_with_header_and_footer(self, items):
method draw_totals_on_new_page (line 612) | def draw_totals_on_new_page(self):
method draw_totals (line 626) | def draw_totals(self, totals_x, line_height, subtotal_y):
FILE: corehq/apps/accounting/invoicing.py
class DomainInvoiceFactory (line 77) | class DomainInvoiceFactory(object):
method __init__ (line 82) | def __init__(self, date_start, date_end, domain, recipients=None):
method create_invoices (line 97) | def create_invoices(self):
method _get_subscriptions (line 114) | def _get_subscriptions(self):
method _create_invoice_for_subscription (line 125) | def _create_invoice_for_subscription(self, subscription):
method _generate_invoice (line 171) | def _generate_invoice(self, subscription, invoice_start, invoice_end):
method subscriber (line 226) | def subscriber(self):
class DomainWireInvoiceFactory (line 230) | class DomainWireInvoiceFactory(object):
method __init__ (line 232) | def __init__(self, domain, date_start=None, date_end=None, contact_ema...
method create_wire_invoice (line 244) | def create_wire_invoice(self, balance):
method create_wire_credits_invoice (line 303) | def create_wire_credits_invoice(self, amount, credit_label, unit_cost,...
method create_subscription_credits_invoice (line 322) | def create_subscription_credits_invoice(self, plan_version, date_start...
method create_prorated_subscription_change_credits_invoice (line 331) | def create_prorated_subscription_change_credits_invoice(self, old_date...
method date_due (line 366) | def date_due(date_start):
class CustomerAccountInvoiceFactory (line 371) | class CustomerAccountInvoiceFactory(object):
method __init__ (line 375) | def __init__(self, date_start, date_end, account, recipients=None):
method create_invoice (line 389) | def create_invoice(self):
method _generate_customer_invoice (line 405) | def _generate_customer_invoice(self):
method _update_invoice_due_date (line 429) | def _update_invoice_due_date(self, invoice, factory_date_end):
method _email_invoice (line 438) | def _email_invoice(self):
function should_create_invoice (line 456) | def should_create_invoice(subscription, domain, invoice_start, invoice_e...
function generate_line_items (line 481) | def generate_line_items(invoice, subscription):
class LineItemFactory (line 496) | class LineItemFactory(object):
method __init__ (line 501) | def __init__(self, subscription, rate, invoice):
method unit_description (line 507) | def unit_description(self):
method base_description (line 515) | def base_description(self):
method unit_cost (line 523) | def unit_cost(self):
method quantity (line 527) | def quantity(self):
method subscribed_domains (line 532) | def subscribed_domains(self):
method create (line 544) | def create(self):
method get_factory_by_feature_type (line 555) | def get_factory_by_feature_type(cls, feature_type):
method _subscription_ends_before_invoice (line 568) | def _subscription_ends_before_invoice(self):
method _subscription_starts_after_invoice (line 576) | def _subscription_starts_after_invoice(self):
method subscription_date_range (line 583) | def subscription_date_range(self):
method _is_partial_invoice (line 601) | def _is_partial_invoice(self):
method is_prorated (line 609) | def is_prorated(self):
method num_prorated_days (line 617) | def num_prorated_days(self):
method _days_in_billing_period (line 630) | def _days_in_billing_period(self):
class ProductLineItemFactory (line 634) | class ProductLineItemFactory(LineItemFactory):
method create (line 636) | def create(self):
method base_description (line 649) | def base_description(self):
method unit_description (line 656) | def unit_description(self):
method unit_cost (line 674) | def unit_cost(self):
method quantity (line 680) | def quantity(self):
method months_product_active_over_period (line 690) | def months_product_active_over_period(self, num_months):
method plan_name (line 703) | def plan_name(self):
method _auto_generate_credits (line 706) | def _auto_generate_credits(self, line_item):
class FeatureLineItemFactory (line 715) | class FeatureLineItemFactory(LineItemFactory):
method create (line 717) | def create(self):
method unit_cost (line 724) | def unit_cost(self):
class UserLineItemFactory (line 728) | class UserLineItemFactory(FeatureLineItemFactory):
method unit_cost (line 731) | def unit_cost(self):
method quantity (line 746) | def quantity(self):
method total_users_for_date (line 755) | def total_users_for_date(self, date):
method all_month_ends_in_invoice (line 769) | def all_month_ends_in_invoice(self):
method _unit_description_by_user_type (line 777) | def _unit_description_by_user_type(self, user_type):
method unit_description (line 792) | def unit_description(self):
class FormSubmittingMobileWorkerLineItemFactory (line 796) | class FormSubmittingMobileWorkerLineItemFactory(UserLineItemFactory):
method total_users_for_date (line 798) | def total_users_for_date(self, date):
method unit_description (line 810) | def unit_description(self):
class WebUserLineItemFactory (line 814) | class WebUserLineItemFactory(UserLineItemFactory):
method total_users_for_date (line 816) | def total_users_for_date(self, date):
method unit_description (line 826) | def unit_description(self):
class SmsLineItemFactory (line 830) | class SmsLineItemFactory(FeatureLineItemFactory):
method _start_date_count_sms (line 834) | def _start_date_count_sms(self):
method _end_date_count_sms (line 847) | def _end_date_count_sms(self):
method unit_cost (line 860) | def unit_cost(self):
method quantity (line 892) | def quantity(self):
method unit_description (line 897) | def unit_description(self):
method sms_billables_queryset (line 939) | def sms_billables_queryset(self):
method sms_billables (line 958) | def sms_billables(self):
method num_sms (line 963) | def num_sms(self):
method is_within_monthly_limit (line 968) | def is_within_monthly_limit(self):
FILE: corehq/apps/accounting/management/commands/add_operations_user.py
class Command (line 11) | class Command(BaseCommand):
method add_arguments (line 14) | def add_arguments(self, parser):
method handle (line 26) | def handle(self, usernames, **options):
FILE: corehq/apps/accounting/management/commands/change_role_for_software_plan_version.py
class OldRoleDoesNotExist (line 12) | class OldRoleDoesNotExist(Exception):
class NewRoleDoesNotExist (line 16) | class NewRoleDoesNotExist(Exception):
class PlanVersionAndRoleMismatch (line 20) | class PlanVersionAndRoleMismatch(Exception):
function change_role_for_software_plan_version (line 24) | def change_role_for_software_plan_version(old_role, new_role, limit_to_p...
class Command (line 67) | class Command(BaseCommand):
method add_arguments (line 70) | def add_arguments(self, parser):
method handle (line 76) | def handle(self, old_role, new_role, **kwargs):
FILE: corehq/apps/accounting/management/commands/create_test_pdf_templates.py
class Command (line 6) | class Command(BaseCommand):
method handle (line 9) | def handle(self, **options):
FILE: corehq/apps/accounting/management/commands/find_inactive_custom_modules.py
class Command (line 16) | class Command(BaseCommand):
method handle (line 18) | def handle(self, **kwargs):
method log_module_info (line 59) | def log_module_info(self, module, path, domains, in_module_map):
FILE: corehq/apps/accounting/management/commands/get_minimum_features_by_domain.py
class Command (line 13) | class Command(BaseCommand):
method add_arguments (line 16) | def add_arguments(self, parser):
method handle (line 23) | def handle(self, domain_names, **kwargs):
function _privilege_to_response_function (line 40) | def _privilege_to_response_function():
function _is_domain_using_privilege (line 51) | def _is_domain_using_privilege(domain_obj, privilege):
function _domain_has_custom_report (line 57) | def _domain_has_custom_report(domain_obj):
function _domain_uses_locations (line 62) | def _domain_uses_locations(domain_obj):
function _domain_has_apps_using_templated_intents (line 67) | def _domain_has_apps_using_templated_intents(domain_obj):
function _domain_has_apps_using_custom_intents (line 83) | def _domain_has_apps_using_custom_intents(domain_obj):
FILE: corehq/apps/accounting/management/commands/get_partner_domain_user_history.py
class Command (line 14) | class Command(BaseCommand):
method add_arguments (line 19) | def add_arguments(self, parser):
method handle (line 24) | def handle(self, start_date, end_date, domains, **kwargs):
FILE: corehq/apps/accounting/management/commands/list_customer_billing_account_software_plan.py
class Command (line 6) | class Command(BaseCommand):
method add_arguments (line 11) | def add_arguments(self, parser):
method handle (line 16) | def handle(self, *args, **kwargs):
FILE: corehq/apps/accounting/management/commands/list_prepayments_by_year.py
function _make_value_safe_for_csv (line 11) | def _make_value_safe_for_csv(value):
function _get_subscription_from_credit_adj (line 15) | def _get_subscription_from_credit_adj(credit_adj):
function _domain_from_adjustment (line 21) | def _domain_from_adjustment(credit_adj):
class Command (line 29) | class Command(BaseCommand):
method add_arguments (line 32) | def add_arguments(self, parser):
method handle (line 35) | def handle(self, year, **options):
FILE: corehq/apps/accounting/management/commands/make_domain_enterprise_level.py
class Command (line 8) | class Command(BaseCommand):
method add_arguments (line 12) | def add_arguments(self, parser):
method handle (line 16) | def handle(self, domain, **kwargs):
FILE: corehq/apps/accounting/migrations/0001_squashed_0052_ensure_report_builder_plans.py
function _cchq_software_plan_bootstrap (line 24) | def _cchq_software_plan_bootstrap(apps, schema_editor):
class Migration (line 32) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0002_auto_20170222_2008.py
function _grandfather_location (line 9) | def _grandfather_location(apps, schema_editor):
class Migration (line 18) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0003_auto_20170328_2102.py
class Migration (line 10) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0004_auto_20170404_0028.py
class Migration (line 10) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0005_automatic_downgrade_adjustment_method.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0006_unique_active_domain_subscription.py
class Migration (line 8) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0007_practice_mobile_workers.py
class Migration (line 10) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0008_update_report_builder_included_feature_numbers.py
function _cchq_software_plan_update (line 10) | def _cchq_software_plan_update(apps, schema_editor):
class Migration (line 14) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0009_make_billingaccount_name_unique.py
function _make_existing_billing_account_names_unique (line 9) | def _make_existing_billing_account_names_unique(apps, schema_editor):
class Migration (line 25) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0010_remove_softwareproduct_product_type.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0011_remove_softwareproduct.py
function _copy_product_name_to_product_rate_name (line 6) | def _copy_product_name_to_product_rate_name(apps, schema_editor):
class Migration (line 13) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0012_replace__product_type__with__is_product.py
function _product_type_to_is_product (line 6) | def _product_type_to_is_product(apps, schema_editor):
class Migration (line 12) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0013_subscription_dates_check.py
class Migration (line 4) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0014_paymentmethod__web_user__nonnullable.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0015_grandfather_login_as.py
function _grandfather_login_as (line 9) | def _grandfather_login_as(apps, schema_editor):
class Migration (line 18) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0016_grandfather_reportbuilder_5_pro.py
function _grandfather_reportbuilder_5_pro (line 9) | def _grandfather_reportbuilder_5_pro(apps, schema_editor):
class Migration (line 18) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0017_nonnullable_char_fields.py
function _assign_default_values (line 6) | def _assign_default_values(apps, schema_editor):
class Migration (line 16) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0018_alter_nonnullable_char_fields.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0019_standard_pricing_march_2018.py
function _bootstrap_new_standard_pricing (line 9) | def _bootstrap_new_standard_pricing(apps, schema_editor):
class Migration (line 13) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0020_payment_method__unique_together.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0021_standard_user_limit_march_2018.py
function _bootstrap_new_standard_user_limit (line 11) | def _bootstrap_new_standard_user_limit(apps, schema_editor):
class Migration (line 15) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0022_add__skip_auto_downgrade_reason.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0023_auto_20180501_1813.py
class Migration (line 8) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0024_unique__transaction_id.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0025_auto_20180508_1952.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0026_auto_20180508_1956.py
function noop (line 6) | def noop(*args, **kwargs):
function _convert_emailed_to_array_field (line 10) | def _convert_emailed_to_array_field(apps, schema_editor):
class Migration (line 24) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0027_auto_20180509_1857.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0028_auto_20180604_1757.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0029_auto_20180605_1826.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0030_softwareplan_max_domains.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0031_billingaccount_billing_admin_emails.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0032_billingaccount_invoicing_plan.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0032_customerinvoice_squashed_0036_customerbillingrecord.py
class Migration (line 10) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0033_auto_20180709_1837.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0034_merge_20180711_1828.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0034_remove_subscription_date_delay_invoicing.py
function assert_date_delay_invoicing_does_not_apply (line 8) | def assert_date_delay_invoicing_does_not_apply(apps, schema_editor):
class Migration (line 16) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0035_enterprise_settings.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0035_merge_20180711_2039.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0036_domainuserhistory.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0037_merge_20180807_0915.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0038_remove_billingaccount_restrict_signup_email.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0039_auto_20180828_2258.py
function noop (line 11) | def noop(*args, **kwargs):
function _bootstrap_new_monthly_pricing (line 15) | def _bootstrap_new_monthly_pricing(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0040_auto_20181002_1721.py
function noop (line 11) | def noop(*args, **kwargs):
function _bootstrap_new_monthly_pricing (line 15) | def _bootstrap_new_monthly_pricing(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0041_auto_20190130_1709.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0042_domain_user_history__unique__and__nonnullable.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0043_grandfather_case_privs.py
function _grandfather_case_privs (line 11) | def _grandfather_case_privs(apps, schema_editor):
class Migration (line 21) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0044_grandfather_odata_privs.py
function _grandfather_odata_privs (line 11) | def _grandfather_odata_privs(apps, schema_editor):
class Migration (line 21) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0045_grandfather_data_forwarding_privs.py
function _grandfather_data_forwarding_privs (line 11) | def _grandfather_data_forwarding_privs(apps, schema_editor):
class Migration (line 21) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0046_new_plans.py
function _grandfather_basic_privs (line 16) | def _grandfather_basic_privs(apps, schema_editor):
function _ensure_new_software_plans (line 26) | def _ensure_new_software_plans(apps, schema_editor):
class Migration (line 30) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0047_invoice_communication.py
class Migration (line 9) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0048_friendly_writeoff.py
class Migration (line 8) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0049_auto_20200924_1753.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0050_app_user_profiles.py
function _grandfather_basic_privs (line 9) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0051_hubspot_restrictions.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0052_geocoder_permissions.py
function _grandfather_basic_privs (line 9) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0053_app_user_profiles_advanced.py
function _grandfather_basic_privs (line 9) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0054_default_export_settings.py
function _grandfather_basic_privs (line 9) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0055_linked_projects.py
function _grandfather_basic_privs (line 7) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 17) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0056_add_release_management.py
function _grandfather_basic_privs (line 7) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 11) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0057_add_sms_report_toggle.py
class Migration (line 4) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0058_delete_linked_projects_role.py
function _revoke_linked_project_priv (line 7) | def _revoke_linked_project_priv(apps, schema_editor):
class Migration (line 17) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0059_add_lite_release_management_priv.py
function _grandfather_basic_privs (line 9) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0060_add_loadtest_users_priv.py
function _grandfather_basic_privs (line 9) | def _grandfather_basic_privs(apps, schema_editor):
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0061_remove_enterprise_v1.py
function _consolidate_enterprise_v1_into_v0 (line 14) | def _consolidate_enterprise_v1_into_v0(apps, schema_editor):
class Migration (line 34) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0062_add_release_management_to_enterprise.py
function _add_release_management_to_enterprise (line 9) | def _add_release_management_to_enterprise(apps, schema_editor):
function _reverse (line 18) | def _reverse():
class Migration (line 29) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0063_replace_linked_projects_ff_with_erm.py
function _migrate_linked_projects_ff_to_erm (line 8) | def _migrate_linked_projects_ff_to_erm(apps, schema_editor):
class Migration (line 12) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0064_add_form_link_workflow_priv.py
function _grandfather_form_link_workflow_privs (line 11) | def _grandfather_form_link_workflow_privs(apps, schema_editor):
class Migration (line 21) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0065_phone_apk_heartbeat_privs.py
function _grandfather_phone_apk_heartbeat_privs (line 11) | def _grandfather_phone_apk_heartbeat_privs(apps, schema_editor):
class Migration (line 21) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0066_data_file_download_priv.py
function _grandfather_data_file_download_priv (line 10) | def _grandfather_data_file_download_priv(apps, schema_editor):
class Migration (line 27) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0067_add_view_app_diff_priv.py
function _grandfather_view_app_diff_privs (line 11) | def _grandfather_view_app_diff_privs(apps, schema_editor):
class Migration (line 21) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0068_regex_field_validation_privilege.py
function _grandfather_regex_field_validation_priv (line 10) | def _grandfather_regex_field_validation_priv(apps, schema_editor):
class Migration (line 27) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0069_location_safe_case_imports_priv.py
function _grandfather_location_safe_case_imports_priv (line 10) | def _grandfather_location_safe_case_imports_priv(apps, schema_editor):
class Migration (line 28) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0070_form_case_ids_case_importer_priv.py
function _grandfather_form_case_ids_case_importer_priv (line 13) | def _grandfather_form_case_ids_case_importer_priv(apps, schema_editor):
class Migration (line 29) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0071_add_billingaccountwebuserhistory.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0072_export_multisort_priv.py
function _grandfather_export_multisort_priv (line 12) | def _grandfather_export_multisort_priv(apps, schema_editor):
class Migration (line 28) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0073_export_ownership_priv.py
function _grandfather_export_ownership_priv (line 11) | def _grandfather_export_ownership_priv(apps, schema_editor):
class Migration (line 28) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0074_filtered_bulk_user_download_priv.py
function _grandfather_filtered_bulk_user_download_priv (line 10) | def _grandfather_filtered_bulk_user_download_priv(apps, schema_editor):
class Migration (line 28) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0075_application_error_report_priv.py
function _grandfather_application_error_report_priv (line 10) | def _grandfather_application_error_report_priv(apps, schema_editor):
class Migration (line 28) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0076_location_owner_in_report_builder_priv.py
function _grandfather_location_owner_in_report_builder_priv (line 10) | def _grandfather_location_owner_in_report_builder_priv(apps, schema_edit...
class Migration (line 27) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0077_case_list_explorer_priv.py
function _add_cle_to_pro_and_above (line 9) | def _add_cle_to_pro_and_above(apps, schema_editor):
function _reverse (line 18) | def _reverse():
class Migration (line 29) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0078_revert_location_owner_in_report_builder_priv.py
function _remove_privilege_from_plan (line 13) | def _remove_privilege_from_plan(apps, schema_editor):
function _grant_privilege_to_plans (line 22) | def _grant_privilege_to_plans(*args, **kwargs):
class Migration (line 52) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0079_add_web_user_feature.py
function _add_web_user_feature (line 8) | def _add_web_user_feature(apps, schema_editor):
function _remove_web_user_feature (line 17) | def _remove_web_user_feature(apps, schema_editor):
class Migration (line 25) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0080_add_web_user_feature_in_other_models.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0081_billingaccount_bill_web_user.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0082_application_error_report_priv.py
function _grandfather_application_error_report_priv (line 11) | def _grandfather_application_error_report_priv(apps, schema_editor):
function _revert_application_error_report_priv (line 33) | def _revert_application_error_report_priv(apps, schema_editor):
class Migration (line 41) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0083_data_dictionary_priv.py
function _grandfather_data_dictionary_priv (line 10) | def _grandfather_data_dictionary_priv(apps, schema_editor):
class Migration (line 28) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0084_copy_cases_priv.py
function _grandfather_copy_cases_priv (line 10) | def _grandfather_copy_cases_priv(apps, schema_editor):
class Migration (line 27) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0085_remove_free_50_sms.py
function _bootstrap_new_standard_pricing (line 9) | def _bootstrap_new_standard_pricing(apps, schema_editor):
class Migration (line 13) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0086_add_duplicate_invoice_id_to_invoice_model.py
class Migration (line 7) | class Migration(migrations.Migration):
method _populate_duplicate_invoice_id (line 13) | def _populate_duplicate_invoice_id(apps, schema_editor):
FILE: corehq/apps/accounting/migrations/0087_invoice_unique_constraints.py
class Migration (line 19) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0088_add_new_softwareplan_visibility.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0089_dedupe_priv.py
function _add_dedupe_to_advanced_and_above (line 10) | def _add_dedupe_to_advanced_and_above(apps, schema_editor):
function _reverse (line 20) | def _reverse(apps, schema_editor):
class Migration (line 35) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0090_custom_domain_alerts_priv.py
function _grandfather_privilege (line 12) | def _grandfather_privilege(apps, schema_editor):
class Migration (line 29) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0091_remove_custom_banner_alerts_feature_flag.py
function _remove_feature_flag (line 13) | def _remove_feature_flag(*args, **kwargs):
class Migration (line 24) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0092_revert_application_error_report_priv.py
function _remove_privilege_from_plan (line 16) | def _remove_privilege_from_plan(apps, schema_editor):
function _grant_privilege_to_plans (line 24) | def _grant_privilege_to_plans(*args, **kwargs):
class Migration (line 47) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0093_defaultproductplan_is_annual_plan.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0094_add_annual_softwareplans.py
function _bootstrap_new_annual_pricing (line 8) | def _bootstrap_new_annual_pricing(apps, schema_editor):
function _remove_annual_default_product_plans (line 12) | def _remove_annual_default_product_plans(apps, schema_editor):
class Migration (line 21) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0095_update_softwareplan_visibilities.py
function change_plan_visibilities (line 10) | def change_plan_visibilities(apps, schema_editor):
class Migration (line 22) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0096_formsubmittingmobileworkerhistory_and_featuretype_choice.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0097_add_form_submitting_mobile_worker_feature.py
function _add_form_submitting_mobile_worker_feature (line 10) | def _add_form_submitting_mobile_worker_feature(apps, schema_editor):
function _remove_form_submitting_mobile_worker_feature (line 22) | def _remove_form_submitting_mobile_worker_feature(apps, schema_editor):
class Migration (line 30) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0098_app_dependencies_priv.py
function _grandfather_privilege (line 12) | def _grandfather_privilege(apps, schema_editor):
class Migration (line 29) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0099_data_cleaning_priv.py
function _add_data_cleaning_to_enterprise (line 9) | def _add_data_cleaning_to_enterprise(apps, schema_editor):
function _reverse (line 19) | def _reverse(apps, schema_editor):
class Migration (line 34) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0100_alter_customerinvoicecommunicationhistory_communication_type_and_more.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0101_update_standard_plan_pricing_users_and_privs.py
function _add_standard_plan_v2_role (line 10) | def _add_standard_plan_v2_role(apps, schema_editor):
function _bootstrap_new_standard_pricing (line 14) | def _bootstrap_new_standard_pricing(apps, schema_editor):
class Migration (line 18) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0102_alter_defaultproductplan_edition_and_more.py
function _rename_community_to_free (line 6) | def _rename_community_to_free(apps, schema_editor):
function _rename_free_to_community (line 14) | def _rename_free_to_community(apps, schema_editor):
class Migration (line 22) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0103_bulk_data_cleaning_priv.py
function _add_data_editing_to_advanced_and_clean_old_privilege (line 9) | def _add_data_editing_to_advanced_and_clean_old_privilege(apps, schema_e...
function _reverse (line 30) | def _reverse(apps, schema_editor):
class Migration (line 48) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0104_fix_priv_community_rename.py
function noop (line 10) | def noop(*args, **kwargs):
function _revoke_accidental_free_privilege (line 15) | def _revoke_accidental_free_privilege(apps, schema_editor):
class Migration (line 27) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0105_alter_billingcontactinfo_city_and_more.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0106_alter_billingcontactinfo_company_name.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0107_two_stage_mobile_worker_creation_priv.py
function _add_two_stage_mw_creation_to_standard (line 11) | def _add_two_stage_mw_creation_to_standard(apps, schema_editor):
function _reverse (line 26) | def _reverse(apps, schema_editor):
class Migration (line 45) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0108_subscription_auto_renew_and_more.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0109_enable_all_add_ons_priv.py
function _add_enable_all_add_ons_to_pro (line 16) | def _add_enable_all_add_ons_to_pro(apps, schema_editor):
function _reverse (line 27) | def _reverse(apps, schema_editor):
class Migration (line 42) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0110_alter_customerinvoicecommunicationhistory_communication_type_and_more.py
class Migration (line 7) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0111_rename_is_auto_invoiceable_billingaccount_require_auto_pay.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0112_data_dict_types_priv.py
function _grandfather_data_dict_types_priv (line 18) | def _grandfather_data_dict_types_priv(apps, schema_editor):
function _revoke_data_dict_types_priv (line 28) | def _revoke_data_dict_types_priv(apps, schema_editor):
class Migration (line 44) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0113_geojson_export_priv.py
function _grandfather_geojson_export_priv (line 18) | def _grandfather_geojson_export_priv(apps, schema_editor):
function _revoke_geojson_export_priv (line 28) | def _revoke_geojson_export_priv(apps, schema_editor):
class Migration (line 44) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0114_custom_icon_badges_priv.py
function _grandfather_privilege (line 13) | def _grandfather_privilege(apps, schema_editor):
class Migration (line 30) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0115_loc_cols_in_user_last_activity_priv.py
function _grandfather_privilege (line 13) | def _grandfather_privilege(apps, schema_editor):
class Migration (line 30) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/migrations/0116_creditadjustment_payment_type.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/accounting/mixins.py
function get_overdue_invoice (line 17) | def get_overdue_invoice(domain_name):
function get_total_credits_available_for_product (line 24) | def get_total_credits_available_for_product(subscription):
function get_credits_available_for_product_in_subscription (line 32) | def get_credits_available_for_product_in_subscription(current_subscripti...
function get_credits_available_for_product_in_account (line 42) | def get_credits_available_for_product_in_account(account):
class BillingModalsMixin (line 51) | class BillingModalsMixin(object):
method main_context (line 54) | def main_context(self):
method _should_display_billing_modals (line 61) | def _should_display_billing_modals(self):
method _downgrade_modal_context (line 72) | def _downgrade_modal_context(self):
method _low_credits_context (line 82) | def _low_credits_context(self):
FILE: corehq/apps/accounting/models.py
class BillingAccountType (line 106) | class BillingAccountType(object):
class InvoicingPlan (line 121) | class InvoicingPlan(object):
class FeatureType (line 132) | class FeatureType(object):
class SoftwarePlanEdition (line 150) | class SoftwarePlanEdition(object):
class SoftwarePlanVisibility (line 183) | class SoftwarePlanVisibility(object):
class CreditAdjustmentReason (line 196) | class CreditAdjustmentReason(object):
class SubscriptionAdjustmentReason (line 215) | class SubscriptionAdjustmentReason(object):
class SubscriptionAdjustmentMethod (line 237) | class SubscriptionAdjustmentMethod(object):
class PaymentMethodType (line 258) | class PaymentMethodType(object):
class SubscriptionType (line 265) | class SubscriptionType(object):
class ProBonoStatus (line 283) | class ProBonoStatus(object):
class FundingSource (line 294) | class FundingSource(object):
class EntryPoint (line 305) | class EntryPoint(object):
class PaymentType (line 316) | class PaymentType(object):
class PreOrPostPay (line 335) | class PreOrPostPay(object):
class CommunicationType (line 346) | class CommunicationType(object):
class Currency (line 361) | class Currency(models.Model):
class Meta (line 375) | class Meta(object):
method get_default (line 379) | def get_default(cls):
class BillingAccount (line 387) | class BillingAccount(ValidateModelMixin, models.Model):
class Meta (line 455) | class Meta(object):
method auto_pay_enabled (line 459) | def auto_pay_enabled(self):
method create_account_for_domain (line 463) | def create_account_for_domain(cls, domain,
method get_or_create_account_by_domain (line 485) | def get_or_create_account_by_domain(cls, domain,
method get_account_by_domain (line 506) | def get_account_by_domain(cls, domain):
method _get_account_by_created_by_domain (line 514) | def _get_account_by_created_by_domain(cls, domain):
method get_enterprise_restricted_signup_accounts (line 530) | def get_enterprise_restricted_signup_accounts(cls):
method autopay_card (line 534) | def autopay_card(self):
method get_domains (line 540) | def get_domains(self):
method has_enterprise_admin (line 544) | def has_enterprise_admin(self, email):
method update_autopay_user (line 548) | def update_autopay_user(self, new_user, domain):
method remove_autopay_user (line 556) | def remove_autopay_user(self):
method _send_autopay_card_removed_email (line 560) | def _send_autopay_card_removed_email(self, new_user, domain):
method _send_autopay_card_added_email (line 594) | def _send_autopay_card_added_email(self, domain):
method get_web_user_usernames (line 629) | def get_web_user_usernames(self):
method should_show_sms_billable_report (line 639) | def should_show_sms_billable_report(domain):
class BillingContactInfo (line 644) | class BillingContactInfo(models.Model):
class Meta (line 689) | class Meta(object):
method __init__ (line 692) | def __init__(self, *args, **kwargs):
method full_name (line 698) | def full_name(self):
class SoftwareProductRate (line 707) | class SoftwareProductRate(models.Model):
class Meta (line 718) | class Meta(object):
method __str__ (line 721) | def __str__(self):
method __eq__ (line 724) | def __eq__(self, other):
method new_rate (line 733) | def new_rate(cls, product_name, monthly_fee, save=True):
class Feature (line 740) | class Feature(models.Model):
class Meta (line 749) | class Meta(object):
method __str__ (line 752) | def __str__(self):
method get_rate (line 755) | def get_rate(self, default_instance=True):
class FeatureRate (line 762) | class FeatureRate(models.Model):
class Meta (line 779) | class Meta(object):
method __str__ (line 782) | def __str__(self):
method __eq__ (line 787) | def __eq__(self, other):
method new_rate (line 796) | def new_rate(cls, feature_name, feature_type,
class SoftwarePlan (line 813) | class SoftwarePlan(models.Model):
class Meta (line 836) | class Meta(object):
method get_version (line 840) | def get_version(self):
method at_max_domains (line 846) | def at_max_domains(self):
class DefaultProductPlan (line 856) | class DefaultProductPlan(models.Model):
class Meta (line 873) | class Meta(object):
method get_default_plan_version (line 880) | def get_default_plan_version(cls, edition=None, is_trial=False,
method get_lowest_edition (line 897) | def get_lowest_edition(cls, requested_privileges, return_plan=False):
class SoftwarePlanVersion (line 907) | class SoftwarePlanVersion(models.Model):
class Meta (line 921) | class Meta(object):
method __str__ (line 924) | def __str__(self):
method save (line 930) | def save(self, *args, **kwargs):
method filter_version_query (line 935) | def filter_version_query(query, edition=None, visibility=None, is_plan...
method get_most_recent_version (line 945) | def get_most_recent_version(cls, edition=None, visibility=None):
method version (line 967) | def version(self):
method user_facing_description (line 973) | def user_facing_description(self):
method user_feature (line 1013) | def user_feature(self):
method user_limit (line 1024) | def user_limit(self):
method user_fee (line 1030) | def user_fee(self):
method feature_charges_exist_for_domain (line 1034) | def feature_charges_exist_for_domain(self, domain, start_date=None, en...
method is_paused (line 1050) | def is_paused(self):
class SubscriberManager (line 1054) | class SubscriberManager(models.Manager):
method safe_get (line 1056) | def safe_get(self, *args, **kwargs):
class Subscriber (line 1063) | class Subscriber(models.Model):
class Meta (line 1072) | class Meta(object):
method __str__ (line 1075) | def __str__(self):
method create_subscription (line 1078) | def create_subscription(self, new_plan_version, new_subscription, is_i...
method change_subscription (line 1087) | def change_subscription(self, downgraded_privileges, upgraded_privileg...
method activate_subscription (line 1098) | def activate_subscription(self, upgraded_privileges, subscription):
method deactivate_subscription (line 1104) | def deactivate_subscription(self, downgraded_privileges, upgraded_priv...
method reactivate_subscription (line 1113) | def reactivate_subscription(self, new_plan_version, subscription):
method _apply_upgrades_and_downgrades (line 1120) | def _apply_upgrades_and_downgrades(self, new_plan_version=None,
method should_send_subscription_notification (line 1153) | def should_send_subscription_notification(old_subscription, new_subscr...
method _process_downgrade (line 1161) | def _process_downgrade(domain, downgraded_privileges, new_plan_version):
method _process_upgrade (line 1169) | def _process_upgrade(domain, upgraded_privileges, new_plan_version):
class VisibleSubscriptionManager (line 1177) | class VisibleSubscriptionManager(models.Manager):
method get_queryset (line 1180) | def get_queryset(self):
class DisabledManager (line 1184) | class DisabledManager(models.Manager):
method get_queryset (line 1186) | def get_queryset(self):
class Subscription (line 1190) | class Subscription(models.Model):
class Meta (line 1234) | class Meta(object):
method __str__ (line 1237) | def __str__(self):
method __eq__ (line 1247) | def __eq__(self, other):
method __hash__ (line 1258) | def __hash__(self):
method save (line 1263) | def save(self, *args, **kwargs):
method delete (line 1279) | def delete(self, *args, **kwargs):
method clear_caches (line 1284) | def clear_caches(cls, domain_name):
method is_free_edition (line 1288) | def is_free_edition(self):
method allowed_attr_changes (line 1292) | def allowed_attr_changes(self):
method next_subscription_filter (line 1301) | def next_subscription_filter(self):
method previous_subscription_filter (line 1308) | def previous_subscription_filter(self):
method is_renewed (line 1315) | def is_renewed(self):
method next_subscription (line 1323) | def next_subscription(self):
method previous_subscription (line 1330) | def previous_subscription(self):
method raise_conflicting_dates (line 1336) | def raise_conflicting_dates(self, date_start, date_end):
method update_subscription (line 1353) | def update_subscription(self, date_start, date_end,
method _update_dates (line 1389) | def _update_dates(self, date_start, date_end):
method _update_properties (line 1410) | def _update_properties(self, **kwargs):
method upgrade_plan_for_consistency (line 1433) | def upgrade_plan_for_consistency(self, new_plan_version, upgrade_note,...
method change_plan (line 1460) | def change_plan(self, new_plan_version, date_end=None,
method reactivate_subscription (line 1557) | def reactivate_subscription(self, date_end=None, note=None, web_user=N...
method renew_subscription (line 1581) | def renew_subscription(self, note=None, web_user=None,
method suppress_subscription (line 1651) | def suppress_subscription(self):
method transfer_credits (line 1661) | def transfer_credits(self, subscription=None):
method set_billing_account_entry_point (line 1689) | def set_billing_account_entry_point(self):
method get_active_domains_for_account (line 1697) | def get_active_domains_for_account(cls, account_name):
method get_active_subscription_by_domain (line 1707) | def get_active_subscription_by_domain(cls, domain_name_or_obj):
method _get_active_subscription_by_domain (line 1717) | def _get_active_subscription_by_domain(cls, domain_name):
method get_subscribed_plan_by_domain (line 1729) | def get_subscribed_plan_by_domain(cls, domain):
method new_domain_subscription (line 1747) | def new_domain_subscription(cls, account, domain, plan_version,
method can_reactivate_domain_subscription (line 1848) | def can_reactivate_domain_subscription(cls, account, domain, plan_vers...
method is_below_minimum_subscription (line 1864) | def is_below_minimum_subscription(self):
method can_auto_renew (line 1876) | def can_auto_renew(self):
method user_can_change_subscription (line 1883) | def user_can_change_subscription(self, user):
method get_plan_and_user_count_by_domain (line 1892) | def get_plan_and_user_count_by_domain(cls, domain):
class InvoiceBaseManager (line 1900) | class InvoiceBaseManager(models.Manager):
method get_queryset (line 1902) | def get_queryset(self):
method create_or_get (line 1905) | def create_or_get(self, **kwargs):
class InvoiceBase (line 1918) | class InvoiceBase(models.Model):
class Meta (line 1935) | class Meta(object):
method is_customer_invoice (line 1939) | def is_customer_invoice(self):
method invoice_number (line 1943) | def invoice_number(self):
method is_wire (line 1948) | def is_wire(self):
method can_pay_by_wire (line 1952) | def can_pay_by_wire(self):
method get_domain (line 1955) | def get_domain(self):
method account (line 1959) | def account(self):
method is_paid (line 1963) | def is_paid(self):
method email_recipients (line 1967) | def email_recipients(self):
class WireInvoice (line 1971) | class WireInvoice(InvoiceBase):
class Meta (line 1975) | class Meta(object):
method account (line 1980) | def account(self):
method subtotal (line 1984) | def subtotal(self):
method is_wire (line 1988) | def is_wire(self):
method is_prepayment (line 1992) | def is_prepayment(self):
method can_pay_by_wire (line 1996) | def can_pay_by_wire(self):
method get_domain (line 1999) | def get_domain(self):
method get_total (line 2002) | def get_total(self):
method email_recipients (line 2006) | def email_recipients(self):
class WirePrepaymentInvoice (line 2019) | class WirePrepaymentInvoice(WireInvoice):
class Meta (line 2021) | class Meta(object):
method get_contact_emails (line 2027) | def get_contact_emails(self):
method is_prepayment (line 2039) | def is_prepayment(self):
class Invoice (line 2043) | class Invoice(InvoiceBase):
class Meta (line 2052) | class Meta(object):
method save (line 2061) | def save(self, *args, **kwargs):
method email_recipients (line 2068) | def email_recipients(self):
method get_contact_emails (line 2074) | def get_contact_emails(self, include_domain_admins=False, filter_out_d...
method can_pay_by_wire (line 2116) | def can_pay_by_wire(self):
method subtotal (line 2120) | def subtotal(self):
method applied_tax (line 2129) | def applied_tax(self):
method account (line 2134) | def account(self):
method applied_credit (line 2138) | def applied_credit(self):
method get_total (line 2143) | def get_total(self):
method update_balance (line 2149) | def update_balance(self):
method calculate_credit_adjustments (line 2156) | def calculate_credit_adjustments(self):
method exists_for_domain (line 2172) | def exists_for_domain(cls, domain):
method get_domain (line 2177) | def get_domain(self):
method autopayable_invoices (line 2181) | def autopayable_invoices(cls, date_due=Ellipsis):
method pay_invoice (line 2193) | def pay_invoice(self, payment_record, payment_type):
class CustomerInvoice (line 2204) | class CustomerInvoice(InvoiceBase):
class Meta (line 2209) | class Meta(object):
method is_customer_invoice (line 2220) | def is_customer_invoice(self):
method get_domain (line 2223) | def get_domain(self):
method email_recipients (line 2227) | def email_recipients(self):
method contact_emails (line 2236) | def contact_emails(self):
method get_contact_emails (line 2239) | def get_contact_emails(self, include_domain_admins=False, filter_out_d...
method can_pay_by_wire (line 2244) | def can_pay_by_wire(self):
method subtotal (line 2248) | def subtotal(self):
method applied_tax (line 2257) | def applied_tax(self):
method applied_credit (line 2261) | def applied_credit(self):
method get_total (line 2266) | def get_total(self):
method update_balance (line 2272) | def update_balance(self):
method calculate_credit_adjustments (line 2279) | def calculate_credit_adjustments(self):
method pay_invoice (line 2286) | def pay_invoice(self, payment_record, payment_type):
method exists_for_domain (line 2297) | def exists_for_domain(cls, domain):
method autopayable_invoices (line 2305) | def autopayable_invoices(cls, date_due):
class SubscriptionAdjustment (line 2316) | class SubscriptionAdjustment(models.Model):
class Meta (line 2346) | class Meta(object):
method record_adjustment (line 2350) | def record_adjustment(cls, subscription, **kwargs):
class BillingRecordBase (line 2362) | class BillingRecordBase(models.Model):
class Meta (line 2375) | class Meta(object):
method pdf (line 2381) | def pdf(self):
method html_template (line 2387) | def html_template(self):
method text_template (line 2391) | def text_template(self):
method should_send_email (line 2395) | def should_send_email(self):
method generate_record (line 2399) | def generate_record(cls, invoice):
method handle_throttled_email (line 2408) | def handle_throttled_email(self, contact_emails):
method email_context (line 2428) | def email_context(self):
method email_subject (line 2456) | def email_subject(self):
method can_view_statement (line 2459) | def can_view_statement(self, web_user):
method send_email (line 2462) | def send_email(self, contact_email=None, cc_emails=None):
class WireBillingRecord (line 2508) | class WireBillingRecord(BillingRecordBase):
class Meta (line 2514) | class Meta(object):
method should_send_email (line 2518) | def should_send_email(self):
method is_email_throttled (line 2523) | def is_email_throttled():
method email_context (line 2526) | def email_context(self):
method email_subject (line 2531) | def email_subject(self):
method email_from (line 2539) | def email_from():
method can_view_statement (line 2542) | def can_view_statement(self, web_user):
class WirePrepaymentBillingRecord (line 2546) | class WirePrepaymentBillingRecord(WireBillingRecord):
class Meta (line 2548) | class Meta(object):
method email_subject (line 2552) | def email_subject(self):
method can_view_statement (line 2570) | def can_view_statement(self, web_user):
class BillingRecord (line 2574) | class BillingRecord(BillingRecordBase):
class Meta (line 2582) | class Meta(object):
method html_template (line 2586) | def html_template(self):
method text_template (line 2596) | def text_template(self):
method should_send_email (line 2606) | def should_send_email(self):
method is_email_throttled (line 2615) | def is_email_throttled(self):
method email_context (line 2625) | def email_context(self):
method credits (line 2673) | def credits(self):
method _add_product_credits (line 2684) | def _add_product_credits(self, credits):
method _add_user_credits (line 2722) | def _add_user_credits(self, credits):
method _add_sms_credits (line 2760) | def _add_sms_credits(self, credits):
method _add_general_credits (line 2798) | def _add_general_credits(self, credits):
method email_subject (line 2835) | def email_subject(self):
method email_from (line 2842) | def email_from(self):
method _get_total_balance (line 2846) | def _get_total_balance(credit_lines):
method can_view_statement (line 2852) | def can_view_statement(self, web_user):
class CustomerBillingRecord (line 2856) | class CustomerBillingRecord(BillingRecordBase):
class Meta (line 2864) | class Meta(object):
method html_template (line 2868) | def html_template(self):
method text_template (line 2875) | def text_template(self):
method should_send_email (line 2881) | def should_send_email(self):
method email_context (line 2884) | def email_context(self):
method credits (line 2921) | def credits(self):
method _add_product_credits (line 2932) | def _add_product_credits(self, credits):
method _add_user_credits (line 2964) | def _add_user_credits(self, credits):
method _add_sms_credits (line 2996) | def _add_sms_credits(self, credits):
method _add_general_credits (line 3028) | def _add_general_credits(self, credits):
method _subscriptions_in_credit_adjustments (line 3059) | def _subscriptions_in_credit_adjustments(self, credit_adjustments):
method email_subject (line 3067) | def email_subject(self):
method email_from (line 3074) | def email_from(self):
method _get_total_balance (line 3078) | def _get_total_balance(credit_lines):
method can_view_statement (line 3084) | def can_view_statement(self, web_user):
class InvoicePdf (line 3091) | class InvoicePdf(BlobMixin, SafeSaveDocument):
method generate_pdf (line 3098) | def generate_pdf(self, invoice):
method get_filename (line 3178) | def get_filename(invoice):
method get_data (line 3184) | def get_data(self, invoice):
class LineItemManager (line 3189) | class LineItemManager(models.Manager):
method get_products (line 3191) | def get_products(self):
method get_features (line 3194) | def get_features(self):
method get_feature_by_type (line 3197) | def get_feature_by_type(self, feature_type):
class LineItem (line 3201) | class LineItem(models.Model):
class Meta (line 3215) | class Meta(object):
method invoice (line 3219) | def invoice(self):
method invoice (line 3226) | def invoice(self, invoice):
method subtotal (line 3233) | def subtotal(self):
method applied_credit (line 3240) | def applied_credit(self):
method total (line 3249) | def total(self):
method calculate_credit_adjustments (line 3252) | def calculate_credit_adjustments(self):
class CreditLine (line 3262) | class CreditLine(models.Model):
class Meta (line 3277) | class Meta(object):
method __str__ (line 3280) | def __str__(self):
method save (line 3294) | def save(self, *args, **kwargs):
method adjust_credit_balance (line 3306) | def adjust_credit_balance(
method get_credits_for_line_item (line 3356) | def get_credits_for_line_item(cls, line_item):
method get_credits_for_line_item_in_invoice (line 3372) | def get_credits_for_line_item_in_invoice(cls, line_item, feature_type,...
method get_credits_for_line_item_in_customer_invoice (line 3380) | def get_credits_for_line_item_in_customer_invoice(cls, line_item, feat...
method get_credits_for_invoice (line 3395) | def get_credits_for_invoice(cls, invoice, feature_type=None, is_produc...
method get_credits_for_customer_invoice (line 3435) | def get_credits_for_customer_invoice(cls, invoice):
method get_credits_for_subscriptions (line 3442) | def get_credits_for_subscriptions(cls, subscriptions, feature_type=Non...
method get_credits_for_account (line 3453) | def get_credits_for_account(cls, account, feature_type=None, is_produc...
method get_non_general_credits_for_account (line 3462) | def get_non_general_credits_for_account(cls, account):
method get_credits_by_subscription_and_features (line 3471) | def get_credits_by_subscription_and_features(cls, subscription,
method get_non_general_credits_by_subscription (line 3483) | def get_non_general_credits_by_subscription(cls, subscription):
method add_credit (line 3490) | def add_credit(
method apply_credits_toward_balance (line 3570) | def apply_credits_toward_balance(cls, credit_lines, balance, **kwargs):
method make_payment_towards_invoice (line 3585) | def make_payment_towards_invoice(cls, invoice, payment_record, payment...
class PaymentMethod (line 3605) | class PaymentMethod(models.Model):
class Meta (line 3622) | class Meta(object):
class StripePaymentMethod (line 3627) | class StripePaymentMethod(PaymentMethod):
class Meta (line 3629) | class Meta(object):
method customer (line 3639) | def customer(self):
method _get_or_create_stripe_customer (line 3642) | def _get_or_create_stripe_customer(self):
method _create_stripe_customer (line 3653) | def _create_stripe_customer(self):
method _get_stripe_customer (line 3662) | def _get_stripe_customer(self):
method all_cards (line 3666) | def all_cards(self):
method all_cards_serialized (line 3677) | def all_cards_serialized(self, billing_account):
method get_card (line 3688) | def get_card(self, card_token):
method get_autopay_card (line 3691) | def get_autopay_card(self, billing_account):
method remove_card (line 3697) | def remove_card(self, card_token):
method _remove_card_from_all_accounts (line 3702) | def _remove_card_from_all_accounts(self, card):
method create_card (line 3708) | def create_card(self, stripe_token, billing_account, domain, autopay=F...
method set_autopay (line 3735) | def set_autopay(self, card, billing_account, domain):
method unset_autopay (line 3747) | def unset_autopay(self, card, billing_account):
method _update_autopay_status (line 3755) | def _update_autopay_status(self, card, billing_account, autopay):
method _remove_autopay_card (line 3759) | def _remove_autopay_card(self, billing_account):
method _remove_other_auto_pay_cards (line 3765) | def _remove_other_auto_pay_cards(billing_account):
method _is_autopay (line 3774) | def _is_autopay(card, billing_account):
method _get_other_autopay_primary_domains (line 3778) | def _get_other_autopay_primary_domains(card, billing_account):
method _auto_pay_card_metadata_key (line 3797) | def _auto_pay_card_metadata_key(billing_account):
method create_charge (line 3806) | def create_charge(self, card, amount_in_dollars, description, idempote...
class PaymentRecord (line 3819) | class PaymentRecord(models.Model):
class Meta (line 3830) | class Meta(object):
method public_transaction_id (line 3834) | def public_transaction_id(self):
method create_record (line 3839) | def create_record(cls, payment_method, transaction_id, amount):
class CreditAdjustment (line 3847) | class CreditAdjustment(ValidateModelMixin, models.Model):
class Meta (line 3869) | class Meta(object):
method clean (line 3872) | def clean(self):
class DomainUserHistoryBase (line 3880) | class DomainUserHistoryBase(models.Model):
class Meta (line 3888) | class Meta:
class DomainUserHistory (line 3893) | class DomainUserHistory(DomainUserHistoryBase):
class FormSubmittingMobileWorkerHistory (line 3902) | class FormSubmittingMobileWorkerHistory(DomainUserHistoryBase):
class BillingAccountWebUserHistory (line 3914) | class BillingAccountWebUserHistory(models.Model):
class Meta (line 3924) | class Meta:
class CommunicationHistoryBase (line 3928) | class CommunicationHistoryBase(models.Model):
class Meta (line 3941) | class Meta(object):
class AccountCommunicationHistory (line 3945) | class AccountCommunicationHistory(CommunicationHistoryBase):
class InvoiceCommunicationHistory (line 3949) | class InvoiceCommunicationHistory(CommunicationHistoryBase):
class CustomerInvoiceCommunicationHistory (line 3953) | class CustomerInvoiceCommunicationHistory(CommunicationHistoryBase):
FILE: corehq/apps/accounting/payment_handlers.py
class BaseStripePaymentHandler (line 30) | class BaseStripePaymentHandler(object):
method __init__ (line 36) | def __init__(self, payment_method, domain):
method cost_item_name (line 42) | def cost_item_name(self):
method create_charge (line 47) | def create_charge(self, amount, card=None, customer=None):
method get_charge_amount (line 54) | def get_charge_amount(self, request):
method update_credits (line 59) | def update_credits(self, payment_record):
method update_payment_information (line 64) | def update_payment_information(self, account):
method process_request (line 69) | def process_request(self, request):
method get_email_context (line 154) | def get_email_context(self):
method send_email (line 159) | def send_email(self, payment_record):
class InvoiceStripePaymentHandler (line 168) | class InvoiceStripePaymentHandler(BaseStripePaymentHandler):
method __init__ (line 172) | def __init__(self, payment_method, domain, invoice):
method cost_item_name (line 177) | def cost_item_name(self):
method create_charge (line 180) | def create_charge(self, amount, card=None, customer=None):
method get_charge_amount (line 189) | def get_charge_amount(self, request):
method update_credits (line 196) | def update_credits(self, payment_record):
method get_email_context (line 222) | def get_email_context(self):
class BulkStripePaymentHandler (line 233) | class BulkStripePaymentHandler(BaseStripePaymentHandler):
method __init__ (line 237) | def __init__(self, payment_method, domain):
method cost_item_name (line 241) | def cost_item_name(self):
method create_charge (line 244) | def create_charge(self, amount, card=None, customer=None):
method invoices (line 254) | def invoices(self):
method balance (line 267) | def balance(self):
method get_charge_amount (line 270) | def get_charge_amount(self, request):
method update_credits (line 275) | def update_credits(self, payment_record):
method get_email_context (line 314) | def get_email_context(self):
class CreditStripePaymentHandler (line 324) | class CreditStripePaymentHandler(BaseStripePaymentHandler):
method __init__ (line 328) | def __init__(self, payment_method, domain, account, subscription=None,...
method cost_item_name (line 343) | def cost_item_name(self):
method get_charge_amount (line 346) | def get_charge_amount(self, request):
method create_charge (line 349) | def create_charge(self, amount, card=None, customer=None):
method update_payment_information (line 358) | def update_payment_information(self, account):
method update_credits (line 363) | def update_credits(self, payment_record):
class AutoPayInvoicePaymentHandler (line 376) | class AutoPayInvoicePaymentHandler(object):
method pay_autopayable_invoices (line 378) | def pay_autopayable_invoices(self, date_due=Ellipsis, domain=None):
method pay_autopayable_customer_invoices (line 393) | def pay_autopayable_customer_invoices(self, date_due=Ellipsis, account...
method _pay_invoice (line 408) | def _pay_invoice(self, invoice):
method _send_payment_receipt (line 453) | def _send_payment_receipt(self, invoice, payment_record):
method _handle_card_declined (line 478) | def _handle_card_declined(invoice, e):
method _handle_card_errors (line 495) | def _handle_card_errors(invoice, error):
method _handle_email_failure (line 503) | def _handle_email_failure(payment_record_id):
FILE: corehq/apps/accounting/static/accounting/js/stripe.js
function getCardElementPromise (line 6) | function getCardElementPromise(key) {
function createStripeToken (line 27) | function createStripeToken(handleResponse) {
FILE: corehq/apps/accounting/subscription_changes.py
class BaseModifySubscriptionHandler (line 45) | class BaseModifySubscriptionHandler(object):
method __init__ (line 47) | def __init__(self, domain, new_plan_version, changed_privs, date_start...
method get_response (line 61) | def get_response(self):
method action_type (line 75) | def action_type(self):
method privilege_to_response_function (line 79) | def privilege_to_response_function(cls):
method supported_privileges (line 83) | def supported_privileges(cls):
class BaseModifySubscriptionActionHandler (line 87) | class BaseModifySubscriptionActionHandler(BaseModifySubscriptionHandler):
method get_response (line 89) | def get_response(self):
function _get_active_immediate_broadcasts (line 94) | def _get_active_immediate_broadcasts(domain, survey_only=False):
function _get_active_scheduled_broadcasts (line 102) | def _get_active_scheduled_broadcasts(domain, survey_only=False):
function _get_active_scheduling_rules (line 110) | def _get_active_scheduling_rules(domain, survey_only=False):
function get_refresh_alert_schedule_instances_call (line 122) | def get_refresh_alert_schedule_instances_call(broadcast):
function get_refresh_timed_schedule_instances_call (line 132) | def get_refresh_timed_schedule_instances_call(broadcast):
function _deactivate_schedules (line 143) | def _deactivate_schedules(domain, survey_only=False):
class DomainDowngradeActionHandler (line 181) | class DomainDowngradeActionHandler(BaseModifySubscriptionActionHandler):
method privilege_to_response_function (line 190) | def privilege_to_response_function(cls):
method get_response (line 206) | def get_response(self):
method response_outbound_sms (line 213) | def response_outbound_sms(domain, new_plan_version):
method response_inbound_sms (line 228) | def response_inbound_sms(domain, new_plan_version):
method response_role_based_access (line 243) | def response_role_based_access(domain, new_plan_version):
method response_data_cleanup (line 259) | def response_data_cleanup(domain, new_plan_version):
method response_commcare_logo_uploader (line 278) | def response_commcare_logo_uploader(domain, new_plan_version):
method response_domain_security (line 286) | def response_domain_security(domain, new_plan_version):
method response_report_builder (line 294) | def response_report_builder(project, new_plan_version):
method response_practice_mobile_workers (line 313) | def response_practice_mobile_workers(project, new_plan_version):
method response_mobile_worker_creation (line 320) | def response_mobile_worker_creation(domain, new_plan_version):
class DomainUpgradeActionHandler (line 349) | class DomainUpgradeActionHandler(BaseModifySubscriptionActionHandler):
method privilege_to_response_function (line 358) | def privilege_to_response_function(cls):
method response_role_based_access (line 371) | def response_role_based_access(domain, new_plan_version):
method response_commcare_logo_uploader (line 380) | def response_commcare_logo_uploader(domain, new_plan_version):
method response_report_builder (line 388) | def response_report_builder(project, new_plan_version):
method response_enable_usercase (line 412) | def response_enable_usercase(project, new_plan_version):
function _fmt_alert (line 417) | def _fmt_alert(message, details=None):
function _has_report_builder_add_on (line 426) | def _has_report_builder_add_on(plan_version):
function _get_report_builder_reports (line 435) | def _get_report_builder_reports(project):
class DomainDowngradeStatusHandler (line 443) | class DomainDowngradeStatusHandler(BaseModifySubscriptionHandler):
method privilege_to_response_function (line 451) | def privilege_to_response_function(cls):
method get_response (line 470) | def get_response(self):
method response_cloudcare (line 479) | def response_cloudcare(domain, new_plan_version):
method response_lookup_tables (line 508) | def response_lookup_tables(domain, new_plan_version):
method response_custom_branding (line 525) | def response_custom_branding(domain, new_plan_version):
method response_outbound_sms (line 536) | def response_outbound_sms(domain, new_plan_version):
method response_inbound_sms (line 559) | def response_inbound_sms(domain, new_plan_version):
method response_deidentified_data (line 582) | def response_deidentified_data(project, new_plan_version):
method response_mobile_worker_creation (line 602) | def response_mobile_worker_creation(self):
method response_role_based_access (line 678) | def response_role_based_access(domain, new_plan_version):
method response_later_subscription (line 704) | def response_later_subscription(self):
method response_data_cleanup (line 733) | def response_data_cleanup(domain, new_plan_version):
method response_domain_security (line 757) | def response_domain_security(domain, new_plan_version):
method response_report_builder (line 786) | def response_report_builder(project, new_plan_version):
method response_practice_mobile_workers (line 796) | def response_practice_mobile_workers(project, new_plan_version):
FILE: corehq/apps/accounting/task_utils.py
function get_context_to_send_autopay_failed_email (line 22) | def get_context_to_send_autopay_failed_email(invoice_id, is_customer_inv...
function get_context_to_send_purchase_receipt (line 66) | def get_context_to_send_purchase_receipt(payment_record_id, domain_name,...
function _user_active_in_domains (line 94) | def _user_active_in_domains(web_user, domain_name, account):
function raise_except_and_log_accounting_comms_error (line 101) | def raise_except_and_log_accounting_comms_error(username):
FILE: corehq/apps/accounting/tasks.py
function _activate_subscription (line 106) | def _activate_subscription(subscription):
function activate_subscriptions (line 116) | def activate_subscriptions(based_on_date=None):
function _deactivate_subscription (line 141) | def _deactivate_subscription(subscription):
function deactivate_subscriptions (line 179) | def deactivate_subscriptions(based_on_date=None):
function warn_subscriptions_still_active (line 198) | def warn_subscriptions_still_active(based_on_date=None):
function warn_subscriptions_not_active (line 212) | def warn_subscriptions_not_active(based_on_date=None):
function warn_active_subscriptions_per_domain_not_one (line 227) | def warn_active_subscriptions_per_domain_not_one():
function warn_subscriptions_without_domain (line 250) | def warn_subscriptions_without_domain():
function update_subscriptions (line 265) | def update_subscriptions():
function check_credit_line_balances (line 280) | def check_credit_line_balances():
function log_error_and_soft_assert (line 300) | def log_error_and_soft_assert(error_message):
function generate_invoices_based_on_date (line 306) | def generate_invoices_based_on_date(invoice_date):
function generate_invoices (line 355) | def generate_invoices():
function send_bookkeeper_email (line 363) | def send_bookkeeper_email(month=None, year=None, emails=None):
function auto_renew_subscriptions (line 426) | def auto_renew_subscriptions(domain_name=None):
function _get_auto_renewable_subscriptions (line 436) | def _get_auto_renewable_subscriptions(domain_name=None):
function auto_renew_subscription (line 455) | def auto_renew_subscription(subscription):
function remind_subscription_ending (line 480) | def remind_subscription_ending():
function send_renewal_reminder_emails (line 491) | def send_renewal_reminder_emails(days_left):
function send_subscription_ending_emails (line 499) | def send_subscription_ending_emails(days_left):
function _try_send_subscription_email (line 507) | def _try_send_subscription_email(subscription, days_left, send_email_func):
function remind_dimagi_contact_subscription_ending (line 520) | def remind_dimagi_contact_subscription_ending():
function send_subscription_reminder_emails_dimagi_contact (line 530) | def send_subscription_reminder_emails_dimagi_contact(days_left):
function _filter_subscriptions_for_reminder_emails (line 540) | def _filter_subscriptions_for_reminder_emails(days_left, **kwargs):
function create_wire_credits_invoice (line 553) | def create_wire_credits_invoice(domain_name,
function send_purchase_receipt (line 608) | def send_purchase_receipt(
function send_autopay_failed (line 628) | def send_autopay_failed(invoice_id, is_customer_invoice=False):
function weekly_digest (line 648) | def weekly_digest():
function pay_autopay_invoices (line 743) | def pay_autopay_invoices():
function update_exchange_rates (line 751) | def update_exchange_rates():
function send_credits_on_hq_report (line 776) | def send_credits_on_hq_report():
function run_auto_pause_process (line 790) | def run_auto_pause_process():
function send_invoice_reminders (line 795) | def send_invoice_reminders():
function archive_logos (line 801) | def archive_logos(self, domain_name):
function restore_logos (line 820) | def restore_logos(self, domain_name):
function send_prepaid_credits_export (line 843) | def send_prepaid_credits_export():
function calculate_users_in_all_domains (line 920) | def calculate_users_in_all_domains(today=None):
function calculate_form_submitting_mobile_workers_in_all_domains (line 944) | def calculate_form_submitting_mobile_workers_in_all_domains(today=None):
function calculate_web_users_in_all_billing_accounts (line 965) | def calculate_web_users_in_all_billing_accounts(today=None):
FILE: corehq/apps/accounting/tests/base_tests.py
class BaseAccountingTest (line 17) | class BaseAccountingTest(TestCase):
method setUpClass (line 20) | def setUpClass(cls):
class BaseInvoiceTestCase (line 25) | class BaseInvoiceTestCase(BaseAccountingTest):
method setUpClass (line 32) | def setUpClass(cls):
method setUp (line 44) | def setUp(self):
method cleanUp (line 59) | def cleanUp(self):
method tearDownClass (line 65) | def tearDownClass(cls):
method create_invoices (line 72) | def create_invoices(date, calculate_users=True, calculate_web_users=Fa...
FILE: corehq/apps/accounting/tests/generator.py
function bootstrap_accounting (line 41) | def bootstrap_accounting():
function bootstrap_test_software_plan_versions (line 58) | def bootstrap_test_software_plan_versions():
function init_default_currency (line 63) | def init_default_currency():
function unique_name (line 75) | def unique_name():
function create_arbitrary_web_user_name (line 80) | def create_arbitrary_web_user_name(is_dimagi=False):
function billing_account (line 84) | def billing_account(web_user_creator, web_user_contact, is_customer_acco...
function arbitrary_contact_info (line 100) | def arbitrary_contact_info(account, web_user_creator):
function subscribable_plan_version (line 117) | def subscribable_plan_version(edition=SoftwarePlanEdition.STANDARD):
function default_feature_rates (line 122) | def default_feature_rates(edition=SoftwarePlanEdition.STANDARD):
function custom_plan_version (line 134) | def custom_plan_version(name='Custom software plan', edition=SoftwarePla...
function generate_domain_subscription (line 154) | def generate_domain_subscription(account, domain, date_start, date_end,
function get_start_date (line 174) | def get_start_date():
function arbitrary_domain (line 182) | def arbitrary_domain():
function arbitrary_domain_and_subscriber (line 192) | def arbitrary_domain_and_subscriber():
function arbitrary_user (line 199) | def arbitrary_user(domain_name, is_active=True, is_webuser=False, **kwar...
function arbitrary_commcare_users_for_domain (line 214) | def arbitrary_commcare_users_for_domain(domain, num_users, is_active=True):
function arbitrary_webusers_for_domain (line 223) | def arbitrary_webusers_for_domain(domain, num_users, is_active=True):
function create_excess_free_users (line 232) | def create_excess_free_users(domain):
class FakeStripeCard (line 240) | class FakeStripeCard(mock.MagicMock):
method __init__ (line 242) | def __init__(self):
method metadata (line 249) | def metadata(self):
method metadata (line 253) | def metadata(self, value):
method save (line 257) | def save(self):
class FakeStripeCustomer (line 261) | class FakeStripeCustomer(mock.MagicMock):
method __init__ (line 263) | def __init__(self, cards):
class FakeStripeCardManager (line 270) | class FakeStripeCardManager:
method create_card (line 274) | def create_card(cls):
method get_card_by_id (line 280) | def get_card_by_id(cls, card_id):
class FakeStripeCustomerManager (line 284) | class FakeStripeCustomerManager:
method create_customer (line 288) | def create_customer(cls, cards):
method get_customer_by_id (line 294) | def get_customer_by_id(cls, customer_id):
FILE: corehq/apps/accounting/tests/test_admin_software_plans.py
class TestUpgradeSoftwarePlanToLatestVersion (line 25) | class TestUpgradeSoftwarePlanToLatestVersion(BaseAccountingTest):
method setUpClass (line 28) | def setUpClass(cls):
method test_that_upgrade_occurs (line 107) | def test_that_upgrade_occurs(self):
class TestKeepSoftwarePlanConsistentManagementCommand (line 149) | class TestKeepSoftwarePlanConsistentManagementCommand(BaseAccountingTest):
method setUpClass (line 152) | def setUpClass(cls):
method test_keep_software_plan_consistent_for_customer_billing_accounts (line 223) | def test_keep_software_plan_consistent_for_customer_billing_accounts(s...
FILE: corehq/apps/accounting/tests/test_autopay.py
class BaseTestBillingAutoPay (line 33) | class BaseTestBillingAutoPay(BaseInvoiceTestCase):
method setUp (line 35) | def setUp(self):
method tearDown (line 45) | def tearDown(self):
method _generate_autopayable_entities (line 49) | def _generate_autopayable_entities(self):
method _generate_non_autopayable_entities (line 62) | def _generate_non_autopayable_entities(self):
method _create_autopay_method (line 87) | def _create_autopay_method(self, fake_customer):
method _run_autopay (line 94) | def _run_autopay(self):
method _assert_no_side_effects (line 97) | def _assert_no_side_effects(self):
class TestBillingAutoPayInvoices (line 103) | class TestBillingAutoPayInvoices(BaseTestBillingAutoPay):
method test_get_autopayable_invoices (line 107) | def test_get_autopayable_invoices(self, fake_customer):
method test_get_autopayable_invoices_returns_nothing (line 119) | def test_get_autopayable_invoices_returns_nothing(self):
method test_pay_autopayable_invoices (line 131) | def test_pay_autopayable_invoices(self, fake_charge, fake_customer):
method test_double_charge_is_prevented_because_paid_invoice_is_never_included (line 148) | def test_double_charge_is_prevented_because_paid_invoice_is_never_incl...
method test_when_stripe_fails_no_side_effects_occur (line 174) | def test_when_stripe_fails_no_side_effects_occur(self, fake_create, fa...
method _run_autopay (line 185) | def _run_autopay(self):
class TestBillingAutoPayCustomerInvoices (line 191) | class TestBillingAutoPayCustomerInvoices(BaseTestBillingAutoPay):
method _generate_autopayable_entities (line 192) | def _generate_autopayable_entities(self):
method _generate_non_autopayable_entities (line 197) | def _generate_non_autopayable_entities(self):
method test_get_autopayable_invoices (line 204) | def test_get_autopayable_invoices(self, fake_customer):
method test_get_autopayable_invoices_returns_nothing (line 216) | def test_get_autopayable_invoices_returns_nothing(self):
method test_pay_autopayable_customer_invoices (line 229) | def test_pay_autopayable_customer_invoices(self, fake_charge, fake_cus...
method test_double_charge_is_prevented_because_paid_invoice_is_never_included (line 246) | def test_double_charge_is_prevented_because_paid_invoice_is_never_incl...
method test_when_stripe_fails_no_side_effects_occur (line 272) | def test_when_stripe_fails_no_side_effects_occur(self, fake_create, fa...
method _run_autopay (line 283) | def _run_autopay(self):
FILE: corehq/apps/accounting/tests/test_autopay_with_api.py
class BaseTestBillingAutoPay (line 29) | class BaseTestBillingAutoPay(BaseInvoiceTestCase):
method setUp (line 31) | def setUp(self):
method _generate_autopayable_entities (line 46) | def _generate_autopayable_entities(self):
method _generate_non_autopayable_entities (line 64) | def _generate_non_autopayable_entities(self):
method _run_autopay (line 90) | def _run_autopay(self):
class TestBillingAutoPayInvoices (line 94) | class TestBillingAutoPayInvoices(BaseTestBillingAutoPay):
method test_get_autopayable_invoices (line 96) | def test_get_autopayable_invoices(self):
method test_get_autopayable_invoices_returns_nothing (line 107) | def test_get_autopayable_invoices_returns_nothing(self):
method test_pay_autopayable_invoices (line 119) | def test_pay_autopayable_invoices(self):
method test_double_charge_is_prevented_and_only_one_payment_record_created (line 133) | def test_double_charge_is_prevented_and_only_one_payment_record_create...
method _run_autopay (line 147) | def _run_autopay(self):
class TestBillingAutoPayCustomerInvoices (line 153) | class TestBillingAutoPayCustomerInvoices(BaseTestBillingAutoPay):
method _generate_autopayable_entities (line 155) | def _generate_autopayable_entities(self):
method _generate_non_autopayable_entities (line 160) | def _generate_non_autopayable_entities(self):
method test_get_autopayable_invoices (line 165) | def test_get_autopayable_invoices(self):
method test_get_autopayable_invoices_returns_nothing (line 176) | def test_get_autopayable_invoices_returns_nothing(self):
method test_pay_autopayable_invoices (line 189) | def test_pay_autopayable_invoices(self):
method test_double_charge_is_prevented_and_only_one_payment_record_created (line 203) | def test_double_charge_is_prevented_and_only_one_payment_record_create...
method _run_autopay (line 217) | def _run_autopay(self):
FILE: corehq/apps/accounting/tests/test_card_utils.py
function mock_fake_card (line 17) | def mock_fake_card(card_id, *, metadata=None, brand='Visa', last4='4242'...
class CardUtilsTest (line 29) | class CardUtilsTest(BaseAccountingTest):
method setUp (line 30) | def setUp(self):
method test_get_autopay_card_and_owner_for_billing_account_no_autopay (line 40) | def test_get_autopay_card_and_owner_for_billing_account_no_autopay(self):
method test_get_autopay_card_and_owner_for_billing_account_with_autopay (line 46) | def test_get_autopay_card_and_owner_for_billing_account_with_autopay(s...
method test_card_as_autopay_for_billing_account_sets_autopay (line 60) | def test_card_as_autopay_for_billing_account_sets_autopay(self):
method test_get_payment_method_for_user_always_returns (line 69) | def test_get_payment_method_for_user_always_returns(self):
method test_get_saved_cards_for_user_no_stripe_key (line 76) | def test_get_saved_cards_for_user_no_stripe_key(self):
method test_get_saved_cards_for_user_with_stripe_key (line 83) | def test_get_saved_cards_for_user_with_stripe_key(self, get_mock):
FILE: corehq/apps/accounting/tests/test_change_role_for_software_plan_version.py
class ChangeRoleForSoftwarePlanVersionTest (line 20) | class ChangeRoleForSoftwarePlanVersionTest(TestCase):
method setUpClass (line 23) | def setUpClass(cls):
method test_raises_old_role_does_not_exist (line 32) | def test_raises_old_role_does_not_exist(self):
method test_raises_new_role_does_not_exist (line 36) | def test_raises_new_role_does_not_exist(self):
method test_changes_active_versions (line 40) | def test_changes_active_versions(self):
method test_changes_inactive_versions (line 59) | def test_changes_inactive_versions(self):
method test_dry_run (line 85) | def test_dry_run(self):
method test_limit_to_plan_version_id (line 97) | def test_limit_to_plan_version_id(self):
method test_raises_exception_if_plan_version_id_does_not_reference_old_role (line 122) | def test_raises_exception_if_plan_version_id_does_not_reference_old_ro...
FILE: corehq/apps/accounting/tests/test_credit_lines.py
class TestCreditLines (line 27) | class TestCreditLines(BaseInvoiceTestCase):
method setUp (line 32) | def setUp(self):
method tearDown (line 43) | def tearDown(self):
method test_product_line_item_credits (line 48) | def test_product_line_item_credits(self):
method test_feature_line_item_credits (line 84) | def test_feature_line_item_credits(self):
method _generate_users_fee_to_credit_against (line 120) | def _generate_users_fee_to_credit_against(self):
method _test_line_item_crediting (line 129) | def _test_line_item_crediting(self, get_line_item_from_invoice):
method _test_credit_use (line 146) | def _test_credit_use(self, credit_line):
method test_invoice_credit (line 153) | def test_invoice_credit(self):
method test_combined_credits (line 188) | def test_combined_credits(self):
method _generate_subscription_and_account_invoice_credits (line 221) | def _generate_subscription_and_account_invoice_credits(self, monthly_f...
method _test_final_invoice_balance (line 233) | def _test_final_invoice_balance(self):
method test_balance_adjustment (line 246) | def test_balance_adjustment(self):
class TestDeactivatedCredits (line 286) | class TestDeactivatedCredits(BaseInvoiceTestCase):
method add_account_credit (line 288) | def add_account_credit(self, amount):
method add_product_credit (line 294) | def add_product_credit(self, amount):
method add_sms_credit (line 303) | def add_sms_credit(self, amount):
method add_user_credit (line 312) | def add_user_credit(self, amount):
method test_get_credits_for_account_only_returns_active_credit (line 321) | def test_get_credits_for_account_only_returns_active_credit(self):
method test_get_credits_by_subscription_and_features_only_returns_active_credits (line 331) | def test_get_credits_by_subscription_and_features_only_returns_active_...
method test_get_non_general_credits_by_subscription_only_returns_active_credits (line 359) | def test_get_non_general_credits_by_subscription_only_returns_active_c...
class TestCreditTransfers (line 382) | class TestCreditTransfers(BaseAccountingTest):
method setUpClass (line 385) | def setUpClass(cls):
method tearDownClass (line 398) | def tearDownClass(cls):
method _ensure_transfer (line 402) | def _ensure_transfer(self, original_credits):
method test_transfers (line 423) | def test_transfers(self):
class TestSubscriptionChangeTransfersSubscriptionLevelCredit (line 480) | class TestSubscriptionChangeTransfersSubscriptionLevelCredit(BaseAccount...
method setUpClass (line 483) | def setUpClass(cls):
method tearDownClass (line 503) | def tearDownClass(cls):
method _get_credit_total (line 507) | def _get_credit_total(self, subscription):
method _change_plan_to_pro_on_date (line 513) | def _change_plan_to_pro_on_date(self, subscription, date):
method _generate_user_history (line 525) | def _generate_user_history(self, num_users, invoice_date):
method test_any_type_credits_transfer_in_invoice (line 533) | def test_any_type_credits_transfer_in_invoice(self):
method test_product_type_credits_transfer_in_invoice (line 568) | def test_product_type_credits_transfer_in_invoice(self):
method test_user_feature_type_credits_transfer_in_invoice (line 603) | def test_user_feature_type_credits_transfer_in_invoice(self):
FILE: corehq/apps/accounting/tests/test_customer_invoicing.py
class BaseCustomerInvoiceCase (line 43) | class BaseCustomerInvoiceCase(BaseAccountingTest):
method setUpClass (line 48) | def setUpClass(cls):
method cleanUpUser (line 106) | def cleanUpUser(self):
method _create_domain (line 117) | def _create_domain(cls, name):
class TestCustomerInvoice (line 123) | class TestCustomerInvoice(BaseCustomerInvoiceCase):
method test_multiple_subscription_invoice (line 125) | def test_multiple_subscription_invoice(self):
method test_no_invoice_before_start (line 142) | def test_no_invoice_before_start(self):
method test_no_invoice_after_end (line 150) | def test_no_invoice_after_end(self):
method test_deleted_domain_in_multiple_subscription_invoice (line 159) | def test_deleted_domain_in_multiple_subscription_invoice(self):
class TestProductLineItem (line 185) | class TestProductLineItem(BaseCustomerInvoiceCase):
method setUp (line 191) | def setUp(self):
method test_product_line_items (line 195) | def test_product_line_items(self):
method test_product_line_items_in_quarterly_invoice (line 212) | def test_product_line_items_in_quarterly_invoice(self):
method test_product_line_items_in_yearly_invoice (line 232) | def test_product_line_items_in_yearly_invoice(self):
method test_account_level_product_credits (line 252) | def test_account_level_product_credits(self):
method test_subscription_level_product_credits (line 267) | def test_subscription_level_product_credits(self):
class TestUserLineItem (line 283) | class TestUserLineItem(BaseCustomerInvoiceCase):
method setUp (line 287) | def setUp(self):
method test_under_limit (line 294) | def test_under_limit(self):
method test_over_limit (line 320) | def test_over_limit(self):
method test_balance_reflects_credit_deduction_for_account_level_user_credits (line 345) | def test_balance_reflects_credit_deduction_for_account_level_user_cred...
method test_balance_reflects_credit_deduction_for_single_subscription_level_user_credit (line 402) | def test_balance_reflects_credit_deduction_for_single_subscription_lev...
class TestSmsLineItem (line 427) | class TestSmsLineItem(BaseCustomerInvoiceCase):
method setUp (line 429) | def setUp(self):
method tearDown (line 439) | def tearDown(self):
method test_under_limit (line 443) | def test_under_limit(self):
method test_over_limit (line 464) | def test_over_limit(self):
method test_balance_reflects_credit_deduction_for_single_subscription_level_sms_credits (line 521) | def test_balance_reflects_credit_deduction_for_single_subscription_lev...
method test_balance_reflects_credit_deduction_for_account_level_sms_credits (line 543) | def test_balance_reflects_credit_deduction_for_account_level_sms_credi...
method _create_sms_line_items (line 565) | def _create_sms_line_items(self):
method _delete_sms_billables (line 573) | def _delete_sms_billables(cls):
class TestQuarterlyInvoicing (line 581) | class TestQuarterlyInvoicing(BaseCustomerInvoiceCase):
method setUp (line 585) | def setUp(self):
method initialize_domain_user_history_objects (line 598) | def initialize_domain_user_history_objects(self):
method test_user_over_limit_in_quarterly_invoice (line 627) | def test_user_over_limit_in_quarterly_invoice(self):
method test_user_over_limit_in_yearly_invoice (line 640) | def test_user_over_limit_in_yearly_invoice(self):
method test_sms_over_limit_in_quarterly_invoice (line 654) | def test_sms_over_limit_in_quarterly_invoice(self):
method test_sms_over_limit_in_yearly_invoice (line 683) | def test_sms_over_limit_in_yearly_invoice(self):
method _create_sms_line_items_for_quarter (line 712) | def _create_sms_line_items_for_quarter(self):
class TestDomainsInLineItemForCustomerInvoicing (line 719) | class TestDomainsInLineItemForCustomerInvoicing(TestCase):
method setUpClass (line 722) | def setUpClass(cls):
method tearDownClass (line 741) | def tearDownClass(cls):
method test_past_subscription_is_excluded (line 746) | def test_past_subscription_is_excluded(self):
method test_future_subscription_is_excluded (line 757) | def test_future_subscription_is_excluded(self):
method test_preexisting_subscription_is_included (line 768) | def test_preexisting_subscription_is_included(self):
method test_preexisting_subscription_without_end_date_is_included (line 779) | def test_preexisting_subscription_without_end_date_is_included(self):
method test_new_subscription_is_included (line 789) | def test_new_subscription_is_included(self):
method test_new_subscription_without_end_date_is_included (line 800) | def test_new_subscription_without_end_date_is_included(self):
FILE: corehq/apps/accounting/tests/test_domain_user_history.py
class TestDomainUserHistory (line 21) | class TestDomainUserHistory(BaseInvoiceTestCase):
method setUpClass (line 24) | def setUpClass(cls):
method setUp (line 28) | def setUp(self):
method tearDown (line 35) | def tearDown(self):
method test_domain_user_history (line 40) | def test_domain_user_history(self):
method test_calculate_users_in_all_domains (line 49) | def test_calculate_users_in_all_domains(self):
class TestBillingAccountWebUserHistory (line 59) | class TestBillingAccountWebUserHistory(TestCase):
method setUpClass (line 62) | def setUpClass(cls):
method test_calculate_web_users_for_enterprise_account (line 128) | def test_calculate_web_users_for_enterprise_account(self):
method test_calculate_web_users_for_standard_account (line 138) | def test_calculate_web_users_for_standard_account(self):
method test_mobile_workers_are_not_counted (line 145) | def test_mobile_workers_are_not_counted(self):
method _get_domain_user_from_account (line 153) | def _get_domain_user_from_account(self):
method tearDownClass (line 160) | def tearDownClass(cls):
FILE: corehq/apps/accounting/tests/test_emails.py
class TestGetReminderEmailContacts (line 19) | class TestGetReminderEmailContacts(BaseInvoiceTestCase):
method test_get_reminder_email_contacts (line 20) | def test_get_reminder_email_contacts(self):
method test_contacts_only_in_one_list (line 43) | def test_contacts_only_in_one_list(self):
method test_to_project_admins_if_no_billing_contact (line 64) | def test_to_project_admins_if_no_billing_contact(self):
class TestEndingReminderContext (line 90) | class TestEndingReminderContext(BaseInvoiceTestCase):
method test_ending_reminder_context (line 93) | def test_ending_reminder_context(self):
method test_ending_on_tomorrow_if_one_day_left (line 113) | def test_ending_on_tomorrow_if_one_day_left(self):
method test_subject_uses_account_if_customer_billing_account (line 121) | def test_subject_uses_account_if_customer_billing_account(self):
FILE: corehq/apps/accounting/tests/test_ensure_plans.py
class TestEnsurePlans (line 20) | class TestEnsurePlans(BaseAccountingTest):
method tearDown (line 22) | def tearDown(self):
method test_ensure_plans (line 26) | def test_ensure_plans(self):
method _test_plan_versions_ensured (line 79) | def _test_plan_versions_ensured(self, bootstrap_config):
FILE: corehq/apps/accounting/tests/test_enterprise_mode.py
class TestEnterpriseMode (line 16) | class TestEnterpriseMode(DomainSubscriptionMixin, BaseAccountingTest):
method setUp (line 18) | def setUp(self):
method tearDown (line 21) | def tearDown(self):
method test_standard_cant_access_advanced (line 25) | def test_standard_cant_access_advanced(self):
method test_no_plan_cant_access_anything (line 31) | def test_no_plan_cant_access_anything(self):
method test_enterprise_can_access_anything (line 34) | def test_enterprise_can_access_anything(self):
FILE: corehq/apps/accounting/tests/test_forms.py
class TestAdjustBalanceForm (line 42) | class TestAdjustBalanceForm(BaseInvoiceTestCase):
method setUp (line 44) | def setUp(self):
method tearDown (line 50) | def tearDown(self):
method test_manual_adjustment (line 53) | def test_manual_adjustment(self):
method test_transfer_credit_with_credit (line 73) | def test_transfer_credit_with_credit(self):
method test_transfer_credit_without_credit (line 103) | def test_transfer_credit_without_credit(self):
class TestAdjustBalanceFormForCustomerAccount (line 134) | class TestAdjustBalanceFormForCustomerAccount(BaseInvoiceTestCase):
method setUp (line 136) | def setUp(self):
method tearDown (line 144) | def tearDown(self):
method test_manual_adjustment (line 147) | def test_manual_adjustment(self):
method test_transfer_credit_with_credit (line 167) | def test_transfer_credit_with_credit(self):
method test_transfer_credit_without_credit (line 196) | def test_transfer_credit_without_credit(self):
class TestSubscriptionForm (line 226) | class TestSubscriptionForm(BaseAccountingTest):
method setUp (line 228) | def setUp(self):
method tearDown (line 260) | def tearDown(self):
method test_regular_plan_not_added_to_customer_account (line 265) | def test_regular_plan_not_added_to_customer_account(self):
method test_customer_plan_not_added_to_regular_account (line 283) | def test_customer_plan_not_added_to_regular_account(self):
method test_form_data_create_subscription (line 301) | def test_form_data_create_subscription(self):
method test_form_data_update_subscription (line 328) | def test_form_data_update_subscription(self):
method shared_keywords (line 350) | def shared_keywords():
class TestTriggerInvoiceForm (line 371) | class TestTriggerInvoiceForm(BaseInvoiceTestCase):
method setUp (line 373) | def setUp(self):
method init_form (line 381) | def init_form(self, form_data, show_testing_options=False):
method form_data (line 387) | def form_data(self, **kwargs):
method test_trigger_invoice (line 396) | def test_trigger_invoice(self):
method test_clean_previous_invoices (line 405) | def test_clean_previous_invoices(self):
method test_show_testing_options (line 418) | def test_show_testing_options(self):
method test_num_mobile_workers (line 427) | def test_num_mobile_workers(self):
method test_num_form_submitting_mobile_workers (line 441) | def test_num_form_submitting_mobile_workers(self):
class TestPlanContactForm (line 456) | class TestPlanContactForm(TestCase):
method setUp (line 457) | def setUp(self):
method test_send_message (line 464) | def test_send_message(self, mock_send):
FILE: corehq/apps/accounting/tests/test_invoice_line_items.py
class TestProductLineItem (line 36) | class TestProductLineItem(BaseInvoiceTestCase):
method setUp (line 41) | def setUp(self):
method test_standard (line 45) | def test_standard(self):
method test_prorate (line 76) | def test_prorate(self):
class TestUserLineItem (line 124) | class TestUserLineItem(BaseInvoiceTestCase):
method setUp (line 128) | def setUp(self):
method test_under_limit (line 133) | def test_under_limit(self):
method test_over_limit (line 167) | def test_over_limit(self):
method test_free_over_limit (line 204) | def test_free_over_limit(self):
class TestFormSubmittingMobileWorkerLineItem (line 248) | class TestFormSubmittingMobileWorkerLineItem(BaseInvoiceTestCase):
method setUp (line 250) | def setUp(self):
method setup_invoice (line 271) | def setup_invoice(self, num_form_submitting_workers):
method _create_worker_history (line 283) | def _create_worker_history(self, history_cls, record_date, num_workers...
method test_under_limit (line 290) | def test_under_limit(self):
method test_over_limit (line 307) | def test_over_limit(self):
class TestWebUserLineItem (line 326) | class TestWebUserLineItem(BaseInvoiceTestCase):
method setUp (line 331) | def setUp(self):
method test_under_limit (line 338) | def test_under_limit(self):
method test_over_limit (line 366) | def test_over_limit(self):
method test_no_line_item_when_bill_web_user_flag_is_false (line 397) | def test_no_line_item_when_bill_web_user_flag_is_false(self):
class TestSmsLineItem (line 416) | class TestSmsLineItem(BaseInvoiceTestCase):
method setUpClass (line 420) | def setUpClass(cls):
method setUp (line 427) | def setUp(self):
method tearDownClass (line 434) | def tearDownClass(cls):
method test_under_limit (line 438) | def test_under_limit(self):
method test_over_limit (line 467) | def test_over_limit(self):
method test_multipart_under_limit (line 499) | def test_multipart_under_limit(self):
method test_multipart_over_limit_and_part_of_the_billable_is_under_limit (line 513) | def test_multipart_over_limit_and_part_of_the_billable_is_under_limit(...
method _create_sms_line_item (line 556) | def _create_sms_line_item(self):
method _create_multipart_billables (line 561) | def _create_multipart_billables(self, total_parts):
method _delete_sms_billables (line 579) | def _delete_sms_billables(cls):
FILE: corehq/apps/accounting/tests/test_invoice_pdf.py
class TestInvoicePdf (line 19) | class TestInvoicePdf(SimpleTestCase):
method test_invoice_template (line 33) | def test_invoice_template(self):
class InvoiceRenderer (line 50) | class InvoiceRenderer:
method iter_invoices (line 76) | def iter_invoices(self):
method iter_all_kwarg_configs (line 121) | def iter_all_kwarg_configs(self):
method iter_multi_page_configs (line 143) | def iter_multi_page_configs(self):
method get_invoice (line 165) | def get_invoice(self, file_or_path, *, is_wire, is_customer, is_prepay...
method get_address (line 203) | def get_address(name):
FILE: corehq/apps/accounting/tests/test_invoicing.py
class TestInvoice (line 46) | class TestInvoice(BaseInvoiceTestCase):
method setUp (line 52) | def setUp(self):
method test_no_invoice_before_start (line 60) | def test_no_invoice_before_start(self):
method test_subscription_invoice (line 67) | def test_subscription_invoice(self):
method test_no_invoice_after_end (line 84) | def test_no_invoice_after_end(self):
method test_no_subscription_no_charges_no_invoice (line 92) | def test_no_subscription_no_charges_no_invoice(self):
method test_free_invoice (line 101) | def test_free_invoice(self):
method test_paused_plan_generates_no_invoice (line 122) | def test_paused_plan_generates_no_invoice(self):
method test_feature_charges (line 139) | def test_feature_charges(self):
method test_date_due_not_set_small_invoice (line 146) | def test_date_due_not_set_small_invoice(self):
method test_date_due_set_large_invoice (line 157) | def test_date_due_set_large_invoice(self):
method test_date_due_gets_set_autopay (line 168) | def test_date_due_gets_set_autopay(self):
class TestContractedInvoices (line 181) | class TestContractedInvoices(BaseInvoiceTestCase):
method setUp (line 183) | def setUp(self):
method test_contracted_invoice_email_recipient (line 194) | def test_contracted_invoice_email_recipient(self):
method test_contracted_invoice_email_template (line 206) | def test_contracted_invoice_email_template(self):
class TestInvoiceRecipients (line 219) | class TestInvoiceRecipients(BaseInvoiceTestCase):
method test_implementation_subscription_with_dimagi_contact (line 221) | def test_implementation_subscription_with_dimagi_contact(self):
method test_implementation_subscription_without_dimagi_contact (line 232) | def test_implementation_subscription_without_dimagi_contact(self):
method test_product_subscription (line 243) | def test_product_subscription(self):
method test_specified_recipients_implementation_with_dimagi_contact (line 255) | def test_specified_recipients_implementation_with_dimagi_contact(self):
method test_specified_recipients_implementation_without_dimagi_contact (line 259) | def test_specified_recipients_implementation_without_dimagi_contact(se...
method test_specified_recipients_product (line 263) | def test_specified_recipients_product(self):
method test_unspecified_recipients_product (line 267) | def test_unspecified_recipients_product(self):
method _setup_implementation_subscription_with_dimagi_contact (line 277) | def _setup_implementation_subscription_with_dimagi_contact(self):
method _setup_implementation_subscription_without_dimagi_contact (line 284) | def _setup_implementation_subscription_without_dimagi_contact(self):
method _setup_product_subscription (line 291) | def _setup_product_subscription(self):
method _setup_product_subscription_with_admin_user (line 297) | def _setup_product_subscription_with_admin_user(self):
method _test_specified_recipients (line 313) | def _test_specified_recipients(self):
class TestInvoicingMethods (line 330) | class TestInvoicingMethods(BaseAccountingTest):
method setUp (line 332) | def setUp(self):
method tearDown (line 356) | def tearDown(self):
method test_should_not_invoice_trial (line 360) | def test_should_not_invoice_trial(self):
method test_should_not_invoice_paused_plan (line 374) | def test_should_not_invoice_paused_plan(self):
method test_should_not_invoice_without_subscription_charges (line 396) | def test_should_not_invoice_without_subscription_charges(self):
method test_should_not_invoice_after_end (line 410) | def test_should_not_invoice_after_end(self):
method test_should_not_invoice_before_start (line 420) | def test_should_not_invoice_before_start(self):
class TestFlaggedPayAnnuallyPrepayInvoice (line 431) | class TestFlaggedPayAnnuallyPrepayInvoice(BaseInvoiceTestCase):
method setUpClass (line 433) | def setUpClass(cls):
method setUp (line 439) | def setUp(self):
method create_invoices (line 449) | def create_invoices(self):
method create_prepayment_invoice (line 454) | def create_prepayment_invoice(self, date_due):
method create_product_credit (line 464) | def create_product_credit(self, amount):
method test_monthly_invoice_product_fully_paid (line 472) | def test_monthly_invoice_product_fully_paid(self):
method test_no_prepayment_invoice (line 479) | def test_no_prepayment_invoice(self):
method test_prepayment_invoice_not_due_yet (line 484) | def test_prepayment_invoice_not_due_yet(self):
method test_matching_prepayment_invoice_exists (line 490) | def test_matching_prepayment_invoice_exists(self):
class TestGetProratedSoftwarePlanCost (line 497) | class TestGetProratedSoftwarePlanCost(SimpleTestCase):
method test_full_month (line 499) | def test_full_month(self):
method test_partial_month (line 507) | def test_partial_month(self):
method test_multiple_months (line 515) | def test_multiple_months(self):
method test_zero_days (line 527) | def test_zero_days(self):
FILE: corehq/apps/accounting/tests/test_migrations.py
class TestExplicitUnpaidSubscriptions (line 18) | class TestExplicitUnpaidSubscriptions(TestCase):
method setUpClass (line 24) | def setUpClass(cls):
method tearDownClass (line 32) | def tearDownClass(cls):
method test_no_preexisting_subscription (line 36) | def test_no_preexisting_subscription(self):
method test_preexisting_current_subscription (line 47) | def test_preexisting_current_subscription(self):
method test_preexisting_future_subscription (line 60) | def test_preexisting_future_subscription(self):
method test_preexisting_past_subscription (line 85) | def test_preexisting_past_subscription(self):
method _assign_unpaid_subscriptions (line 113) | def _assign_unpaid_subscriptions(self):
method _most_recently_created_free_plan_version (line 119) | def _most_recently_created_free_plan_version(self):
method _most_recently_created_paused_plan_version (line 123) | def _most_recently_created_paused_plan_version(self):
method _random_plan_version (line 127) | def _random_plan_version(self):
method _preexisting_subscription_account (line 133) | def _preexisting_subscription_account(self):
FILE: corehq/apps/accounting/tests/test_model_validation.py
class TestCreditAdjustmentValidation (line 17) | class TestCreditAdjustmentValidation(BaseAccountingTest):
method test_clean (line 19) | def test_clean(self):
FILE: corehq/apps/accounting/tests/test_models.py
class TestBillingAccount (line 43) | class TestBillingAccount(BaseAccountingTest):
method setUp (line 45) | def setUp(self):
method test_creation (line 52) | def test_creation(self):
method test_deletions (line 55) | def test_deletions(self):
method test_autopay_user (line 58) | def test_autopay_user(self):
method tearDown (line 74) | def tearDown(self):
class TestSubscription (line 85) | class TestSubscription(BaseAccountingTest):
method setUp (line 87) | def setUp(self):
method test_creation (line 106) | def test_creation(self):
method test_no_activation (line 109) | def test_no_activation(self):
method test_no_activation_date_start_equals_date_end (line 114) | def test_no_activation_date_start_equals_date_end(self):
method test_no_activation_after_date_end (line 121) | def test_no_activation_after_date_end(self):
method test_activation (line 128) | def test_activation(self):
method test_no_deactivation (line 133) | def test_no_deactivation(self):
method test_deactivation (line 139) | def test_deactivation(self):
method test_deletions (line 144) | def test_deletions(self):
method test_is_hidden_to_ops (line 149) | def test_is_hidden_to_ops(self):
method test_next_subscription (line 158) | def test_next_subscription(self):
method test_get_active_domains_for_account (line 174) | def test_get_active_domains_for_account(self):
method tearDown (line 180) | def tearDown(self):
class TestBillingRecord (line 186) | class TestBillingRecord(BaseAccountingTest):
method setUp (line 188) | def setUp(self):
method tearDown (line 215) | def tearDown(self):
method test_should_send_email (line 219) | def test_should_send_email(self):
method test_should_send_email_contracted (line 222) | def test_should_send_email_contracted(self):
method test_should_send_email_autogenerate_credits (line 232) | def test_should_send_email_autogenerate_credits(self):
method test_should_send_email_hidden (line 239) | def test_should_send_email_hidden(self):
class TestCustomerBillingRecord (line 246) | class TestCustomerBillingRecord(BaseAccountingTest):
method setUp (line 248) | def setUp(self):
method tearDown (line 277) | def tearDown(self):
method test_should_send_email (line 281) | def test_should_send_email(self):
method test_should_send_email_hidden (line 284) | def test_should_send_email_hidden(self):
class TestStripePaymentMethod (line 292) | class TestStripePaymentMethod(BaseAccountingTest):
method setUp (line 294) | def setUp(self):
method test_set_autopay (line 313) | def test_set_autopay(self, mock_send_email, fake_customer):
method test_unset_autopay (line 337) | def test_unset_autopay(self, fake_customer):
class SimpleBillingAccountTest (line 349) | class SimpleBillingAccountTest(SimpleTestCase):
method test_has_enterprise_admin_does_case_insensitive_match (line 350) | def test_has_enterprise_admin_does_case_insensitive_match(self):
FILE: corehq/apps/accounting/tests/test_new_domain_subscription.py
class TestNewDomainSubscription (line 18) | class TestNewDomainSubscription(BaseAccountingTest):
method setUp (line 20) | def setUp(self):
method tearDown (line 43) | def tearDown(self):
method test_new_susbscription_in_future (line 48) | def test_new_susbscription_in_future(self):
method test_conflicting_dates (line 78) | def test_conflicting_dates(self):
method test_update_billing_account_entry_point_self_serve (line 123) | def test_update_billing_account_entry_point_self_serve(self):
method test_update_billing_account_entry_point_contracted (line 130) | def test_update_billing_account_entry_point_contracted(self):
method test_dont_update_billing_account_if_set (line 138) | def test_dont_update_billing_account_if_set(self):
method test_exceeding_max_domains_prevents_new_domains (line 149) | def test_exceeding_max_domains_prevents_new_domains(self):
method test_customer_plan_not_added_to_regular_account (line 158) | def test_customer_plan_not_added_to_regular_account(self):
method test_regular_plan_not_added_to_customer_account (line 164) | def test_regular_plan_not_added_to_customer_account(self):
FILE: corehq/apps/accounting/tests/test_race_condition_is_prevented.py
class UniqueConstraintInvoiceTest (line 13) | class UniqueConstraintInvoiceTest(BaseInvoiceTestCase):
method test_unique_constraint_prevents_duplicate_invoice (line 15) | def test_unique_constraint_prevents_duplicate_invoice(self):
class UniqueConstraintCustomerInvoiceTest (line 39) | class UniqueConstraintCustomerInvoiceTest(BaseCustomerInvoiceCase):
method test_unique_constraint_prevents_duplicate_customer_invoice (line 40) | def test_unique_constraint_prevents_duplicate_customer_invoice(self):
FILE: corehq/apps/accounting/tests/test_renew_subscription.py
class TestRenewSubscriptions (line 16) | class TestRenewSubscriptions(BaseAccountingTest):
method setUp (line 18) | def setUp(self):
method tearDown (line 48) | def tearDown(self):
method test_simple_renewal (line 52) | def test_simple_renewal(self):
method test_change_plan_on_renewal (line 59) | def test_change_plan_on_renewal(self):
method test_annual_plan_renewal_length (line 69) | def test_annual_plan_renewal_length(self):
method test_next_subscription_filter (line 83) | def test_next_subscription_filter(self):
method test_next_subscription_filter_no_end_date (line 94) | def test_next_subscription_filter_no_end_date(self):
FILE: corehq/apps/accounting/tests/test_revoke_grants.py
class TestRevokePrivsForGrantees (line 9) | class TestRevokePrivsForGrantees(SimpleTestCase):
method test_specified_priv_for_grantee_is_revoked (line 11) | def test_specified_priv_for_grantee_is_revoked(self):
method test_dry_run_does_not_delete_grants (line 27) | def test_dry_run_does_not_delete_grants(self):
method test_privilege_already_revoked (line 43) | def test_privilege_already_revoked(self):
method test_grantee_does_not_exist (line 62) | def test_grantee_does_not_exist(self):
method test_privilege_does_not_exist (line 74) | def test_privilege_does_not_exist(self):
FILE: corehq/apps/accounting/tests/test_software_plan_version_filter.py
class SoftwarePlanVersionTest (line 13) | class SoftwarePlanVersionTest(BaseAccountingTest):
method setUp (line 15) | def setUp(self):
method test_get_most_recent_version_is_most_recent (line 32) | def test_get_most_recent_version_is_most_recent(self):
function _create_plan_version (line 45) | def _create_plan_version(software_plan):
FILE: corehq/apps/accounting/tests/test_stripe_payment.py
class MockFailingStripeObject (line 17) | class MockFailingStripeObject(object):
method id (line 20) | def id(self):
class TestCreditStripePaymentHandler (line 24) | class TestCreditStripePaymentHandler(TestCase):
method setUpClass (line 27) | def setUpClass(cls):
method tearDownClass (line 38) | def tearDownClass(cls):
method test_working_process_request (line 43) | def test_working_process_request(self, mock_create):
method test_failure_after_checkpoint (line 54) | def test_failure_after_checkpoint(self, mock_create):
method test_when_stripe_errors_no_payment_record_exists (line 64) | def test_when_stripe_errors_no_payment_record_exists(self, mock_create):
method test_when_create_record_fails_stripe_is_not_charged (line 73) | def test_when_create_record_fails_stripe_is_not_charged(self, mock_cre...
method _call_process_request (line 81) | def _call_process_request(self):
FILE: corehq/apps/accounting/tests/test_stripe_payment_method_with_api.py
class TestStripePaymentMethod (line 11) | class TestStripePaymentMethod(BaseAccountingTest):
method setUp (line 13) | def setUp(self):
method test_setup_autopay_for_first_time (line 36) | def test_setup_autopay_for_first_time(self):
method test_replace_card_for_autopay (line 46) | def test_replace_card_for_autopay(self):
method test_same_card_used_by_multiple_billing_accounts (line 68) | def test_same_card_used_by_multiple_billing_accounts(self):
method test_unset_autopay (line 84) | def test_unset_autopay(self):
method test_get_stripe_customer_if_existed (line 95) | def test_get_stripe_customer_if_existed(self):
method test_create_stripe_customer_if_not_existed (line 99) | def test_create_stripe_customer_if_not_existed(self):
method test_all_cards_raise_authentication_error_when_stripe_key_is_wrong (line 106) | def test_all_cards_raise_authentication_error_when_stripe_key_is_wrong...
method test_all_cards_return_the_correct_collection_of_cards_for_a_customer (line 111) | def test_all_cards_return_the_correct_collection_of_cards_for_a_custom...
method test_all_cards_return_empty_array_for_customer_have_no_cards (line 122) | def test_all_cards_return_empty_array_for_customer_have_no_cards(self):
method test_all_cards_return_empty_array_if_no_stripe_key (line 129) | def test_all_cards_return_empty_array_if_no_stripe_key(self):
method test_all_cards_serialized_return_the_correct_property_of_a_card (line 134) | def test_all_cards_serialized_return_the_correct_property_of_a_card(se...
method test_get_card_return_the_correct_card_object (line 145) | def test_get_card_return_the_correct_card_object(self):
method test_get_autopay_card_when_no_autopay_card (line 150) | def test_get_autopay_card_when_no_autopay_card(self):
method test_get_autopay_card_when_only_one_autopay (line 156) | def test_get_autopay_card_when_only_one_autopay(self):
method test_get_autopay_card_when_one_of_many_card_is_autopay (line 161) | def test_get_autopay_card_when_one_of_many_card_is_autopay(self):
method test_remove_card_successful (line 173) | def test_remove_card_successful(self):
method test_remove_card_non_existent (line 184) | def test_remove_card_non_existent(self):
method test_create_card_creates_card (line 188) | def test_create_card_creates_card(self):
method test_create_charge_success (line 194) | def test_create_charge_success(self):
method test_create_charge_with_idempotency_key (line 210) | def test_create_charge_with_idempotency_key(self):
FILE: corehq/apps/accounting/tests/test_stripe_utils_with_api.py
class StripeUtilsTests (line 17) | class StripeUtilsTests(TestCase):
method setUp (line 19) | def setUp(self):
method test_get_customer_cards (line 43) | def test_get_customer_cards(self):
method test_charge_through_stripe_successful (line 51) | def test_charge_through_stripe_successful(self):
FILE: corehq/apps/accounting/tests/test_subscription_changes.py
class TestSubscriptionEmailLogic (line 51) | class TestSubscriptionEmailLogic(SimpleTestCase):
method test_new_trial_with_no_previous (line 53) | def test_new_trial_with_no_previous(self):
method test_non_trial_with_no_previous (line 56) | def test_non_trial_with_no_previous(self):
method test_non_trial_with_previous (line 59) | def test_non_trial_with_previous(self):
method _run_test (line 63) | def _run_test(self, old_sub, new_sub, expected_output):
class TestUserRoleSubscriptionChanges (line 67) | class TestUserRoleSubscriptionChanges(BaseAccountingTest):
method setUp (line 69) | def setUp(self):
method test_cancellation (line 118) | def test_cancellation(self):
method test_resubscription (line 132) | def test_resubscription(self):
method _change_std_roles (line 148) | def _change_std_roles(self):
method _assertInitialRoles (line 165) | def _assertInitialRoles(self):
method _assertStdUsers (line 173) | def _assertStdUsers(self):
method tearDown (line 188) | def tearDown(self):
class TestSubscriptionChangeResourceConflict (line 194) | class TestSubscriptionChangeResourceConflict(BaseAccountingTest):
method setUp (line 196) | def setUp(self):
method tearDown (line 205) | def tearDown(self):
method test_domain_changes (line 209) | def test_domain_changes(self):
class TestSoftwarePlanChanges (line 229) | class TestSoftwarePlanChanges(BaseAccountingTest):
method setUp (line 231) | def setUp(self):
method tearDown (line 251) | def tearDown(self):
method test_change_plan_blocks_on_max_domains (line 256) | def test_change_plan_blocks_on_max_domains(self):
class DeactivateScheduleTest (line 267) | class DeactivateScheduleTest(TransactionTestCase):
method setUp (line 269) | def setUp(self):
method create_survey_content (line 302) | def create_survey_content(self):
method create_scheduled_broadcast (line 309) | def create_scheduled_broadcast(self, domain, content):
method create_immediate_broadcast (line 319) | def create_immediate_broadcast(self, domain, content):
method create_conditional_alert (line 328) | def create_conditional_alert(self, domain, content):
method tearDown (line 338) | def tearDown(self):
method assertScheduleActiveFlag (line 360) | def assertScheduleActiveFlag(self, obj, active_flag):
method assertSchedulesActive (line 372) | def assertSchedulesActive(self, objects):
method assertSchedulesInactive (line 376) | def assertSchedulesInactive(self, objects):
method test_deactivate_all_schedules (line 380) | def test_deactivate_all_schedules(self):
method test_deactivate_only_survey_schedules (line 428) | def test_deactivate_only_survey_schedules(self):
class TestUsercaseSubscriptionChanges (line 459) | class TestUsercaseSubscriptionChanges(TestCase):
method test_upgrade (line 463) | def test_upgrade(self, mock_create_usercases):
FILE: corehq/apps/accounting/tests/test_subscription_permissions_changes.py
class TestSubscriptionPermissionsChanges (line 29) | class TestSubscriptionPermissionsChanges(BaseAccountingTest):
method setUp (line 31) | def setUp(self):
method _init_pro_with_rb_plan_and_version (line 44) | def _init_pro_with_rb_plan_and_version(self):
method _subscribe_to_advanced (line 72) | def _subscribe_to_advanced(self):
method _subscribe_to_pro_with_rb (line 78) | def _subscribe_to_pro_with_rb(self):
method test_app_icon_permissions (line 91) | def test_app_icon_permissions(self):
method test_report_builder_datasource_deactivation (line 137) | def test_report_builder_datasource_deactivation(self):
FILE: corehq/apps/accounting/tests/test_task_utils.py
class TestGetContextToSendAutopayFailedEmail (line 15) | class TestGetContextToSendAutopayFailedEmail(SimpleTestCase):
method test_context_contains_expected_keys (line 17) | def test_context_contains_expected_keys(self):
method test_recipient_is_autopay_email_if_no_web_user_exists (line 39) | def test_recipient_is_autopay_email_if_no_web_user_exists(self):
method test_recipient_is_web_users_email_if_web_user_exists (line 47) | def test_recipient_is_web_users_email_if_web_user_exists(self):
method test_billing_url_is_correct (line 55) | def test_billing_url_is_correct(self):
method test_domain_url_is_correct (line 66) | def test_domain_url_is_correct(self):
class MockAutopayFailedInfo (line 76) | class MockAutopayFailedInfo:
method __init__ (line 77) | def __init__(self, invoice_id, web_user_email=None, auto_payer=None, s...
method __enter__ (line 87) | def __enter__(self):
method __exit__ (line 135) | def __exit__(self, exc_type, exc_val, exc_tb):
class TestGetContextToSendPurchaseReceipt (line 142) | class TestGetContextToSendPurchaseReceipt(SimpleTestCase):
method test_context_has_expected_keys (line 144) | def test_context_has_expected_keys(self):
method test_template_context_has_expected_keys (line 158) | def test_template_context_has_expected_keys(self):
method test_template_context_has_additional_keys_if_supplied (line 172) | def test_template_context_has_additional_keys_if_supplied(self):
method test_recipient_is_web_user_if_web_user_exists (line 188) | def test_recipient_is_web_user_if_web_user_exists(self):
method test_recipient_is_username_if_web_user_does_not_exist (line 203) | def test_recipient_is_username_if_web_user_does_not_exist(self):
method test_exception_is_logged_if_web_user_does_not_exist (line 218) | def test_exception_is_logged_if_web_user_does_not_exist(self):
method test_returns_account_name_if_no_domain_name (line 233) | def test_returns_account_name_if_no_domain_name(self):
class MockPurchaseReceiptInfo (line 249) | class MockPurchaseReceiptInfo:
method __init__ (line 251) | def __init__(self, username, web_user_email=None, account_name=None, d...
method __enter__ (line 259) | def __enter__(self):
method __exit__ (line 292) | def __exit__(self, exc_type, exc_val, exc_tb):
FILE: corehq/apps/accounting/tests/test_tasks.py
class TestCalculateFormSubmittingMobileWorkers (line 26) | class TestCalculateFormSubmittingMobileWorkers(BaseInvoiceTestCase):
method setUp (line 28) | def setUp(self):
method _submit_form (line 41) | def _submit_form(self, user, received_on):
method tearDown (line 51) | def tearDown(self):
method test_calculate_form_submitting_mobile_workers_in_all_domains (line 56) | def test_calculate_form_submitting_mobile_workers_in_all_domains(self):
class TestSubscriptionReminderEmails (line 67) | class TestSubscriptionReminderEmails(BaseInvoiceTestCase):
method test_sends_renewal_reminder_email (line 69) | def test_sends_renewal_reminder_email(self):
method test_no_renewal_reminder_if_service_type_not_product (line 78) | def test_no_renewal_reminder_if_service_type_not_product(self):
method test_no_renewal_reminder_if_is_trial (line 87) | def test_no_renewal_reminder_if_is_trial(self):
method test_no_renewal_reminder_if_customer_billing_account (line 96) | def test_no_renewal_reminder_if_customer_billing_account(self):
method test_no_renewal_reminder_if_is_renewed (line 106) | def test_no_renewal_reminder_if_is_renewed(self):
method test_sends_subscription_ending_email (line 115) | def test_sends_subscription_ending_email(self):
method test_no_subscription_ending_email_if_auto_renew_enabled (line 124) | def test_no_subscription_ending_email_if_auto_renew_enabled(self):
method test_no_subscription_ending_email_if_service_type_not_product (line 134) | def test_no_subscription_ending_email_if_service_type_not_product(self):
method test_no_subscription_ending_email_if_is_trial (line 143) | def test_no_subscription_ending_email_if_is_trial(self):
method test_no_subscription_ending_email_if_customer_billing_account (line 152) | def test_no_subscription_ending_email_if_customer_billing_account(self):
method test_no_subscription_ending_email_if_is_renewed (line 162) | def test_no_subscription_ending_email_if_is_renewed(self):
class TestAutoRenewableSubscriptions (line 173) | class TestAutoRenewableSubscriptions(BaseInvoiceTestCase):
method test_includes_subscription_exactly_30_days_left (line 175) | def test_includes_subscription_exactly_30_days_left(self):
method test_includes_subscription_less_than_30_days_left (line 183) | def test_includes_subscription_less_than_30_days_left(self):
method test_excludes_subscription_more_than_30_days_left (line 188) | def test_excludes_subscription_more_than_30_days_left(self):
method test_excludes_subscription_end_date_in_past (line 196) | def test_excludes_subscription_end_date_in_past(self):
method test_excludes_auto_renew_false (line 204) | def test_excludes_auto_renew_false(self):
method test_excludes_service_type_not_product (line 209) | def test_excludes_service_type_not_product(self):
method test_excludes_customer_billing_account (line 214) | def test_excludes_customer_billing_account(self):
method test_filters_by_domain (line 219) | def test_filters_by_domain(self):
method test_auto_renew_ignores_already_renewed (line 238) | def test_auto_renew_ignores_already_renewed(self):
method _set_auto_renew_properties (line 246) | def _set_auto_renew_properties(
class TestAutoRenewSubscription (line 264) | class TestAutoRenewSubscription(BaseInvoiceTestCase):
method setUp (line 266) | def setUp(self):
method test_auto_renew_subscription (line 276) | def test_auto_renew_subscription(self):
method test_creates_prepayment_invoice_if_is_annual_plan (line 294) | def test_creates_prepayment_invoice_if_is_annual_plan(self):
FILE: corehq/apps/accounting/tests/test_unpaid_invoice.py
function _generate_invoice_and_subscription (line 27) | def _generate_invoice_and_subscription(days_ago, is_customer_billing_acc...
class TestDowngrades (line 81) | class TestDowngrades(BaseAccountingTest):
method setUpClass (line 84) | def setUpClass(cls):
method setUp (line 89) | def setUp(self):
method tearDown (line 93) | def tearDown(self):
method tearDownClass (line 101) | def tearDownClass(cls):
method _simulate_downgrade (line 105) | def _simulate_downgrade(self, days_overdue, is_customer_billing_accoun...
method test_no_notification (line 114) | def test_no_notification(self):
method test_overdue_notification (line 122) | def test_overdue_notification(self):
method test_belated_overdue_notification (line 140) | def test_belated_overdue_notification(self):
method test_downgrade_warning (line 153) | def test_downgrade_warning(self):
method test_downgrade (line 171) | def test_downgrade(self):
method test_overdue_customer_notification (line 194) | def test_overdue_customer_notification(self):
method test_overdue_customer_downgrade_warning (line 205) | def test_overdue_customer_downgrade_warning(self):
class TestInvoiceReminder (line 217) | class TestInvoiceReminder(BaseAccountingTest):
method setUpClass (line 220) | def setUpClass(cls):
method setUp (line 225) | def setUp(self):
method tearDown (line 229) | def tearDown(self):
method tearDownClass (line 237) | def tearDownClass(cls):
method _send_invoice_reminders (line 241) | def _send_invoice_reminders(self, days_before_due, is_customer_billing...
method _setup_invoices (line 247) | def _setup_invoices(self, days_before_due, is_customer_billing_account...
method _run_action (line 256) | def _run_action():
method test_invoice_reminder (line 259) | def test_invoice_reminder(self):
method test_belated_invoice_reminder (line 277) | def test_belated_invoice_reminder(self):
method test_invoice_reminder_not_sent_early (line 287) | def test_invoice_reminder_not_sent_early(self):
method test_no_reminder_if_not_should_send_email (line 297) | def test_no_reminder_if_not_should_send_email(self):
method test_no_reminder_if_skipped_email (line 308) | def test_no_reminder_if_skipped_email(self):
method test_customer_invoice_reminder (line 322) | def test_customer_invoice_reminder(self):
method test_belated_customer_invoice_reminder (line 341) | def test_belated_customer_invoice_reminder(self):
method test_customer_invoice_reminder_not_sent_early (line 352) | def test_customer_invoice_reminder_not_sent_early(self):
FILE: corehq/apps/accounting/tests/test_utils.py
class TestIsDateRangeOverlapping (line 8) | class TestIsDateRangeOverlapping(SimpleTestCase):
method test_first_range_is_contained_in_second_range (line 9) | def test_first_range_is_contained_in_second_range(self):
method test_second_range_is_contained_in_first_range (line 13) | def test_second_range_is_contained_in_first_range(self):
method test_partial_overlap_start (line 17) | def test_partial_overlap_start(self):
method test_partial_overlap_end (line 21) | def test_partial_overlap_end(self):
method test_exact_overlap (line 25) | def test_exact_overlap(self):
method test_no_overlap_before (line 29) | def test_no_overlap_before(self):
method test_no_overlap_after (line 33) | def test_no_overlap_after(self):
method test_adjacent_ranges_do_not_overlap (line 37) | def test_adjacent_ranges_do_not_overlap(self):
method test_same_start_date_is_overlap (line 46) | def test_same_start_date_is_overlap(self):
method test_same_end_date_is_overlap (line 50) | def test_same_end_date_is_overlap(self):
method test_first_range_infinite_end (line 54) | def test_first_range_infinite_end(self):
method test_second_range_infinite_end (line 58) | def test_second_range_infinite_end(self):
method test_both_ranges_infinite_end (line 62) | def test_both_ranges_infinite_end(self):
method test_first_range_infinite_end_but_start_after_second_range_end (line 66) | def test_first_range_infinite_end_but_start_after_second_range_end(self):
method test_second_range_infinite_end_but_start_after_first_range_end (line 70) | def test_second_range_infinite_end_but_start_after_first_range_end(self):
FILE: corehq/apps/accounting/tests/test_wire_invoice.py
class TestWireInvoice (line 11) | class TestWireInvoice(BaseInvoiceTestCase):
method setUp (line 13) | def setUp(self):
method test_factory (line 23) | def test_factory(self):
class TestCustomerAccountWireInvoice (line 38) | class TestCustomerAccountWireInvoice(BaseInvoiceTestCase):
method setUp (line 40) | def setUp(self):
method test_factory (line 51) | def test_factory(self):
FILE: corehq/apps/accounting/tests/utils.py
class DomainSubscriptionMixin (line 19) | class DomainSubscriptionMixin(object):
method setup_subscription (line 28) | def setup_subscription(cls, domain_name, software_plan, use_annual_pla...
method teardown_subscriptions (line 51) | def teardown_subscriptions(cls):
method teardown_subscription (line 56) | def teardown_subscription(cls, domain):
function mocked_stripe_api (line 67) | def mocked_stripe_api():
function setup_stripe_common_mocks (line 74) | def setup_stripe_common_mocks(mock_list_sources, mock_modify_source):
FILE: corehq/apps/accounting/usage.py
class FeatureUsageCalculator (line 11) | class FeatureUsageCalculator(object):
method __init__ (line 13) | def __init__(self, feature_rate, domain_name,
method usage_fns (line 26) | def usage_fns(self):
method get_usage (line 34) | def get_usage(self):
method _get_user_usage (line 41) | def _get_user_usage(self):
method _get_sms_usage (line 44) | def _get_sms_usage(self):
method _get_web_user_usage (line 52) | def _get_web_user_usage(self):
method _get_form_submitting_mobile_worker_user_usage (line 55) | def _get_form_submitting_mobile_worker_user_usage(self):
function get_web_user_usage (line 59) | def get_web_user_usage(domains):
FILE: corehq/apps/accounting/user_text.py
function get_feature_name (line 59) | def get_feature_name(feature_type):
function get_feature_recurring_interval (line 65) | def get_feature_recurring_interval(feature_type):
FILE: corehq/apps/accounting/utils/__init__.py
function log_accounting_error (line 29) | def log_accounting_error(message, show_stack_trace=False):
function log_accounting_info (line 33) | def log_accounting_info(message):
function get_first_day_x_months_later (line 37) | def get_first_day_x_months_later(reference_date, months_from_date):
function ensure_domain_instance (line 42) | def ensure_domain_instance(domain):
function fmt_feature_rate_dict (line 48) | def fmt_feature_rate_dict(feature, feature_rate=None):
function fmt_product_rate_dict (line 65) | def fmt_product_rate_dict(product_name, product_rate=None):
function get_privileges (line 86) | def get_privileges(plan_version):
function get_change_status (line 94) | def get_change_status(from_plan_version, to_plan_version):
function domain_has_privilege_cache_args (line 118) | def domain_has_privilege_cache_args(domain, privilege_slug, **assignment):
function domain_has_privilege (line 126) | def domain_has_privilege(domain, privilege_slug, **assignment):
function get_domains_with_privilege (line 144) | def get_domains_with_privilege(privilege_slug):
function domain_is_on_trial (line 152) | def domain_is_on_trial(domain_name):
function is_active_subscription (line 158) | def is_active_subscription(date_start, date_end, today=None):
function has_subscription_already_ended (line 164) | def has_subscription_already_ended(subscription):
function get_money_str (line 169) | def get_money_str(amount):
function get_address_from_invoice (line 180) | def get_address_from_invoice(invoice):
function get_dimagi_from_email (line 207) | def get_dimagi_from_email():
function quantize_accounting_decimal (line 213) | def quantize_accounting_decimal(decimal_value):
function fmt_dollar_amount (line 217) | def fmt_dollar_amount(decimal_value):
function is_accounting_admin (line 221) | def is_accounting_admin(user):
function make_anchor_tag (line 231) | def make_anchor_tag(href, name, attrs=None):
function get_default_domain_url (line 240) | def get_default_domain_url(domain):
function get_all_roles_by_slug (line 248) | def get_all_roles_by_slug():
function get_granted_privs_for_grantee (line 252) | def get_granted_privs_for_grantee():
function ensure_grants (line 264) | def ensure_grants(privs_to_ensure_for_grantee, dry_run=False, verbose=Fa...
function revoke_privs_for_grantees (line 306) | def revoke_privs_for_grantees(privs_for_grantees, dry_run=False, verbose...
function get_grants (line 341) | def get_grants(from_role, to_role):
function delete_grants (line 348) | def delete_grants(grants):
function log_removed_grants (line 354) | def log_removed_grants(priv_slugs, dry_run=False):
function get_account_name_from_default_name (line 363) | def get_account_name_from_default_name(default_name):
function cancel_future_subscriptions (line 377) | def cancel_future_subscriptions(domain_name, from_date, web_user):
function pause_current_subscription (line 397) | def pause_current_subscription(domain_name, web_user, current_subscripti...
function is_downgrade (line 441) | def is_downgrade(current_edition, next_edition):
function clear_plan_version_cache (line 447) | def clear_plan_version_cache():
function get_paused_plan_context (line 453) | def get_paused_plan_context(request, domain):
function get_pending_plan_context (line 475) | def get_pending_plan_context(request, domain):
function count_form_submitting_mobile_workers (line 493) | def count_form_submitting_mobile_workers(domain, start, end):
function self_signup_workflow_in_progress (line 510) | def self_signup_workflow_in_progress(domain):
function is_date_range_overlapping (line 515) | def is_date_range_overlapping(start_1, end_1, start_2, end_2):
FILE: corehq/apps/accounting/utils/account.py
function get_account_or_404 (line 8) | def get_account_or_404(domain):
function request_has_permissions_for_enterprise_admin (line 15) | def request_has_permissions_for_enterprise_admin(request, account):
FILE: corehq/apps/accounting/utils/cards.py
function get_autopay_card_and_owner_for_billing_account (line 6) | def get_autopay_card_and_owner_for_billing_account(account):
function set_card_as_autopay_for_billing_account (line 18) | def set_card_as_autopay_for_billing_account(payment_method, card_token, ...
function get_payment_method_for_user (line 23) | def get_payment_method_for_user(username):
function get_saved_cards_for_user (line 31) | def get_saved_cards_for_user(username, account):
function serialize_account_card (line 39) | def serialize_account_card(card, owner):
FILE: corehq/apps/accounting/utils/invoicing.py
function _get_all_unpaid_saas_invoices (line 23) | def _get_all_unpaid_saas_invoices():
function get_domains_with_subscription_invoices_overdue (line 32) | def get_domains_with_subscription_invoices_overdue(today):
function get_domains_with_subscription_invoices_due_soon (line 37) | def get_domains_with_subscription_invoices_due_soon(today):
function _get_unpaid_saas_invoices_in_downgrade_daterange (line 42) | def _get_unpaid_saas_invoices_in_downgrade_daterange(today):
function _get_unpaid_saas_invoices_in_reminder_daterange (line 48) | def _get_unpaid_saas_invoices_in_reminder_daterange(today):
function _get_domains_over_threshold (line 57) | def _get_domains_over_threshold(invoices, today, get_oldest_invoice_fn):
function get_oldest_overdue_invoice_over_threshold (line 66) | def get_oldest_overdue_invoice_over_threshold(today, domain):
function get_oldest_due_soon_invoice_over_threshold (line 71) | def get_oldest_due_soon_invoice_over_threshold(today, domain):
function _get_oldest_invoice_over_threshold (line 76) | def _get_oldest_invoice_over_threshold(domain, invoices):
function get_accounts_with_customer_invoices_due_soon (line 90) | def get_accounts_with_customer_invoices_due_soon(today):
function get_accounts_with_customer_invoices_overdue (line 96) | def get_accounts_with_customer_invoices_overdue(today):
function _get_accounts_over_threshold_in_daterange (line 102) | def _get_accounts_over_threshold_in_daterange(date_start, date_end):
function get_flagged_pay_annually_prepay_invoice (line 130) | def get_flagged_pay_annually_prepay_invoice(invoice):
function get_prorated_software_plan_cost (line 152) | def get_prorated_software_plan_cost(date_start, date_end, monthly_fee):
function get_next_due_invoice (line 172) | def get_next_due_invoice(subscription, today):
function get_next_due_customer_invoice (line 181) | def get_next_due_customer_invoice(account, today, subscription=None):
FILE: corehq/apps/accounting/utils/software_plans.py
function upgrade_subscriptions_to_latest_plan_version (line 4) | def upgrade_subscriptions_to_latest_plan_version(old_plan_version, web_u...
FILE: corehq/apps/accounting/utils/stripe.py
function get_customer_cards (line 10) | def get_customer_cards(username):
function charge_through_stripe (line 33) | def charge_through_stripe(card, customer, amount_in_dollars, currency, d...
FILE: corehq/apps/accounting/utils/subscription.py
function assign_explicit_unpaid_subscription (line 12) | def assign_explicit_unpaid_subscription(domain_name, start_date, method,...
function ensure_free_or_paused_subscription (line 47) | def ensure_free_or_paused_subscription(domain_name, from_date, method, w...
function is_domain_enterprise (line 67) | def is_domain_enterprise(domain):
FILE: corehq/apps/accounting/utils/unpaid_invoice.py
class UnpaidInvoiceAction (line 40) | class UnpaidInvoiceAction:
method run_action (line 42) | def run_action(cls, only_downgrade_domain=None):
method _apply_process (line 75) | def _apply_process(cls, oldest_unpaid_invoice, total, today, subscript...
method _get_communication_model_context (line 82) | def _get_communication_model_context(domain, oldest_unpaid_invoice):
class InvoiceReminder (line 109) | class InvoiceReminder(UnpaidInvoiceAction):
method is_subscription_eligible_for_process (line 115) | def is_subscription_eligible_for_process(subscription):
method _check_and_perform_action (line 119) | def _check_and_perform_action(cls, communication_model, context,
method _should_send_invoice_reminder (line 126) | def _should_send_invoice_reminder(communication_model, invoice):
method _send_reminder_email (line 142) | def _send_reminder_email(invoice, communication_model, context):
method _update_email_context (line 174) | def _update_email_context(context, invoice, total, today, subscription):
class Downgrade (line 201) | class Downgrade(UnpaidInvoiceAction):
method is_subscription_eligible_for_process (line 207) | def is_subscription_eligible_for_process(subscription):
method _check_and_perform_action (line 216) | def _check_and_perform_action(cls, communication_model, context,
method _can_trigger_downgrade (line 244) | def _can_trigger_downgrade(today, days_ago, communication_model, invoi...
method _can_send_downgrade_warning (line 263) | def _can_send_downgrade_warning(days_ago, communication_model, invoice):
method _can_send_overdue_notification (line 272) | def _can_send_overdue_notification(days_ago, communication_model, invo...
method _downgrade_domain (line 281) | def _downgrade_domain(subscription):
method _send_downgrade_notice (line 292) | def _send_downgrade_notice(invoice, context):
method _send_downgrade_warning (line 304) | def _send_downgrade_warning(invoice, communication_model, context):
method _send_overdue_notice (line 338) | def _send_overdue_notice(invoice, communication_model, context):
function can_domain_unpause (line 355) | def can_domain_unpause(domain):
FILE: corehq/apps/accounting/views.py
function accounting_default (line 133) | def accounting_default(request):
class AccountingSectionView (line 137) | class AccountingSectionView(BaseSectionPageView):
method section_url (line 141) | def section_url(self):
method dispatch (line 146) | def dispatch(self, request, *args, **kwargs):
class BillingAccountsSectionView (line 150) | class BillingAccountsSectionView(AccountingSectionView):
method parent_pages (line 153) | def parent_pages(self):
class NewBillingAccountView (line 160) | class NewBillingAccountView(BillingAccountsSectionView):
method account_form (line 167) | def account_form(self):
method page_context (line 173) | def page_context(self):
method page_url (line 179) | def page_url(self):
method post (line 182) | def post(self, request, *args, **kwargs):
class ManageBillingAccountView (line 190) | class ManageBillingAccountView(BillingAccountsSectionView, AsyncHandlerM...
method account (line 200) | def account(self):
method basic_account_form (line 208) | def basic_account_form(self):
method contact_form (line 216) | def contact_form(self):
method credit_form (line 224) | def credit_form(self):
method remove_autopay_form (line 232) | def remove_autopay_form(self):
method page_context (line 238) | def page_context(self):
method page_url (line 263) | def page_url(self):
method post (line 266) | def post(self, request, *args, **kwargs):
class NewSubscriptionView (line 297) | class NewSubscriptionView(AccountingSectionView, AsyncHandlerMixin):
method account_id (line 307) | def account_id(self):
method subscription_form (line 312) | def subscription_form(self):
method page_context (line 321) | def page_context(self):
method page_url (line 327) | def page_url(self):
method parent_pages (line 331) | def parent_pages(self):
method post (line 337) | def post(self, request, *args, **kwargs):
class NewSubscriptionViewNoDefaultDomain (line 353) | class NewSubscriptionViewNoDefaultDomain(NewSubscriptionView):
method account_id (line 358) | def account_id(self):
method page_url (line 362) | def page_url(self):
class EditSubscriptionView (line 366) | class EditSubscriptionView(AccountingSectionView, AsyncHandlerMixin):
method subscription_id (line 376) | def subscription_id(self):
method subscription (line 381) | def subscription(self):
method subscription_form (line 389) | def subscription_form(self):
method change_subscription_form (line 399) | def change_subscription_form(self):
method credit_form (line 411) | def credit_form(self):
method cancel_form (line 419) | def cancel_form(self):
method suppress_form (line 426) | def suppress_form(self):
method invoice_context (line 432) | def invoice_context(self):
method page_context (line 453) | def page_context(self):
method page_url (line 476) | def page_url(self):
method parent_pages (line 480) | def parent_pages(self):
method post (line 486) | def post(self, request, *args, **kwargs):
method cancel_subscription (line 516) | def cancel_subscription(self):
class NewSoftwarePlanView (line 525) | class NewSoftwarePlanView(AccountingSectionView):
method plan_info_form (line 532) | def plan_info_form(self):
method page_context (line 538) | def page_context(self):
method page_url (line 544) | def page_url(self):
method parent_pages (line 548) | def parent_pages(self):
method post (line 554) | def post(self, request, *args, **kwargs):
class EditSoftwarePlanView (line 561) | class EditSoftwarePlanView(AccountingSectionView, AsyncHandlerMixin):
method plan (line 573) | def plan(self):
method plan_info_form (line 581) | def plan_info_form(self):
method software_plan_version_form (line 588) | def software_plan_version_form(self):
method page_context (line 608) | def page_context(self):
method page_url (line 618) | def page_url(self):
method parent_pages (line 622) | def parent_pages(self):
method post (line 628) | def post(self, request, *args, **kwargs):
class SoftwarePlanVersionView (line 643) | class SoftwarePlanVersionView(AccountingSectionView):
method post (line 648) | def post(self, request, *args, **kwargs):
method plan_version (line 660) | def plan_version(self):
method page_context (line 667) | def page_context(self):
method page_url (line 692) | def page_url(self):
method upgrade_subscriptions_form (line 697) | def upgrade_subscriptions_form(self):
class TriggerInvoiceView (line 709) | class TriggerInvoiceView(AccountingSectionView, AsyncHandlerMixin):
method is_testing_enabled (line 719) | def is_testing_enabled(self):
method trigger_form (line 724) | def trigger_form(self):
method page_url (line 733) | def page_url(self):
method page_context (line 737) | def page_context(self):
method post (line 742) | def post(self, request, *args, **kwargs):
class TriggerCustomerInvoiceView (line 757) | class TriggerCustomerInvoiceView(AccountingSectionView, AsyncHandlerMixin):
method trigger_customer_invoice_form (line 767) | def trigger_customer_invoice_form(self):
method page_url (line 773) | def page_url(self):
method page_context (line 777) | def page_context(self):
method post (line 782) | def post(self, request, *args, **kwargs):
class TriggerBookkeeperEmailView (line 798) | class TriggerBookkeeperEmailView(AccountingSectionView):
method trigger_email_form (line 805) | def trigger_email_form(self):
method page_url (line 811) | def page_url(self):
method page_context (line 815) | def page_context(self):
method post (line 820) | def post(self, request, *args, **kwargs):
class TestRenewalEmailView (line 828) | class TestRenewalEmailView(AccountingSectionView):
method reminder_email_form (line 835) | def reminder_email_form(self):
method page_url (line 841) | def page_url(self):
method page_context (line 845) | def page_context(self):
method post (line 850) | def post(self, request, *args, **kwargs):
class InvoiceSummaryViewBase (line 858) | class InvoiceSummaryViewBase(AccountingSectionView):
method invoice (line 863) | def invoice(self):
method page_title (line 870) | def page_title(self):
method page_url (line 874) | def page_url(self):
method page_context (line 878) | def page_context(self):
method billing_records (line 898) | def billing_records(self):
method can_send_email (line 902) | def can_send_email(self):
method resend_email_form (line 907) | def resend_email_form(self):
method invoice_info_form (line 914) | def invoice_info_form(self):
method suppress_invoice_form (line 919) | def suppress_invoice_form(self):
method hide_invoice_form (line 926) | def hide_invoice_form(self):
method post (line 931) | def post(self, request, *args, **kwargs):
class WireInvoiceSummaryView (line 959) | class WireInvoiceSummaryView(InvoiceSummaryViewBase):
method parent_pages (line 964) | def parent_pages(self):
method billing_records (line 972) | def billing_records(self):
method can_send_email (line 976) | def can_send_email(self):
class InvoiceSummaryView (line 980) | class InvoiceSummaryView(InvoiceSummaryViewBase):
method parent_pages (line 985) | def parent_pages(self):
method adjust_balance_form (line 993) | def adjust_balance_form(self):
method billing_records (line 1000) | def billing_records(self):
method adjustment_list (line 1005) | def adjustment_list(self):
method can_send_email (line 1010) | def can_send_email(self):
method page_context (line 1014) | def page_context(self):
class CustomerInvoiceSummaryView (line 1023) | class CustomerInvoiceSummaryView(InvoiceSummaryViewBase):
method parent_pages (line 1028) | def parent_pages(self):
method adjust_balance_form (line 1036) | def adjust_balance_form(self):
method billing_records (line 1043) | def billing_records(self):
method adjustment_list (line 1048) | def adjustment_list(self):
method can_send_email (line 1053) | def can_send_email(self):
method page_context (line 1057) | def page_context(self):
class CustomerInvoicePdfView (line 1066) | class CustomerInvoicePdfView(View):
method dispatch (line 1069) | def dispatch(self, request, *args, **kwargs):
method get (line 1072) | def get(self, request, *args, **kwargs):
class ManageAccountingAdminsView (line 1105) | class ManageAccountingAdminsView(AccountingSectionView, CRUDPaginatedVie...
method page_url (line 1118) | def page_url(self):
method page_context (line 1122) | def page_context(self):
method accounting_admin_queryset (line 1126) | def accounting_admin_queryset(self):
method paginated_admins (line 1131) | def paginated_admins(self):
method total (line 1136) | def total(self):
method column_names (line 1140) | def column_names(self):
method paginated_list (line 1147) | def paginated_list(self):
method _fmt_admin_data (line 1155) | def _fmt_admin_data(admin):
method get_create_form (line 1161) | def get_create_form(self, is_blank=False):
method get_create_item_data (line 1166) | def get_create_item_data(self, create_form):
method delete_item (line 1178) | def delete_item(self, item_id):
method post (line 1191) | def post(self, *args, **kwargs):
class AccountingSingleOptionResponseView (line 1195) | class AccountingSingleOptionResponseView(View, AsyncHandlerMixin):
method dispatch (line 1212) | def dispatch(self, request, *args, **kwargs):
method post (line 1215) | def post(self, request, *args, **kwargs):
class BaseTriggerAccountingTestView (line 1222) | class BaseTriggerAccountingTestView(AccountingSectionView, AsyncHandlerM...
method trigger_form (line 1230) | def trigger_form(self):
method page_url (line 1234) | def page_url(self):
method page_context (line 1238) | def page_context(self):
class TriggerDowngradeView (line 1244) | class TriggerDowngradeView(BaseTriggerAccountingTestView):
method trigger_form (line 1250) | def trigger_form(self):
method post (line 1255) | def post(self, request, *args, **kwargs):
class TriggerAutopaymentsView (line 1280) | class TriggerAutopaymentsView(BaseTriggerAccountingTestView):
method trigger_form (line 1286) | def trigger_form(self):
method post (line 1291) | def post(self, request, *args, **kwargs):
class TriggerAutoRenewalView (line 1312) | class TriggerAutoRenewalView(BaseTriggerAccountingTestView):
method trigger_form (line 1318) | def trigger_form(self):
method post (line 1323) | def post(self, request, *args, **kwargs):
class TriggerRemovedSsoUserAutoDeactivationView (line 1338) | class TriggerRemovedSsoUserAutoDeactivationView(BaseTriggerAccountingTes...
method trigger_form (line 1344) | def trigger_form(self):
method post (line 1349) | def post(self, request, *args, **kwargs):
FILE: corehq/apps/analytics/ab_tests.py
class SessionAbTestConfig (line 12) | class SessionAbTestConfig(object):
method __init__ (line 14) | def __init__(self, name, slug, options, is_debug=False, force_refresh=...
class SessionAbTest (line 32) | class SessionAbTest(object):
method __init__ (line 39) | def __init__(self, config, request):
method _cookie_id (line 54) | def _cookie_id(self):
method _cache_id (line 58) | def _cache_id(self):
method version (line 62) | def version(self, assign_if_blank=True):
method cache_version (line 77) | def cache_version(self, version):
method _clear_cache (line 83) | def _clear_cache(self):
method _debug_message (line 87) | def _debug_message(self, message):
method update_response (line 91) | def update_response(self, response):
method _clear_response (line 99) | def _clear_response(self, response):
method context (line 104) | def context(self):
FILE: corehq/apps/analytics/management/commands/audit_user_in_hubspot.py
class Command (line 11) | class Command(BaseCommand):
method add_arguments (line 14) | def add_arguments(self, parser):
method handle (line 17) | def handle(self, email, **options):
method show_status_of_possibly_unblocked_user (line 37) | def show_status_of_possibly_unblocked_user(self, email, blocked_domains):
method show_status_of_blocked_user (line 60) | def show_status_of_blocked_user(self, email, blocked_memberships):
method display_first_conversion_status (line 68) | def display_first_conversion_status(self, email):
FILE: corehq/apps/analytics/management/commands/blocked_hubspot_users_summary.py
class Command (line 13) | class Command(BaseCommand):
method add_arguments (line 17) | def add_arguments(self, parser):
method handle (line 33) | def handle(self, **options):
method print_domain_summary (line 62) | def print_domain_summary(self, domain):
FILE: corehq/apps/analytics/management/commands/list_blocked_from_hubspot.py
class Command (line 9) | class Command(BaseCommand):
method handle (line 12) | def handle(self, **options):
FILE: corehq/apps/analytics/management/commands/manually_cleanup_blocked_hubspot_contacts.py
class Command (line 9) | class Command(BaseCommand):
method handle (line 12) | def handle(self, **options):
FILE: corehq/apps/analytics/management/commands/update_hubspot_properties.py
class Command (line 17) | class Command(BaseCommand):
method add_arguments (line 21) | def add_arguments(self, parser):
method handle (line 28) | def handle(self, property_names, **options):
method get_active_users (line 40) | def get_active_users(cls):
method get_user_data (line 46) | def get_user_data(cls, couch_user, property_names):
FILE: corehq/apps/analytics/migrations/0001_initial.py
class Migration (line 8) | class Migration(migrations.Migration):
FILE: corehq/apps/analytics/migrations/0002_data_point_unique_constraint.py
class Migration (line 6) | class Migration(migrations.Migration):
FILE: corehq/apps/analytics/models.py
class PartnerAnalyticsContact (line 5) | class PartnerAnalyticsContact(models.Model):
class PartnerAnalyticsDataPoint (line 13) | class PartnerAnalyticsDataPoint(models.Model):
class Meta (line 20) | class Meta:
class PartnerAnalyticsReport (line 29) | class PartnerAnalyticsReport(models.Model):
FILE: corehq/apps/analytics/signals.py
function user_save_callback (line 28) | def user_save_callback(sender, **kwargs):
function domain_save_callback (line 39) | def domain_save_callback(sender, domain, **kwargs):
function get_domain_membership_properties (line 47) | def get_domain_membership_properties(couch_user):
function track_user_login (line 58) | def track_user_login(sender, request, user, **kwargs):
FILE: corehq/apps/analytics/static/analytix/js/gtx.js
function setAllowedTagTypes (line 19) | function setAllowedTagTypes() {
function addUserPropertiesToDataLayer (line 42) | function addUserPropertiesToDataLayer() {
FILE: corehq/apps/analytics/tasks.py
function _raise_for_urllib3_response (line 74) | def _raise_for_urllib3_response(response):
function _track_on_hubspot (line 82) | def _track_on_hubspot(webuser, properties):
function _track_on_hubspot_by_email (line 101) | def _track_on_hubspot_by_email(email, properties):
function set_analytics_opt_out (line 115) | def set_analytics_opt_out(webuser, analytics_enabled):
function batch_track_on_hubspot (line 133) | def batch_track_on_hubspot(users_json):
function _hubspot_post (line 154) | def _hubspot_post(url, data):
function _send_post_data (line 172) | def _send_post_data(url, data, headers):
function _get_user_hubspot_id (line 178) | def _get_user_hubspot_id(web_user, retry_num=0):
function _send_form_to_hubspot (line 222) | def _send_form_to_hubspot(form_id, webuser, hubspot_cookie, meta, extra_...
function _send_hubspot_form_request (line 254) | def _send_hubspot_form_request(hubspot_id, form_id, data):
function update_hubspot_properties (line 270) | def update_hubspot_properties(webuser_id, properties):
function track_web_user_registration_hubspot (line 277) | def track_web_user_registration_hubspot(request, web_user, properties):
function track_user_sign_in_on_hubspot (line 309) | def track_user_sign_in_on_hubspot(webuser_id, hubspot_cookie, meta):
function track_built_app_on_hubspot (line 315) | def track_built_app_on_hubspot(webuser_id):
function track_confirmed_account_on_hubspot (line 324) | def track_confirmed_account_on_hubspot(webuser_id):
function send_hubspot_form (line 340) | def send_hubspot_form(form_id, request, user=None, extra_fields=None):
function send_hubspot_form_task (line 356) | def send_hubspot_form_task(form_id, web_user_id, hubspot_cookie, meta,
function track_job_candidate_on_hubspot (line 364) | def track_job_candidate_on_hubspot(user_email):
function track_workflow_noop (line 371) | def track_workflow_noop(email, event, properties=None):
function _get_export_count (line 383) | def _get_export_count(domain):
function _get_report_count (line 390) | def _get_report_count(domain):
function track_periodic_data (line 397) | def track_periodic_data():
function _email_is_valid (line 571) | def _email_is_valid(email):
function submit_data_to_hubspot (line 584) | def submit_data_to_hubspot(submit_json):
function get_ab_test_properties (line 595) | def get_ab_test_properties(user):
function update_subscription_properties_by_domain (line 605) | def update_subscription_properties_by_domain(domain):
function update_subscription_properties_by_user (line 616) | def update_subscription_properties_by_user(web_user_id, properties):
function get_subscription_properties_by_user (line 620) | def get_subscription_properties_by_user(couch_user):
function cleanup_blocked_hubspot_contacts (line 687) | def cleanup_blocked_hubspot_contacts():
function generate_partner_reports (line 715) | def generate_partner_reports():
function record_google_analytics_event (line 739) | def record_google_analytics_event(event_name, couch_user, event_properti...
function _record_google_analytics_event_task (line 774) | def _record_google_analytics_event_task(event_json):
FILE: corehq/apps/analytics/tests/test_hubspot.py
class TestSendToHubspot (line 40) | class TestSendToHubspot(TestCase):
method test_registration (line 43) | def test_registration(self, _send_hubspot_form_request):
method setUpClass (line 54) | def setUpClass(cls):
method tearDownClass (line 61) | def tearDownClass(cls):
method get_request (line 65) | def get_request(self):
class TestBlockedHubspotData (line 74) | class TestBlockedHubspotData(TestCase):
method setUpClass (line 77) | def setUpClass(cls):
method test_get_blocked_domains (line 147) | def test_get_blocked_domains(self):
method test_get_blocked_hubspot_accounts (line 153) | def test_get_blocked_hubspot_accounts(self):
method test_is_domain_blocked_from_hubspot (line 158) | def test_is_domain_blocked_from_hubspot(self):
method test_hubspot_enabled_for_user (line 163) | def test_hubspot_enabled_for_user(self):
method test_hubspot_enabled_for_email (line 168) | def test_hubspot_enabled_for_email(self):
method test_removed_user_is_still_blocked (line 173) | def test_removed_user_is_still_blocked(self):
method test_emails_that_accepted_invitations_to_blocked_hubspot_domains (line 183) | def test_emails_that_accepted_invitations_to_blocked_hubspot_domains(s...
method test_couch_user_is_blocked (line 189) | def test_couch_user_is_blocked(self):
method tearDownClass (line 197) | def tearDownClass(cls):
FILE: corehq/apps/analytics/tests/tes
Copy disabled (too large)
Download .json
Condensed preview — 9878 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (97,757K chars).
[
{
"path": ".coderabbit.yaml",
"chars": 2429,
"preview": "language: en-US\nearly_access: true\nreviews:\n high_level_summary: false\n review_status: false\n poem: false\n c"
},
{
"path": ".coveragerc",
"chars": 148,
"preview": "[run]\nbranch = True\nsource = corehq\n\n[report]\nomit = \n */migrations/*\n */tests/*\n */static/*\n */templates/*\n\n[html]\n"
},
{
"path": ".dockerignore",
"chars": 103,
"preview": "**/__pycache__/\nsharedfiles/\nstaticfiles/\n*.log\napplication.properties\nformplayer.jar\nlocalsettings.py\n"
},
{
"path": ".editorconfig",
"chars": 344,
"preview": "# top-most EditorConfig file\nroot = true\n\n[*]\n# Unix-style newlines with a newline ending every file\nend_of_line = lf\nin"
},
{
"path": ".github/CODEOWNERS",
"chars": 3286,
"preview": "# https://help.github.com/en/articles/about-code-owners\n#\n# Contributors can added themselves as \"Code Owners\" for speci"
},
{
"path": ".github/FUNDING.yml",
"chars": 62,
"preview": "# These are supported funding model platforms\n\ngithub: dimagi\n"
},
{
"path": ".github/ISSUE_TEMPLATE/commcare-enhancement-proposal.md",
"chars": 1324,
"preview": "---\nname: CommCare Enhancement Proposal\nabout: Suggest changes to CommCare\ntitle: \"[CEP] title\"\nlabels: CEP\nassignees: '"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 1954,
"preview": "## Product Description\n<!-- Where applicable, describe user-facing effects and include screenshots. -->\n\n## Technical Su"
},
{
"path": ".github/dependabot.yml",
"chars": 941,
"preview": "version: 2\nupdates:\n- package-ecosystem: uv\n directory: \"/\"\n schedule:\n interval: daily\n time: \"10:00\"\n open-pu"
},
{
"path": ".github/labels.yml",
"chars": 3572,
"preview": "# See https://github.com/dimagi/label-bot\ndisabled_actions:\n - triage\n - review\n - lgtm\n\n# Label rules\nbrace_expansio"
},
{
"path": ".github/release.yml",
"chars": 416,
"preview": "# .github/release.yml\n\nchangelog:\n exclude:\n authors:\n - dependabot\n - token-generator-for-cchq\n categori"
},
{
"path": ".github/workflows/build-static.yml",
"chars": 2076,
"preview": "name: Build Static Files\non:\n push:\n branches:\n - autostaging\njobs:\n build:\n runs-on: ubuntu-22.04\n step"
},
{
"path": ".github/workflows/codeql-analysis.yml",
"chars": 2896,
"preview": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# Y"
},
{
"path": ".github/workflows/dependency-metrics.yml",
"chars": 804,
"preview": "name: commcare-hq dependency metrics\non:\n schedule:\n - cron: \"0 0 * * *\"\n workflow_dispatch:\n\njobs:\n collect-metrics"
},
{
"path": ".github/workflows/docker-image.yml",
"chars": 882,
"preview": "name: Build Test Docker Image\non:\n push:\n branches:\n - master\n workflow_dispatch:\n\njobs:\n build:\n if: ${{ "
},
{
"path": ".github/workflows/lint.yml",
"chars": 1282,
"preview": "name: Lint\n\non:\n pull_request:\n branches:\n - master\n\npermissions:\n checks: write\n contents: write\n\njobs:\n li"
},
{
"path": ".github/workflows/rebuild-staging.yml",
"chars": 617,
"preview": "name: Rebuild staging branch\non:\n workflow_dispatch:\n\njobs:\n rebuild-staging:\n runs-on: ubuntu-latest\n timeout-m"
},
{
"path": ".github/workflows/required-labels.yml",
"chars": 1281,
"preview": "name: Labels\n\non:\n pull_request_target: # safe as long as we do not check out and run code from the branch to be merge"
},
{
"path": ".github/workflows/test-docs.yml",
"chars": 789,
"preview": "name: commcare-hq docs\non:\n pull_request:\n branches:\n - master\n workflow_dispatch:\njobs:\n test:\n runs-on: "
},
{
"path": ".github/workflows/tests.yml",
"chars": 2255,
"preview": "name: commcare-hq tests\non:\n pull_request:\n branches:\n - master\n - hotfix-deploy\n schedule:\n # see corehq/"
},
{
"path": ".github/workflows/update-translations.yml",
"chars": 2501,
"preview": "on:\n workflow_dispatch:\n schedule:\n - cron: '0 6 * * 3' # Every Wed at 06:00 UTC\n\nname: Update transifex translati"
},
{
"path": ".gitignore",
"chars": 1290,
"preview": "*.pyc\n*.pyo\n*~\n*.swp\n*.swo\n/local.ini*\n/localsettings*\n/loadtest/test_scripts/localsettings.py\n!localsettings.example.py"
},
{
"path": ".gitmodules",
"chars": 908,
"preview": "[submodule \"submodules/commcare-translations\"]\n\tpath = submodules/commcare-translations\n\turl = https://github.com/dimagi"
},
{
"path": ".isort.cfg",
"chars": 381,
"preview": "# https://github.com/timothycrosley/isort/wiki/isort-Settings\n[settings]\nmulti_line_output=3\ninclude_trailing_comma=true"
},
{
"path": ".python-version",
"chars": 5,
"preview": "3.13\n"
},
{
"path": ".pytype.cfg",
"chars": 417,
"preview": "[pytype]\nexclude =\n **/*_test.py\n **/test_*.py\n# Space-separated list of files or directories to process.\ninputs ="
},
{
"path": ".readthedocs.yml",
"chars": 700,
"preview": "# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n# Doc builds are generated automatically fro"
},
{
"path": ".scss-lint.yml",
"chars": 1701,
"preview": "scss_files: 'assets/style/**/*.css.scss'\n\nlinters:\n BorderZero:\n enabled: false\n Indentation:\n severity: warning"
},
{
"path": ".transifexrc.example",
"chars": 162,
"preview": "[https://www.transifex.com]\napi_hostname = https://api.transifex.com\nhostname = https://www.transifex.com\nrest_hostname "
},
{
"path": ".tx/config",
"chars": 812,
"preview": "[main]\nhost = https://www.transifex.com\n\n[o:dimagi:p:commcare-hq:r:djangopo]\nfile_filter = locale/<lang>/LC_MESSAGES/dja"
},
{
"path": "AGENTS.md",
"chars": 4359,
"preview": "# Guidelines for AI Agents\n\nThis document provides technical information about the CommCare HQ codebase\nfor AI coding as"
},
{
"path": "CODE_STANDARDS.md",
"chars": 3713,
"preview": "# Code Standards for CommCare HQ\n\nThis document outlines coding standards and best practices for the\nCommCare HQ codebas"
},
{
"path": "CONTRIBUTING.rst",
"chars": 9683,
"preview": "===========================\nContributing to CommCare HQ\n===========================\n\nCommCare HQ is primarily developed "
},
{
"path": "DEV_FAQ.md",
"chars": 6295,
"preview": "# Developer FAQ\n\nThis is a starting point for troubleshooting issues that frequently occur once your local environment\ni"
},
{
"path": "DEV_SETUP.md",
"chars": 35173,
"preview": "# Setting up CommCare HQ for Developers\n\nThis document describes setting up a development environment for working on\nCom"
},
{
"path": "DEV_SETUP_MAC.md",
"chars": 7095,
"preview": "# Supplementary Guide for Developers Running CommCare HQ on MacOS\n\n\n## Prerequisites\n\n- You will need `brew` aka [Homebr"
},
{
"path": "Dockerfile",
"chars": 2321,
"preview": "# syntax=docker/dockerfile:1\n\n# This Dockerfile is built as the `dimagi/commcarehq-pyX.Y` image, where X.Y\n# is the vers"
},
{
"path": "Dockerfile_incl",
"chars": 2724,
"preview": "# syntax=docker/dockerfile:1\n\n# Dockerfile_incl is built as the `commcarehq_incl` image, and is used\n# for running a sim"
},
{
"path": "Gruntfile.js",
"chars": 5138,
"preview": "/* globals module, process, require */\nmodule.exports = function (grunt) {\n var headless = require('mocha-headless-ch"
},
{
"path": "LICENSE",
"chars": 1554,
"preview": "BSD 3-Clause License\n\nCopyright (c) 2009-2020, Dimagi Inc., and individual contributors.\nAll rights reserved.\n\nRedistrib"
},
{
"path": "Makefile",
"chars": 454,
"preview": ".PHONY: all requirements upgrade-requirements docs migrations.lock serializer-pickle-files.lock translations\n\nall: requi"
},
{
"path": "README.md",
"chars": 1491,
"preview": "CommCare HQ\n===========\n\nCommCare HQ is a web application for building complex, customizable, frontline worker solutions"
},
{
"path": "STANDARDS.rst",
"chars": 2778,
"preview": "====================================\nProject Standards and Best Practices\n====================================\n\nThis doc"
},
{
"path": "codecov.yml",
"chars": 426,
"preview": "# https://docs.codecov.com/docs/codecovyml-reference\ncoverage:\n # https://docs.codecov.com/docs/commit-status\n status:"
},
{
"path": "corehq/README.rst",
"chars": 2629,
"preview": "corehq\n############################\n\nA few broad areas of functionality are stored directly in this directory.\n\napps\n "
},
{
"path": "corehq/__init__.py",
"chars": 2110,
"preview": "# This must not import any module that performs app initialization on\n# import since it is loaded by manage.py very earl"
},
{
"path": "corehq/apps/README.rst",
"chars": 11453,
"preview": "Django Apps in CommCare HQ\n##########################\n\nMost CommCare HQ functionality is contained in a django app.\nA fe"
},
{
"path": "corehq/apps/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "corehq/apps/accounting/README.md",
"chars": 1005,
"preview": "### Adding a new Privilege\n\nTo add a new `Privilege`\n\n+ Make sure there are no existing Privileges that you can reuse\n+ "
},
{
"path": "corehq/apps/accounting/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "corehq/apps/accounting/admin.py",
"chars": 289,
"preview": "from django.contrib import admin\n\nfrom .models import DomainUserHistory\n\n\n@admin.register(DomainUserHistory)\nclass Domai"
},
{
"path": "corehq/apps/accounting/async_handlers.py",
"chars": 16479,
"preview": "import json\n\nfrom django.conf import settings\nfrom django.db.models import F, Q\n\nfrom corehq.apps.accounting.models impo"
},
{
"path": "corehq/apps/accounting/automated_reports.py",
"chars": 5883,
"preview": "import datetime\nimport io\nfrom decimal import Decimal\n\nfrom django.conf import settings\nfrom django.template.loader impo"
},
{
"path": "corehq/apps/accounting/bootstrap/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "corehq/apps/accounting/bootstrap/config/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "corehq/apps/accounting/bootstrap/config/annual_plans_may_2024.py",
"chars": 1117,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/enterprise.py",
"chars": 540,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import (\n UNLIMITED_FEATURE_USAGE,\n FeatureType,\n "
},
{
"path": "corehq/apps/accounting/bootstrap/config/form_submitting_mobile_worker_feature_rate.py",
"chars": 243,
"preview": "from decimal import Decimal\nfrom corehq.apps.accounting.models import FeatureType\n\nBOOTSTRAP_CONFIG = {\n \"feature_rat"
},
{
"path": "corehq/apps/accounting/bootstrap/config/new_plans_dec_2019.py",
"chars": 1411,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/remove_free_50_sms_sep_2023.py",
"chars": 1099,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/report_builder_v0.py",
"chars": 1141,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/resellers_and_managed_hosting.py",
"chars": 786,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/standard_pricing_march_2018.py",
"chars": 454,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/standard_update_april_2025.py",
"chars": 828,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/standard_user_limit_march_2018.py",
"chars": 455,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/standard_user_limit_october_2018.py",
"chars": 454,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/testing.py",
"chars": 3273,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import (\n UNLIMITED_FEATURE_USAGE,\n FeatureType,\n "
},
{
"path": "corehq/apps/accounting/bootstrap/config/user_buckets_august_2018.py",
"chars": 1825,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import (\n UNLIMITED_FEATURE_USAGE,\n FeatureType,\n "
},
{
"path": "corehq/apps/accounting/bootstrap/config/user_buckets_jan_2017.py",
"chars": 1740,
"preview": "from decimal import Decimal\n\nfrom corehq.apps.accounting.models import FeatureType, SoftwarePlanEdition\n\nBOOTSTRAP_CONFI"
},
{
"path": "corehq/apps/accounting/bootstrap/config/web_user_feature_rate.py",
"chars": 221,
"preview": "from decimal import Decimal\nfrom corehq.apps.accounting.models import FeatureType\n\nBOOTSTRAP_CONFIG = {\n \"feature_rat"
},
{
"path": "corehq/apps/accounting/bootstrap/features.py",
"chars": 4855,
"preview": "\"\"\"\nThese are the feature allocations for all tiers of our Software Plan Editions\navailable for self-service.\n\"\"\"\nfrom c"
},
{
"path": "corehq/apps/accounting/bootstrap/utils.py",
"chars": 9573,
"preview": "from collections import namedtuple\n\nfrom corehq.apps.accounting.const import COMMCARE_PRODUCT_TYPE, DIMAGI_ONLY\nfrom cor"
},
{
"path": "corehq/apps/accounting/const.py",
"chars": 498,
"preview": "EXCHANGE_RATE_DECIMAL_PLACES = 9\n\nSMALL_INVOICE_THRESHOLD = 1\nOVERDUE_INVOICE_LIMIT_DAYS = 10\nDAYS_PAST_DUE_TO_TRIGGER_D"
},
{
"path": "corehq/apps/accounting/decorators.py",
"chars": 5684,
"preview": "from functools import wraps\n\nfrom django.http import HttpResponse, JsonResponse\n\nfrom django_prbac.decorators import req"
},
{
"path": "corehq/apps/accounting/dispatcher.py",
"chars": 688,
"preview": "from django.utils.decorators import method_decorator\n\nfrom django_prbac.decorators import requires_privilege_raise404\n\nf"
},
{
"path": "corehq/apps/accounting/emails.py",
"chars": 12076,
"preview": "from django.conf import settings\nfrom django.template.loader import render_to_string\nfrom django.utils.translation impor"
},
{
"path": "corehq/apps/accounting/exceptions.py",
"chars": 1109,
"preview": "class AccountingError(Exception):\n pass\n\n\nclass LineItemError(Exception):\n pass\n\n\nclass InvoiceError(Exception):\n "
},
{
"path": "corehq/apps/accounting/filters.py",
"chars": 13979,
"preview": "import calendar\nimport datetime\n\nfrom django.urls import reverse\nfrom django.utils.translation import gettext_noop as _\n"
},
{
"path": "corehq/apps/accounting/forms.py",
"chars": 114733,
"preview": "import datetime\nimport json\nfrom decimal import Decimal\n\nfrom django import forms\nfrom django.conf import settings\nfrom "
},
{
"path": "corehq/apps/accounting/interface.py",
"chars": 59333,
"preview": "import datetime\nfrom decimal import Decimal\n\nfrom django.db.models import Q\nfrom django.template.loader import render_to"
},
{
"path": "corehq/apps/accounting/invoice_pdf.py",
"chars": 26029,
"preview": "import os\n\nfrom django.conf import settings\n\nfrom reportlab.lib.styles import ParagraphStyle\nfrom reportlab.lib.units im"
},
{
"path": "corehq/apps/accounting/invoicing.py",
"chars": 37045,
"preview": "import calendar\nimport datetime\nfrom collections import defaultdict\nfrom decimal import Decimal\n\nfrom django.conf import"
},
{
"path": "corehq/apps/accounting/management/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "corehq/apps/accounting/management/commands/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "corehq/apps/accounting/management/commands/add_operations_user.py",
"chars": 2993,
"preview": "# Use modern Python\n\nfrom django.contrib.auth.models import User\nfrom django.core.management import BaseCommand\n\nfrom dj"
},
{
"path": "corehq/apps/accounting/management/commands/change_role_for_software_plan_version.py",
"chars": 3295,
"preview": "import logging\n\nfrom django.core.management import BaseCommand\n\nfrom django_prbac.models import Role\n\nfrom corehq.apps.a"
},
{
"path": "corehq/apps/accounting/management/commands/create_test_pdf_templates.py",
"chars": 453,
"preview": "from django.core.management import BaseCommand\n\nfrom corehq.apps.accounting.tests.test_invoice_pdf import InvoiceRendere"
},
{
"path": "corehq/apps/accounting/management/commands/find_inactive_custom_modules.py",
"chars": 3230,
"preview": "from collections import defaultdict\nfrom importlib import import_module\n\nfrom django.apps import apps\nfrom django.conf i"
},
{
"path": "corehq/apps/accounting/management/commands/get_minimum_features_by_domain.py",
"chars": 3323,
"preview": "from django.core.management import BaseCommand\n\nfrom corehq import privileges\nfrom corehq.apps.accounting.models import "
},
{
"path": "corehq/apps/accounting/management/commands/get_partner_domain_user_history.py",
"chars": 3218,
"preview": "import datetime\n\nfrom dateutil import parser\n\nfrom django.core.management import BaseCommand\n\nfrom corehq.apps.accountin"
},
{
"path": "corehq/apps/accounting/management/commands/list_customer_billing_account_software_plan.py",
"chars": 5079,
"preview": "from django.core.management.base import BaseCommand\nfrom corehq.apps.accounting.models import BillingAccount, Subscripti"
},
{
"path": "corehq/apps/accounting/management/commands/list_prepayments_by_year.py",
"chars": 2049,
"preview": "from datetime import date\n\nfrom django.core.management import BaseCommand\n\nfrom corehq.apps.accounting.models import (\n "
},
{
"path": "corehq/apps/accounting/management/commands/make_domain_enterprise_level.py",
"chars": 650,
"preview": "from django.core.management import BaseCommand\n\nfrom corehq.apps.domain.forms import DimagiOnlyEnterpriseForm\nfrom coreh"
},
{
"path": "corehq/apps/accounting/migrations/0001_squashed_0052_ensure_report_builder_plans.py",
"chars": 28407,
"preview": "from decimal import Decimal\n\nimport django.core.validators\nimport django.db.models.deletion\nfrom django.db import migrat"
},
{
"path": "corehq/apps/accounting/migrations/0002_auto_20170222_2008.py",
"chars": 628,
"preview": "# Generated by Django 1.9.12 on 2017-02-22 20:08\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0003_auto_20170328_2102.py",
"chars": 385,
"preview": "# Generated by Django 1.10.6 on 2017-03-28 21:02\n\nfrom django.db import migrations\n\nfrom corehq.apps.hqadmin.management."
},
{
"path": "corehq/apps/accounting/migrations/0004_auto_20170404_0028.py",
"chars": 385,
"preview": "# Generated by Django 1.10.6 on 2017-04-04 00:28\n\nfrom django.db import migrations\n\nfrom corehq.apps.hqadmin.management."
},
{
"path": "corehq/apps/accounting/migrations/0005_automatic_downgrade_adjustment_method.py",
"chars": 580,
"preview": "# Generated by Django 1.10.7 on 2017-04-14 19:04\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0006_unique_active_domain_subscription.py",
"chars": 814,
"preview": "# Generated by Django 1.10.7 on 2017-04-22 17:18\n\nfrom django.db import migrations\n\nfrom corehq.util.django_migrations i"
},
{
"path": "corehq/apps/accounting/migrations/0007_practice_mobile_workers.py",
"chars": 399,
"preview": "# Generated by Django 1.10.7 on 2017-06-21 14:47\n\nfrom django.db import migrations\n\nfrom corehq.apps.hqadmin.management."
},
{
"path": "corehq/apps/accounting/migrations/0008_update_report_builder_included_feature_numbers.py",
"chars": 595,
"preview": "# Generated by Django 1.10.7 on 2017-07-28 21:10\n\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.bootstra"
},
{
"path": "corehq/apps/accounting/migrations/0009_make_billingaccount_name_unique.py",
"chars": 1299,
"preview": "# Generated by Django 1.10.7 on 2017-08-01 21:47\n\nfrom django.db import migrations, models\nfrom django.db.models import "
},
{
"path": "corehq/apps/accounting/migrations/0010_remove_softwareproduct_product_type.py",
"chars": 360,
"preview": "# Generated by Django 1.10.7 on 2017-08-22 18:42\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migratio"
},
{
"path": "corehq/apps/accounting/migrations/0011_remove_softwareproduct.py",
"chars": 1144,
"preview": "# Generated by Django 1.10.7 on 2017-04-24 02:10\n\nfrom django.db import migrations, models\n\n\ndef _copy_product_name_to_p"
},
{
"path": "corehq/apps/accounting/migrations/0012_replace__product_type__with__is_product.py",
"chars": 1009,
"preview": "# Generated by Django 1.10.7 on 2017-10-01 19:45\n\nfrom django.db import migrations, models\n\n\ndef _product_type_to_is_pro"
},
{
"path": "corehq/apps/accounting/migrations/0013_subscription_dates_check.py",
"chars": 560,
"preview": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n dependencies = [\n ('accounting', '"
},
{
"path": "corehq/apps/accounting/migrations/0014_paymentmethod__web_user__nonnullable.py",
"chars": 420,
"preview": "# Generated by Django 1.11.6 on 2017-10-16 21:52\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0015_grandfather_login_as.py",
"chars": 576,
"preview": "# Generated by Django 1.11.6 on 2017-10-24 18:11\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0016_grandfather_reportbuilder_5_pro.py",
"chars": 607,
"preview": "# Generated by Django 1.11.6 on 2017-10-24 18:11\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0017_nonnullable_char_fields.py",
"chars": 636,
"preview": "# Generated by Django 1.11.11 on 2018-03-13 17:56\n\nfrom django.db import migrations\n\n\ndef _assign_default_values(apps, s"
},
{
"path": "corehq/apps/accounting/migrations/0018_alter_nonnullable_char_fields.py",
"chars": 709,
"preview": "# Generated by Django 1.11.11 on 2018-03-14 18:02\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0019_standard_pricing_march_2018.py",
"chars": 544,
"preview": "from django.db import migrations\n\nfrom corehq.apps.accounting.bootstrap.config.standard_pricing_march_2018 import (\n "
},
{
"path": "corehq/apps/accounting/migrations/0020_payment_method__unique_together.py",
"chars": 388,
"preview": "# Generated by Django 1.11.11 on 2018-03-26 13:46\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migrati"
},
{
"path": "corehq/apps/accounting/migrations/0021_standard_user_limit_march_2018.py",
"chars": 606,
"preview": "# Generated by Django 1.11.11 on 2018-03-27 12:40\n\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.bootstr"
},
{
"path": "corehq/apps/accounting/migrations/0022_add__skip_auto_downgrade_reason.py",
"chars": 440,
"preview": "# Generated by Django 1.11.11 on 2018-03-14 12:33\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0023_auto_20180501_1813.py",
"chars": 499,
"preview": "# Generated by Django 1.11.12 on 2018-05-01 18:13\n\nfrom django.db import migrations\n\nimport corehq.apps.accounting.model"
},
{
"path": "corehq/apps/accounting/migrations/0024_unique__transaction_id.py",
"chars": 420,
"preview": "# Generated by Django 1.11.13 on 2018-05-06 16:52\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0025_auto_20180508_1952.py",
"chars": 900,
"preview": "# Generated by Django 1.11.13 on 2018-05-08 19:52\n\nimport django.contrib.postgres.fields\nfrom django.db import migration"
},
{
"path": "corehq/apps/accounting/migrations/0026_auto_20180508_1956.py",
"chars": 924,
"preview": "# Generated by Django 1.11.13 on 2018-05-08 19:56\n\nfrom django.db import migrations\n\n\ndef noop(*args, **kwargs):\n pas"
},
{
"path": "corehq/apps/accounting/migrations/0027_auto_20180509_1857.py",
"chars": 462,
"preview": "# Generated by Django 1.11.13 on 2018-05-09 18:57\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migrati"
},
{
"path": "corehq/apps/accounting/migrations/0028_auto_20180604_1757.py",
"chars": 421,
"preview": "# Generated by Django 1.11.13 on 2018-06-04 17:57\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0029_auto_20180605_1826.py",
"chars": 417,
"preview": "# Generated by Django 1.11.13 on 2018-06-05 18:26\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0030_softwareplan_max_domains.py",
"chars": 411,
"preview": "# Generated by Django 1.11.13 on 2018-06-11 19:20\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0031_billingaccount_billing_admin_emails.py",
"chars": 608,
"preview": "# Generated by Django 1.11.13 on 2018-06-13 19:09\n\nimport django.contrib.postgres.fields\nfrom django.db import migration"
},
{
"path": "corehq/apps/accounting/migrations/0032_billingaccount_invoicing_plan.py",
"chars": 570,
"preview": "# Generated by Django 1.11.13 on 2018-06-27 18:30\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0032_customerinvoice_squashed_0036_customerbillingrecord.py",
"chars": 3502,
"preview": "# Generated by Django 1.11.14 on 2018-07-06 17:45\n\nfrom decimal import Decimal\n\nimport django.contrib.postgres.fields\nim"
},
{
"path": "corehq/apps/accounting/migrations/0033_auto_20180709_1837.py",
"chars": 440,
"preview": "# Generated by Django 1.11.14 on 2018-07-09 18:37\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migrati"
},
{
"path": "corehq/apps/accounting/migrations/0034_merge_20180711_1828.py",
"chars": 292,
"preview": "# Generated by Django 1.11.14 on 2018-07-11 18:28\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migrati"
},
{
"path": "corehq/apps/accounting/migrations/0034_remove_subscription_date_delay_invoicing.py",
"chars": 791,
"preview": "# Generated by Django 1.11.13 on 2018-07-09 18:33\n\nfrom datetime import date\n\nfrom django.db import migrations\n\n\ndef ass"
},
{
"path": "corehq/apps/accounting/migrations/0035_enterprise_settings.py",
"chars": 1974,
"preview": "# Generated by Django 1.11.14 on 2018-07-31 19:21\n\nimport django.contrib.postgres.fields\nfrom django.db import migration"
},
{
"path": "corehq/apps/accounting/migrations/0035_merge_20180711_2039.py",
"chars": 304,
"preview": "# Generated by Django 1.11.14 on 2018-07-11 20:39\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migrati"
},
{
"path": "corehq/apps/accounting/migrations/0036_domainuserhistory.py",
"chars": 660,
"preview": "# Generated by Django 1.11.14 on 2018-07-14 01:46\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0037_merge_20180807_0915.py",
"chars": 281,
"preview": "# Generated by Django 1.11.14 on 2018-08-07 09:15\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migrati"
},
{
"path": "corehq/apps/accounting/migrations/0038_remove_billingaccount_restrict_signup_email.py",
"chars": 357,
"preview": "# Generated by Django 1.11.14 on 2018-08-23 21:12\n\nfrom django.db import migrations\n\n\nclass Migration(migrations.Migrati"
},
{
"path": "corehq/apps/accounting/migrations/0039_auto_20180828_2258.py",
"chars": 838,
"preview": "# Generated by Django 1.11.14 on 2018-08-28 22:58\n\nfrom django.db import migrations, models\n\nfrom corehq.apps.accounting"
},
{
"path": "corehq/apps/accounting/migrations/0040_auto_20181002_1721.py",
"chars": 643,
"preview": "# Generated by Django 1.11.16 on 2018-10-02 17:21\n\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.bootstr"
},
{
"path": "corehq/apps/accounting/migrations/0041_auto_20190130_1709.py",
"chars": 440,
"preview": "# Generated by Django 1.11.16 on 2019-01-30 17:09\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0042_domain_user_history__unique__and__nonnullable.py",
"chars": 555,
"preview": "# Generated by Django 1.11.20 on 2019-05-17 18:36\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations"
},
{
"path": "corehq/apps/accounting/migrations/0043_grandfather_case_privs.py",
"chars": 740,
"preview": "# Generated by Django 1.11.21 on 2019-07-23 16:43\n\nfrom django.core.management import call_command\nfrom django.db import"
},
{
"path": "corehq/apps/accounting/migrations/0044_grandfather_odata_privs.py",
"chars": 721,
"preview": "# Generated by Django 1.11.21 on 2019-10-07 13:21\n\nfrom django.core.management import call_command\nfrom django.db import"
},
{
"path": "corehq/apps/accounting/migrations/0045_grandfather_data_forwarding_privs.py",
"chars": 729,
"preview": "# Generated by Django 1.11.21 on 2019-11-27 11:11\n\nfrom django.core.management import call_command\nfrom django.db import"
},
{
"path": "corehq/apps/accounting/migrations/0046_new_plans.py",
"chars": 1132,
"preview": "# -*- coding: utf-8 -*-\n# Generated by Django 1.11.22 on 2019-09-13 22:26\nfrom __future__ import unicode_literals\n\nfrom "
},
{
"path": "corehq/apps/accounting/migrations/0047_invoice_communication.py",
"chars": 2399,
"preview": "# -*- coding: utf-8 -*-\n# Generated by Django 1.11.28 on 2020-04-27 16:40\nfrom __future__ import unicode_literals\n\nfrom "
},
{
"path": "corehq/apps/accounting/migrations/0048_friendly_writeoff.py",
"chars": 994,
"preview": "# -*- coding: utf-8 -*-\n# Generated by Django 1.11.28 on 2020-04-28 20:57\nfrom __future__ import unicode_literals\n\nfrom "
},
{
"path": "corehq/apps/accounting/migrations/0049_auto_20200924_1753.py",
"chars": 1053,
"preview": "# Generated by Django 2.2.13 on 2020-09-24 17:53\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0050_app_user_profiles.py",
"chars": 688,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\nfrom corehq.util.django_migrations impo"
},
{
"path": "corehq/apps/accounting/migrations/0051_hubspot_restrictions.py",
"chars": 773,
"preview": "# Generated by Django 2.2.16 on 2020-10-15 17:48\n\nimport django.contrib.postgres.fields\nfrom django.db import migrations"
},
{
"path": "corehq/apps/accounting/migrations/0052_geocoder_permissions.py",
"chars": 672,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\nfrom corehq.util.django_migrations impo"
},
{
"path": "corehq/apps/accounting/migrations/0053_app_user_profiles_advanced.py",
"chars": 681,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\nfrom corehq.util.django_migrations impo"
},
{
"path": "corehq/apps/accounting/migrations/0054_default_export_settings.py",
"chars": 708,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\nfrom corehq.util.django_migrations impo"
},
{
"path": "corehq/apps/accounting/migrations/0055_linked_projects.py",
"chars": 644,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\nfrom corehq.util.django_migrations impo"
},
{
"path": "corehq/apps/accounting/migrations/0056_add_release_management.py",
"chars": 460,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\nfrom corehq.util.django_migrations impo"
},
{
"path": "corehq/apps/accounting/migrations/0057_add_sms_report_toggle.py",
"chars": 377,
"preview": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n dependencies = [\n ('accoun"
},
{
"path": "corehq/apps/accounting/migrations/0058_delete_linked_projects_role.py",
"chars": 599,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\nfrom corehq.util.django_migrations impo"
},
{
"path": "corehq/apps/accounting/migrations/0059_add_lite_release_management_priv.py",
"chars": 696,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\n\nfrom corehq.privileges import LITE_REL"
},
{
"path": "corehq/apps/accounting/migrations/0060_add_loadtest_users_priv.py",
"chars": 758,
"preview": "from django.db import migrations\nfrom django.core.management import call_command\n\nfrom corehq.privileges import LOADTEST"
},
{
"path": "corehq/apps/accounting/migrations/0061_remove_enterprise_v1.py",
"chars": 1449,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom django_prbac.models import Role\n\n"
},
{
"path": "corehq/apps/accounting/migrations/0062_add_release_management_to_enterprise.py",
"chars": 984,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.privileges import RELEASE_"
},
{
"path": "corehq/apps/accounting/migrations/0063_replace_linked_projects_ff_with_erm.py",
"chars": 628,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.util.django_migrations imp"
},
{
"path": "corehq/apps/accounting/migrations/0064_add_form_link_workflow_priv.py",
"chars": 762,
"preview": "# Generated by Django 3.2.16 on 2022-12-20 06:20\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0065_phone_apk_heartbeat_privs.py",
"chars": 759,
"preview": "# Generated by Django 1.11.21 on 2019-11-27 11:11\n\nfrom django.core.management import call_command\nfrom django.db import"
},
{
"path": "corehq/apps/accounting/migrations/0066_data_file_download_priv.py",
"chars": 1043,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0067_add_view_app_diff_priv.py",
"chars": 740,
"preview": "# Generated by Django 3.2.16 on 2023-02-03 06:57\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0068_regex_field_validation_privilege.py",
"chars": 1044,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0069_location_safe_case_imports_priv.py",
"chars": 1112,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0070_form_case_ids_case_importer_priv.py",
"chars": 1096,
"preview": "# Generated by Django 3.2.16 on 2023-03-22 07:18\n\nfrom django.db import migrations\nfrom django.core.management import ca"
},
{
"path": "corehq/apps/accounting/migrations/0071_add_billingaccountwebuserhistory.py",
"chars": 886,
"preview": "# Generated by Django 3.2.18 on 2023-03-15 07:37\n\nfrom django.db import migrations, models\nimport django.db.models.delet"
},
{
"path": "corehq/apps/accounting/migrations/0072_export_multisort_priv.py",
"chars": 1042,
"preview": "# Generated by Django 3.2.16 on 2023-04-05 06:55\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0073_export_ownership_priv.py",
"chars": 1068,
"preview": "# Generated by Django 3.2.16 on 2023-04-13 10:49\nfrom django.core.management import call_command\nfrom django.db import m"
},
{
"path": "corehq/apps/accounting/migrations/0074_filtered_bulk_user_download_priv.py",
"chars": 1106,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0075_application_error_report_priv.py",
"chars": 1102,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0076_location_owner_in_report_builder_priv.py",
"chars": 1100,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0077_case_list_explorer_priv.py",
"chars": 953,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.privileges import CASE_LIS"
},
{
"path": "corehq/apps/accounting/migrations/0078_revert_location_owner_in_report_builder_priv.py",
"chars": 2194,
"preview": "from django.db import migrations\n\nfrom django_prbac.models import Role, Grant\nfrom corehq.util.django_migrations import "
},
{
"path": "corehq/apps/accounting/migrations/0079_add_web_user_feature.py",
"chars": 1244,
"preview": "from django.db import migrations\n\nfrom corehq.apps.accounting.bootstrap.utils import ensure_feature_rates\nfrom corehq.ap"
},
{
"path": "corehq/apps/accounting/migrations/0080_add_web_user_feature_in_other_models.py",
"chars": 740,
"preview": "# Generated by Django 3.2.20 on 2023-07-18 22:01\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0081_billingaccount_bill_web_user.py",
"chars": 424,
"preview": "# Generated by Django 3.2.20 on 2023-07-19 03:54\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0082_application_error_report_priv.py",
"chars": 1748,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\nfrom django_prbac.models import Role\n\nf"
},
{
"path": "corehq/apps/accounting/migrations/0083_data_dictionary_priv.py",
"chars": 1054,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0084_copy_cases_priv.py",
"chars": 973,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.models imp"
},
{
"path": "corehq/apps/accounting/migrations/0085_remove_free_50_sms.py",
"chars": 530,
"preview": "from django.db import migrations\n\nfrom corehq.apps.accounting.bootstrap.config.remove_free_50_sms_sep_2023 import (\n "
},
{
"path": "corehq/apps/accounting/migrations/0086_add_duplicate_invoice_id_to_invoice_model.py",
"chars": 1400,
"preview": "# Generated by Django 3.2.20 on 2023-09-14 01:13\n\nfrom django.db import migrations, models\nfrom django.db.models import "
},
{
"path": "corehq/apps/accounting/migrations/0087_invoice_unique_constraints.py",
"chars": 2342,
"preview": "# Generated by Django 3.2.20 on 2023-08-25 15:38\n\nfrom django.db import migrations, models\n\n# Raw SQL to add unique cons"
},
{
"path": "corehq/apps/accounting/migrations/0088_add_new_softwareplan_visibility.py",
"chars": 711,
"preview": "# Generated by Django 3.2.20 on 2023-10-02 13:13\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0089_dedupe_priv.py",
"chars": 1185,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.privileges import CASE_DED"
},
{
"path": "corehq/apps/accounting/migrations/0090_custom_domain_alerts_priv.py",
"chars": 1024,
"preview": "# Generated by Django 3.2.23 on 2024-01-01 12:03\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0091_remove_custom_banner_alerts_feature_flag.py",
"chars": 909,
"preview": "# Generated by Django 3.2.23 on 2024-01-30 19:08\n\nfrom django.db import migrations\n\nfrom couchdbkit import ResourceNotFo"
},
{
"path": "corehq/apps/accounting/migrations/0092_revert_application_error_report_priv.py",
"chars": 1771,
"preview": "from django.db import migrations\n\nfrom django_prbac.models import Grant, Role\n\nfrom corehq import privileges, toggles\nfr"
},
{
"path": "corehq/apps/accounting/migrations/0093_defaultproductplan_is_annual_plan.py",
"chars": 429,
"preview": "# Generated by Django 4.2.11 on 2024-05-02 16:17\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0094_add_annual_softwareplans.py",
"chars": 1255,
"preview": "from django.db import migrations\nfrom corehq.apps.accounting.bootstrap.config.annual_plans_may_2024 import (\n BOOTSTR"
},
{
"path": "corehq/apps/accounting/migrations/0095_update_softwareplan_visibilities.py",
"chars": 1511,
"preview": "from datetime import datetime\n\nfrom django.db import migrations, models\n\nfrom corehq.apps.accounting.models import Softw"
},
{
"path": "corehq/apps/accounting/migrations/0096_formsubmittingmobileworkerhistory_and_featuretype_choice.py",
"chars": 1502,
"preview": "# Generated by Django 4.2.11 on 2024-07-04 17:26\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0097_add_form_submitting_mobile_worker_feature.py",
"chars": 1553,
"preview": "from django.db import migrations\n\nfrom corehq.apps.accounting.bootstrap.config.form_submitting_mobile_worker_feature_rat"
},
{
"path": "corehq/apps/accounting/migrations/0098_app_dependencies_priv.py",
"chars": 1046,
"preview": "# Generated by Django 4.2.11 on 2024-10-16 16:15\n\nfrom django.core.management import call_command\nfrom django.db import "
},
{
"path": "corehq/apps/accounting/migrations/0099_data_cleaning_priv.py",
"chars": 1176,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.util.django_migrations imp"
},
{
"path": "corehq/apps/accounting/migrations/0100_alter_customerinvoicecommunicationhistory_communication_type_and_more.py",
"chars": 1305,
"preview": "# Generated by Django 4.2.18 on 2025-03-11 15:48\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
},
{
"path": "corehq/apps/accounting/migrations/0101_update_standard_plan_pricing_users_and_privs.py",
"chars": 799,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.apps.accounting.bootstrap."
},
{
"path": "corehq/apps/accounting/migrations/0102_alter_defaultproductplan_edition_and_more.py",
"chars": 2365,
"preview": "# Generated by Django 4.2.20 on 2025-04-09 20:59\n\nfrom django.db import migrations, models\n\n\ndef _rename_community_to_fr"
},
{
"path": "corehq/apps/accounting/migrations/0103_bulk_data_cleaning_priv.py",
"chars": 1679,
"preview": "from django.core.management import call_command\nfrom django.db import migrations\n\nfrom corehq.privileges import BULK_DAT"
},
{
"path": "corehq/apps/accounting/migrations/0104_fix_priv_community_rename.py",
"chars": 1057,
"preview": "# Generated by Django 4.2.20 on 2025-07-07 15:41\n\nfrom django.db import migrations\nfrom django.core.management import ca"
}
]
// ... and 9678 more files (download for full content)
About this extraction
This page contains the full source code of the dimagi/commcare-hq GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9878 files (85.4 MB), approximately 23.0M tokens, and a symbol index with 52825 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.