From 4b32aec9341c567eec5b760a6e3b122442b9e804 Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Thu, 12 Sep 2024 13:44:52 +0200 Subject: [PATCH 001/148] extend task tracker to handle messages --- corehq/apps/geospatial/utils.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/corehq/apps/geospatial/utils.py b/corehq/apps/geospatial/utils.py index e43d12921d8b..57e056fae4e4 100644 --- a/corehq/apps/geospatial/utils.py +++ b/corehq/apps/geospatial/utils.py @@ -221,8 +221,9 @@ class CeleryTaskTracker(object): Simple Helper class using redis to track if a celery task was requested and is not completed yet. """ - def __init__(self, task_key): + def __init__(self, task_key, message_key=None): self.task_key = task_key + self.message_key = message_key self._client = get_redis_client() def mark_requested(self, timeout=ONE_DAY): @@ -234,4 +235,14 @@ def is_active(self): return self._client.has_key(self.task_key) def mark_completed(self): + self.clear_message() return self._client.delete(self.task_key) + + def get_message(self): + return self._client.get(self.message_key) + + def set_message(self, message, timeout=ONE_DAY * 3): + return self._client.set(self.message_key, message, timeout=timeout) + + def clear_message(self): + return self._client.delete(self.message_key) From 6280c1785c813ebc957da63d8b6a9de0519a8b75 Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Thu, 12 Sep 2024 13:55:28 +0200 Subject: [PATCH 002/148] refactor index command functions --- .../index_geolocation_case_properties.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py b/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py index 2419a88483ae..58e767bfeeea 100644 --- a/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py +++ b/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py @@ -41,15 +41,23 @@ def index_case_docs(domain, query_limit=DEFAULT_QUERY_LIMIT, chunk_size=DEFAULT_ query = _es_case_query(domain, geo_case_property, case_type) count = query.count() print(f'{count} case(s) to process') - batch_count = 1 - if query_limit: - batch_count = math.ceil(count / query_limit) + batch_count = get_batch_count(count, query_limit) print(f"Cases will be processed in {batch_count} batches") for i in range(batch_count): print(f'Processing {i+1}/{batch_count}') - query = _es_case_query(domain, geo_case_property, case_type, size=query_limit) - case_ids = query.get_ids() - _index_case_ids(domain, case_ids, chunk_size) + process_batch(domain, geo_case_property, case_type, query_limit, chunk_size) + + +def get_batch_count(doc_count, query_limit): + if not query_limit: + return 1 + return math.ceil(doc_count / query_limit) + + +def process_batch(domain, geo_case_property, case_type, query_limit, chunk_size): + query = _es_case_query(domain, geo_case_property, case_type, size=query_limit) + case_ids = query.get_ids() + _index_case_ids(domain, case_ids, chunk_size) def _index_case_ids(domain, case_ids, chunk_size): From 7f52f0e3b54eb35d91065fe81a3fc887baa5cac3 Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Thu, 12 Sep 2024 13:57:03 +0200 Subject: [PATCH 003/148] helper func to get celery task tracker --- corehq/apps/geospatial/utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/corehq/apps/geospatial/utils.py b/corehq/apps/geospatial/utils.py index 57e056fae4e4..7a592f58124a 100644 --- a/corehq/apps/geospatial/utils.py +++ b/corehq/apps/geospatial/utils.py @@ -33,6 +33,12 @@ def get_geo_user_property(domain): return config.user_location_property_name +def get_celery_task_tracker(domain, base_key): + task_key = f'{base_key}_{domain}' + message_key = f'{base_key}_message_{domain}' + return CeleryTaskTracker(task_key, message_key) + + def _format_coordinates(lat, lon): return f"{lat} {lon} 0.0 0.0" From 2184e768eaf59f69835d6d184fcf94239d5396ad Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Thu, 12 Sep 2024 13:58:30 +0200 Subject: [PATCH 004/148] start celery task to index docs on enabling feature flag --- corehq/apps/geospatial/const.py | 2 ++ corehq/apps/geospatial/tasks.py | 56 ++++++++++++++++++++++++++++++++- corehq/toggles/__init__.py | 11 ++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/corehq/apps/geospatial/const.py b/corehq/apps/geospatial/const.py index 14142a7815ae..d744adef18a3 100644 --- a/corehq/apps/geospatial/const.py +++ b/corehq/apps/geospatial/const.py @@ -133,3 +133,5 @@ } } } + +INDEX_ES_TASK_HELPER_BASE_KEY = 'geo_cases_index_cases' diff --git a/corehq/apps/geospatial/tasks.py b/corehq/apps/geospatial/tasks.py index 62724e88a382..541cc620e80e 100644 --- a/corehq/apps/geospatial/tasks.py +++ b/corehq/apps/geospatial/tasks.py @@ -1,5 +1,24 @@ +from django.utils.translation import gettext as _ + +from corehq.util.decorators import serial_task + from corehq.apps.celery import task -from corehq.apps.geospatial.utils import CeleryTaskTracker, update_cases_owner +from corehq.apps.geospatial.const import INDEX_ES_TASK_HELPER_BASE_KEY +from corehq.apps.geospatial.utils import ( + get_celery_task_tracker, + CeleryTaskTracker, + update_cases_owner, + get_geo_case_property, +) +from corehq.apps.geospatial.management.commands.index_geolocation_case_properties import ( + _es_case_query, + get_batch_count, + process_batch, + DEFAULT_QUERY_LIMIT, + DEFAULT_CHUNK_SIZE, +) + +from settings import MAX_GEOSPATIAL_INDEX_DOC_LIMIT @task(queue="background_queue", ignore_result=True) @@ -9,3 +28,38 @@ def geo_cases_reassignment_update_owners(domain, case_owner_updates_dict, task_k finally: celery_task_tracker = CeleryTaskTracker(task_key) celery_task_tracker.mark_completed() + + +@serial_task('async-index-es-docs', timeout=30 * 60, queue='background_queue', ignore_result=True) +def index_es_docs_with_location_props(domain): + celery_task_tracker = get_celery_task_tracker(domain, INDEX_ES_TASK_HELPER_BASE_KEY) + if celery_task_tracker.is_active(): + return + + geo_case_prop = get_geo_case_property(domain) + query = _es_case_query(domain, geo_case_prop) + doc_count = query.count() + if doc_count > MAX_GEOSPATIAL_INDEX_DOC_LIMIT: + celery_task_tracker.set_message( + _('This domain contains too many cases and so they will not be made available ' + 'for use by this feature. Please reach out to support.') + ) + return + + celery_task_tracker.mark_requested() + batch_count = get_batch_count(doc_count, DEFAULT_QUERY_LIMIT) + try: + for i in range(batch_count): + progress = (i / batch_count) * 100 + celery_task_tracker.set_message( + _(f'Cases are being made ready for use by this feature. Please be patient. ({progress}%)') + ) + process_batch( + domain, + geo_case_prop, + case_type=None, + query_limit=DEFAULT_QUERY_LIMIT, + chunk_size=DEFAULT_CHUNK_SIZE, + ) + finally: + celery_task_tracker.mark_completed() diff --git a/corehq/toggles/__init__.py b/corehq/toggles/__init__.py index 54d6e2a18e89..3122c31a06c8 100644 --- a/corehq/toggles/__init__.py +++ b/corehq/toggles/__init__.py @@ -2555,13 +2555,22 @@ def _handle_attendance_tracking_role(domain, is_enabled): save_fn=_handle_attendance_tracking_role, ) + +def _handle_geospatial_es_index(domain, is_enabled): + from corehq.apps.geospatial.es import index_es_docs_with_location_props + + if is_enabled: + index_es_docs_with_location_props.delay(domain) + + GEOSPATIAL = StaticToggle( 'geospatial', 'Allows access to GIS functionality', TAG_SOLUTIONS_LIMITED, namespaces=[NAMESPACE_DOMAIN], description='Additional views will be added allowing for visually viewing ' - 'and assigning cases on a map.' + 'and assigning cases on a map.', + save_fn=_handle_geospatial_es_index, ) From d7abcee1d3181e759994f8462aff641e8d55a56c Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Thu, 12 Sep 2024 13:59:01 +0200 Subject: [PATCH 005/148] send task message to front-end --- corehq/apps/geospatial/reports.py | 4 ++++ corehq/apps/geospatial/views.py | 21 ++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/corehq/apps/geospatial/reports.py b/corehq/apps/geospatial/reports.py index 362eeb7a1548..9bc7b3ef7ad8 100644 --- a/corehq/apps/geospatial/reports.py +++ b/corehq/apps/geospatial/reports.py @@ -25,6 +25,7 @@ from corehq.util.quickcache import quickcache from .dispatchers import CaseManagementMapDispatcher +from corehq.apps.geospatial.const import INDEX_ES_TASK_HELPER_BASE_KEY from .es import ( BUCKET_CASES_AGG, CASE_PROPERTIES_AGG, @@ -38,6 +39,7 @@ geojson_to_es_geoshape, get_geo_case_property, validate_geometry, + get_celery_task_tracker, ) @@ -59,12 +61,14 @@ class BaseCaseMapReport(ProjectReport, CaseListMixin, XpathCaseSearchFilterMixin def template_context(self): # Whatever is specified here can be accessed through initial_page_data context = super(BaseCaseMapReport, self).template_context + celery_task_tracker = get_celery_task_tracker(self.domain, base_key=INDEX_ES_TASK_HELPER_BASE_KEY) context.update({ 'mapbox_access_token': settings.MAPBOX_ACCESS_TOKEN, 'saved_polygons': [ {'id': p.id, 'name': p.name, 'geo_json': p.geo_json} for p in GeoPolygon.objects.filter(domain=self.domain).all() ], + 'es_indexing_message': celery_task_tracker.get_message() }) return context diff --git a/corehq/apps/geospatial/views.py b/corehq/apps/geospatial/views.py index b11789345787..076f8c689c8d 100644 --- a/corehq/apps/geospatial/views.py +++ b/corehq/apps/geospatial/views.py @@ -47,7 +47,11 @@ from corehq.form_processor.models import CommCareCase from corehq.util.timezones.utils import get_timezone -from .const import GPS_POINT_CASE_PROPERTY, POLYGON_COLLECTION_GEOJSON_SCHEMA +from .const import ( + GPS_POINT_CASE_PROPERTY, + POLYGON_COLLECTION_GEOJSON_SCHEMA, + INDEX_ES_TASK_HELPER_BASE_KEY, +) from .models import GeoConfig, GeoPolygon from .utils import ( CaseOwnerUpdate, @@ -59,6 +63,7 @@ set_case_gps_property, set_user_gps_property, update_cases_owner, + get_celery_task_tracker, ) @@ -66,6 +71,16 @@ def geospatial_default(request, *args, **kwargs): return HttpResponseRedirect(CaseManagementMap.get_url(*args, **kwargs)) +class BaseGeospatialView(BaseDomainView): + + @property + def main_context(self): + context = super().main_context + celery_task_tracker = get_celery_task_tracker(self.domain, base_key=INDEX_ES_TASK_HELPER_BASE_KEY) + context['es_indexing_message'] = celery_task_tracker.get_message() + return context + + class CaseDisbursementAlgorithm(BaseDomainView): urlname = "case_disbursement" @@ -151,7 +166,7 @@ def delete(self, request, *args, **kwargs): }) -class BaseConfigView(BaseDomainView): +class BaseConfigView(BaseGeospatialView): section_name = _("Data") @method_decorator(toggles.GEOSPATIAL.required_decorator()) @@ -229,7 +244,7 @@ def page_context(self): return context -class GPSCaptureView(BaseDomainView): +class GPSCaptureView(BaseGeospatialView): urlname = 'gps_capture' template_name = 'gps_capture_view.html' From 5409b020af384aa54ac7d498d69c870db9d26d16 Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Thu, 12 Sep 2024 13:59:17 +0200 Subject: [PATCH 006/148] display task message --- corehq/apps/geospatial/templates/case_grouping_map.html | 1 + corehq/apps/geospatial/templates/case_management.html | 1 + .../templates/geospatial/case_management_base.html | 1 + .../templates/geospatial/partials/index_alert.html | 9 +++++++++ .../apps/geospatial/templates/geospatial/settings.html | 1 + corehq/apps/geospatial/templates/gps_capture_view.html | 2 ++ 6 files changed, 15 insertions(+) create mode 100644 corehq/apps/geospatial/templates/geospatial/partials/index_alert.html diff --git a/corehq/apps/geospatial/templates/case_grouping_map.html b/corehq/apps/geospatial/templates/case_grouping_map.html index 16739daeb771..18152231f7c8 100644 --- a/corehq/apps/geospatial/templates/case_grouping_map.html +++ b/corehq/apps/geospatial/templates/case_grouping_map.html @@ -4,6 +4,7 @@ {% load hq_shared_tags %} {% block reportcontent %} +{% include 'geospatial/partials/index_alert.html' %}
diff --git a/corehq/apps/geospatial/templates/case_management.html b/corehq/apps/geospatial/templates/case_management.html index dcb46694a9cd..7d9bf4ef5694 100644 --- a/corehq/apps/geospatial/templates/case_management.html +++ b/corehq/apps/geospatial/templates/case_management.html @@ -2,6 +2,7 @@ {% load i18n %} {% block reportcontent %} +{% include 'geospatial/partials/index_alert.html' %}
diff --git a/corehq/apps/geospatial/templates/geospatial/case_management_base.html b/corehq/apps/geospatial/templates/geospatial/case_management_base.html index 78e070943920..4c1037cac6ea 100644 --- a/corehq/apps/geospatial/templates/geospatial/case_management_base.html +++ b/corehq/apps/geospatial/templates/geospatial/case_management_base.html @@ -23,3 +23,4 @@ {% registerurl 'location_search' domain %} {% registerurl 'reassign_cases' domain %} {% endblock %} +{% include 'geospatial/partials/index_alert.html' %} diff --git a/corehq/apps/geospatial/templates/geospatial/partials/index_alert.html b/corehq/apps/geospatial/templates/geospatial/partials/index_alert.html new file mode 100644 index 000000000000..2d478281cd68 --- /dev/null +++ b/corehq/apps/geospatial/templates/geospatial/partials/index_alert.html @@ -0,0 +1,9 @@ +{% load i18n %} + +{% if es_indexing_message %} +
+

+ {{ es_indexing_message }} +

+
+{% endif %} \ No newline at end of file diff --git a/corehq/apps/geospatial/templates/geospatial/settings.html b/corehq/apps/geospatial/templates/geospatial/settings.html index 6d7b6a935eca..cc587f25e22a 100644 --- a/corehq/apps/geospatial/templates/geospatial/settings.html +++ b/corehq/apps/geospatial/templates/geospatial/settings.html @@ -13,6 +13,7 @@ {% initial_page_data 'road_network_algorithm_slug' road_network_algorithm_slug %}
+ {% include 'geospatial/partials/index_alert.html' %} {% crispy form %}
{% endblock %} diff --git a/corehq/apps/geospatial/templates/gps_capture_view.html b/corehq/apps/geospatial/templates/gps_capture_view.html index 819dd74bad7d..d27ca6628194 100644 --- a/corehq/apps/geospatial/templates/gps_capture_view.html +++ b/corehq/apps/geospatial/templates/gps_capture_view.html @@ -24,7 +24,9 @@ {% registerurl 'paginate_mobile_workers' domain %} {% initial_page_data 'case_types_with_gps' case_types_with_gps %} {% initial_page_data 'couch_user_username' couch_user_username %} +{% registerurl 'geo_polygons' domain %} +{% include 'geospatial/partials/index_alert.html' %}
  • Debugging During Development
  • +
  • + Loading Indicators + +
  • {% endblock toc %} @@ -246,4 +254,49 @@

    You can check out the HqHtmxDebugMixin directly for additional documentation and usage guidance.

    + +

    + Loading Indicators +

    +

    + You can use the hx-indicator attribute + (see docs here) + to mark which element gets the htmx-request class appended to it during a request. + We've added custom styling to _htmx.scss to support the common states outlined + later in this section. +

    +

    + By default, if an element triggers an HTMX request, it will automatically get the htmx-request + CSS class applied to it. No extra usage of the hx-indicator attribute is necessary. The + example submitting elements below showcase this default behavior without the need for + specific hx-indicator usage. +

    +

    + It's often a great idea to pair button requests with hx-disabled-elt="this" + (see docs for hx-disabled-elt), + like the examples below, so that the requesting element or related element is disabled during the request. +

    +

    + Buttons +

    + {% include 'styleguide/bootstrap5/code_display.html' with content=examples.loading_button %} + +

    + Checkboxes +

    + {% include 'styleguide/bootstrap5/code_display.html' with content=examples.loading_checkbox %} + +

    + Forms +

    +

    + In the example below, note that the form is the triggering element, so the + hx-disabled-elt value is set to find button to disable all button children + of that form during an HTMX request. Since form is the submitting element, by default + it has the htmx-request class applied. Our styles ensure that + button type="submit" elements of a submitting form show the loading indicator during + an HTMX request. +

    + {% include 'styleguide/bootstrap5/code_display.html' with content=examples.loading_form %} + {% endblock content %} diff --git a/corehq/apps/styleguide/urls.py b/corehq/apps/styleguide/urls.py index c09538c2ca25..7857cadf0370 100644 --- a/corehq/apps/styleguide/urls.py +++ b/corehq/apps/styleguide/urls.py @@ -57,6 +57,8 @@ name="styleguide_datatables_data"), url(r'^b5/data/paginated_table_data$', bootstrap5_data.paginated_table_data, name="styleguide_paginated_table_data"), + url(r'^b5/data/a_hanging_view$', bootstrap5_data.a_hanging_view, + name="styleguide_a_hanging_view"), url(r'^b5/example/', include(example_urlpatterns)), url(r'^b5/guidelines/$', bootstrap5.styleguide_code_guidelines, name="styleguide_code_guidelines_b5"), diff --git a/corehq/apps/styleguide/views/bootstrap5_data.py b/corehq/apps/styleguide/views/bootstrap5_data.py index 2403d3002f07..69f9faee8e82 100644 --- a/corehq/apps/styleguide/views/bootstrap5_data.py +++ b/corehq/apps/styleguide/views/bootstrap5_data.py @@ -3,7 +3,7 @@ from collections import namedtuple from gettext import gettext -from django.http import JsonResponse +from django.http import JsonResponse, HttpResponse from django.shortcuts import render from corehq.apps.styleguide.utils import get_fake_tabular_data @@ -111,3 +111,8 @@ def paginated_table_data(request): "total": len(fake_data), "rows": fake_data[start:end], }) + + +def a_hanging_view(request): + time.sleep(10) + return HttpResponse("Done") From ead151563d93c26f55f4f646c0da497a842f6852 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Mon, 21 Oct 2024 09:06:49 -0400 Subject: [PATCH 055/148] Update comment that referenced nose --- custom/abt/reports/tests/test_flagspecs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/abt/reports/tests/test_flagspecs.py b/custom/abt/reports/tests/test_flagspecs.py index ccc529281647..37b9dc82a1bb 100644 --- a/custom/abt/reports/tests/test_flagspecs.py +++ b/custom/abt/reports/tests/test_flagspecs.py @@ -12,4 +12,4 @@ def test_yaml_formatting(filename): try: load(f.read(), Loader=Loader) except parser.ParserError: - assert False # nose will tell us which file, and what went wrong. + assert False # the test runner will tell us which file, and what went wrong. From c833f7be396977ab9548326ce3825ad9bcd0fcc4 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Thu, 24 Oct 2024 08:06:14 -0400 Subject: [PATCH 056/148] Remove unnecessary import in function --- corehq/tests/pytest_plugins/reusedb.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/corehq/tests/pytest_plugins/reusedb.py b/corehq/tests/pytest_plugins/reusedb.py index bcc632b91fd7..b272fdc3aecd 100644 --- a/corehq/tests/pytest_plugins/reusedb.py +++ b/corehq/tests/pytest_plugins/reusedb.py @@ -91,7 +91,6 @@ def pytest_configure(config): @pytest.hookimpl(wrapper=True) def pytest_collection_modifyitems(session, items): """Sort and filter tests, inject database setup""" - import pytest_django.plugin as mod django_key = None class items_for_django: @@ -104,10 +103,10 @@ def skip_django_modifyitems(): called.append(1) return False - mod.pytest_collection_modifyitems(items_for_django) + django_plugin.pytest_collection_modifyitems(items_for_django) called = [] # use patch to skip django-pytest pytest_collection_modifyitems - with patch.object(mod, "django_settings_is_configured", skip_django_modifyitems): + with patch.object(django_plugin, "django_settings_is_configured", skip_django_modifyitems): yield assert called, "django_settings_is_configured patch was ineffective. " \ "HQ-speicific test filtering and sorting may not have happened." From 8de6ea7deb7f48e79f5d8f4837935cbd5fd631d1 Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Fri, 22 Nov 2024 10:25:43 +0200 Subject: [PATCH 057/148] refactor alert messages --- .../templates/geospatial/partials/index_alert.html | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/corehq/apps/geospatial/templates/geospatial/partials/index_alert.html b/corehq/apps/geospatial/templates/geospatial/partials/index_alert.html index 20981d04e1ca..629b883fb28e 100644 --- a/corehq/apps/geospatial/templates/geospatial/partials/index_alert.html +++ b/corehq/apps/geospatial/templates/geospatial/partials/index_alert.html @@ -5,13 +5,14 @@

    {% if task_status.error_slug == 'TOO_MANY_CASES' %} {% blocktrans %} - This domain contains too many cases and so they will not be made available - for use by this feature. Please reach out to support. + Existing cases in the domain were not processed to be available in Microplanning reports + because there were too many to be processed. New or updated cases will still be available + for use for Microplanning. Please reach out to support if you need support with existing cases. {% endblocktrans %} {% elif task_status.error_slug == 'CELERY' %} {% blocktrans %} - Oops! Something went wrong while attempting to make cases ready for use - by this feature. Please reach out to support. + Oops! Something went wrong while processing existing cases to be available in Microplanning + reports. Please reach out to support. {% endblocktrans %} {% endif %}

    @@ -20,7 +21,8 @@

    {% blocktrans %} - Cases are being made ready for use by this feature. Please be patient. + Existing cases in the domain are being processed to be available in + Microplanning reports. Please be patient. {% endblocktrans %} ({{ task_status.progress}}%)

    From 6731670351c080a27da2ad86edc6872aa6e14160 Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Fri, 22 Nov 2024 10:44:29 +0200 Subject: [PATCH 058/148] specify type when adding argument --- .../management/commands/index_geolocation_case_properties.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py b/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py index 76fc411ef91e..a0cfd0ca91a1 100644 --- a/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py +++ b/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py @@ -14,13 +14,13 @@ class Command(BaseCommand): def add_arguments(self, parser): parser.add_argument('domain') parser.add_argument('--case_type', required=False) - parser.add_argument('--query_limit', required=False, default=DEFAULT_QUERY_LIMIT) + parser.add_argument('--query_limit', type=int, required=False, default=DEFAULT_QUERY_LIMIT) parser.add_argument('--chunk_size', required=False) def handle(self, *args, **options): domain = options['domain'] case_type = options.get('case_type') - query_limit = int(options.get('query_limit')) + query_limit = options.get('query_limit') chunk_size = options.get('chunk_size') index_case_docs(domain, query_limit, chunk_size, case_type) From 1de4f927ad98c2b1eb96ed16d4c9728f5e9ec79a Mon Sep 17 00:00:00 2001 From: Zandre Engelbrecht Date: Fri, 22 Nov 2024 11:22:46 +0200 Subject: [PATCH 059/148] do not sort and offset if less than query limit --- corehq/apps/geospatial/es.py | 10 +++++++--- .../commands/index_geolocation_case_properties.py | 10 +++++++++- .../geospatial/management/commands/index_utils.py | 12 ++++++++++-- corehq/apps/geospatial/tasks.py | 1 + 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/corehq/apps/geospatial/es.py b/corehq/apps/geospatial/es.py index 9605c1168214..6f55ad326691 100644 --- a/corehq/apps/geospatial/es.py +++ b/corehq/apps/geospatial/es.py @@ -136,7 +136,9 @@ def mid(lower, upper): return ceil(lower + (upper - lower) / 2) -def case_query_for_missing_geopoint_val(domain, geo_case_property, case_type=None, size=None, offset=0): +def case_query_for_missing_geopoint_val( + domain, geo_case_property, case_type=None, size=None, offset=0, should_sort=False +): query = ( CaseSearchES() .domain(domain) @@ -144,8 +146,10 @@ def case_query_for_missing_geopoint_val(domain, geo_case_property, case_type=Non ) if case_type: query = query.case_type(case_type) - query.sort('opened_on') - query.start(offset) + if should_sort: + query.sort('opened_on') + if offset: + query.start(offset) if size: query = query.size(size) return query diff --git a/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py b/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py index a0cfd0ca91a1..4518cf0bc98e 100644 --- a/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py +++ b/corehq/apps/geospatial/management/commands/index_geolocation_case_properties.py @@ -36,4 +36,12 @@ def index_case_docs(domain, query_limit=DEFAULT_QUERY_LIMIT, chunk_size=DEFAULT_ print(f"Cases will be processed in {batch_count} batches") for i in range(batch_count): print(f'Processing {i+1}/{batch_count}') - process_batch(domain, geo_case_property, case_type, query_limit, chunk_size, with_progress=True) + process_batch( + domain, + geo_case_property, + case_type, + query_limit, + chunk_size, + with_progress=True, + total_count=count + ) diff --git a/corehq/apps/geospatial/management/commands/index_utils.py b/corehq/apps/geospatial/management/commands/index_utils.py index 63a227345888..6dce36f2fa02 100644 --- a/corehq/apps/geospatial/management/commands/index_utils.py +++ b/corehq/apps/geospatial/management/commands/index_utils.py @@ -6,9 +6,17 @@ from corehq.util.log import with_progress_bar -def process_batch(domain, geo_case_property, case_type, query_limit, chunk_size, with_progress=False, offset=0): +def process_batch( + domain, geo_case_property, case_type, + query_limit, + chunk_size, + total_count, + with_progress=False, + offset=0, +): + should_sort = bool(total_count > query_limit) query = case_query_for_missing_geopoint_val( - domain, geo_case_property, case_type, size=query_limit, offset=offset + domain, geo_case_property, case_type, size=query_limit, offset=offset, should_sort=should_sort ) case_ids = query.get_ids() _index_case_ids(domain, case_ids, chunk_size, with_progress) diff --git a/corehq/apps/geospatial/tasks.py b/corehq/apps/geospatial/tasks.py index d6b05b936ddd..c0f5160bb89a 100644 --- a/corehq/apps/geospatial/tasks.py +++ b/corehq/apps/geospatial/tasks.py @@ -54,6 +54,7 @@ def index_es_docs_with_location_props(domain): query_limit=limit, chunk_size=DEFAULT_CHUNK_SIZE, offset=i * DEFAULT_QUERY_LIMIT, + total_count=doc_count ) current_batch = i + 1 celery_task_tracker.update_progress( From 3d7465badc673ee56d15b5cd76f72a709f7a87b8 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Fri, 22 Nov 2024 09:07:54 -0500 Subject: [PATCH 060/148] Improve db blocker error on couch access --- corehq/tests/pytest_plugins/reusedb.py | 13 ++++++++++++- corehq/tests/test_reusedb.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/corehq/tests/pytest_plugins/reusedb.py b/corehq/tests/pytest_plugins/reusedb.py index b272fdc3aecd..fcc5fb5979fb 100644 --- a/corehq/tests/pytest_plugins/reusedb.py +++ b/corehq/tests/pytest_plugins/reusedb.py @@ -421,7 +421,7 @@ class CouchSpec(object): def mock_couch(app): dbname = dbs.get(app, main_db_url).rsplit("/", 1)[1] - return Mock(name=dbname, dbname=dbname, spec_set=CouchSpec) + return BlockedMock(name=dbname, dbname=dbname, spec_set=CouchSpec) # register our dbs with the extension document classes main_db_url = settings.COUCH_DATABASE @@ -442,4 +442,15 @@ def unblock(): return block, unblock +class BlockedMock(Mock): + def __getattr__(self, name): + try: + return super().__getattr__(name) + except AttributeError: + raise RuntimeError( + 'Database access not allowed, use the "django_db" mark, or ' + 'the "db" or "transactional_db" fixtures to enable it.' + ) + + _db_context = DeferredDatabaseContext() diff --git a/corehq/tests/test_reusedb.py b/corehq/tests/test_reusedb.py index bd63060c7845..56ce4dc27273 100644 --- a/corehq/tests/test_reusedb.py +++ b/corehq/tests/test_reusedb.py @@ -13,7 +13,7 @@ def test_database_blocker(): assert not settings.DB_ENABLED - with pytest.raises(AttributeError, match="Mock object has no attribute 'info'"): + with pytest.raises(RuntimeError, match=re.compile("^Database access not allowed")): CouchModel.get_db().info with pytest.raises(RuntimeError, match=re.compile("^Database access not allowed")): From 158f15bcf07d233959e532982330c0df339f1d8b Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Fri, 22 Nov 2024 09:21:55 -0500 Subject: [PATCH 061/148] Improve test cleanup The error raised if a "long_request" domain existed in Couch (due to previous incomplete test cleanup) was unintuitive (TypeError: the document is not saved). The root cause (NameUnavailableException: long_request) was buried back in the traceback. --- corehq/tests/test_middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/corehq/tests/test_middleware.py b/corehq/tests/test_middleware.py index 2f0f78d48bfb..fb0252f5bfca 100644 --- a/corehq/tests/test_middleware.py +++ b/corehq/tests/test_middleware.py @@ -176,8 +176,8 @@ class TestLogLongRequestMiddlewareReports(TestCase): def setUpClass(cls): super().setUpClass() cls.domain = Domain(name="long_request", is_active=True) - cls.addClassCleanup(cls.domain.delete) cls.domain.save() + cls.addClassCleanup(cls.domain.delete) cls.username = 'fingile' cls.password = '*******' From 859b51df933661f5ad1eb4bc4d91143b85d00289 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Thu, 24 Oct 2024 10:30:28 -0400 Subject: [PATCH 062/148] Warn on premature database setup --- corehq/tests/pytest_plugins/reusedb.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/corehq/tests/pytest_plugins/reusedb.py b/corehq/tests/pytest_plugins/reusedb.py index fcc5fb5979fb..36e260b53f54 100644 --- a/corehq/tests/pytest_plugins/reusedb.py +++ b/corehq/tests/pytest_plugins/reusedb.py @@ -15,6 +15,7 @@ import logging import os import sys +import warnings from contextlib import ExitStack, contextmanager, nullcontext from functools import partial from unittest.mock import Mock, patch @@ -42,6 +43,8 @@ from ..tools import nottest log = logging.getLogger(__name__) +_test_sorting_key = pytest.StashKey() +_premature_db_did_warn = pytest.StashKey() REUSE_DB_HELP = """ To be used in conjunction with the environment variable REUSE_DB=1. @@ -114,11 +117,21 @@ def skip_django_modifyitems(): "setup may not be done at the correct point in the test run." +@pytest.hookimpl(trylast=True) +def pytest_runtest_teardown(item): + """Check for premature database setup""" + if not item.session.stash.get(_premature_db_did_warn, False): + sortkey = item.session.stash[_test_sorting_key] + if _db_context.is_setup and sortkey(item) == 0: + item.session.stash[_premature_db_did_warn] = True # warn only once + warnings.warn(f"non-database test {item.nodeid} triggered database setup") + + def filter_and_sort(items, key, session): def is_db_test(item): return bool(new_key(item)) - new_key = reorder(key) + new_key = session.stash[_test_sorting_key] = reorder(key) if session.config.should_run_database_tests == "only": should_run = is_db_test elif session.config.should_run_database_tests == "skip": @@ -194,6 +207,10 @@ class db_blocker: class DeferredDatabaseContext: + @property + def is_setup(self): + return "setup_databases" in self.__dict__ + @timelimit(480) def setup_databases(self, db_blocker): """Setup databases for tests""" @@ -213,9 +230,9 @@ def teardown(do_teardown): with db_blocker.unblock(): do_teardown() - assert "teardown_databases" not in self.__dict__, "already set up" - db_blocker = get_request().getfixturevalue("django_db_blocker") + assert not self.is_setup, "already set up" self.setup_databases = lambda b: None # do not set up more than once + db_blocker = get_request().getfixturevalue("django_db_blocker") session = get_request().session with ExitStack() as stack: try: From 5853da69fd7c2121017a50f69ad2cc51208c29a2 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Thu, 24 Oct 2024 10:52:31 -0400 Subject: [PATCH 063/148] Move non-db test out of module with db fixture custom/inddex/tests/test_master_report.py has an autouse fixture that uses databases, so non-db tests cannot live there. --- custom/inddex/tests/test_doctests.py | 8 ++++++++ custom/inddex/tests/test_master_report.py | 11 +---------- 2 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 custom/inddex/tests/test_doctests.py diff --git a/custom/inddex/tests/test_doctests.py b/custom/inddex/tests/test_doctests.py new file mode 100644 index 000000000000..b899c35376b0 --- /dev/null +++ b/custom/inddex/tests/test_doctests.py @@ -0,0 +1,8 @@ +import doctest + +import custom.inddex.reports.r4_nutrient_stats + + +def test_doctests(): + results = doctest.testmod(custom.inddex.reports.r4_nutrient_stats) + assert results.failed == 0 diff --git a/custom/inddex/tests/test_master_report.py b/custom/inddex/tests/test_master_report.py index bff108e389d3..aef1897a503d 100644 --- a/custom/inddex/tests/test_master_report.py +++ b/custom/inddex/tests/test_master_report.py @@ -1,10 +1,9 @@ import csv -import doctest import os from contextlib import ExitStack from datetime import date -from django.test import SimpleTestCase, TestCase +from django.test import TestCase from django.utils.functional import cached_property from memoized import memoized @@ -13,7 +12,6 @@ from dimagi.utils.dates import DateSpan -import custom.inddex.reports.r4_nutrient_stats from corehq.apps.domain.shortcuts import create_domain from corehq.apps.fixtures.models import LookupTable from corehq.apps.userreports.util import get_indicator_adapter, get_ucr_datasource_config_by_id @@ -302,10 +300,3 @@ def test_sharing_filtered_food_data(self): filter_selections={'owner_id': ['not-a-user']}) self.assertEqual([], list(IntakeData(food_data).rows)) self.assertEqual([], list(DailyIntakeData(food_data).rows)) - - -class DocTests(SimpleTestCase): - - def test_doctests(self): - results = doctest.testmod(custom.inddex.reports.r4_nutrient_stats) - self.assertEqual(results.failed, 0) From a908d8eb05f6af4285da97dd5ae58998c0a1ad99 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Thu, 24 Oct 2024 10:54:11 -0400 Subject: [PATCH 064/148] Run all test_master_report tests on one test node --- custom/inddex/tests/test_master_report.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/custom/inddex/tests/test_master_report.py b/custom/inddex/tests/test_master_report.py index aef1897a503d..84c6cbcc555a 100644 --- a/custom/inddex/tests/test_master_report.py +++ b/custom/inddex/tests/test_master_report.py @@ -97,6 +97,15 @@ def with_db(func): yield +def setup_module(): + """Make dividedwerun run all tests in this module on the same node + + This could be eliminated if dividedwerun had a way to determine that + tests in this module use a fixture (inddex_domain) that uses the + database. + """ + + @memoized def get_food_data(*args, **kwargs): # This class takes a while to run. Memoizing lets me share between tests From f6b2732991d6e0c03357b6afb65267274c0b35ce Mon Sep 17 00:00:00 2001 From: Matt Riley Date: Fri, 22 Nov 2024 12:04:08 -0500 Subject: [PATCH 065/148] Bootstrap 5 Migration - Rebuilt diffs --- .../hqwebapp/includes/global_navigation_bar.html.diff.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/includes/global_navigation_bar.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/includes/global_navigation_bar.html.diff.txt index 4fa8d36f7a94..903075c84570 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/includes/global_navigation_bar.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/includes/global_navigation_bar.html.diff.txt @@ -37,7 +37,7 @@ - - -
  • -- +- - {% trans "Enterprise Console" %} - -
  • @@ -86,7 +86,7 @@ +
  • + +
  • -+ ++ + {% trans "Enterprise Console" %} -
  • From e916560d502cbb68a8038546fc134d1403cf4051 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Fri, 22 Nov 2024 13:35:46 -0500 Subject: [PATCH 066/148] Migrated groups pages --- corehq/apps/groups/static/groups/js/all_groups.js | 1 + corehq/apps/groups/static/groups/js/group_members.js | 1 + corehq/apps/groups/templates/groups/all_groups.html | 3 ++- corehq/apps/groups/templates/groups/group_members.html | 4 ++-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/corehq/apps/groups/static/groups/js/all_groups.js b/corehq/apps/groups/static/groups/js/all_groups.js index 6e1813221973..7b0c01197ad0 100644 --- a/corehq/apps/groups/static/groups/js/all_groups.js +++ b/corehq/apps/groups/static/groups/js/all_groups.js @@ -5,6 +5,7 @@ hqDefine("groups/js/all_groups", [ 'es6!hqwebapp/js/bootstrap5_loader', // Just importing main.py so the post-link function is accessible, function parameter not needed 'hqwebapp/js/bootstrap5/main', + 'commcarehq', ], function ( $, googleAnalytics, diff --git a/corehq/apps/groups/static/groups/js/group_members.js b/corehq/apps/groups/static/groups/js/group_members.js index d231895909db..8f7bd45c2884 100644 --- a/corehq/apps/groups/static/groups/js/group_members.js +++ b/corehq/apps/groups/static/groups/js/group_members.js @@ -9,6 +9,7 @@ hqDefine("groups/js/group_members", [ "hqwebapp/js/ui_elements/bootstrap5/ui-element-key-val-list", "hqwebapp/js/select_2_ajax_widget", // "Group Membership" select2 "hqwebapp/js/components/select_toggle", // select toggle for "Edit Setings" popup + "commcarehq", ], function ( $, _, diff --git a/corehq/apps/groups/templates/groups/all_groups.html b/corehq/apps/groups/templates/groups/all_groups.html index 17c669dc89ce..021d84ea19ac 100644 --- a/corehq/apps/groups/templates/groups/all_groups.html +++ b/corehq/apps/groups/templates/groups/all_groups.html @@ -1,7 +1,8 @@ {% extends "hqwebapp/bootstrap5/base_section.html" %} {% load i18n %} {% load hq_shared_tags %} -{% requirejs_main_b5 "groups/js/all_groups" %} + +{% js_entry "groups/js/all_groups" %} {% block page_title %} {% trans "Groups" %} diff --git a/corehq/apps/groups/templates/groups/group_members.html b/corehq/apps/groups/templates/groups/group_members.html index 2544ab3d52bc..d9d279001254 100644 --- a/corehq/apps/groups/templates/groups/group_members.html +++ b/corehq/apps/groups/templates/groups/group_members.html @@ -11,7 +11,7 @@ {% endblock %} -{% requirejs_main_b5 "groups/js/group_members" %} +{% js_entry "groups/js/group_members" %} {% block page_title %} {% if request.is_view_only %} @@ -81,7 +81,7 @@ data-html="true" data-content="{% blocktrans %} For all active mobile workers in this group, and for each phone number, this will - initiate an SMS verification workflow. When a user replies to the SMS< their phone + initiate an SMS verification workflow. When a user replies to the SMS, their phone number will be verified.

    If the phone number is already verified or if the phone number is already in use by another contact in the system, nothing will happen. If the phone number is pending verification, the verification SMS From b45ca36fdcefd9b00339287e3eb9e29dc1d62d79 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Fri, 22 Nov 2024 13:47:27 -0500 Subject: [PATCH 067/148] Added datatables aliases and migration chat contacts page --- corehq/apps/sms/static/sms/js/chat_contacts.js | 1 + corehq/apps/sms/templates/sms/chat_contacts.html | 2 +- webpack/webpack.b3.common.js | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/corehq/apps/sms/static/sms/js/chat_contacts.js b/corehq/apps/sms/static/sms/js/chat_contacts.js index 5ee8b23e0b9f..288e1df5bb01 100644 --- a/corehq/apps/sms/static/sms/js/chat_contacts.js +++ b/corehq/apps/sms/static/sms/js/chat_contacts.js @@ -4,6 +4,7 @@ hqDefine('sms/js/chat_contacts', [ 'underscore', 'hqwebapp/js/initial_page_data', 'datatables.bootstrap', + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/sms/templates/sms/chat_contacts.html b/corehq/apps/sms/templates/sms/chat_contacts.html index f993516b6094..e069b02a7df8 100644 --- a/corehq/apps/sms/templates/sms/chat_contacts.html +++ b/corehq/apps/sms/templates/sms/chat_contacts.html @@ -2,7 +2,7 @@ {% load i18n %} {% load hq_shared_tags %} -{% requirejs_main 'sms/js/chat_contacts' %} +{% js_entry_b3 'sms/js/chat_contacts' %} {% block page_content %} {% registerurl "chat_contact_list" domain %} diff --git a/webpack/webpack.b3.common.js b/webpack/webpack.b3.common.js index 0253e96f0b15..3764dc4a6d10 100644 --- a/webpack/webpack.b3.common.js +++ b/webpack/webpack.b3.common.js @@ -24,6 +24,8 @@ module.exports = Object.assign({}, commonDefault, { resolve: { alias: Object.assign({}, commonDefault.resolve.alias, { "commcarehq": path.resolve(utils.getStaticPathForApp('hqwebapp', 'js/bootstrap3/'), 'commcarehq'), + "datatables": "datatables.net/js/jquery.dataTables.min", + "datatables.bootstrap": "datatables-bootstrap3/BS3/assets/js/datatables", }), }, From 9a30b0f30675b5349cc1034de8a600cb56c5645a Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Fri, 22 Nov 2024 14:43:59 -0500 Subject: [PATCH 068/148] Moved ko component templates and demo modals above js in base.html All HTML should be above the start of js in this template. This was causing the feature flags page to throw an error about the select-toggle template not being found. --- .../hqwebapp/templates/hqwebapp/base.html | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/corehq/apps/hqwebapp/templates/hqwebapp/base.html b/corehq/apps/hqwebapp/templates/hqwebapp/base.html index 9e3628f52ba2..7c124e8154d2 100644 --- a/corehq/apps/hqwebapp/templates/hqwebapp/base.html +++ b/corehq/apps/hqwebapp/templates/hqwebapp/base.html @@ -305,6 +305,29 @@ {% endblock %}
    + {% if is_demo_visible %} + {% if use_bootstrap5 %} + {% include "hqwebapp/partials/bootstrap5/get_demo_modals.html" %} + {% else %} + {% include "hqwebapp/partials/bootstrap3/get_demo_modals.html" %} + {% endif %} + {% endif %} + + {# Knockout component templates #} + {% if use_bootstrap5 %} + {% include 'hqwebapp/partials/bootstrap5/ko_pagination.html' %} + {% include 'hqwebapp/partials/bootstrap5/ko_inline_edit.html' %} + {% include 'hqwebapp/partials/bootstrap5/ko_search_box.html' %} + {% include 'hqwebapp/partials/bootstrap5/ko_select_toggle.html' %} + {% include 'hqwebapp/partials/bootstrap5/ko_feedback.html' %} + {% else %} + {% include 'hqwebapp/partials/bootstrap3/ko_pagination.html' %} + {% include 'hqwebapp/partials/bootstrap3/ko_inline_edit.html' %} + {% include 'hqwebapp/partials/bootstrap3/ko_search_box.html' %} + {% include 'hqwebapp/partials/bootstrap3/ko_select_toggle.html' %} + {% include 'hqwebapp/partials/bootstrap3/ko_feedback.html' %} + {% endif %} + {# javascript below this line #} {% include "hqwebapp/partials/requirejs.html" with BASE_MAIN=True %} @@ -421,29 +444,6 @@ {% endif %} {% endif %} - {% if is_demo_visible %} - {% if use_bootstrap5 %} - {% include "hqwebapp/partials/bootstrap5/get_demo_modals.html" %} - {% else %} - {% include "hqwebapp/partials/bootstrap3/get_demo_modals.html" %} - {% endif %} - {% endif %} - - {# Knockout component templates #} - {% if use_bootstrap5 %} - {% include 'hqwebapp/partials/bootstrap5/ko_pagination.html' %} - {% include 'hqwebapp/partials/bootstrap5/ko_inline_edit.html' %} - {% include 'hqwebapp/partials/bootstrap5/ko_search_box.html' %} - {% include 'hqwebapp/partials/bootstrap5/ko_select_toggle.html' %} - {% include 'hqwebapp/partials/bootstrap5/ko_feedback.html' %} - {% else %} - {% include 'hqwebapp/partials/bootstrap3/ko_pagination.html' %} - {% include 'hqwebapp/partials/bootstrap3/ko_inline_edit.html' %} - {% include 'hqwebapp/partials/bootstrap3/ko_search_box.html' %} - {% include 'hqwebapp/partials/bootstrap3/ko_select_toggle.html' %} - {% include 'hqwebapp/partials/bootstrap3/ko_feedback.html' %} - {% endif %} - {% if show_overdue_invoice_modal and not use_js_bundler %} {% if use_bootstrap5 %} From 667f188fc4e7631c613dbe5c9832437faca51fb2 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Fri, 22 Nov 2024 14:51:19 -0500 Subject: [PATCH 069/148] Migrated toggle_ui app --- corehq/apps/toggle_ui/static/toggle_ui/js/edit-flag.js | 1 + corehq/apps/toggle_ui/static/toggle_ui/js/flags.js | 1 + corehq/apps/toggle_ui/templates/toggle/edit_flag.html | 2 +- corehq/apps/toggle_ui/templates/toggle/flags.html | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/corehq/apps/toggle_ui/static/toggle_ui/js/edit-flag.js b/corehq/apps/toggle_ui/static/toggle_ui/js/edit-flag.js index 748eae578d77..07e115a0b37c 100644 --- a/corehq/apps/toggle_ui/static/toggle_ui/js/edit-flag.js +++ b/corehq/apps/toggle_ui/static/toggle_ui/js/edit-flag.js @@ -5,6 +5,7 @@ hqDefine('toggle_ui/js/edit-flag', [ 'hqwebapp/js/initial_page_data', 'hqwebapp/js/bootstrap3/main', 'hqwebapp/js/bootstrap3/knockout_bindings.ko', // save button + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/toggle_ui/static/toggle_ui/js/flags.js b/corehq/apps/toggle_ui/static/toggle_ui/js/flags.js index 4916f1d1f409..bd08471256f1 100644 --- a/corehq/apps/toggle_ui/static/toggle_ui/js/flags.js +++ b/corehq/apps/toggle_ui/static/toggle_ui/js/flags.js @@ -5,6 +5,7 @@ hqDefine('toggle_ui/js/flags', [ 'hqwebapp/js/bootstrap3/alert_user', 'reports/js/bootstrap3/config.dataTables.bootstrap', 'hqwebapp/js/components/select_toggle', + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/toggle_ui/templates/toggle/edit_flag.html b/corehq/apps/toggle_ui/templates/toggle/edit_flag.html index cc2ea8322c6a..a6dbb5b1bfca 100644 --- a/corehq/apps/toggle_ui/templates/toggle/edit_flag.html +++ b/corehq/apps/toggle_ui/templates/toggle/edit_flag.html @@ -3,7 +3,7 @@ {% load hq_shared_tags %} {% load compress %} -{% requirejs_main 'toggle_ui/js/edit-flag' %} +{% js_entry_b3 'toggle_ui/js/edit-flag' %} {% block title %}{% trans "Edit Feature Flag: " %}{{ static_toggle.label }}{% endblock %} diff --git a/corehq/apps/toggle_ui/templates/toggle/flags.html b/corehq/apps/toggle_ui/templates/toggle/flags.html index 16de14949d00..f5b9ac5bfe23 100644 --- a/corehq/apps/toggle_ui/templates/toggle/flags.html +++ b/corehq/apps/toggle_ui/templates/toggle/flags.html @@ -2,7 +2,7 @@ {% load i18n %} {% load hq_shared_tags %} -{% requirejs_main 'toggle_ui/js/flags' %} +{% js_entry_b3 'toggle_ui/js/flags' %} {% block stylesheets %}{{ block.super }} {% endblock %} -{% block js %} {{ block.super }} - - - -{% endblock %} - {% block page_content %} {% initial_page_data 'uploaders' uploaders_js %} {% registerurl 'hqmedia_references' app.domain app.id %} From 5d27879333c6ae9ec32eadb7b5ed466e193807a3 Mon Sep 17 00:00:00 2001 From: Biyeun Buczyk Date: Mon, 25 Nov 2024 14:56:31 +0000 Subject: [PATCH 079/148] "Bootstrap 5 Migration - Rebuilt diffs" --- .../stylesheets/style-imports.commcarehq.style.diff.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/stylesheets/style-imports.commcarehq.style.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/stylesheets/style-imports.commcarehq.style.diff.txt index 37c4020bc961..32ff4413ad9b 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/stylesheets/style-imports.commcarehq.style.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/stylesheets/style-imports.commcarehq.style.diff.txt @@ -1,6 +1,6 @@ --- +++ -@@ -1,44 +1,95 @@ +@@ -1,44 +1,96 @@ -@import "_hq/includes/variables.less"; -@import "_hq/includes/mixins.less"; @@ -123,6 +123,7 @@ +@import "commcarehq/form_steps"; +@import "commcarehq/helpbubble"; +@import "commcarehq/hubspot"; ++@import "commcarehq/htmx"; +@import "commcarehq/icons"; +@import "commcarehq/inline_edit"; +@import "commcarehq/label"; From 01d70aa4151966bce5ff554af7b85543e8684241 Mon Sep 17 00:00:00 2001 From: Biyeun Buczyk Date: Thu, 21 Nov 2024 20:41:19 +0100 Subject: [PATCH 080/148] fix issue with missing default error template and any actionable template context --- .../hqwebapp/htmx/htmx_action_error.html | 5 +++++ corehq/util/htmx_action.py | 15 ++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 corehq/apps/hqwebapp/templates/hqwebapp/htmx/htmx_action_error.html diff --git a/corehq/apps/hqwebapp/templates/hqwebapp/htmx/htmx_action_error.html b/corehq/apps/hqwebapp/templates/hqwebapp/htmx/htmx_action_error.html new file mode 100644 index 000000000000..c43e1bfac07a --- /dev/null +++ b/corehq/apps/hqwebapp/templates/hqwebapp/htmx/htmx_action_error.html @@ -0,0 +1,5 @@ +{% load i18n %} + +{% blocktrans %} + Encountered error "{{ htmx_error }}" while performing action {{ action }}. +{% endblocktrans %} diff --git a/corehq/util/htmx_action.py b/corehq/util/htmx_action.py index d7badd274f82..2eb330a02a20 100644 --- a/corehq/util/htmx_action.py +++ b/corehq/util/htmx_action.py @@ -49,14 +49,20 @@ def make_edit(request, *args, **kwargs): Example docs here: commcarehq.org/styleguide/b5/htmx_alpine/ See working demo here: commcarehq.org/styleguide/demo/htmx_todo/ """ - default_htmx_error_template = "prototype/htmx/partials/htmx_action_error.html" + default_htmx_error_template = "" - def get_htmx_error_context(self, **kwargs): + def get_htmx_error_context(self, action, htmx_error, **kwargs): """ Use this method to return the context for the HTMX error template. + + :param action: string (the slug for the HTMX action taken) + :param htmx_error: HtmxResponseException :return: dict """ - return {} + return { + "action": action, + "htmx_error": htmx_error, + } def get_htmx_error_template(self, action, htmx_error): """ @@ -117,8 +123,7 @@ def dispatch(self, request, *args, **kwargs): try: response = handler(request, *args, **kwargs) except HtmxResponseException as err: - context = self.get_htmx_error_context(**kwargs) - context["htmx_error"] = err + context = self.get_htmx_error_context(action, err, **kwargs) self.template_name = self.get_htmx_error_template(action, err) return self.render_to_response(context) return response From 12afbba03603ba172ebfc449531dbfc2647690a7 Mon Sep 17 00:00:00 2001 From: Jing Cheng Date: Mon, 25 Nov 2024 10:33:21 -0500 Subject: [PATCH 081/148] Delete ProjectLimit model --- .../migrations/0015_delete_projectlimit.py | 16 ++++++++++++++++ corehq/apps/domain/models.py | 14 -------------- corehq/apps/domain/tests/test_deletion_models.py | 1 - corehq/apps/dump_reload/sql/dump.py | 1 - migrations.lock | 1 + 5 files changed, 17 insertions(+), 16 deletions(-) create mode 100644 corehq/apps/domain/migrations/0015_delete_projectlimit.py diff --git a/corehq/apps/domain/migrations/0015_delete_projectlimit.py b/corehq/apps/domain/migrations/0015_delete_projectlimit.py new file mode 100644 index 000000000000..63f7c46ecad8 --- /dev/null +++ b/corehq/apps/domain/migrations/0015_delete_projectlimit.py @@ -0,0 +1,16 @@ +# Generated by Django 4.2.16 on 2024-11-25 15:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('domain', '0014_appreleasemodesetting'), + ] + + operations = [ + migrations.DeleteModel( + name='ProjectLimit', + ), + ] diff --git a/corehq/apps/domain/models.py b/corehq/apps/domain/models.py index 29550b52f21e..73ade2f0348f 100644 --- a/corehq/apps/domain/models.py +++ b/corehq/apps/domain/models.py @@ -1107,20 +1107,6 @@ def disallowed_ucr_expressions(cls, domain_name): return restricted_expressions - allowed_expressions_for_domain -class ProjectLimitType(): - LIVE_GOOGLE_SHEETS = 'lgs' - - CHOICES = ( - (LIVE_GOOGLE_SHEETS, "Live Google Sheets"), - ) - - -class ProjectLimit(models.Model): - domain = models.CharField(max_length=256, db_index=True) - limit_type = models.CharField(max_length=5, choices=ProjectLimitType.CHOICES) - limit_value = models.IntegerField(default=20) - - class OperatorCallLimitSettings(models.Model): CALL_LIMIT_MINIMUM = 1 CALL_LIMIT_MAXIMUM = 1000 diff --git a/corehq/apps/domain/tests/test_deletion_models.py b/corehq/apps/domain/tests/test_deletion_models.py index 19fbf4af2b01..ca90869a603e 100644 --- a/corehq/apps/domain/tests/test_deletion_models.py +++ b/corehq/apps/domain/tests/test_deletion_models.py @@ -54,7 +54,6 @@ 'cleanup.DeletedCouchDoc', 'cleanup.DeletedSQLDoc', 'domain.DomainAuditRecordEntry', - 'domain.ProjectLimit', 'domain.SuperuserProjectEntryRecord', 'dropbox.DropboxUploadHelper', 'export.DefaultExportSettings', diff --git a/corehq/apps/dump_reload/sql/dump.py b/corehq/apps/dump_reload/sql/dump.py index bd09a7635e07..f683e2d94816 100644 --- a/corehq/apps/dump_reload/sql/dump.py +++ b/corehq/apps/dump_reload/sql/dump.py @@ -215,7 +215,6 @@ FilteredModelIteratorBuilder('domain.AllowedUCRExpressionSettings', SimpleFilter('domain')), FilteredModelIteratorBuilder('domain.DomainAuditRecordEntry', SimpleFilter('domain')), FilteredModelIteratorBuilder('domain.OperatorCallLimitSettings', SimpleFilter('domain')), - FilteredModelIteratorBuilder('domain.ProjectLimit', SimpleFilter('domain')), FilteredModelIteratorBuilder('domain.SMSAccountConfirmationSettings', SimpleFilter('domain')), FilteredModelIteratorBuilder('domain.SuperuserProjectEntryRecord', SimpleFilter('domain')), FilteredModelIteratorBuilder('domain.TransferDomainRequest', SimpleFilter('domain')), diff --git a/migrations.lock b/migrations.lock index 515762313673..abda6fae94de 100644 --- a/migrations.lock +++ b/migrations.lock @@ -407,6 +407,7 @@ domain 0012_operatorcalllimitsettings 0013_accountconfirmationsettings_squashed_0016_alter_smsaccountconfirmationsettings_project_name (4 squashed migrations) 0014_appreleasemodesetting + 0015_delete_projectlimit domain_migration_flags 0001_initial 0002_migrate_data_from_tzmigration From bf28ddb9617f72230ca0752226f085be9c11fbec Mon Sep 17 00:00:00 2001 From: Jing Cheng Date: Mon, 25 Nov 2024 10:52:43 -0500 Subject: [PATCH 082/148] remove gsheet ff --- corehq/toggles/__init__.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/corehq/toggles/__init__.py b/corehq/toggles/__init__.py index fcfa0c0ef4e3..cda57732211c 100644 --- a/corehq/toggles/__init__.py +++ b/corehq/toggles/__init__.py @@ -2467,17 +2467,6 @@ def _commtrackify(domain_name, toggle_is_enabled): """ ) -GOOGLE_SHEETS_INTEGRATION = StaticToggle( - 'google-sheet-integration', - 'Unlock the Google Sheets view in Exports', - TAG_SAAS_CONDITIONAL, - namespaces=[NAMESPACE_USER], - description=""" - Toggle only when testing the new Google Sheet Integration. The Google Sheet Integration can be found - on the Exports page. - """ -) - APP_DEPENDENCIES = StaticToggle( slug='app-dependencies', label='Set Android app dependencies that must be installed before using a CommCare app', From 7ca8f484346b68171481f619a2c08d549ad2701a Mon Sep 17 00:00:00 2001 From: Jing Cheng Date: Mon, 25 Nov 2024 10:53:11 -0500 Subject: [PATCH 083/148] Remove IntegrationFormat --- corehq/apps/export/views/list.py | 14 +++++--------- corehq/ex-submodules/couchexport/models.py | 15 --------------- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/corehq/apps/export/views/list.py b/corehq/apps/export/views/list.py index 2512d3cac3e2..425b3032b100 100644 --- a/corehq/apps/export/views/list.py +++ b/corehq/apps/export/views/list.py @@ -22,7 +22,7 @@ from corehq.apps.accounting.decorators import requires_privilege_with_fallback from corehq.apps.export.exceptions import ExportTooLargeException from corehq.apps.export.views.download import DownloadDETSchemaView -from couchexport.models import Format, IntegrationFormat +from couchexport.models import Format from couchexport.writers import XlsLengthException from dimagi.utils.couch import CriticalSection from dimagi.utils.logging import notify_exception @@ -359,8 +359,7 @@ def bulk_download_url(self): def _should_appear_in_list(self, export): return (export['is_daily_saved_export'] and not export['export_format'] == "html" - and not export['is_odata_config'] - and not IntegrationFormat.is_integration_format(export['export_format'])) + and not export['is_odata_config']) def _edit_view(self, export): from corehq.apps.export.views.edit import EditFormDailySavedExportView, EditCaseDailySavedExportView @@ -400,8 +399,7 @@ def create_export_form_title(self): def _should_appear_in_list(self, export): return (not export['is_daily_saved_export'] - and not export['is_odata_config'] - and not IntegrationFormat.is_integration_format(export['export_format'])) + and not export['is_odata_config']) def _edit_view(self, export): from corehq.apps.export.views.edit import EditNewCustomFormExportView @@ -418,8 +416,7 @@ class CaseExportListHelper(ExportListHelper): def _should_appear_in_list(self, export): return (not export['is_daily_saved_export'] - and not export['is_odata_config'] - and not IntegrationFormat.is_integration_format(export['export_format'])) + and not export['is_odata_config']) def _edit_view(self, export): from corehq.apps.export.views.edit import EditNewCustomCaseExportView @@ -451,8 +448,7 @@ def _priv_check(self): def _should_appear_in_list(self, export): return (export['is_daily_saved_export'] and export['export_format'] == "html" - and not export['is_odata_config'] - and not IntegrationFormat.is_integration_format(export['export_format'])) + and not export['is_odata_config']) def _edit_view(self, export): from corehq.apps.export.views.edit import EditFormFeedView, EditCaseFeedView diff --git a/corehq/ex-submodules/couchexport/models.py b/corehq/ex-submodules/couchexport/models.py index b290f4f348c3..b071225e4d9c 100644 --- a/corehq/ex-submodules/couchexport/models.py +++ b/corehq/ex-submodules/couchexport/models.py @@ -62,18 +62,3 @@ def from_format(cls, format): if format not in cls.VALID_FORMATS: raise URLError("Unsupported export format: %s!" % format) return cls(format, **cls.FORMAT_DICT[format]) - - -class IntegrationFormat(object): - LIVE_GOOGLE_SHEETS = "live_google_sheets" - - VALID_FORMATS = [ - LIVE_GOOGLE_SHEETS, - ] - - @classmethod - def is_integration_format(cls, format): - format = format.lower() - if format in cls.VALID_FORMATS: - return True - return False From 819630cc272e4a76e9caa76fafeb9c65e37bbb67 Mon Sep 17 00:00:00 2001 From: Jing Cheng Date: Mon, 25 Nov 2024 10:46:38 -0500 Subject: [PATCH 084/148] Delete oauth_integrations app --- .../apps/domain/tests/test_deletion_models.py | 1 - .../dump_reload/tests/test_dump_models.py | 3 - corehq/apps/oauth_integrations/__init__.py | 0 .../migrations/0001_initial.py | 26 ---- .../0002_livegooglesheetschedule.py | 23 --- .../0003_livegooglesheetrefreshstatus.py | 24 --- .../migrations/0004_auto_20220304_1139.py | 24 --- .../oauth_integrations/migrations/__init__.py | 0 corehq/apps/oauth_integrations/models.py | 43 ------ .../apps/oauth_integrations/tests/__init__.py | 0 .../oauth_integrations/tests/test_util.py | 89 ----------- .../oauth_integrations/tests/test_views.py | 146 ------------------ corehq/apps/oauth_integrations/urls.py | 16 -- corehq/apps/oauth_integrations/utils.py | 92 ----------- .../apps/oauth_integrations/views/__init__.py | 0 .../apps/oauth_integrations/views/google.py | 100 ------------ migrations.lock | 5 - settings.py | 7 - urls.py | 1 - 19 files changed, 600 deletions(-) delete mode 100644 corehq/apps/oauth_integrations/__init__.py delete mode 100644 corehq/apps/oauth_integrations/migrations/0001_initial.py delete mode 100644 corehq/apps/oauth_integrations/migrations/0002_livegooglesheetschedule.py delete mode 100644 corehq/apps/oauth_integrations/migrations/0003_livegooglesheetrefreshstatus.py delete mode 100644 corehq/apps/oauth_integrations/migrations/0004_auto_20220304_1139.py delete mode 100644 corehq/apps/oauth_integrations/migrations/__init__.py delete mode 100644 corehq/apps/oauth_integrations/models.py delete mode 100644 corehq/apps/oauth_integrations/tests/__init__.py delete mode 100644 corehq/apps/oauth_integrations/tests/test_util.py delete mode 100644 corehq/apps/oauth_integrations/tests/test_views.py delete mode 100644 corehq/apps/oauth_integrations/urls.py delete mode 100644 corehq/apps/oauth_integrations/utils.py delete mode 100644 corehq/apps/oauth_integrations/views/__init__.py delete mode 100644 corehq/apps/oauth_integrations/views/google.py diff --git a/corehq/apps/domain/tests/test_deletion_models.py b/corehq/apps/domain/tests/test_deletion_models.py index ca90869a603e..e62a6b197b20 100644 --- a/corehq/apps/domain/tests/test_deletion_models.py +++ b/corehq/apps/domain/tests/test_deletion_models.py @@ -38,7 +38,6 @@ 'telerivet', 'toggle_ui', 'sso', - 'oauth_integrations', } IGNORE_MODELS = { diff --git a/corehq/apps/dump_reload/tests/test_dump_models.py b/corehq/apps/dump_reload/tests/test_dump_models.py index 0bf30270d94b..f5c9c3eea1c8 100644 --- a/corehq/apps/dump_reload/tests/test_dump_models.py +++ b/corehq/apps/dump_reload/tests/test_dump_models.py @@ -137,9 +137,6 @@ "oauth2_provider.Grant", "oauth2_provider.IDToken", "oauth2_provider.RefreshToken", - "oauth_integrations.GoogleApiToken", - "oauth_integrations.LiveGoogleSheetRefreshStatus", - "oauth_integrations.LiveGoogleSheetSchedule", "registration.AsyncSignupRequest", "registration.RegistrationRequest", "reminders.EmailUsage", diff --git a/corehq/apps/oauth_integrations/__init__.py b/corehq/apps/oauth_integrations/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/corehq/apps/oauth_integrations/migrations/0001_initial.py b/corehq/apps/oauth_integrations/migrations/0001_initial.py deleted file mode 100644 index 2b638c4095a4..000000000000 --- a/corehq/apps/oauth_integrations/migrations/0001_initial.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.2.24 on 2022-01-10 13:57 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='GoogleApiToken', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('token', models.CharField(max_length=700)), - ('date_created', models.DateField(auto_now_add=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='google_api_tokens', to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/corehq/apps/oauth_integrations/migrations/0002_livegooglesheetschedule.py b/corehq/apps/oauth_integrations/migrations/0002_livegooglesheetschedule.py deleted file mode 100644 index 078f1b071a12..000000000000 --- a/corehq/apps/oauth_integrations/migrations/0002_livegooglesheetschedule.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.27 on 2022-02-28 10:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('oauth_integrations', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='LiveGoogleSheetSchedule', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('export_config_id', models.CharField(db_index=True, max_length=250)), - ('is_active', models.BooleanField(default=True)), - ('start_time', models.IntegerField(default=200)), - ('google_sheet_id', models.CharField(max_length=250)), - ], - ), - ] diff --git a/corehq/apps/oauth_integrations/migrations/0003_livegooglesheetrefreshstatus.py b/corehq/apps/oauth_integrations/migrations/0003_livegooglesheetrefreshstatus.py deleted file mode 100644 index df4e94bb6d33..000000000000 --- a/corehq/apps/oauth_integrations/migrations/0003_livegooglesheetrefreshstatus.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.27 on 2022-02-28 11:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('oauth_integrations', '0002_livegooglesheetschedule'), - ] - - operations = [ - migrations.CreateModel( - name='LiveGoogleSheetRefreshStatus', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date_start', models.DateTimeField(auto_now_add=True)), - ('date_end', models.DateTimeField(blank=True, null=True)), - ('refresh_error_reason', models.CharField(choices=[(None, 'No Error'), ('token', 'Invalid Token'), ('timeout', 'Data Timeout'), ('other', 'Other...')], default=None, max_length=7, null=True)), - ('refresh_error_note', models.TextField(blank=True, null=True)), - ('schedule', models.ForeignKey(on_delete=models.CASCADE, to='oauth_integrations.LiveGoogleSheetSchedule')), - ], - ), - ] diff --git a/corehq/apps/oauth_integrations/migrations/0004_auto_20220304_1139.py b/corehq/apps/oauth_integrations/migrations/0004_auto_20220304_1139.py deleted file mode 100644 index 19d83b2b61bc..000000000000 --- a/corehq/apps/oauth_integrations/migrations/0004_auto_20220304_1139.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.27 on 2022-03-04 11:39 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('oauth_integrations', '0003_livegooglesheetrefreshstatus'), - ] - - operations = [ - migrations.AlterField( - model_name='livegooglesheetrefreshstatus', - name='refresh_error_reason', - field=models.CharField(choices=[(None, 'No Error'), ('token', 'Invalid Token'), ('timeout', 'Data Timeout'), ('other', 'Other...')], default=None, max_length=16, null=True), - ), - migrations.AlterField( - model_name='livegooglesheetrefreshstatus', - name='schedule', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='oauth_integrations.LiveGoogleSheetSchedule'), - ), - ] diff --git a/corehq/apps/oauth_integrations/migrations/__init__.py b/corehq/apps/oauth_integrations/migrations/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/corehq/apps/oauth_integrations/models.py b/corehq/apps/oauth_integrations/models.py deleted file mode 100644 index 2262ab9c86cb..000000000000 --- a/corehq/apps/oauth_integrations/models.py +++ /dev/null @@ -1,43 +0,0 @@ -from django.db import models - -from django.contrib.auth.models import User - - -class GoogleApiToken(models.Model): - user = models.ForeignKey(User, related_name='google_api_tokens', on_delete=models.CASCADE) - token = models.CharField(max_length=700) - date_created = models.DateField(auto_now_add=True) - - -class LiveGoogleSheetSchedule(models.Model): - export_config_id = models.CharField(max_length=250, db_index=True) - is_active = models.BooleanField(default=True) - start_time = models.IntegerField(default=200) - google_sheet_id = models.CharField(max_length=250) - - -class LiveGoogleSheetErrorReason(): - NO_ERROR = None - INVALID_TOKEN = 'token' - TIMEOUT = 'timeout' - OTHER = 'other' - - CHOICES = ( - (NO_ERROR, "No Error"), - (INVALID_TOKEN, "Invalid Token"), - (TIMEOUT, "Data Timeout"), - (OTHER, "Other..."), - ) - - -class LiveGoogleSheetRefreshStatus(models.Model): - schedule = models.ForeignKey(LiveGoogleSheetSchedule, on_delete=models.CASCADE) - date_start = models.DateTimeField(auto_now_add=True) - date_end = models.DateTimeField(null=True, blank=True) - refresh_error_reason = models.CharField( - max_length=16, - choices=LiveGoogleSheetErrorReason.CHOICES, - null=True, - default=LiveGoogleSheetErrorReason.NO_ERROR, - ) - refresh_error_note = models.TextField(null=True, blank=True) diff --git a/corehq/apps/oauth_integrations/tests/__init__.py b/corehq/apps/oauth_integrations/tests/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/corehq/apps/oauth_integrations/tests/test_util.py b/corehq/apps/oauth_integrations/tests/test_util.py deleted file mode 100644 index f3126aa3af75..000000000000 --- a/corehq/apps/oauth_integrations/tests/test_util.py +++ /dev/null @@ -1,89 +0,0 @@ -from datetime import datetime - -from django.test import SimpleTestCase, TestCase -from django.contrib.auth.models import User - -from google.oauth2.credentials import Credentials - -from corehq.apps.oauth_integrations.models import GoogleApiToken -from corehq.apps.oauth_integrations.utils import get_token, load_credentials, stringify_credentials - - -class TestUtils(TestCase): - - def setUp(self): - super().setUp() - self.user = User() - self.user.username = 'test@user.com' - self.user.save() - self.credentials = Credentials( - token="token", - refresh_token="refresh_token", - id_token="id_token", - token_uri="token_uri", - client_id="client_id", - client_secret="client_secret", - scopes="scopes", - expiry=datetime(2020, 1, 1) - ) - - def tearDown(self): - self.credentials = None - self.user.delete() - return super().tearDown() - - def test_get_token_with_created_token(self): - GoogleApiToken.objects.create( - user=self.user, - token=stringify_credentials(self.credentials) - ) - - token = get_token(self.user) - - self.assertIsNotNone(token) - self.tearDowntoken() - - def test_get_token_without_token(self): - token = get_token(self.user) - - self.assertIsNone(token) - - def tearDowntoken(self): - objects = GoogleApiToken.objects.get(user=self.user) - objects.delete() - - -class TestCredentialsUtils(SimpleTestCase): - def setUp(self): - super().setUp() - self.credentials = Credentials( - token="token", - refresh_token="refresh_token", - id_token="id_token", - token_uri="token_uri", - client_id="client_id", - client_secret="client_secret", - scopes="scopes", - expiry=datetime(2020, 1, 1) - ) - - def tearDown(self): - self.credentials = None - return super().tearDown() - - def test_stringify_credentials(self): - desired_credentials = ('{"token": "token", "refresh_token": "refresh_token", "id_token": "id_token", ' - '"token_uri": "token_uri", "client_id": "client_id", "client_secret": "client_secret", ' - '"scopes": "scopes", "expiry": "2020-01-01 00:00:00"}') - - stringified_credentials = stringify_credentials(self.credentials) - - self.assertEqual(desired_credentials, stringified_credentials) - - def test_load_credentials(self): - desired_credentials = self.credentials - - stringified_credentials = stringify_credentials(self.credentials) - loaded_credentials = load_credentials(stringified_credentials) - - self.assertEqual(loaded_credentials.token, desired_credentials.token) diff --git a/corehq/apps/oauth_integrations/tests/test_views.py b/corehq/apps/oauth_integrations/tests/test_views.py deleted file mode 100644 index 25f5121c25b7..000000000000 --- a/corehq/apps/oauth_integrations/tests/test_views.py +++ /dev/null @@ -1,146 +0,0 @@ -from datetime import datetime - -from unittest.mock import patch - -from django.test import TestCase, RequestFactory, override_settings -from django.contrib.auth.models import User - -from google.oauth2.credentials import Credentials -from corehq.apps.oauth_integrations.utils import get_token, load_credentials, stringify_credentials - -from corehq.apps.oauth_integrations.views.google import redirect_oauth_view, call_back_view -from corehq.apps.domain.shortcuts import create_domain -from corehq.apps.oauth_integrations.models import GoogleApiToken - - -class TestViews(TestCase): - - def test_redirect_oauth_view_without_credentials(self): - self.mocked_get_url.return_value = "googleredirecturl.com" - request = self.factory.get('') - request.user = self.user - - response = redirect_oauth_view(request, self.domain) - - self.assertEqual(response.url, 'googleredirecturl.com') - - def test_redirect_oauth_view_with_credentials(self): - self.setUp_credentials() - self.mocked_refresh_credentials.return_value = self.create_new_credentials(token="new_token") - self.mocked_get_url.return_value = "googleredirecturl.com" - request = self.factory.get('') - request.user = self.user - - response = redirect_oauth_view(request, self.domain) - stringified_creds = get_token(self.user) - creds = load_credentials(stringified_creds.token) - - self.assertEqual(response.url, "placeholder.com") - self.assertEqual(creds.token, 'new_token') - - @override_settings(GOOGLE_OATH_CONFIG={ - "web": { - "client_id": "test_id", - "project_id": "test_project_id", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_secret": "test_client_secret"} - }) - def test_call_back_view_with_token_updates_credentials(self): - self.setUp_credentials() - self.mocked_get_token.return_value = stringify_credentials(self.create_new_credentials("new_token")) - request = self.factory.get('', {'state': 101}) - request.user = self.user - - call_back_view(request, self.domain) - - stringified_creds = get_token(self.user) - creds = load_credentials(stringified_creds.token) - - self.assertEqual(creds.token, "new_token") - - @override_settings(GOOGLE_OATH_CONFIG={ - "web": { - "client_id": "test_id", - "project_id": "test_project_id", - "auth_uri": "https://accounts.google.com/o/oauth2/auth", - "token_uri": "https://oauth2.googleapis.com/token", - "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", - "client_secret": "test_client_secret"} - }) - def test_call_back_view_without_token_creates_credentials(self): - self.mocked_get_token.return_value = stringify_credentials(self.create_new_credentials("new_token")) - request = self.factory.get('', {'state': 101}) - request.user = self.user - - call_back_view(request, self.domain) - - creds = get_token(self.user) - - self.assertIsNotNone(creds) - - def setUp(self): - self.setUp_mocks() - self.factory = RequestFactory() - self.user = User() - self.user.username = 'test@user.com' - self.user.save() - - self.domain = create_domain("test_domain") - - self.credentials = Credentials( - token="token", - refresh_token="refresh_token", - id_token="id_token", - token_uri="token_uri", - client_id="client_id", - client_secret="client_secret", - scopes="scopes", - expiry=datetime(2020, 1, 1) - ) - self.stringified_token = stringify_credentials(self.credentials) - - return super().setUp() - - def tearDown(self): - token = get_token(self.user) - if token: - token.delete() - self.user.delete() - self.domain.delete() - - return super().tearDown() - - def setUp_mocks(self): - get_url_from_google_patcher = patch('corehq.apps.oauth_integrations.views.google.get_url_from_google') - refresh_credentials_patcher = patch('corehq.apps.oauth_integrations.views.google.refresh_credentials') - get_token_patcher = patch('corehq.apps.oauth_integrations.views.google.get_token_from_google') - - self.mocked_get_url = get_url_from_google_patcher.start() - self.mocked_refresh_credentials = refresh_credentials_patcher.start() - self.mocked_get_token = get_token_patcher.start() - - self.addCleanup(get_url_from_google_patcher.stop) - self.addCleanup(refresh_credentials_patcher.stop) - self.addCleanup(get_token_patcher.stop) - - def setUp_credentials(self): - return GoogleApiToken.objects.create( - user=self.user, - token=self.stringified_token - ) - - def create_new_credentials(self, token="new_token"): - credentials = Credentials( - token=token, - refresh_token="refresh_token", - id_token="id_token", - token_uri="token_uri", - client_id="client_id", - client_secret="client_secret", - scopes="scopes", - expiry=datetime(2020, 1, 1) - ) - - return credentials diff --git a/corehq/apps/oauth_integrations/urls.py b/corehq/apps/oauth_integrations/urls.py deleted file mode 100644 index e97dd60a1987..000000000000 --- a/corehq/apps/oauth_integrations/urls.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.urls import re_path as url - -from corehq.apps.oauth_integrations.views.google import ( - redirect_oauth_view, - call_back_view -) - -urlpatterns = [ - # OAuth redirect views - url(r"^google_sheets_oauth/redirect/$", - redirect_oauth_view, - name="google_sheet_oauth_redirect"), - url(r"^google_sheets_oauth/callback/$", - call_back_view, - name="google_sheet_oauth_callback") -] diff --git a/corehq/apps/oauth_integrations/utils.py b/corehq/apps/oauth_integrations/utils.py deleted file mode 100644 index a0d9c7de4c9f..000000000000 --- a/corehq/apps/oauth_integrations/utils.py +++ /dev/null @@ -1,92 +0,0 @@ -import json - -from django.conf import settings - -from datetime import datetime - -from google.oauth2.credentials import Credentials - -from corehq.util.couch import get_document_or_404 - -from corehq.apps.export.esaccessors import get_case_export_base_query -from corehq.apps.oauth_integrations.models import GoogleApiToken - -from googleapiclient.discovery import build - - -def stringify_credentials(credentials): - credentials_dict = { - 'token': credentials.token, - 'refresh_token': credentials.refresh_token, - 'id_token': credentials.id_token, - 'token_uri': credentials.token_uri, - 'client_id': credentials.client_id, - 'client_secret': credentials.client_secret, - 'scopes': credentials.scopes, - 'expiry': datetime.strftime(credentials.expiry, '%Y-%m-%d %H:%M:%S') - } - return json.dumps(credentials_dict) - - -def load_credentials(stringified_credentials): - credentials_dict = json.loads(stringified_credentials) - credentials = Credentials( - credentials_dict['token'], - refresh_token=credentials_dict['refresh_token'], - id_token=credentials_dict['id_token'], - token_uri=credentials_dict['token_uri'], - client_id=credentials_dict['client_id'], - client_secret=credentials_dict['client_secret'], - scopes=credentials_dict['scopes'], - ) - return credentials - - -def get_token(user): - try: - return GoogleApiToken.objects.get(user=user) - except GoogleApiToken.DoesNotExist: - return None - - -def get_query_results(export_instance, domain, id): - export = get_document_or_404(export_instance, domain, id) - query = get_case_export_base_query(domain, export.case_type) - results = query.run() - return results - - -def create_spreadsheet(spreadsheet_data, user): - token = GoogleApiToken.objects.get(user=user) - credentials = load_credentials(token.token) - - service = build(settings.GOOGLE_SHEETS_API_NAME, settings.GOOGLE_SHEETS_API_VERSION, credentials=credentials) - sheets_file = service.spreadsheets().create().execute() - spreadsheet_id = sheets_file['spreadsheetId'] - - for chunk in spreadsheet_data: - value_range_body = { - 'majorDimension': 'ROWS', - 'values': chunk - } - service.spreadsheets().values().append( - spreadsheetId=spreadsheet_id, - valueInputOption='USER_ENTERED', - body=value_range_body, - range='A1' - ).execute() - - return sheets_file - - -def listify_data(query_results): - data = [] - for row_number, document in enumerate(query_results): - row_values = document.get("case_json") - - if(row_number == 0): - headers = list(row_values.keys()) - data.append(headers) - data.append(list(row_values.values())) - - return data diff --git a/corehq/apps/oauth_integrations/views/__init__.py b/corehq/apps/oauth_integrations/views/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/corehq/apps/oauth_integrations/views/google.py b/corehq/apps/oauth_integrations/views/google.py deleted file mode 100644 index e4f53434f701..000000000000 --- a/corehq/apps/oauth_integrations/views/google.py +++ /dev/null @@ -1,100 +0,0 @@ -from django.conf import settings -from django.contrib import messages -from django.http import HttpResponseRedirect -from django.urls import reverse -from django.utils.translation import gettext as _ - -from corehq.apps.oauth_integrations.models import GoogleApiToken -from corehq.apps.oauth_integrations.utils import ( - stringify_credentials, - load_credentials, - get_token, -) -from corehq.apps.export.exceptions import InvalidLoginException - -from google.auth.transport.requests import Request -from google_auth_oauthlib.flow import Flow -from google.auth.exceptions import RefreshError - - -def redirect_oauth_view(request, domain): - redirect_uri = request.build_absolute_uri(reverse("google_sheet_oauth_callback")) - token = get_token(request.user) - - if token is None: - return HttpResponseRedirect(get_url_from_google(redirect_uri)) - else: - credentials = load_credentials(token.token) - try: - token.token = stringify_credentials(refresh_credentials(credentials)) - token.save() - # When we lose access to a user's refresh token, we get this refresh error. - # This will simply have them log into google sheets again to give us another refresh token - except RefreshError: - return HttpResponseRedirect(get_url_from_google(redirect_uri)) - #replace with google sheet view - return HttpResponseRedirect("placeholder.com") - - -def refresh_credentials(credentials, user): - return credentials.refresh(Request()) - - -def get_url_from_google(redirect_uri): - INDEX_URL = 0 - flow = Flow.from_client_config( - settings.GOOGLE_OATH_CONFIG, - settings.GOOGLE_OAUTH_SCOPES, - redirect_uri=redirect_uri - ) - # Returns a tuple containing (url, state) and we only want the url - auth_tuple = flow.authorization_url(prompt='consent') - return auth_tuple[INDEX_URL] - - -def call_back_view(request, domain): - redirect_uri = request.build_absolute_uri(reverse("google_sheet_oauth_callback")) - - try: - check_state(request) - - flow = Flow.from_client_config( - settings.GOOGLE_OATH_CONFIG, - settings.GOOGLE_OAUTH_SCOPES, - redirect_uri=redirect_uri - ) - flow.redirect_uri = redirect_uri - - # Fetch the token and stringify it - stringified_token = get_token_from_google(request, flow) - - token = get_token(request.user) - - if not token: - GoogleApiToken.objects.create( - user=request.user, - token=stringified_token - ) - else: - token.token = stringified_token - token.save() - - except InvalidLoginException: - messages.error(request, _("Something went wrong when trying to sign you in to Google. Please try again.")) - - #replace with google sheet view - return HttpResponseRedirect("placeholder.com") - - -def check_state(request): - state = request.GET.get('state', None) - - if not state: - raise InvalidLoginException - - -def get_token_from_google(request, flow): - authorization_response = request.build_absolute_uri() - flow.fetch_token(authorization_response) - credentials = flow.credentials - return stringify_credentials(credentials) diff --git a/migrations.lock b/migrations.lock index abda6fae94de..56a93bf2fa43 100644 --- a/migrations.lock +++ b/migrations.lock @@ -734,11 +734,6 @@ oauth2_provider 0005_auto_20211222_2352 0006_alter_application_client_secret 0007_application_post_logout_redirect_uris -oauth_integrations - 0001_initial - 0002_livegooglesheetschedule - 0003_livegooglesheetrefreshstatus - 0004_auto_20220304_1139 ota 0001_initial 0002_alter_db_index diff --git a/settings.py b/settings.py index 503f4b4160d9..f4cc62f39daf 100755 --- a/settings.py +++ b/settings.py @@ -320,7 +320,6 @@ 'corehq.apps.smsforms', 'corehq.apps.sso', 'corehq.apps.ivr', - 'corehq.apps.oauth_integrations', 'corehq.messaging.MessagingAppConfig', 'corehq.messaging.scheduling', 'corehq.messaging.scheduling.scheduling_partitioned', @@ -1153,12 +1152,6 @@ def _pkce_required(client_id): # used by periodic tasks that delete soft deleted data older than PERMANENT_DELETION_WINDOW days PERMANENT_DELETION_WINDOW = 30 # days -# GSheets related work that was dropped, but should be picked up in the near future -GOOGLE_OATH_CONFIG = {} -GOOGLE_OAUTH_SCOPES = ['https://www.googleapis.com/auth/spreadsheets'] -GOOGLE_SHEETS_API_NAME = "sheets" -GOOGLE_SHEETS_API_VERSION = "v4" -DAYS_KEEP_GSHEET_STATUS = 14 try: # try to see if there's an environmental variable set for local_settings diff --git a/urls.py b/urls.py index fc6a60d8f51d..72b538dc1171 100644 --- a/urls.py +++ b/urls.py @@ -160,7 +160,6 @@ r'(?P[\w.%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,})/(?P[\w-]+)/', ReportNotificationUnsubscribeView.as_view(), name=ReportNotificationUnsubscribeView.urlname), url(r'^phone/list_apps', list_apps, name="list_accessible_apps"), - url(r'^oauth/', include('corehq.apps.oauth_integrations.urls')), ] + LOCAL_APP_URLS if settings.ENABLE_PRELOGIN_SITE: From 210fe217bdc236995b60146f53cc7e92cfb8f3d3 Mon Sep 17 00:00:00 2001 From: Jing Cheng Date: Mon, 25 Nov 2024 11:04:55 -0500 Subject: [PATCH 085/148] Migration to delete oauth database model Copied from https://github.com/dimagi/commcare-hq/pull/34109 --- .../0017_delete_oauth_integrations_models.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 corehq/apps/cleanup/migrations/0017_delete_oauth_integrations_models.py diff --git a/corehq/apps/cleanup/migrations/0017_delete_oauth_integrations_models.py b/corehq/apps/cleanup/migrations/0017_delete_oauth_integrations_models.py new file mode 100644 index 000000000000..1469eaaee38a --- /dev/null +++ b/corehq/apps/cleanup/migrations/0017_delete_oauth_integrations_models.py @@ -0,0 +1,50 @@ +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cleanup', '0016_add_deletedsqldoc'), + ] + + operations = [ + migrations.RunSQL(""" + DROP TABLE IF EXISTS "oauth_integrations_googleapitoken" CASCADE; + DROP TABLE IF EXISTS "oauth_integrations_livegooglesheetrefreshstatus" CASCADE; + DROP TABLE IF EXISTS "oauth_integrations_livegooglesheetschedule" CASCADE; + """), + ] + + +""" +Dropped entities can be checked with the following query: + select pg_describe_object(classid, objid, objsubid) + from pg_depend + where refobjid in ( + 'oauth_integrations_googleapitoken'::regclass, + 'oauth_integrations_livegooglesheetrefreshstatus'::regclass, + 'oauth_integrations_livegooglesheetschedule'::regclass + ); +Example output (from staging, the same as a local dev setup): + type oauth_integrations_livegooglesheetschedule + type oauth_integrations_livegooglesheetrefreshstatus + type oauth_integrations_googleapitoken + toast table pg_toast.pg_toast_2980995 + toast table pg_toast.pg_toast_2980982 + toast table pg_toast.pg_toast_2892669 + sequence oauth_integrations_livegooglesheetschedule_id_seq + sequence oauth_integrations_livegooglesheetrefreshstatus_id_seq + sequence oauth_integrations_googleapitoken_id_seq + index oauth_integrations_livegoo_schedule_id_064aa4f4 + index oauth_integrations_livegoo_export_config_id_200127ab + index oauth_integrations_liveg_export_config_id_200127ab_like + index oauth_integrations_googleapitoken_user_id_9d01255f + default value for column id of table oauth_integrations_livegooglesheetschedule + default value for column id of table oauth_integrations_livegooglesheetrefreshstatus + default value for column id of table oauth_integrations_googleapitoken + constraint oauth_integrations_livegooglesheetschedule_pkey on table oauth_integrations_livegooglesheetschedule + constraint oauth_integrations_livegooglesheetrefreshstatus_pkey on table oauth_integrations_livegooglesheetrefreshstatus + constraint oauth_integrations_l_schedule_id_064aa4f4_fk_oauth_int on table oauth_integrations_livegooglesheetrefreshstatus + constraint oauth_integrations_googleapitoken_pkey on table oauth_integrations_googleapitoken + constraint oauth_integrations_g_user_id_9d01255f_fk_auth_user on table oauth_integrations_googleapitoken +""" From 0ee12872203bab561474f0ee53a152e875bfb41d Mon Sep 17 00:00:00 2001 From: Jing Cheng Date: Mon, 25 Nov 2024 11:13:48 -0500 Subject: [PATCH 086/148] Remove dependencies --- requirements/base-requirements.in | 3 --- requirements/dev-requirements.txt | 16 ++++------------ requirements/docs-requirements.txt | 16 ++++------------ requirements/prod-requirements.txt | 16 ++++------------ requirements/requirements.txt | 16 ++++------------ requirements/test-requirements.txt | 16 ++++------------ 6 files changed, 20 insertions(+), 63 deletions(-) diff --git a/requirements/base-requirements.in b/requirements/base-requirements.in index 3a96beafe987..baf6e21ad785 100644 --- a/requirements/base-requirements.in +++ b/requirements/base-requirements.in @@ -49,9 +49,6 @@ ethiopian-date-converter eulxml firebase-admin # India Division - used for FCM Push Notifications gevent -google-api-python-client==2.32.0 -google-auth-httplib2==0.1.0 -google-auth-oauthlib==1.2.0 greenlet gunicorn haversine # SolTech - Used in Geospatial features diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index e4ced0010e24..eea7e9fe7b63 100644 --- a/requirements/dev-requirements.txt +++ b/requirements/dev-requirements.txt @@ -294,28 +294,22 @@ gnureadline==8.1.2 google-api-core==2.11.0 # via # firebase-admin + # google-api-core # google-api-python-client # google-cloud-core # google-cloud-firestore # google-cloud-storage google-api-python-client==2.32.0 - # via - # -r base-requirements.in - # firebase-admin + # via firebase-admin google-auth==2.18.1 # via # google-api-core # google-api-python-client # google-auth-httplib2 - # google-auth-oauthlib # google-cloud-core # google-cloud-storage google-auth-httplib2==0.1.0 - # via - # -r base-requirements.in - # google-api-python-client -google-auth-oauthlib==1.2.0 - # via -r base-requirements.in + # via google-api-python-client google-cloud-core==2.3.2 # via # google-cloud-firestore @@ -666,9 +660,7 @@ requests==2.32.3 requests-mock==1.9.3 # via -r test-requirements.in requests-oauthlib==1.3.1 - # via - # -r base-requirements.in - # google-auth-oauthlib + # via -r base-requirements.in requests-toolbelt==1.0.0 # via -r base-requirements.in rjsmin==1.2.1 diff --git a/requirements/docs-requirements.txt b/requirements/docs-requirements.txt index a17c991d9baa..341ba06f2d2d 100644 --- a/requirements/docs-requirements.txt +++ b/requirements/docs-requirements.txt @@ -253,28 +253,22 @@ gevent==23.9.1 google-api-core==2.11.0 # via # firebase-admin + # google-api-core # google-api-python-client # google-cloud-core # google-cloud-firestore # google-cloud-storage google-api-python-client==2.32.0 - # via - # -r base-requirements.in - # firebase-admin + # via firebase-admin google-auth==2.18.1 # via # google-api-core # google-api-python-client # google-auth-httplib2 - # google-auth-oauthlib # google-cloud-core # google-cloud-storage google-auth-httplib2==0.1.0 - # via - # -r base-requirements.in - # google-api-python-client -google-auth-oauthlib==1.2.0 - # via -r base-requirements.in + # via google-api-python-client google-cloud-core==2.3.2 # via # google-cloud-firestore @@ -557,9 +551,7 @@ requests==2.32.3 # transifex-python # twilio requests-oauthlib==1.3.1 - # via - # -r base-requirements.in - # google-auth-oauthlib + # via -r base-requirements.in requests-toolbelt==1.0.0 # via -r base-requirements.in rjsmin==1.2.1 diff --git a/requirements/prod-requirements.txt b/requirements/prod-requirements.txt index c6edc653569c..5eea7122f686 100644 --- a/requirements/prod-requirements.txt +++ b/requirements/prod-requirements.txt @@ -251,28 +251,22 @@ gevent==23.9.1 google-api-core==2.11.0 # via # firebase-admin + # google-api-core # google-api-python-client # google-cloud-core # google-cloud-firestore # google-cloud-storage google-api-python-client==2.32.0 - # via - # -r base-requirements.in - # firebase-admin + # via firebase-admin google-auth==2.18.1 # via # google-api-core # google-api-python-client # google-auth-httplib2 - # google-auth-oauthlib # google-cloud-core # google-cloud-storage google-auth-httplib2==0.1.0 - # via - # -r base-requirements.in - # google-api-python-client -google-auth-oauthlib==1.2.0 - # via -r base-requirements.in + # via google-api-python-client google-cloud-core==2.3.2 # via # google-cloud-firestore @@ -564,9 +558,7 @@ requests==2.32.3 # transifex-python # twilio requests-oauthlib==1.3.1 - # via - # -r base-requirements.in - # google-auth-oauthlib + # via -r base-requirements.in requests-toolbelt==1.0.0 # via -r base-requirements.in rjsmin==1.2.1 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index ddc0827498e0..173744f51677 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -240,28 +240,22 @@ gevent==23.9.1 google-api-core==2.11.0 # via # firebase-admin + # google-api-core # google-api-python-client # google-cloud-core # google-cloud-firestore # google-cloud-storage google-api-python-client==2.32.0 - # via - # -r base-requirements.in - # firebase-admin + # via firebase-admin google-auth==2.18.1 # via # google-api-core # google-api-python-client # google-auth-httplib2 - # google-auth-oauthlib # google-cloud-core # google-cloud-storage google-auth-httplib2==0.1.0 - # via - # -r base-requirements.in - # google-api-python-client -google-auth-oauthlib==1.2.0 - # via -r base-requirements.in + # via google-api-python-client google-cloud-core==2.3.2 # via # google-cloud-firestore @@ -527,9 +521,7 @@ requests==2.32.3 # transifex-python # twilio requests-oauthlib==1.3.1 - # via - # -r base-requirements.in - # google-auth-oauthlib + # via -r base-requirements.in requests-toolbelt==1.0.0 # via -r base-requirements.in rjsmin==1.2.1 diff --git a/requirements/test-requirements.txt b/requirements/test-requirements.txt index 7e5262eaa2d0..48bf96b5d64a 100644 --- a/requirements/test-requirements.txt +++ b/requirements/test-requirements.txt @@ -257,28 +257,22 @@ gevent==23.9.1 google-api-core==2.11.0 # via # firebase-admin + # google-api-core # google-api-python-client # google-cloud-core # google-cloud-firestore # google-cloud-storage google-api-python-client==2.32.0 - # via - # -r base-requirements.in - # firebase-admin + # via firebase-admin google-auth==2.18.1 # via # google-api-core # google-api-python-client # google-auth-httplib2 - # google-auth-oauthlib # google-cloud-core # google-cloud-storage google-auth-httplib2==0.1.0 - # via - # -r base-requirements.in - # google-api-python-client -google-auth-oauthlib==1.2.0 - # via -r base-requirements.in + # via google-api-python-client google-cloud-core==2.3.2 # via # google-cloud-firestore @@ -577,9 +571,7 @@ requests==2.32.3 requests-mock==1.9.3 # via -r test-requirements.in requests-oauthlib==1.3.1 - # via - # -r base-requirements.in - # google-auth-oauthlib + # via -r base-requirements.in requests-toolbelt==1.0.0 # via -r base-requirements.in rjsmin==1.2.1 From a13cb2abe51d8f4670d47dfd0a5072cead49068b Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 11:32:14 -0500 Subject: [PATCH 087/148] Removed ace script tags from motech pages --- .../dhis2/templates/dhis2/dataset_map_json.html | 11 ----------- .../motech/openmrs/templates/openmrs/edit_config.html | 11 ++--------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/corehq/motech/dhis2/templates/dhis2/dataset_map_json.html b/corehq/motech/dhis2/templates/dhis2/dataset_map_json.html index b69d15786bab..f4de2db0e020 100644 --- a/corehq/motech/dhis2/templates/dhis2/dataset_map_json.html +++ b/corehq/motech/dhis2/templates/dhis2/dataset_map_json.html @@ -2,17 +2,6 @@ {% load i18n %} {% load hq_shared_tags %} -{# Required to define ACE #} -{% load compress %} -{% block js %}{{ block.super }} - {% compress js %} - - - - - {% endcompress %} -{% endblock %} - {% js_entry "dhis2/js/dataset_map_json" %} {% block page_content %} diff --git a/corehq/motech/openmrs/templates/openmrs/edit_config.html b/corehq/motech/openmrs/templates/openmrs/edit_config.html index cee7002b49b1..b7aea47a3df7 100644 --- a/corehq/motech/openmrs/templates/openmrs/edit_config.html +++ b/corehq/motech/openmrs/templates/openmrs/edit_config.html @@ -3,15 +3,8 @@ {% load crispy_forms_tags %} {% load hq_shared_tags %} {% block title %}Edit Config : OpenMRS :: {% endblock %} -{% block js %}{{ block.super }} - {% compress js %} - - - - - - {% endcompress %} -{% endblock %} + +{% js_entry 'hqwebapp/js/base_ace' %} {% block page_content %} {% crispy form %} From cb549aadfeb7972c1d6051694134495f723861bd Mon Sep 17 00:00:00 2001 From: Biyeun Buczyk Date: Mon, 25 Nov 2024 21:00:09 +0100 Subject: [PATCH 088/148] update default error template to hqwebapp/htmx/htmx_action_error.html --- corehq/util/htmx_action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/corehq/util/htmx_action.py b/corehq/util/htmx_action.py index 2eb330a02a20..7d184bc53183 100644 --- a/corehq/util/htmx_action.py +++ b/corehq/util/htmx_action.py @@ -49,7 +49,7 @@ def make_edit(request, *args, **kwargs): Example docs here: commcarehq.org/styleguide/b5/htmx_alpine/ See working demo here: commcarehq.org/styleguide/demo/htmx_todo/ """ - default_htmx_error_template = "" + default_htmx_error_template = "hqwebapp/htmx/htmx_action_error.html" def get_htmx_error_context(self, action, htmx_error, **kwargs): """ From 59f1cada29f5797a96895d879fa14bb64f0bcc3e Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 20:31:02 +0000 Subject: [PATCH 089/148] "Bootstrap 5 Migration - Rebuilt diffs" --- .../domain/admin/global_sms_rates.html.diff.txt | 2 +- .../data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt index e12d49307352..ee865cfebb64 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt @@ -7,7 +7,7 @@ {% load crispy_forms_tags %} {% load i18n %} --{% requirejs_main 'smsbillables/js/smsbillables.rate_calc' %} +-{% js_entry_b3 'smsbillables/js/rate_calc' %} +{% requirejs_main_b5 'smsbillables/js/smsbillables.rate_calc' %} {% block title %}{% trans "SMS Pricing" %}{% endblock %} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt index 81b11895e924..6e7b60024091 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt @@ -7,7 +7,7 @@ {% load crispy_forms_tags %} {% load i18n %} --{% requirejs_main 'smsbillables/js/smsbillables.rate_calc' %} +-{% js_entry_b3 'smsbillables/js/rate_calc' %} +{% requirejs_main_b5 'smsbillables/js/smsbillables.rate_calc' %} {% block page_content %} From 37bf81904b8bf851a4bedd896c7401a49e854135 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 16:00:56 -0500 Subject: [PATCH 090/148] Migrated langcodes/js/langcodes --- corehq/apps/sms/static/sms/js/sms_language_main.js | 1 + corehq/apps/sms/templates/sms/languages.html | 2 +- webpack/appPaths.js | 2 ++ webpack/webpack.common.js | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/corehq/apps/sms/static/sms/js/sms_language_main.js b/corehq/apps/sms/static/sms/js/sms_language_main.js index 5342fd2a82ad..9003d2ef1ac7 100644 --- a/corehq/apps/sms/static/sms/js/sms_language_main.js +++ b/corehq/apps/sms/static/sms/js/sms_language_main.js @@ -2,6 +2,7 @@ hqDefine("sms/js/sms_language_main",[ "langcodes/js/langcodes", "hqwebapp/js/bulk_upload_file", "sms/js/languages", + "commcarehq", ],function () { }); diff --git a/corehq/apps/sms/templates/sms/languages.html b/corehq/apps/sms/templates/sms/languages.html index 90f31e75ddaf..87363459a217 100644 --- a/corehq/apps/sms/templates/sms/languages.html +++ b/corehq/apps/sms/templates/sms/languages.html @@ -6,7 +6,7 @@ {% trans "SMS Languages" %} {% endblock %} -{% requirejs_main 'sms/js/sms_language_main' %} +{% js_entry_b3 'sms/js/sms_language_main' %} {% block stylesheets %}{{ block.super }} diff --git a/webpack/appPaths.js b/webpack/appPaths.js index e6ec77fff259..e1535b64017a 100644 --- a/webpack/appPaths.js +++ b/webpack/appPaths.js @@ -8,6 +8,7 @@ const BUILD_ARTIFACTS_DIR = path.resolve(__dirname, '_build'); const TEMPLATES_DIR = 'templates'; const APPS_PATH = path.resolve(__BASE, 'corehq', 'apps'); const EX_SUBMODULES_PATH = path.resolve(__BASE, 'corehq', 'ex-submodules'); +const SUBMODULES_PATH = path.resolve(__BASE, 'submodules'); const MESSAGING_PATH = path.resolve(__BASE, 'corehq', 'messaging'); const MOTECH_PATH = path.resolve(__BASE, 'corehq', 'motech'); const CUSTOM_PATH = path.resolve(__BASE, 'custom'); @@ -15,6 +16,7 @@ const CUSTOM_PATH = path.resolve(__BASE, 'custom'); const nonStandardAppPaths = { "case": path.resolve(EX_SUBMODULES_PATH, 'casexml', 'apps', 'case'), "soil": path.resolve(EX_SUBMODULES_PATH, 'soil'), + "langcodes": path.resolve(SUBMODULES_PATH, 'langcodes'), "motech": MOTECH_PATH, "telerivet": path.resolve(MESSAGING_PATH, 'smsbackends', 'telerivet'), // the path itself is standard, but the app has no templates so getStandardAppPaths filters it out diff --git a/webpack/webpack.common.js b/webpack/webpack.common.js index 301619a33bbb..f6aa74132639 100644 --- a/webpack/webpack.common.js +++ b/webpack/webpack.common.js @@ -8,6 +8,7 @@ const aliases = { "commcarehq": path.resolve(utils.getStaticPathForApp('hqwebapp', 'js/bootstrap5/'), 'commcarehq'), "jquery": require.resolve('jquery'), + "langcodes/js/langcodes": path.resolve("submodules/langcodes/static/langcodes/js/langcodes"), // todo after completing requirejs migration, // remove this file and the yarn modernizr post-install step From e2f899c3e572a92a3d81a4296b330f019593a9c4 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 16:04:32 -0500 Subject: [PATCH 091/148] Migrated accounting admins page --- .../apps/accounting/templates/accounting/accounting_admins.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/corehq/apps/accounting/templates/accounting/accounting_admins.html b/corehq/apps/accounting/templates/accounting/accounting_admins.html index c2210bd509ac..eedd1a187ea6 100644 --- a/corehq/apps/accounting/templates/accounting/accounting_admins.html +++ b/corehq/apps/accounting/templates/accounting/accounting_admins.html @@ -3,7 +3,7 @@ {% load i18n %} {% load crispy_forms_tags %} -{% requirejs_main "hqwebapp/js/bootstrap3/crud_paginated_list_init" %} +{% js_entry_b3 "hqwebapp/js/bootstrap3/crud_paginated_list_init" %} {% block pagination_header %}

    {% trans 'Manage Accounting Admins' %}

    From ba0f53ed075f1fb059d44be8e0e83753e9da94eb Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 16:12:21 -0500 Subject: [PATCH 092/148] Migrated soil status page --- corehq/apps/hqwebapp/static/hqwebapp/js/soil.js | 1 + .../templates/hqwebapp/bootstrap3/soil_status_full.html | 2 +- .../templates/hqwebapp/bootstrap5/soil_status_full.html | 2 +- .../bootstrap5_diffs/hqwebapp/soil_status_full.html.diff.txt | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/corehq/apps/hqwebapp/static/hqwebapp/js/soil.js b/corehq/apps/hqwebapp/static/hqwebapp/js/soil.js index 3d24fc526ff8..849397e49026 100644 --- a/corehq/apps/hqwebapp/static/hqwebapp/js/soil.js +++ b/corehq/apps/hqwebapp/static/hqwebapp/js/soil.js @@ -2,6 +2,7 @@ hqDefine("hqwebapp/js/soil", [ "jquery", "hqwebapp/js/initial_page_data", + "commcarehq", ], function ( $, initialPageData diff --git a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/soil_status_full.html b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/soil_status_full.html index 1f265a31c3fa..0fb80b32eea2 100644 --- a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/soil_status_full.html +++ b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/soil_status_full.html @@ -2,7 +2,7 @@ {% load i18n %} {% load hq_shared_tags %} -{% requirejs_main "hqwebapp/js/soil" %} +{% js_entry_b3 "hqwebapp/js/soil" %} {% block page_content %} {% initial_page_data 'download_id' download_id %} diff --git a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/soil_status_full.html b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/soil_status_full.html index 13c961ec4e6f..62eb6d047563 100644 --- a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/soil_status_full.html +++ b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/soil_status_full.html @@ -2,7 +2,7 @@ {% load i18n %} {% load hq_shared_tags %} -{% requirejs_main_b5 "hqwebapp/js/soil" %} +{% js_entry "hqwebapp/js/soil" %} {% block page_content %} {% initial_page_data 'download_id' download_id %} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/soil_status_full.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/soil_status_full.html.diff.txt index 61de87ab3915..cd3d2236a7d8 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/soil_status_full.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/soil_status_full.html.diff.txt @@ -6,8 +6,8 @@ {% load i18n %} {% load hq_shared_tags %} --{% requirejs_main "hqwebapp/js/soil" %} -+{% requirejs_main_b5 "hqwebapp/js/soil" %} +-{% js_entry_b3 "hqwebapp/js/soil" %} ++{% js_entry "hqwebapp/js/soil" %} {% block page_content %} {% initial_page_data 'download_id' download_id %} From c41d8d536b038dd0ca6dc6f5ad3a58cbd3f246f1 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 16:25:43 -0500 Subject: [PATCH 093/148] Migrated B5 versions of SMS rates pages This is basically a bad merge, these should already have been migrated. But these pages aren't live yet, the B3 versions are, so it's fine. --- .../templates/domain/admin/bootstrap5/global_sms_rates.html | 2 +- .../domain/templates/domain/admin/bootstrap5/sms_rates.html | 2 +- .../domain/bootstrap5/internal_subscription_management.html | 2 +- .../domain/admin/global_sms_rates.html.diff.txt | 2 +- .../data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt | 2 +- .../domain/internal_subscription_management.html.diff.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/corehq/apps/domain/templates/domain/admin/bootstrap5/global_sms_rates.html b/corehq/apps/domain/templates/domain/admin/bootstrap5/global_sms_rates.html index a6a16ec4bd51..e2d80da9fdc7 100644 --- a/corehq/apps/domain/templates/domain/admin/bootstrap5/global_sms_rates.html +++ b/corehq/apps/domain/templates/domain/admin/bootstrap5/global_sms_rates.html @@ -3,7 +3,7 @@ {% load crispy_forms_tags %} {% load i18n %} -{% requirejs_main_b5 'smsbillables/js/smsbillables.rate_calc' %} +{% js_entry 'smsbillables/js/rate_calc' %} {% block title %}{% trans "SMS Pricing" %}{% endblock %} diff --git a/corehq/apps/domain/templates/domain/admin/bootstrap5/sms_rates.html b/corehq/apps/domain/templates/domain/admin/bootstrap5/sms_rates.html index df9504a25988..393524df7134 100644 --- a/corehq/apps/domain/templates/domain/admin/bootstrap5/sms_rates.html +++ b/corehq/apps/domain/templates/domain/admin/bootstrap5/sms_rates.html @@ -3,7 +3,7 @@ {% load crispy_forms_tags %} {% load i18n %} -{% requirejs_main_b5 'smsbillables/js/smsbillables.rate_calc' %} +{% js_entry 'smsbillables/js/rate_calc' %} {% block page_content %}
    {# todo B5: css-well #} diff --git a/corehq/apps/domain/templates/domain/bootstrap5/internal_subscription_management.html b/corehq/apps/domain/templates/domain/bootstrap5/internal_subscription_management.html index 7e518d29499a..aa350bf3efc0 100644 --- a/corehq/apps/domain/templates/domain/bootstrap5/internal_subscription_management.html +++ b/corehq/apps/domain/templates/domain/bootstrap5/internal_subscription_management.html @@ -3,7 +3,7 @@ {% load crispy_forms_tags %} {% load hq_shared_tags %} -{% requirejs_main_b5 'domain/js/bootstrap5/internal_subscription_management' %} +{% js_entry 'domain/js/bootstrap5/internal_subscription_management' %} {% block page_content %} {% blocktrans %} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt index ee865cfebb64..9a5ad0d1bd22 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/global_sms_rates.html.diff.txt @@ -8,7 +8,7 @@ {% load i18n %} -{% js_entry_b3 'smsbillables/js/rate_calc' %} -+{% requirejs_main_b5 'smsbillables/js/smsbillables.rate_calc' %} ++{% js_entry 'smsbillables/js/rate_calc' %} {% block title %}{% trans "SMS Pricing" %}{% endblock %} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt index 6e7b60024091..f29abb5f1703 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/admin/sms_rates.html.diff.txt @@ -8,7 +8,7 @@ {% load i18n %} -{% js_entry_b3 'smsbillables/js/rate_calc' %} -+{% requirejs_main_b5 'smsbillables/js/smsbillables.rate_calc' %} ++{% js_entry 'smsbillables/js/rate_calc' %} {% block page_content %} -
    diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_subscription_management.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_subscription_management.html.diff.txt index dbf9733f86e8..1819b0749390 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_subscription_management.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_subscription_management.html.diff.txt @@ -8,7 +8,7 @@ {% load hq_shared_tags %} -{% js_entry_b3 'domain/js/bootstrap3/internal_subscription_management' %} -+{% requirejs_main_b5 'domain/js/bootstrap5/internal_subscription_management' %} ++{% js_entry 'domain/js/bootstrap5/internal_subscription_management' %} {% block page_content %} {% blocktrans %} From 0391c415b193ec7f5da06f4bd805f6dd656d4219 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 16:38:37 -0500 Subject: [PATCH 094/148] Migrated domain/js/internal_settings --- corehq/apps/domain/static/domain/js/internal_settings.js | 1 + .../domain/templates/domain/bootstrap3/internal_settings.html | 2 +- .../domain/templates/domain/bootstrap5/internal_settings.html | 2 +- .../bootstrap5_diffs/domain/internal_settings.html.diff.txt | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/corehq/apps/domain/static/domain/js/internal_settings.js b/corehq/apps/domain/static/domain/js/internal_settings.js index 200edf2fc51f..64701c565208 100644 --- a/corehq/apps/domain/static/domain/js/internal_settings.js +++ b/corehq/apps/domain/static/domain/js/internal_settings.js @@ -5,6 +5,7 @@ hqDefine("domain/js/internal_settings", [ 'hqwebapp/js/initial_page_data', 'hqwebapp/js/multiselect_utils', 'jquery-ui/ui/widgets/datepicker', + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/domain/templates/domain/bootstrap3/internal_settings.html b/corehq/apps/domain/templates/domain/bootstrap3/internal_settings.html index a8f02c328f1f..b4c75803049b 100644 --- a/corehq/apps/domain/templates/domain/bootstrap3/internal_settings.html +++ b/corehq/apps/domain/templates/domain/bootstrap3/internal_settings.html @@ -3,7 +3,7 @@ {% load crispy_forms_tags %} {% load i18n %} -{% requirejs_main 'domain/js/internal_settings' %} +{% js_entry_b3 'domain/js/internal_settings' %} {% block stylesheets %} {{ block.super }} diff --git a/corehq/apps/domain/templates/domain/bootstrap5/internal_settings.html b/corehq/apps/domain/templates/domain/bootstrap5/internal_settings.html index fc1cc9fba803..af437f557b2e 100644 --- a/corehq/apps/domain/templates/domain/bootstrap5/internal_settings.html +++ b/corehq/apps/domain/templates/domain/bootstrap5/internal_settings.html @@ -3,7 +3,7 @@ {% load crispy_forms_tags %} {% load i18n %} -{% requirejs_main_b5 'domain/js/internal_settings' %} +{% js_entry 'domain/js/internal_settings' %} {% block stylesheets %} {{ block.super }} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_settings.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_settings.html.diff.txt index ff00626f7835..6f35896cb25e 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_settings.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/internal_settings.html.diff.txt @@ -7,8 +7,8 @@ {% load crispy_forms_tags %} {% load i18n %} --{% requirejs_main 'domain/js/internal_settings' %} -+{% requirejs_main_b5 'domain/js/internal_settings' %} +-{% js_entry_b3 'domain/js/internal_settings' %} ++{% js_entry 'domain/js/internal_settings' %} {% block stylesheets %} {{ block.super }} From 39b636f70a184c2cd19a1d4ec7e9874bc305fdcd Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 16:56:52 -0500 Subject: [PATCH 095/148] Migrated subscription renewal pages These are trivial, I skipped them earlier to avoid the hassle of finding a renew-able domain to test. --- .../accounting/static/accounting/js/renew_plan_selection.js | 1 + .../domain/bootstrap3/confirm_subscription_renewal.html | 2 +- .../apps/domain/templates/domain/bootstrap3/renew_plan.html | 2 +- .../domain/bootstrap5/confirm_subscription_renewal.html | 2 +- .../apps/domain/templates/domain/bootstrap5/renew_plan.html | 2 +- .../domain/confirm_subscription_renewal.html.diff.txt | 4 ++-- .../data/bootstrap5_diffs/domain/renew_plan.html.diff.txt | 4 ++-- 7 files changed, 9 insertions(+), 8 deletions(-) diff --git a/corehq/apps/accounting/static/accounting/js/renew_plan_selection.js b/corehq/apps/accounting/static/accounting/js/renew_plan_selection.js index 5a92e11666dd..e5221bbf1fcf 100644 --- a/corehq/apps/accounting/static/accounting/js/renew_plan_selection.js +++ b/corehq/apps/accounting/static/accounting/js/renew_plan_selection.js @@ -4,6 +4,7 @@ hqDefine('accounting/js/renew_plan_selection', [ 'knockout', 'hqwebapp/js/initial_page_data', 'hqwebapp/js/toggles', + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/domain/templates/domain/bootstrap3/confirm_subscription_renewal.html b/corehq/apps/domain/templates/domain/bootstrap3/confirm_subscription_renewal.html index fb1641b34d7c..172907bb1ec1 100644 --- a/corehq/apps/domain/templates/domain/bootstrap3/confirm_subscription_renewal.html +++ b/corehq/apps/domain/templates/domain/bootstrap3/confirm_subscription_renewal.html @@ -3,7 +3,7 @@ {% load hq_shared_tags %} {% load i18n %} -{% requirejs_main 'accounting/js/widgets' %} +{% js_entry_b3 'accounting/js/widgets' %} {% block plan_breadcrumbs %}{% endblock %} diff --git a/corehq/apps/domain/templates/domain/bootstrap3/renew_plan.html b/corehq/apps/domain/templates/domain/bootstrap3/renew_plan.html index e5e3c46b8a6c..cc3429d38c5f 100644 --- a/corehq/apps/domain/templates/domain/bootstrap3/renew_plan.html +++ b/corehq/apps/domain/templates/domain/bootstrap3/renew_plan.html @@ -4,7 +4,7 @@ {% load compress %} {% load menu_tags %} -{% requirejs_main 'accounting/js/renew_plan_selection' %} +{% js_entry_b3 'accounting/js/renew_plan_selection' %} {% block plan_breadcrumbs %}{% endblock %} diff --git a/corehq/apps/domain/templates/domain/bootstrap5/confirm_subscription_renewal.html b/corehq/apps/domain/templates/domain/bootstrap5/confirm_subscription_renewal.html index 58d9b04f14f2..ccfb5bb916ed 100644 --- a/corehq/apps/domain/templates/domain/bootstrap5/confirm_subscription_renewal.html +++ b/corehq/apps/domain/templates/domain/bootstrap5/confirm_subscription_renewal.html @@ -3,7 +3,7 @@ {% load hq_shared_tags %} {% load i18n %} -{% requirejs_main_b5 'accounting/js/widgets' %} +{% js_entry 'accounting/js/widgets' %} {% block plan_breadcrumbs %}{% endblock %} diff --git a/corehq/apps/domain/templates/domain/bootstrap5/renew_plan.html b/corehq/apps/domain/templates/domain/bootstrap5/renew_plan.html index b00b78e6366c..2afee1bcb594 100644 --- a/corehq/apps/domain/templates/domain/bootstrap5/renew_plan.html +++ b/corehq/apps/domain/templates/domain/bootstrap5/renew_plan.html @@ -4,7 +4,7 @@ {% load compress %} {% load menu_tags %} -{% requirejs_main_b5 'accounting/js/renew_plan_selection' %} +{% js_entry 'accounting/js/renew_plan_selection' %} {% block plan_breadcrumbs %}{% endblock %} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/confirm_subscription_renewal.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/confirm_subscription_renewal.html.diff.txt index 7f72069b129a..4893460f33ca 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/confirm_subscription_renewal.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/confirm_subscription_renewal.html.diff.txt @@ -7,8 +7,8 @@ {% load hq_shared_tags %} {% load i18n %} --{% requirejs_main 'accounting/js/widgets' %} -+{% requirejs_main_b5 'accounting/js/widgets' %} +-{% js_entry_b3 'accounting/js/widgets' %} ++{% js_entry 'accounting/js/widgets' %} {% block plan_breadcrumbs %}{% endblock %} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/renew_plan.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/renew_plan.html.diff.txt index 5f02d14d4a64..bd6b8372e4d1 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/renew_plan.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/domain/renew_plan.html.diff.txt @@ -8,8 +8,8 @@ {% load compress %} {% load menu_tags %} --{% requirejs_main 'accounting/js/renew_plan_selection' %} -+{% requirejs_main_b5 'accounting/js/renew_plan_selection' %} +-{% js_entry_b3 'accounting/js/renew_plan_selection' %} ++{% js_entry 'accounting/js/renew_plan_selection' %} {% block plan_breadcrumbs %}{% endblock %} From cd168589d3c195ea9c4842e5a9157b9563b4ef65 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 17:06:00 -0500 Subject: [PATCH 096/148] Migrated new user registration page --- .../registration/static/registration/js/register_new_user.js | 1 + .../registration/templates/registration/register_new_user.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/corehq/apps/registration/static/registration/js/register_new_user.js b/corehq/apps/registration/static/registration/js/register_new_user.js index dfc8ecb135fb..3607b745c0c4 100644 --- a/corehq/apps/registration/static/registration/js/register_new_user.js +++ b/corehq/apps/registration/static/registration/js/register_new_user.js @@ -6,6 +6,7 @@ hqDefine('registration/js/register_new_user', [ 'hqwebapp/js/initial_page_data', 'analytix/js/kissmetrix', 'registration/js/login', + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/registration/templates/registration/register_new_user.html b/corehq/apps/registration/templates/registration/register_new_user.html index 8235bfe46489..1ca49652a747 100644 --- a/corehq/apps/registration/templates/registration/register_new_user.html +++ b/corehq/apps/registration/templates/registration/register_new_user.html @@ -4,7 +4,7 @@ {% load compress %} {% load crispy_forms_tags %} -{% requirejs_main 'registration/js/register_new_user' %} +{% js_entry_b3 'registration/js/register_new_user' %} {% block title %}{% trans "Create an Account" %}{% endblock title %} From 76ef13dbadb3690ccd37b2a946fa205d48e5a7a2 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 17:09:24 -0500 Subject: [PATCH 097/148] Migrated iframe SSO login --- .../apps/hqwebapp/static/hqwebapp/js/sso_inactivity.js | 2 +- .../hqwebapp/bootstrap3/iframe_sso_login_success.html | 2 +- .../hqwebapp/bootstrap5/iframe_sso_login_success.html | 2 +- .../hqwebapp/iframe_sso_login_success.html.diff.txt | 9 +++++++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/corehq/apps/hqwebapp/static/hqwebapp/js/sso_inactivity.js b/corehq/apps/hqwebapp/static/hqwebapp/js/sso_inactivity.js index d97f5f99484d..97dbaa858996 100644 --- a/corehq/apps/hqwebapp/static/hqwebapp/js/sso_inactivity.js +++ b/corehq/apps/hqwebapp/static/hqwebapp/js/sso_inactivity.js @@ -3,7 +3,7 @@ * Handles communication about the status of SSO login after the inactivity * timer has requested a re-login */ -hqDefine('hqwebapp/js/sso_inactivity', [], function () { +hqDefine('hqwebapp/js/sso_inactivity', ['commcarehq'], function () { localStorage.setItem('ssoInactivityMessage', JSON.stringify({ isLoggedIn: true, })); diff --git a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/iframe_sso_login_success.html b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/iframe_sso_login_success.html index 17b7975c3d88..a2e22ef90707 100644 --- a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/iframe_sso_login_success.html +++ b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap3/iframe_sso_login_success.html @@ -5,7 +5,7 @@ {% block title %}{% trans "Thank you for logging in with Single Sign-On!" %}{% endblock %} -{% requirejs_main 'hqwebapp/js/sso_inactivity' %} +{% js_entry_b3 'hqwebapp/js/sso_inactivity' %} {% block container_class %}container-fluid{% endblock %} diff --git a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/iframe_sso_login_success.html b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/iframe_sso_login_success.html index 15e4fe422694..aaf1f7bb1b6a 100644 --- a/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/iframe_sso_login_success.html +++ b/corehq/apps/hqwebapp/templates/hqwebapp/bootstrap5/iframe_sso_login_success.html @@ -5,7 +5,7 @@ {% block title %}{% trans "Thank you for logging in with Single Sign-On!" %}{% endblock %} -{% requirejs_main 'hqwebapp/js/sso_inactivity' %} +{% js_entry 'hqwebapp/js/sso_inactivity' %} {% block container_class %}container-fluid{% endblock %} diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/iframe_sso_login_success.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/iframe_sso_login_success.html.diff.txt index d7a137a2d3ff..4ce2f7a93ebb 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/iframe_sso_login_success.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/hqwebapp/iframe_sso_login_success.html.diff.txt @@ -1,5 +1,14 @@ --- +++ +@@ -5,7 +5,7 @@ + + {% block title %}{% trans "Thank you for logging in with Single Sign-On!" %}{% endblock %} + +-{% js_entry_b3 'hqwebapp/js/sso_inactivity' %} ++{% js_entry 'hqwebapp/js/sso_inactivity' %} + + {% block container_class %}container-fluid{% endblock %} + @@ -15,7 +15,7 @@

    {% trans "Thank you for logging in with Single Sign-On!" %}

    From 2d5bf8f284bfc37e2d74811225b8d8ef1e84747c Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 17:32:27 -0500 Subject: [PATCH 098/148] Migrated case data page --- .../javascript/reports/js/case_details.js.diff.txt | 6 +++--- .../reports/reportdata/case_data.html.diff.txt | 4 ++-- .../reports/static/reports/js/bootstrap3/case_details.js | 1 + .../reports/static/reports/js/bootstrap5/case_details.js | 1 + .../templates/reports/reportdata/bootstrap3/case_data.html | 2 +- .../templates/reports/reportdata/bootstrap5/case_data.html | 2 +- webpack/generateDetails.js | 1 + 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/javascript/reports/js/case_details.js.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/javascript/reports/js/case_details.js.diff.txt index 698a873d7350..252c6da317b1 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/javascript/reports/js/case_details.js.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/javascript/reports/js/case_details.js.diff.txt @@ -20,8 +20,8 @@ + 'reports/js/bootstrap5/readable_form', 'bootstrap', // needed for $.tab 'jquery-memoized-ajax/jquery.memoized.ajax.min', - ], function ( -@@ -92,7 +92,7 @@ + 'commcarehq', +@@ -93,7 +93,7 @@ // form data panel uses sticky tabs when it's its own page // but that behavior would be disruptive here $panel.find(".sticky-tabs").removeClass("sticky-tabs"); @@ -30,7 +30,7 @@ singleForm.initSingleForm({ instance_id: data.xform_id, -@@ -275,7 +275,7 @@ +@@ -276,7 +276,7 @@ $propertiesModal.koApplyBindings(modalData); $casePropertyNames.click(function () { modalData.init($(this).data('property-name')); diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/reportdata/case_data.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/reportdata/case_data.html.diff.txt index 262fde0e8978..57973592078e 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/reportdata/case_data.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/reportdata/case_data.html.diff.txt @@ -10,8 +10,8 @@ {% endblock %} --{% requirejs_main 'reports/js/bootstrap3/case_details' %} -+{% requirejs_main_b5 'reports/js/bootstrap5/case_details' %} +-{% js_entry_b3 'reports/js/bootstrap3/case_details' %} ++{% js_entry 'reports/js/bootstrap5/case_details' %} {% block page_content %} diff --git a/corehq/apps/reports/static/reports/js/bootstrap3/case_details.js b/corehq/apps/reports/static/reports/js/bootstrap3/case_details.js index 30b4b1fb225b..505c2cf1eae1 100644 --- a/corehq/apps/reports/static/reports/js/bootstrap3/case_details.js +++ b/corehq/apps/reports/static/reports/js/bootstrap3/case_details.js @@ -14,6 +14,7 @@ hqDefine("reports/js/bootstrap3/case_details", [ 'reports/js/bootstrap3/readable_form', 'bootstrap', // needed for $.tab 'jquery-memoized-ajax/jquery.memoized.ajax.min', + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/reports/static/reports/js/bootstrap5/case_details.js b/corehq/apps/reports/static/reports/js/bootstrap5/case_details.js index 0bb585df2e1c..11c6f3cc531e 100644 --- a/corehq/apps/reports/static/reports/js/bootstrap5/case_details.js +++ b/corehq/apps/reports/static/reports/js/bootstrap5/case_details.js @@ -14,6 +14,7 @@ hqDefine("reports/js/bootstrap5/case_details", [ 'reports/js/bootstrap5/readable_form', 'bootstrap', // needed for $.tab 'jquery-memoized-ajax/jquery.memoized.ajax.min', + 'commcarehq', ], function ( $, ko, diff --git a/corehq/apps/reports/templates/reports/reportdata/bootstrap3/case_data.html b/corehq/apps/reports/templates/reports/reportdata/bootstrap3/case_data.html index c3bc69e8db71..e2d14354ce57 100644 --- a/corehq/apps/reports/templates/reports/reportdata/bootstrap3/case_data.html +++ b/corehq/apps/reports/templates/reports/reportdata/bootstrap3/case_data.html @@ -8,7 +8,7 @@ {% endblock %} -{% requirejs_main 'reports/js/bootstrap3/case_details' %} +{% js_entry_b3 'reports/js/bootstrap3/case_details' %} {% block page_content %} diff --git a/corehq/apps/reports/templates/reports/reportdata/bootstrap5/case_data.html b/corehq/apps/reports/templates/reports/reportdata/bootstrap5/case_data.html index 698083365a0e..ef0a28780a93 100644 --- a/corehq/apps/reports/templates/reports/reportdata/bootstrap5/case_data.html +++ b/corehq/apps/reports/templates/reports/reportdata/bootstrap5/case_data.html @@ -8,7 +8,7 @@ {% endblock %} -{% requirejs_main_b5 'reports/js/bootstrap5/case_details' %} +{% js_entry 'reports/js/bootstrap5/case_details' %} {% block page_content %} diff --git a/webpack/generateDetails.js b/webpack/generateDetails.js index 55257c460076..828e7e9f4371 100644 --- a/webpack/generateDetails.js +++ b/webpack/generateDetails.js @@ -124,6 +124,7 @@ if (require.main === module) { // these apps, but there are no existing webpack entries from these apps (yet). const alwaysIncludeApps = [ "analytix", + "case", "hqwebapp", "notifications", "registration", From 301ed7dc90659cca041db68c08009beae16d8f43 Mon Sep 17 00:00:00 2001 From: Jenny Schweers Date: Mon, 25 Nov 2024 17:37:53 -0500 Subject: [PATCH 099/148] Migrated UCR expression pages --- corehq/apps/userreports/static/userreports/js/ucr_expression.js | 1 + .../apps/userreports/static/userreports/js/ucr_expressions.js | 1 + .../apps/userreports/templates/userreports/ucr_expression.html | 2 +- .../apps/userreports/templates/userreports/ucr_expressions.html | 2 +- .../users/templates/users/commcare_user_confirm_account.html | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/corehq/apps/userreports/static/userreports/js/ucr_expression.js b/corehq/apps/userreports/static/userreports/js/ucr_expression.js index f5d49b4f3319..db22411cb913 100644 --- a/corehq/apps/userreports/static/userreports/js/ucr_expression.js +++ b/corehq/apps/userreports/static/userreports/js/ucr_expression.js @@ -5,6 +5,7 @@ hqDefine("userreports/js/ucr_expression", [ 'hqwebapp/js/initial_page_data', 'hqwebapp/js/base_ace', 'hqwebapp/js/bootstrap3/alert_user', + 'commcarehq', ], function ( moment, ko, diff --git a/corehq/apps/userreports/static/userreports/js/ucr_expressions.js b/corehq/apps/userreports/static/userreports/js/ucr_expressions.js index 2fb0545b7c50..895d408d7cf6 100644 --- a/corehq/apps/userreports/static/userreports/js/ucr_expressions.js +++ b/corehq/apps/userreports/static/userreports/js/ucr_expressions.js @@ -3,6 +3,7 @@ hqDefine('userreports/js/ucr_expressions', [ "hqwebapp/js/initial_page_data", "hqwebapp/js/bootstrap3/crud_paginated_list_init", 'hqwebapp/js/base_ace', + 'commcarehq', ], function () { // used to include base_ace on the ucr_statements page diff --git a/corehq/apps/userreports/templates/userreports/ucr_expression.html b/corehq/apps/userreports/templates/userreports/ucr_expression.html index 85f1b7592943..2407d2d34b0f 100644 --- a/corehq/apps/userreports/templates/userreports/ucr_expression.html +++ b/corehq/apps/userreports/templates/userreports/ucr_expression.html @@ -13,7 +13,7 @@ {% endcompress %} {% endblock %} -{% requirejs_main "userreports/js/ucr_expression" %} +{% js_entry_b3 "userreports/js/ucr_expression" %} {% block page_content %} {% initial_page_data "expression" expression %} diff --git a/corehq/apps/userreports/templates/userreports/ucr_expressions.html b/corehq/apps/userreports/templates/userreports/ucr_expressions.html index d66ef472a830..2d8af316455f 100644 --- a/corehq/apps/userreports/templates/userreports/ucr_expressions.html +++ b/corehq/apps/userreports/templates/userreports/ucr_expressions.html @@ -2,7 +2,7 @@ {% load i18n %} {% load hq_shared_tags %} -{% requirejs_main "userreports/js/ucr_expressions" %} +{% js_entry_b3 "userreports/js/ucr_expressions" %} {% block pagination_templates %} - {% include 'hqwebapp/includes/ui_element_js.html' %} {% compress js %} diff --git a/corehq/apps/app_manager/templates/app_manager/partials/releases/releases_deploy_modal_sms.html b/corehq/apps/app_manager/templates/app_manager/partials/releases/releases_deploy_modal_sms.html index b06eae5f116a..3930b79d85e7 100644 --- a/corehq/apps/app_manager/templates/app_manager/partials/releases/releases_deploy_modal_sms.html +++ b/corehq/apps/app_manager/templates/app_manager/partials/releases/releases_deploy_modal_sms.html @@ -12,11 +12,15 @@
    -
    {% csrf_token %} + + {% csrf_token %}
    - +