import datetime from argparse import ArgumentParser from typing import Any, Optional from django.db.models import Count, QuerySet from django.utils.timezone import now as timezone_now from zerver.lib.management import ZulipBaseCommand from zerver.models import UserActivity class Command(ZulipBaseCommand): help = """Report rough client activity globally, for a realm, or for a user Usage examples: ./manage.py client_activity --target server ./manage.py client_activity --target realm --realm zulip ./manage.py client_activity --target user --user hamlet@zulip.com --realm zulip""" def add_arguments(self, parser: ArgumentParser) -> None: parser.add_argument('--target', dest='target', required=True, type=str, help="'server' will calculate client activity of the entire server. " "'realm' will calculate client activity of realm. " "'user' will calculate client activity of the user.") parser.add_argument('--user', dest='user', type=str, help="The email address of the user you want to calculate activity.") self.add_realm_args(parser) def compute_activity(self, user_activity_objects: QuerySet) -> None: # Report data from the past week. # # This is a rough report of client activity because we inconsistently # register activity from various clients; think of it as telling you # approximately how many people from a group have used a particular # client recently. For example, this might be useful to get a sense of # how popular different versions of a desktop client are. # # Importantly, this does NOT tell you anything about the relative # volumes of requests from clients. threshold = timezone_now() - datetime.timedelta(days=7) client_counts = user_activity_objects.filter( last_visit__gt=threshold).values("client__name").annotate( count=Count('client__name')) total = 0 counts = [] for client_type in client_counts: count = client_type["count"] client = client_type["client__name"] total += count counts.append((count, client)) counts.sort() for count in counts: print("%25s %15d" % (count[1], count[0])) print("Total:", total) def handle(self, *args: Any, **options: Optional[str]) -> None: realm = self.get_realm(options) if options["user"] is None: if options["target"] == "server" and realm is None: # Report global activity. self.compute_activity(UserActivity.objects.all()) elif options["target"] == "realm" and realm is not None: self.compute_activity(UserActivity.objects.filter(user_profile__realm=realm)) else: self.print_help("./manage.py", "client_activity") elif options["target"] == "user": user_profile = self.get_user(options["user"], realm) self.compute_activity(UserActivity.objects.filter(user_profile=user_profile)) else: self.print_help("./manage.py", "client_activity")