diff --git a/analytics/tests/test_activity_views.py b/analytics/tests/test_activity_views.py index d6e42c4d68..83135128a4 100644 --- a/analytics/tests/test_activity_views.py +++ b/analytics/tests/test_activity_views.py @@ -31,10 +31,14 @@ class ActivityTest(ZulipTestCase): user_profile.is_staff = True user_profile.save(update_fields=["is_staff"]) - with self.assert_database_query_count(18): + with self.assert_database_query_count(17): result = self.client_get("/activity") self.assertEqual(result.status_code, 200) + with self.assert_database_query_count(4): + result = self.client_get("/activity/remote") + self.assertEqual(result.status_code, 200) + with self.assert_database_query_count(8): result = self.client_get("/realm_activity/zulip/") self.assertEqual(result.status_code, 200) diff --git a/analytics/urls.py b/analytics/urls.py index 65c952f542..8d7380a906 100644 --- a/analytics/urls.py +++ b/analytics/urls.py @@ -4,7 +4,10 @@ from django.conf.urls import include from django.urls import path from django.urls.resolvers import URLPattern, URLResolver -from analytics.views.installation_activity import get_installation_activity +from analytics.views.installation_activity import ( + get_installation_activity, + get_remote_server_activity, +) from analytics.views.realm_activity import get_realm_activity from analytics.views.stats import ( get_chart_data, @@ -25,6 +28,7 @@ from zerver.lib.rest import rest_path i18n_urlpatterns: List[Union[URLPattern, URLResolver]] = [ # Server admin (user_profile.is_staff) visible stats pages path("activity", get_installation_activity), + path("activity/remote", get_remote_server_activity), path("activity/support", support, name="support"), path("realm_activity//", get_realm_activity), path("user_activity//", get_user_activity), diff --git a/analytics/views/activity_common.py b/analytics/views/activity_common.py index 4aae757e93..7371ce9cb2 100644 --- a/analytics/views/activity_common.py +++ b/analytics/views/activity_common.py @@ -95,7 +95,7 @@ def remote_installation_stats_link(server_id: int, hostname: str) -> Markup: from analytics.views.stats import stats_for_remote_installation url = reverse(stats_for_remote_installation, kwargs=dict(remote_server_id=server_id)) - return Markup('{hostname}').format( + return Markup(' {hostname}').format( url=url, hostname=hostname ) diff --git a/analytics/views/installation_activity.py b/analytics/views/installation_activity.py index 4d143160b3..063074db2e 100644 --- a/analytics/views/installation_activity.py +++ b/analytics/views/installation_activity.py @@ -355,52 +355,51 @@ def user_activity_intervals() -> Tuple[Markup, Dict[str, float]]: return content, realm_minutes -def ad_hoc_queries() -> List[Dict[str, str]]: - def get_page( - query: Composable, cols: Sequence[str], title: str, totals_columns: Sequence[int] = [] - ) -> Dict[str, str]: - cursor = connection.cursor() - cursor.execute(query) - rows = cursor.fetchall() - rows = list(map(list, rows)) - cursor.close() +def get_page( + query: Composable, cols: Sequence[str], title: str, totals_columns: Sequence[int] = [] +) -> Dict[str, str]: + cursor = connection.cursor() + cursor.execute(query) + rows = cursor.fetchall() + rows = list(map(list, rows)) + cursor.close() - def fix_rows( - i: int, fixup_func: Union[Callable[[str], Markup], Callable[[datetime], str]] - ) -> None: + def fix_rows( + i: int, fixup_func: Union[Callable[[str], Markup], Callable[[datetime], str]] + ) -> None: + for row in rows: + row[i] = fixup_func(row[i]) + + total_row = [] + for i, col in enumerate(cols): + if col == "Realm": + fix_rows(i, realm_activity_link) + elif col in ["Last time", "Last visit"]: + fix_rows(i, format_date_for_activity_reports) + elif col == "Hostname": for row in rows: - row[i] = fixup_func(row[i]) - - total_row = [] - for i, col in enumerate(cols): - if col == "Realm": - fix_rows(i, realm_activity_link) - elif col in ["Last time", "Last visit"]: - fix_rows(i, format_date_for_activity_reports) - elif col == "Hostname": - for row in rows: - row[i] = remote_installation_stats_link(row[0], row[i]) - if len(totals_columns) > 0: - if i == 0: - total_row.append("Total") - elif i in totals_columns: - total_row.append(str(sum(row[i] for row in rows if row[i] is not None))) - else: - total_row.append("") + row[i] = remote_installation_stats_link(row[0], row[i]) if len(totals_columns) > 0: - rows.insert(0, total_row) + if i == 0: + total_row.append("Total") + elif i in totals_columns: + total_row.append(str(sum(row[i] for row in rows if row[i] is not None))) + else: + total_row.append("") + if len(totals_columns) > 0: + rows.insert(0, total_row) - content = make_table(title, cols, rows) + content = make_table(title, cols, rows) - return dict( - content=content, - title=title, - ) + return dict( + content=content, + title=title, + ) + +def ad_hoc_queries() -> List[Dict[str, str]]: pages = [] - ### - for mobile_type in ["Android", "ZulipiOS"]: title = f"{mobile_type} usage" @@ -548,7 +547,32 @@ def ad_hoc_queries() -> List[Dict[str, str]]: pages.append(get_page(query, cols, title)) - title = "Remote Zulip servers" + return pages + + +@require_server_admin +@has_request_variables +def get_installation_activity(request: HttpRequest) -> HttpResponse: + duration_content, realm_minutes = user_activity_intervals() + counts_content: str = realm_summary_table(realm_minutes) + data = [ + ("Counts", counts_content), + ("Durations", duration_content), + *((page["title"], page["content"]) for page in ad_hoc_queries()), + ] + + title = "Activity" + + return render( + request, + "analytics/activity.html", + context=dict(data=data, title=title, is_home=True), + ) + + +@require_server_admin +def get_remote_server_activity(request: HttpRequest) -> HttpResponse: + title = "Remote servers" query = SQL( """ @@ -590,26 +614,10 @@ def ad_hoc_queries() -> List[Dict[str, str]]: "Last update time", ] - pages.append(get_page(query, cols, title, totals_columns=[3, 4])) - - return pages - - -@require_server_admin -@has_request_variables -def get_installation_activity(request: HttpRequest) -> HttpResponse: - duration_content, realm_minutes = user_activity_intervals() - counts_content: str = realm_summary_table(realm_minutes) - data = [ - ("Counts", counts_content), - ("Durations", duration_content), - *((page["title"], page["content"]) for page in ad_hoc_queries()), - ] - - title = "Activity" + remote_servers = get_page(query, cols, title, totals_columns=[3, 4]) return render( request, - "analytics/activity.html", - context=dict(data=data, title=title, is_home=True), + "analytics/activity_details_template.html", + context=dict(data=remote_servers["content"], title=remote_servers["title"], is_home=False), ) diff --git a/templates/analytics/activity_details_template.html b/templates/analytics/activity_details_template.html new file mode 100644 index 0000000000..d0412cd097 --- /dev/null +++ b/templates/analytics/activity_details_template.html @@ -0,0 +1,22 @@ +{% extends "zerver/base.html" %} +{% set entrypoint = "activity" %} + +{# Template for installation activity pages #} + +{% block title %} +{{ title }} | Zulip analytics +{% endblock %} + + +{% block content %} + + {% if not is_home %} + Home +
+ {% endif %} + +
+ {{ data|safe }} +
+ +{% endblock %} diff --git a/templates/analytics/realm_summary_table.html b/templates/analytics/realm_summary_table.html index 3fa11313e3..30106ae42a 100644 --- a/templates/analytics/realm_summary_table.html +++ b/templates/analytics/realm_summary_table.html @@ -2,6 +2,13 @@

{{ utctime }}

+

Installation information:

+ + +

Counts chart key: