activity: Add separate page for remote server information/table.

Moves the "Remote Zulip servers" tab in the "/activity" page for
an installation to a separate page, "/activity/remote".

Prototype for moving other tabs in "/activity" to separate pages.
This commit is contained in:
Lauryn Menard 2023-10-04 20:59:03 +02:00 committed by Tim Abbott
parent 91f81d3962
commit afc1d2a409
6 changed files with 106 additions and 62 deletions

View File

@ -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)

View File

@ -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/<realm_str>/", get_realm_activity),
path("user_activity/<user_profile_id>/", get_user_activity),

View File

@ -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('<a href="{url}"><i class="fa fa-pie-chart"></i>{hostname}</a>').format(
return Markup('<a href="{url}"><i class="fa fa-pie-chart"></i></a> {hostname}').format(
url=url, hostname=hostname
)

View File

@ -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),
)

View File

@ -0,0 +1,22 @@
{% extends "zerver/base.html" %}
{% set entrypoint = "activity" %}
{# Template for installation activity pages #}
{% block title %}
<title>{{ title }} | Zulip analytics</title>
{% endblock %}
{% block content %}
{% if not is_home %}
<a class="show-all" href="/activity">Home</a>
<br />
{% endif %}
<div class="container">
{{ data|safe }}
</div>
{% endblock %}

View File

@ -2,6 +2,13 @@
<p style="text-align: center;">{{ utctime }}</p>
<h4>Installation information:</h4>
<ul>
<li><a href="/stats/installation">Server total /stats style graphs</a></li>
<li><a href="/activity/remote">Remote servers</a></li>
</ul>
<h4>Counts chart key:</h4>
<ul>
<li><strong>active (site)</strong> - has ≥5 DAUs</li>
<li>sites are listed if ≥1 users active in last 2 weeks</li>
@ -20,7 +27,6 @@
<li><strong>WAU</strong> (weekly active users) - users active in last 7 * 24hr</li>
<li><strong>DAT</strong> (daily active time) - total user-activity time in last 24hr</li>
<li><strong>Human message</strong> - message sent by non-bot user, and not with known-bot client</li>
<li><a href="/stats/installation">Server total /stats style graphs</a></li>
</ul>
<table class="table summary-table sortable table-striped table-bordered">