mirror of https://github.com/zulip/zulip.git
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:
parent
91f81d3962
commit
afc1d2a409
|
@ -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)
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -355,52 +355,51 @@ 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]:
|
cursor = connection.cursor()
|
||||||
cursor = connection.cursor()
|
cursor.execute(query)
|
||||||
cursor.execute(query)
|
rows = cursor.fetchall()
|
||||||
rows = cursor.fetchall()
|
rows = list(map(list, rows))
|
||||||
rows = list(map(list, rows))
|
cursor.close()
|
||||||
cursor.close()
|
|
||||||
|
|
||||||
def fix_rows(
|
def fix_rows(
|
||||||
i: int, fixup_func: Union[Callable[[str], Markup], Callable[[datetime], str]]
|
i: int, fixup_func: Union[Callable[[str], Markup], Callable[[datetime], str]]
|
||||||
) -> None:
|
) -> 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:
|
for row in rows:
|
||||||
row[i] = fixup_func(row[i])
|
row[i] = remote_installation_stats_link(row[0], 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("")
|
|
||||||
if len(totals_columns) > 0:
|
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(
|
return dict(
|
||||||
content=content,
|
content=content,
|
||||||
title=title,
|
title=title,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ad_hoc_queries() -> List[Dict[str, str]]:
|
||||||
pages = []
|
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),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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 %}
|
|
@ -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">
|
||||||
|
|
Loading…
Reference in New Issue