From 55d7ec843e65bf5e6b54cf472f087edea941eacb Mon Sep 17 00:00:00 2001 From: Lauryn Menard Date: Mon, 23 Sep 2024 21:11:09 +0200 Subject: [PATCH] installation-activity: Add export version of chart. Adds a boolean export parameter to the installation activity view. When true, the initial links column is removed from the chart, a blank column is removed before human message counts anda column with all organization owner and admin emails is added. Updates the links at the top of the installation activity page to have an option to toggle between the export and non-export versions of the chart. --- corporate/views/installation_activity.py | 51 ++++++++++++++++--- .../activity/installation_activity_table.html | 28 ++++++++-- 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/corporate/views/installation_activity.py b/corporate/views/installation_activity.py index d4ed71d703..72a6a27c93 100644 --- a/corporate/views/installation_activity.py +++ b/corporate/views/installation_activity.py @@ -1,4 +1,5 @@ from collections import defaultdict +from typing import Any from django.conf import settings from django.db import connection @@ -8,6 +9,7 @@ from django.template import loader from django.utils.timezone import now as timezone_now from markupsafe import Markup from psycopg2.sql import SQL +from pydantic import Json from analytics.lib.counts import COUNT_STATS from corporate.lib.activity import ( @@ -26,10 +28,11 @@ from corporate.lib.activity import ( from corporate.lib.stripe import cents_to_dollar_string from corporate.views.support import get_plan_type_string from zerver.decorator import require_server_admin -from zerver.lib.typed_endpoint import typed_endpoint_without_parameters +from zerver.lib.typed_endpoint import typed_endpoint from zerver.models import Realm from zerver.models.realm_audit_logs import AuditLogEventType from zerver.models.realms import get_org_type_display_name +from zerver.models.users import UserProfile def get_realm_day_counts() -> dict[str, dict[str, Markup]]: @@ -88,7 +91,7 @@ def get_realm_day_counts() -> dict[str, dict[str, Markup]]: return result -def realm_summary_table() -> str: +def realm_summary_table(export: bool) -> str: now = timezone_now() query = SQL( @@ -103,7 +106,8 @@ def realm_summary_table() -> str: coalesce(user_count_table.value, 0) user_profile_count, coalesce(bot_count_table.value, 0) bot_count, coalesce(realm_audit_log_table.how_realm_creator_found_zulip, '') how_realm_creator_found_zulip, - coalesce(realm_audit_log_table.how_realm_creator_found_zulip_extra_context, '') how_realm_creator_found_zulip_extra_context + coalesce(realm_audit_log_table.how_realm_creator_found_zulip_extra_context, '') how_realm_creator_found_zulip_extra_context, + realm_admin_user.delivery_email admin_email FROM zerver_realm as realm LEFT OUTER JOIN ( @@ -168,6 +172,17 @@ def realm_summary_table() -> str: WHERE event_type = %(realm_creation_event_type)s ) as realm_audit_log_table ON realm.id = realm_audit_log_table.realm_id + LEFT OUTER JOIN ( + SELECT + delivery_email, + realm_id + from + zerver_userprofile + WHERE + is_bot=False + AND is_active=True + AND role IN %(admin_roles)s + ) as realm_admin_user ON realm.id = realm_admin_user.realm_id WHERE _14day_active_humans IS NOT NULL or realm.plan_type = 3 @@ -190,11 +205,25 @@ def realm_summary_table() -> str: "active_users_audit:is_bot:day" ].last_successful_fill(), "realm_creation_event_type": AuditLogEventType.REALM_CREATED, + "admin_roles": (UserProfile.ROLE_REALM_ADMINISTRATOR, UserProfile.ROLE_REALM_OWNER), }, ) - rows = dictfetchall(cursor) + raw_rows = dictfetchall(cursor) cursor.close() + rows: list[dict[str, Any]] = [] + admin_emails: dict[str, str] = {} + # Process duplicate realm rows due to multiple admin users, + # and collect all admin user emails into one string. + for row in raw_rows: + realm_string_id = row["string_id"] + admin_email = row.pop("admin_email") + if realm_string_id in admin_emails: + admin_emails[realm_string_id] = admin_emails[realm_string_id] + ", " + admin_email + else: + admin_emails[realm_string_id] = admin_email + rows.append(row) + realm_messages_per_day_counts = get_realm_day_counts() total_arr = 0 num_active_sites = 0 @@ -253,6 +282,10 @@ def realm_summary_table() -> str: total_bot_count += int(row["bot_count"]) total_wau_count += int(row["wau_count"]) + # Add admin users email string + if export: + row["admin_emails"] = admin_emails[realm_string_id] + total_row = [ "Total", "", @@ -277,6 +310,9 @@ def realm_summary_table() -> str: "", ] + if export: + total_row.pop(1) + content = loader.render_to_string( "corporate/activity/installation_activity_table.html", dict( @@ -285,15 +321,16 @@ def realm_summary_table() -> str: num_active_sites=num_active_sites, utctime=now.strftime("%Y-%m-%d %H:%M %Z"), billing_enabled=settings.BILLING_ENABLED, + export=export, ), ) return content @require_server_admin -@typed_endpoint_without_parameters -def get_installation_activity(request: HttpRequest) -> HttpResponse: - content: str = realm_summary_table() +@typed_endpoint +def get_installation_activity(request: HttpRequest, *, export: Json[bool] = False) -> HttpResponse: + content: str = realm_summary_table(export) title = "Installation activity" return render( diff --git a/templates/corporate/activity/installation_activity_table.html b/templates/corporate/activity/installation_activity_table.html index c20243939d..7f5bad81e1 100644 --- a/templates/corporate/activity/installation_activity_table.html +++ b/templates/corporate/activity/installation_activity_table.html @@ -2,11 +2,16 @@

{{ utctime }}

-

Installation information:

+

Other views:

Counts chart key:

@@ -33,7 +38,9 @@ + {% if not export %} Links + {% endif %} Realm Created (green if ≤12wk) {% if billing_enabled %} @@ -47,21 +54,26 @@ WAU Total users Bots + {% if not export %} + {% endif %} Human messages sent, last 8 UTC days (today-so-far first) + {% if export %} + Admin emails + {% endif %} {% for row in rows %} - + {% if not export %} {{ row.realm_url }} {{ row.stats_link }} {{ row.support_link }} - + {% endif %} {{ row.activity_link }} @@ -113,14 +125,20 @@ {{ row.bot_count }} - + {% if not export %}   - + {% endif %} {% if row.history %} {{ row.history|safe }} {% else %} {% endif %} + + {% if export %} + + {{ row.admin_emails }} + + {% endif %} {% endfor %}