diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/javascript/reports/js/application_status.js.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/javascript/reports/js/application_status.js.diff.txt new file mode 100644 index 000000000000..0e67208e3293 --- /dev/null +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/javascript/reports/js/application_status.js.diff.txt @@ -0,0 +1,9 @@ +--- ++++ +@@ -1,5 +1,5 @@ + 'use strict'; +-hqDefine("reports/js/bootstrap3/application_status", [ ++hqDefine("reports/js/bootstrap5/application_status", [ + "jquery", + ], function ( + $ diff --git a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/standard/base_template.html.diff.txt b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/standard/base_template.html.diff.txt index 7b449f35c29a..d34556b3fd1e 100644 --- a/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/standard/base_template.html.diff.txt +++ b/corehq/apps/hqwebapp/tests/data/bootstrap5_diffs/reports/standard/base_template.html.diff.txt @@ -1,6 +1,6 @@ --- +++ -@@ -1,44 +1,44 @@ +@@ -1,45 +1,45 @@ -{% extends "hqwebapp/bootstrap3/two_column.html" %} +{% extends "hqwebapp/bootstrap5/two_column.html" %} {% load compress %} @@ -48,8 +48,10 @@ - - +- + + ++ - @@ -57,7 +59,7 @@ {% endcompress %} {% endblock %} -@@ -58,8 +58,8 @@ +@@ -59,8 +59,8 @@ {% block title %}{{ report.title|default:"Project Reports" }}{% endblock %} {% block page_breadcrumbs %} @@ -68,7 +70,7 @@
  • {% trans report.section_name|default:"Reports" %}
  • -@@ -98,17 +98,17 @@ +@@ -99,17 +99,17 @@ {% initial_page_data 'slug' report.slug %} {% block filter_panel %} @@ -90,7 +92,7 @@ aria-label="Close" data-bind="click: resetModal"> @@ -107,7 +109,7 @@

    {% trans 'Notice' %}

    {{ report.special_notice }}

    -@@ -137,7 +137,7 @@ +@@ -138,7 +138,7 @@ {% block reportcontent %} {% endblock %} {% else %} diff --git a/corehq/apps/reports/standard/deployments.py b/corehq/apps/reports/standard/deployments.py index 2a859ac8f4e6..fe5a755e24c2 100644 --- a/corehq/apps/reports/standard/deployments.py +++ b/corehq/apps/reports/standard/deployments.py @@ -47,6 +47,7 @@ ProjectReportParametersMixin, ) from corehq.apps.reports.util import format_datatables_data +from corehq.apps.users.models import CouchUser from corehq.apps.users.util import user_display_string from corehq.const import USER_DATE_FORMAT from corehq.util.quickcache import quickcache @@ -81,6 +82,10 @@ def _columns(self): DataTablesColumn(_("Username"), prop_name='username.exact', sql_col='user_dim__username'), + DataTablesColumn(_("Assigned Location(s)"), + help_text=_('Assigned locations for the user, with the primary ' + 'location highlighted in bold.'), + sortable=False), DataTablesColumn(_("Last Submission"), prop_name='reporting_metadata.last_submissions.submission_date', alt_prop_name='reporting_metadata.last_submission_for_user.submission_date', @@ -119,7 +124,7 @@ def headers(self): sortable=False) ) headers = DataTablesHeader(*columns) - headers.custom_sort = [[1, 'desc']] + headers.custom_sort = [[2, 'desc']] return headers @cached_property @@ -316,6 +321,7 @@ def process_rows(self, users, fmt_for_export=False): grouped_ancestor_locs = self.get_bulk_ancestors(location_ids) self.required_loc_columns = self.get_location_columns(grouped_ancestor_locs) + loc_names_dict = self._locations_names_dict(users) for user in users: last_build = last_seen = last_sub = last_sync = last_sync_date = app_name = commcare_version = None last_build_profile_name = device = device_app_meta = num_unsent_forms = None @@ -373,6 +379,7 @@ def process_rows(self, users, fmt_for_export=False): user_display_string(user.get('username', ''), user.get('first_name', ''), user.get('last_name', '')), + self._get_location_column(user, loc_names_dict), _fmt_date(last_seen, fmt_for_export), _fmt_date(last_sync_date, fmt_for_export), app_name or "---", build_version, commcare_version or '---', num_unsent_forms if num_unsent_forms is not None else "---", @@ -388,6 +395,23 @@ def process_rows(self, users, fmt_for_export=False): rows.append(row_data) return rows + def _locations_names_dict(self, user_es_docs): + """ + Returns a dict of all assigned location names for given `user_docs`. + The dict has the following structure: + { + 'loc_id': 'loc_name' + } + """ + all_loc_ids = set() + for user_es_doc in user_es_docs: + user = CouchUser.wrap_correctly(user_es_doc) + for loc_id in user.get_location_ids(self.domain): + all_loc_ids.add(loc_id) + return dict(SQLLocation.objects.filter( + location_id__in=all_loc_ids, domain=self.domain + ).values_list('location_id', 'name')) + def process_facts(self, app_status_facts, fmt_for_export=False): rows = [] for fact in app_status_facts: @@ -477,12 +501,51 @@ def _fmt_timestamp(timestamp): for row in table[1:]: # Last submission - row[len(location_colums) + 1] = _fmt_timestamp(row[len(location_colums) + 1]) - # Last sync row[len(location_colums) + 2] = _fmt_timestamp(row[len(location_colums) + 2]) + # Last sync + row[len(location_colums) + 3] = _fmt_timestamp(row[len(location_colums) + 3]) result[0][1] = table return result + def _get_location_column(self, user_es_doc, user_loc_dict): + user = CouchUser.wrap_correctly(user_es_doc) + if not user.get_location_ids(self.domain): + return '---' + return self._get_formatted_assigned_location_names(user, user_loc_dict) + + def _get_formatted_assigned_location_names(self, user, user_loc_dict): + """ + Create an HTML formatted string of the given assigned location names. + The primary location will be highlighted in bold. + """ + assigned_location_ids = user.get_location_ids(self.domain) + primary_location_id = user.get_location_id(self.domain) + formatted_loc_names = [] + for loc_id in assigned_location_ids: + loc_name = user_loc_dict.get(loc_id) + if loc_id == primary_location_id: + formatted_loc_names.insert( + 0, f'{loc_name}' + ) + else: + formatted_loc_names.append(loc_name) + + formatted_str = ', '.join(formatted_loc_names[:4]) + html_nodes = [ + f'{formatted_str}', + ] + if len(formatted_loc_names) > 4: + all_str = ', '.join(formatted_loc_names) + view_controls_html_nodes = [ + f'{_("...See more")}', + f'', + ] + html_nodes += [ + f'', + f'{"".join(view_controls_html_nodes)}', + ] + return format_html(f'
    {"".join(html_nodes)}
    ') + def _get_commcare_version(app_version_info): commcare_version = ( diff --git a/corehq/apps/reports/static/reports/js/bootstrap3/application_status.js b/corehq/apps/reports/static/reports/js/bootstrap3/application_status.js new file mode 100644 index 000000000000..ab91e49c35f6 --- /dev/null +++ b/corehq/apps/reports/static/reports/js/bootstrap3/application_status.js @@ -0,0 +1,15 @@ +'use strict'; +hqDefine("reports/js/bootstrap3/application_status", [ + "jquery", +], function ( + $ +) { + $(function () { + $('#report-content').on('click', '.toggle-all-locations', function (e) { + $(this).prevAll('.locations-list').toggle(); + $(this).children('.loc-view-control').toggle(); + $(this).prevAll('.all-locations-list').toggle(); + e.preventDefault(); + }); + }); +}); \ No newline at end of file diff --git a/corehq/apps/reports/static/reports/js/bootstrap5/application_status.js b/corehq/apps/reports/static/reports/js/bootstrap5/application_status.js new file mode 100644 index 000000000000..8465cbf00c0e --- /dev/null +++ b/corehq/apps/reports/static/reports/js/bootstrap5/application_status.js @@ -0,0 +1,15 @@ +'use strict'; +hqDefine("reports/js/bootstrap5/application_status", [ + "jquery", +], function ( + $ +) { + $(function () { + $('#report-content').on('click', '.toggle-all-locations', function (e) { + $(this).prevAll('.locations-list').toggle(); + $(this).children('.loc-view-control').toggle(); + $(this).prevAll('.all-locations-list').toggle(); + e.preventDefault(); + }); + }); +}); \ No newline at end of file diff --git a/corehq/apps/reports/templates/reports/standard/bootstrap3/base_template.html b/corehq/apps/reports/templates/reports/standard/bootstrap3/base_template.html index 127918c16f50..aa62e3737d05 100644 --- a/corehq/apps/reports/templates/reports/standard/bootstrap3/base_template.html +++ b/corehq/apps/reports/templates/reports/standard/bootstrap3/base_template.html @@ -36,6 +36,7 @@ + diff --git a/corehq/apps/reports/templates/reports/standard/bootstrap5/base_template.html b/corehq/apps/reports/templates/reports/standard/bootstrap5/base_template.html index 5c6463780a43..2aa429586c16 100644 --- a/corehq/apps/reports/templates/reports/standard/bootstrap5/base_template.html +++ b/corehq/apps/reports/templates/reports/standard/bootstrap5/base_template.html @@ -36,6 +36,7 @@ +