text: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  
  from typing import Any
  
  from argparse import ArgumentParser
  import datetime
  import pytz
  
  from django.core.management.base import BaseCommand
  from django.db.models import Count
  from zerver.models import UserProfile, Realm, Stream, Message, Recipient, UserActivity, \
      Subscription, UserMessage, get_realm
  
  MOBILE_CLIENT_LIST = ["Android", "ios"]
  HUMAN_CLIENT_LIST = MOBILE_CLIENT_LIST + ["website"]
  
  human_messages = Message.objects.filter(sending_client__name__in=HUMAN_CLIENT_LIST)
  
  class Command(BaseCommand):
      help = "Generate statistics on realm activity."
  
      def add_arguments(self, parser):
          # type: (ArgumentParser) -> None
          parser.add_argument('realms', metavar='<realm>', type=str, nargs='*',
                              help="realm to generate statistics for")
  
      def active_users(self, realm):
          # type: (Realm) -> List[UserProfile]
          # Has been active (on the website, for now) in the last 7 days.
          activity_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=7)
          return [activity.user_profile for activity in \
                      UserActivity.objects.filter(user_profile__realm=realm,
                                                  user_profile__is_active=True,
                                                  last_visit__gt=activity_cutoff,
                                                  query="/json/users/me/pointer",
                                                  client__name="website")]
  
      def messages_sent_by(self, user, days_ago):
          # type: (UserProfile, int) -> int
          sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
          return human_messages.filter(sender=user, pub_date__gt=sent_time_cutoff).count()
  
      def total_messages(self, realm, days_ago):
          # type: (Realm, int) -> int
          sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
          return Message.objects.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
  
      def human_messages(self, realm, days_ago):
          # type: (Realm, int) -> int
          sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
          return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
  
      def api_messages(self, realm, days_ago):
          # type: (Realm, int) -> int
          return (self.total_messages(realm, days_ago) - self.human_messages(realm, days_ago))
  
      def stream_messages(self, realm, days_ago):
          # type: (Realm, int) -> int
          sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
          return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff,
                                       recipient__type=Recipient.STREAM).count()
  
      def private_messages(self, realm, days_ago):
          # type: (Realm, int) -> int
          sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
          return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
              recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.HUDDLE).count()
  
      def group_private_messages(self, realm, days_ago):
          # type: (Realm, int) -> int
          sent_time_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=days_ago)
          return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
              recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.PERSONAL).count()
  
      def report_percentage(self, numerator, denominator, text):
          # type: (float, float, str) -> None
          if not denominator:
              fraction = 0.0
          else:
              fraction = numerator / float(denominator)
          print("%.2f%% of" % (fraction * 100,), text)
  
      def handle(self, *args, **options):
          # type: (*Any, **Any) -> None
          if options['realms']:
              try:
                  realms = [get_realm(domain) for domain in options['realms']]
              except Realm.DoesNotExist as e:
                  print(e)
                  exit(1)
          else:
              realms = Realm.objects.all()
  
          for realm in realms:
              print(realm.domain)
  
              user_profiles = UserProfile.objects.filter(realm=realm, is_active=True)
              active_users = self.active_users(realm)
              num_active = len(active_users)
  
              print("%d active users (%d total)" % (num_active, len(user_profiles)))
              streams = Stream.objects.filter(realm=realm).extra(
                  tables=['zerver_subscription', 'zerver_recipient'],
                  where=['zerver_subscription.recipient_id = zerver_recipient.id',
                         'zerver_recipient.type = 2',
                         'zerver_recipient.type_id = zerver_stream.id',
                         'zerver_subscription.active = true']).annotate(count=Count("name"))
              print("%d streams" % (streams.count(),))
  
              for days_ago in (1, 7, 30):
                  print("In last %d days, users sent:" % (days_ago,))
                  sender_quantities = [self.messages_sent_by(user, days_ago) for user in user_profiles]
                  for quantity in sorted(sender_quantities, reverse=True):
                      print(quantity, end=' ')
                  print("")
  
                  print("%d stream messages" % (self.stream_messages(realm, days_ago),))
                  print("%d one-on-one private messages" % (self.private_messages(realm, days_ago),))
                  print("%d messages sent via the API" % (self.api_messages(realm, days_ago),))
                  print("%d group private messages" % (self.group_private_messages(realm, days_ago),))
  
              num_notifications_enabled = len([x for x in active_users if x.enable_desktop_notifications == True])
              self.report_percentage(num_notifications_enabled, num_active,
                                     "active users have desktop notifications enabled")
  
              num_enter_sends = len([x for x in active_users if x.enter_sends])
              self.report_percentage(num_enter_sends, num_active,
                                     "active users have enter-sends")
  
              all_message_count = human_messages.filter(sender__realm=realm).count()
              multi_paragraph_message_count = human_messages.filter(
                  sender__realm=realm, content__contains="\n\n").count()
              self.report_percentage(multi_paragraph_message_count, all_message_count,
                                     "all messages are multi-paragraph")
  
              # Starred messages
              starrers = UserMessage.objects.filter(user_profile__in=user_profiles,
                                                    flags=UserMessage.flags.starred).values(
                  "user_profile").annotate(count=Count("user_profile"))
              print("%d users have starred %d messages" % (
                  len(starrers), sum([elt["count"] for elt in starrers])))
  
              active_user_subs = Subscription.objects.filter(
                  user_profile__in=user_profiles, active=True)
  
              # Streams not in home view
              non_home_view = active_user_subs.filter(in_home_view=False).values(
                  "user_profile").annotate(count=Count("user_profile"))
              print("%d users have %d streams not in home view" % (
                  len(non_home_view), sum([elt["count"] for elt in non_home_view])))
  
              # Code block markup
              markup_messages = human_messages.filter(
                  sender__realm=realm, content__contains="~~~").values(
                  "sender").annotate(count=Count("sender"))
              print("%d users have used code block markup on %s messages" % (
                  len(markup_messages), sum([elt["count"] for elt in markup_messages])))
  
              # Notifications for stream messages
              notifications = active_user_subs.filter(notifications=True).values(
                  "user_profile").annotate(count=Count("user_profile"))
              print("%d users receive desktop notifications for %d streams" % (
                  len(notifications), sum([elt["count"] for elt in notifications])))
  
              print("")
  