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.is_staff = True
user_profile.save(update_fields=["is_staff"]) 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") result = self.client_get("/activity")
self.assertEqual(result.status_code, 200) 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): with self.assert_database_query_count(8):
result = self.client_get("/realm_activity/zulip/") result = self.client_get("/realm_activity/zulip/")
self.assertEqual(result.status_code, 200) 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 import path
from django.urls.resolvers import URLPattern, URLResolver 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.realm_activity import get_realm_activity
from analytics.views.stats import ( from analytics.views.stats import (
get_chart_data, get_chart_data,
@ -25,6 +28,7 @@ from zerver.lib.rest import rest_path
i18n_urlpatterns: List[Union[URLPattern, URLResolver]] = [ i18n_urlpatterns: List[Union[URLPattern, URLResolver]] = [
# Server admin (user_profile.is_staff) visible stats pages # Server admin (user_profile.is_staff) visible stats pages
path("activity", get_installation_activity), path("activity", get_installation_activity),
path("activity/remote", get_remote_server_activity),
path("activity/support", support, name="support"), path("activity/support", support, name="support"),
path("realm_activity/<realm_str>/", get_realm_activity), path("realm_activity/<realm_str>/", get_realm_activity),
path("user_activity/<user_profile_id>/", get_user_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 from analytics.views.stats import stats_for_remote_installation
url = reverse(stats_for_remote_installation, kwargs=dict(remote_server_id=server_id)) 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 url=url, hostname=hostname
) )

View File

@ -355,7 +355,6 @@ def user_activity_intervals() -> Tuple[Markup, Dict[str, float]]:
return content, realm_minutes return content, realm_minutes
def ad_hoc_queries() -> List[Dict[str, str]]:
def get_page( def get_page(
query: Composable, cols: Sequence[str], title: str, totals_columns: Sequence[int] = [] query: Composable, cols: Sequence[str], title: str, totals_columns: Sequence[int] = []
) -> Dict[str, str]: ) -> Dict[str, str]:
@ -397,9 +396,9 @@ def ad_hoc_queries() -> List[Dict[str, str]]:
title=title, title=title,
) )
pages = []
### def ad_hoc_queries() -> List[Dict[str, str]]:
pages = []
for mobile_type in ["Android", "ZulipiOS"]: for mobile_type in ["Android", "ZulipiOS"]:
title = f"{mobile_type} usage" title = f"{mobile_type} usage"
@ -548,7 +547,32 @@ def ad_hoc_queries() -> List[Dict[str, str]]:
pages.append(get_page(query, cols, title)) 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( query = SQL(
""" """
@ -590,26 +614,10 @@ def ad_hoc_queries() -> List[Dict[str, str]]:
"Last update time", "Last update time",
] ]
pages.append(get_page(query, cols, title, totals_columns=[3, 4])) remote_servers = 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"
return render( return render(
request, request,
"analytics/activity.html", "analytics/activity_details_template.html",
context=dict(data=data, title=title, is_home=True), 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> <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> <ul>
<li><strong>active (site)</strong> - has ≥5 DAUs</li> <li><strong>active (site)</strong> - has ≥5 DAUs</li>
<li>sites are listed if ≥1 users active in last 2 weeks</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>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>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><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> </ul>
<table class="table summary-table sortable table-striped table-bordered"> <table class="table summary-table sortable table-striped table-bordered">