Annotate most Zulip management commands.

This commit is contained in:
Tim Abbott
2016-06-04 07:52:18 -07:00
parent c2bea0fa08
commit a1a27b1789
63 changed files with 321 additions and 15 deletions

View File

@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import print_function
from django.core.management.base import BaseCommand
from typing import Any
from zerver.models import UserPresence, UserActivity
from zerver.lib.utils import statsd, statsd_key
@@ -15,6 +16,7 @@ class Command(BaseCommand):
Run as a cron job that runs every 10 minutes."""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
# Get list of all active users in the last 1 week
cutoff = datetime.now() - timedelta(minutes=30, hours=168)

View File

@@ -5,6 +5,7 @@ import datetime
import pytz
from optparse import make_option
from typing import Any
from django.core.management.base import BaseCommand
from zerver.lib.statistics import activity_averages_during_day
@@ -16,6 +17,7 @@ class Command(BaseCommand):
help="Day to query in format 2013-12-05. Default is yesterday"),)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options["date"] is None:
date = datetime.datetime.now() - datetime.timedelta(days=1)
else:

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from optparse import make_option
from django.core.management.base import BaseCommand
from zerver.models import Recipient, Message
@@ -10,6 +12,7 @@ import time
import logging
def compute_stats(log_level):
# type: (int) -> None
logger = logging.getLogger()
logger.setLevel(log_level)
@@ -76,6 +79,7 @@ class Command(BaseCommand):
help = "Compute statistics on MIT Zephyr usage."
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
level = logging.INFO
if options["verbose"]:
level = logging.DEBUG

View File

@@ -2,6 +2,8 @@ from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from typing import Any, Dict
from zerver.lib.statistics import seconds_usage_between
from optparse import make_option
@@ -11,6 +13,7 @@ import datetime
from django.utils.timezone import utc
def analyze_activity(options):
# type: (Dict[str, Any]) -> None
day_start = datetime.datetime.strptime(options["date"], "%Y-%m-%d").replace(tzinfo=utc)
day_end = day_start + datetime.timedelta(days=options["duration"])
@@ -56,4 +59,5 @@ is shown for all realms"""
)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
analyze_activity(options)

View File

@@ -1,8 +1,11 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from django.db.models import Count
from django.db.models import Count, QuerySet
from zerver.models import UserActivity, UserProfile, Realm, \
get_realm, get_user_profile_by_email
@@ -19,10 +22,12 @@ python manage.py client_activity zulip.com
python manage.py client_activity jesstess@zulip.com"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('arg', metavar='<arg>', type=str, nargs='?', default=None,
help="realm or user to estimate client activity for")
def compute_activity(self, user_activity_objects):
# type: (QuerySet) -> None
# Report data from the past week.
#
# This is a rough report of client activity because we inconsistently
@@ -54,6 +59,7 @@ python manage.py client_activity jesstess@zulip.com"""
def handle(self, *args, **options):
# type: (*Any, **str) -> None
if options['arg'] is None:
# Report global activity.
self.compute_activity(UserActivity.objects.all())

View File

@@ -2,6 +2,9 @@ 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
@@ -19,10 +22,12 @@ 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 \
@@ -33,36 +38,44 @@ class Command(BaseCommand):
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:
@@ -70,6 +83,7 @@ class Command(BaseCommand):
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']]

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from django.db.models import Q
from zerver.models import Realm, Stream, Message, Subscription, Recipient, get_realm
@@ -9,10 +12,12 @@ class Command(BaseCommand):
help = "Generate statistics on the streams for a realm."
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('realms', metavar='<realm>', type=str, nargs='*',
help="realm to generate statistics for")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
if options['realms']:
try:
realms = [get_realm(domain) for domain in options['realms']]

View File

@@ -1,8 +1,10 @@
from __future__ import absolute_import
from __future__ import print_function
from argparse import ArgumentParser
import datetime
import pytz
from typing import Any
from django.core.management.base import BaseCommand
from zerver.models import UserProfile, Realm, Stream, Message, get_realm
@@ -12,15 +14,18 @@ class Command(BaseCommand):
help = "Generate statistics on user 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 messages_sent_by(self, user, week):
# type: (UserProfile, int) -> int
start = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=(week + 1)*7)
end = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=week*7)
return Message.objects.filter(sender=user, pub_date__gt=start, pub_date__lte=end).count()
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options['realms']:
try:
realms = [get_realm(domain) for domain in options['realms']]

View File

@@ -4,6 +4,7 @@
__revision__ = '$Id: cleanupconfirmation.py 5 2008-11-18 09:10:12Z jarek.zgoda $'
from typing import Any
from django.core.management.base import NoArgsCommand
@@ -14,4 +15,5 @@ class Command(NoArgsCommand):
help = 'Delete expired confirmations from database'
def handle_noargs(self, **options):
# type: (**Any) -> None
Confirmation.objects.delete_expired_confirmations()

View File

@@ -3,6 +3,8 @@ from __future__ import print_function
from optparse import make_option
from typing import Any
from django.core.management.base import BaseCommand
from zerver.lib.actions import create_stream_if_needed, do_add_subscription
@@ -32,6 +34,7 @@ class Command(BaseCommand):
)
def handle(self, **options):
# type: (**Any) -> None
if options["domain"] is None or options["streams"] is None or \
(options["users"] is None and options["all_users"] is None):
self.print_help("python manage.py", "add_users_to_streams")

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.actions import do_update_message_flags
@@ -10,10 +13,12 @@ class Command(BaseCommand):
help = """Bankrupt one or many users."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('emails', metavar='<email>', type=str, nargs='+',
help='email address to bankrupt')
def handle(self, *args, **options):
# type: (*Any, **str) -> None
for email in options['emails']:
try:
user_profile = get_user_profile_by_email(email)

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.actions import do_change_full_name
@@ -10,10 +13,12 @@ class Command(BaseCommand):
help = """Change the names for many users."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('data_file', metavar='<data file>', type=str,
help="file containing rows of the form <email>,<desired name>")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
data_file = options['data_file']
with open(data_file, "r") as f:
for line in f:

View File

@@ -1,8 +1,10 @@
from __future__ import absolute_import
from __future__ import print_function
from django.core.management.base import BaseCommand
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.actions import do_change_user_email
from zerver.models import UserProfile, get_user_profile_by_email
@@ -10,12 +12,14 @@ class Command(BaseCommand):
help = """Change the email address for a user."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('old_email', metavar='<old email>', type=str,
help='email address to change')
parser.add_argument('new_email', metavar='<new email>', type=str,
help='new email address')
def handle(self, *args, **options):
# type: (*Any, **str) -> None
old_email = options['old_email']
new_email = options['new_email']
try:

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import
from typing import Any
from django.core.management.base import BaseCommand
from zerver.lib.push_notifications import check_apns_feedback
@@ -10,4 +12,5 @@ class Command(BaseCommand):
Usage: ./manage.py check_apns_tokens"""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
check_apns_feedback()

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any, Callable, Optional
from zerver.models import get_user_profile_by_id
from zerver.lib.rate_limiter import client, max_api_calls, max_api_window
@@ -24,6 +26,7 @@ class Command(BaseCommand):
)
def _check_within_range(self, key, count_func, trim_func=None):
# type: ignore # should be (str, Callable[[], int], Optional[Callable[[str, int], None]]) -> None
user_id = int(key.split(':')[1])
try:
user = get_user_profile_by_id(user_id)
@@ -44,6 +47,7 @@ than max_api_calls! (trying to trim) %s %s" % (key, count))
trim_func(key, max_calls)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if not settings.RATE_LIMITING:
print("This machine is not using redis or rate limiting, aborting")
exit(1)

View File

@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import print_function
from optparse import make_option
from typing import Any
from django.core.management.base import BaseCommand
from django.conf import settings
@@ -11,7 +12,8 @@ class Command(BaseCommand):
help = """Checks your Zulip Voyager Django configuration for issues."""
option_list = BaseCommand.option_list + ()
def handle(self, **options):
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
for (setting_name, default) in settings.REQUIRED_SETTINGS:
try:
if settings.__getattr__(setting_name) != default:

View File

@@ -2,6 +2,8 @@ from __future__ import absolute_import
from __future__ import print_function
from optparse import make_option
from typing import Any
from django.conf import settings
from django.core.management.base import BaseCommand
from zerver.lib.actions import do_create_realm, set_default_streams
@@ -40,6 +42,7 @@ Usage: python manage.py create_realm --domain=foo.com --name='Foo, Inc.'"""
)
def validate_domain(self, domain):
# type: (str) -> None
# Domains can't contain whitespace if they are to be used in memcached
# keys. Seems safer to leave that as the default case regardless of
# which backing store we use.
@@ -56,6 +59,7 @@ Usage: python manage.py create_realm --domain=foo.com --name='Foo, Inc.'"""
raise ValueError("Cannot create a new realm that is already an alias for an existing realm")
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options["domain"] is None or options["name"] is None:
print("\033[1;31mPlease provide both a domain and name.\033[0m\n", file=sys.stderr)
self.print_help("python manage.py", "create_realm")

View File

@@ -1,11 +1,14 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from django.core.management.base import BaseCommand
from zerver.lib.actions import do_create_stream
from zerver.models import Realm, get_realm
from argparse import ArgumentParser
import sys
class Command(BaseCommand):
@@ -15,12 +18,14 @@ This should be used for TESTING only, unless you understand the limitations of
the command."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('domain', metavar='<domain>', type=str,
help='domain in which to create the stream')
parser.add_argument('stream_name', metavar='<stream name>', type=str,
help='name of stream to create')
def handle(self, *args, **options):
# type: (*Any, **str) -> None
domain = options['domain']
stream_name = options['stream_name']
encoding = sys.getfilesystemencoding()

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
import sys
import argparse
@@ -25,6 +27,7 @@ Omit both <email> and <full name> for interactive user creation.
"""
def add_arguments(self, parser):
# type: (argparse.ArgumentParser) -> None
parser.add_argument('--this-user-has-accepted-the-tos',
dest='tos',
action="store_true",
@@ -40,6 +43,7 @@ Omit both <email> and <full name> for interactive user creation.
help='full name of new user')
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if not options["tos"]:
raise CommandError("""You must confirm that this user has accepted the
Terms of Service by passing --this-user-has-accepted-the-tos.""")

View File

@@ -1,8 +1,11 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from django.core.management.base import BaseCommand
from argparse import ArgumentParser
import sys
from zerver.lib.actions import do_deactivate_realm
@@ -12,10 +15,12 @@ class Command(BaseCommand):
help = """Script to deactivate a realm."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('domain', metavar='<domain>', type=str,
help='domain of realm to deactivate')
def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"])
if realm is None:
print("Could not find realm %s" % (options["domain"],))

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from optparse import make_option
from django.core.management.base import BaseCommand
@@ -12,6 +15,7 @@ class Command(BaseCommand):
help = "Deactivate a user, including forcibly logging them out."
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-f', '--for-real',
dest='for_real',
action='store_true',
@@ -21,6 +25,7 @@ class Command(BaseCommand):
help='email of user to deactivate')
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
user_profile = get_user_profile_by_email(options['email'])
print("Deactivating %s (%s) - %s" % (user_profile.full_name,

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.actions import do_delete_old_unclaimed_attachments
@@ -12,6 +15,7 @@ class Command(BaseCommand):
One week is taken as the default value."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-w', '--weeks',
dest='delta_weeks',
default=1,
@@ -24,7 +28,7 @@ class Command(BaseCommand):
help="Actually remove the files from the storage.")
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
delta_weeks = options['delta_weeks']
print("Deleting unclaimed attached files older than %s" % (delta_weeks,))
print("")

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from optparse import make_option
from django.core.management.base import BaseCommand
from zerver.models import get_realm, Message, Realm, Stream, Recipient
@@ -24,6 +26,7 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
realm = get_realm(options["domain"])
streams = Stream.objects.filter(realm=realm, invite_only=False)
recipients = Recipient.objects.filter(

View File

@@ -36,6 +36,9 @@ This script can be used via two mechanisms:
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
import email
import os
from email.header import decode_header
@@ -123,9 +126,11 @@ def login_failed(failure):
return failure
def done(_):
# type: (Any) -> None
reactor.callLater(0, reactor.stop)
def main():
# type: () -> None
imap_client = protocol.ClientCreator(reactor, imap4.IMAP4Client)
d = imap_client.connectSSL(settings.EMAIL_GATEWAY_IMAP_SERVER, settings.EMAIL_GATEWAY_IMAP_PORT, ssl.ClientContextFactory())
d.addCallbacks(connected, login_failed)
@@ -135,10 +140,12 @@ class Command(BaseCommand):
help = __doc__
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('recipient', metavar='<recipient>', type=str, nargs='?', default=None,
help="original recipient")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
rcpt_to = os.environ.get("ORIGINAL_RECIPIENT", options['recipient'])
if rcpt_to is not None:
if is_missed_message_address(rcpt_to):

View File

@@ -3,6 +3,8 @@ import datetime
import pytz
import logging
from typing import Any
from django.conf import settings
from django.core.management.base import BaseCommand
@@ -25,6 +27,7 @@ logger.addHandler(file_handler)
VALID_DIGEST_DAYS = (1, 2, 3, 4)
def inactive_since(user_profile, cutoff):
# type: (UserProfile, datetime.datetime) -> bool
# Hasn't used the app in the last 24 business-day hours.
most_recent_visit = [row.last_visit for row in \
UserActivity.objects.filter(
@@ -38,6 +41,7 @@ def inactive_since(user_profile, cutoff):
return last_visit < cutoff
def last_business_day():
# type: () -> datetime.datetime
one_day = datetime.timedelta(hours=23)
previous_day = datetime.datetime.now(tz=pytz.utc) - one_day
while previous_day.weekday() not in VALID_DIGEST_DAYS:
@@ -47,12 +51,14 @@ def last_business_day():
# Changes to this should also be reflected in
# zerver/worker/queue_processors.py:DigestWorker.consume()
def queue_digest_recipient(user_profile, cutoff):
# type: (UserProfile, datetime.datetime) -> None
# Convert cutoff to epoch seconds for transit.
event = {"user_profile_id": user_profile.id,
"cutoff": cutoff.strftime('%s')}
queue_json_publish("digest_emails", event, lambda event: None)
def domains_for_this_deployment():
# type: () -> List[str]
if settings.ZULIP_COM:
# Voyager deployments don't have a Deployment entry.
# Only send zulip.com digests on staging.
@@ -69,6 +75,7 @@ def domains_for_this_deployment():
return []
def should_process_digest(domain, deployment_domains):
# type: (str, List[str]) -> bool
if settings.VOYAGER:
# Voyager. We ship with a zulip.com realm for the feedback bot, but
# don't try to send e-mails to it.
@@ -85,6 +92,7 @@ class Command(BaseCommand):
in a while.
"""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
# To be really conservative while we don't have user timezones or
# special-casing for companies with non-standard workweeks, only
# try to send mail on Tuesdays, Wednesdays, and Thursdays.

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.queue import queue_json_publish
@@ -9,6 +12,7 @@ import ujson
def error(*args):
# type: (*Any) -> None
raise Exception('We cannot enqueue because settings.USING_RABBITMQ is False.')
class Command(BaseCommand):
@@ -23,12 +27,14 @@ You can use "-" to represent stdin.
"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('queue_name', metavar='<queue>', type=str,
help="name of worker queue to enqueue to")
parser.add_argument('file_name', metavar='<file>', type=str,
help="name of file containing JSON lines")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
queue_name = options['queue_name']
file_name = options['file_name']

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import
from typing import Any
from optparse import make_option
from django.core.management.base import BaseCommand
from zerver.lib.cache_helpers import fill_remote_cache, cache_fillers
@@ -10,8 +12,10 @@ class Command(BaseCommand):
help = "Populate the memcached cache of messages."
def handle(self, *args, **options):
# type: (*Any, **str) -> None
if options["cache"] is not None:
return fill_remote_cache(options["cache"])
fill_remote_cache(options["cache"])
return
for cache in cache_fillers.keys():
fill_remote_cache(cache)

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from confirmation.models import Confirmation
from zerver.models import UserProfile, PreregistrationUser, \
@@ -10,6 +13,7 @@ class Command(BaseCommand):
help = "Generate activation links for users and print them to stdout."
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('--domain',
dest='domain',
type=str,
@@ -23,6 +27,7 @@ class Command(BaseCommand):
help='email of user to generate an activation link for')
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
duplicates = False
for email in options['emails']:
try:

View File

@@ -1,5 +1,8 @@
from __future__ import absolute_import
from typing import Any
from argparse import ArgumentParser
import requests
from zerver.models import get_user_profile_by_email, UserProfile
from zerver.lib.avatar import gravatar_hash
@@ -13,12 +16,14 @@ email addresses are specified, use the Gravatar for the first and upload the ima
for both email addresses."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('old_email', metavar='<old email>', type=str,
help="user whose Gravatar should be migrated")
parser.add_argument('new_email', metavar='<new email>', type=str, nargs='?', default=None,
help="user to copy the Gravatar to")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
old_email = options['old_email']
if options['new_email']:

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import
from typing import Any, Iterable, Tuple
from django.core.management.base import BaseCommand
from django.contrib.sites.models import Site
@@ -14,6 +16,7 @@ from optparse import make_option
settings.TORNADO_SERVER = None
def create_users(name_list, bot_type=None):
# type: (Iterable[Tuple[str, str]], int) -> None
realms = {}
for realm in Realm.objects.all():
realms[realm.domain] = realm
@@ -35,7 +38,8 @@ class Command(BaseCommand):
help='The number of extra users to create'),
)
def handle(self, **options):
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
Realm.objects.create(domain="zulip.com")
names = [(settings.FEEDBACK_BOT_NAME, settings.FEEDBACK_BOT)]

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand, CommandError
from django.core.exceptions import ValidationError
@@ -15,6 +18,7 @@ ONLY perform this on customer request from an authorized person.
"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-f', '--for-real',
dest='ack',
action="store_true",
@@ -34,6 +38,7 @@ ONLY perform this on customer request from an authorized person.
help="email of user to knight")
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
email = options['email']
try:
profile = UserProfile.objects.get(email=email)

View File

@@ -1,5 +1,6 @@
from __future__ import absolute_import
from typing import Any
from optparse import make_option
from django.core.management.base import BaseCommand
@@ -24,6 +25,7 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options["realm"]:
realm = get_realm(options["realm"])
delete_realm_user_sessions(realm)

View File

@@ -30,6 +30,9 @@ http://stackoverflow.com/questions/2090717/getting-translation-strings-for-jinja
"""
from __future__ import absolute_import
from typing import Any, Dict, Iterable, Mapping, Set, Tuple
from argparse import ArgumentParser
import os
import re
import glob
@@ -53,6 +56,7 @@ regexes = ['{{#tr .*?}}(.*?){{/tr}}',
frontend_compiled_regexes = [re.compile(regex) for regex in regexes]
def strip_whitespaces(src):
# type: (str) -> str
src = strip_whitespace_left.sub(r'\1', src)
src = strip_whitespace_right.sub(r'\1', src)
return src
@@ -60,6 +64,7 @@ def strip_whitespaces(src):
class Command(makemessages.Command):
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
super(Command, self).add_arguments(parser)
parser.add_argument('--frontend-source', type=str,
default='static/templates',
@@ -72,10 +77,12 @@ class Command(makemessages.Command):
help='Namespace of the frontend locale file')
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
self.handle_django_locales(*args, **options)
self.handle_frontend_locales(*args, **options)
def handle_frontend_locales(self, *args, **options):
# type: (*Any, **Any) -> None
self.frontend_source = options.get('frontend_source')
self.frontend_output = options.get('frontend_output')
self.frontend_namespace = options.get('frontend_namespace')
@@ -87,6 +94,7 @@ class Command(makemessages.Command):
self.write_translation_strings(translation_strings)
def handle_django_locales(self, *args, **options):
# type: (*Any, **Any) -> None
old_endblock_re = trans_real.endblock_re
old_block_re = trans_real.block_re
old_constant_re = trans_real.constant_re
@@ -117,7 +125,8 @@ class Command(makemessages.Command):
trans_real.constant_re = old_constant_re
def extract_strings(self, data):
translation_strings = {}
# type: (str) -> Dict[str, str]
translation_strings = {} # type: Dict[str, str]
for regex in frontend_compiled_regexes:
for match in regex.findall(data):
translation_strings[match] = ""
@@ -125,6 +134,7 @@ class Command(makemessages.Command):
return translation_strings
def get_translation_strings(self):
# type: () -> Dict[str, str]
translation_strings = {} # type: Dict[str, str]
dirname = self.get_template_dir()
@@ -137,13 +147,16 @@ class Command(makemessages.Command):
return translation_strings
def get_template_dir(self):
# type: () -> str
return self.frontend_source
def get_namespace(self):
# type: () -> str
return self.frontend_namespace
def get_locales(self):
# type: () -> Iterable[str]
locale = self.frontend_locale
exclude = self.frontend_exclude
process_all = self.frontend_all
@@ -159,9 +172,11 @@ class Command(makemessages.Command):
return set(locales) - set(exclude)
def get_base_path(self):
# type: () -> str
return self.frontend_output
def get_output_paths(self):
# type: () -> Iterable[str]
base_path = self.get_base_path()
locales = self.get_locales()
for path in [os.path.join(base_path, locale) for locale in locales]:
@@ -171,17 +186,19 @@ class Command(makemessages.Command):
yield os.path.join(path, self.get_namespace())
def get_new_strings(self, old_strings, translation_strings):
# type: (Mapping[str, str], Iterable[str]) -> Dict[str, str]
"""
Missing strings are removed, new strings are added and already
translated strings are not touched.
"""
new_strings = {}
new_strings = {} # Dict[str, str]
for k in translation_strings:
new_strings[k] = old_strings.get(k, k)
return new_strings
def write_translation_strings(self, translation_strings):
# type: (Iterable[str]) -> None
for locale, output_path in zip(self.get_locales(), self.get_output_paths()):
self.stdout.write("[frontend] processing locale {}".format(locale))
try:

View File

@@ -7,6 +7,7 @@ Shows backlog count of ScheduledJobs of type Email
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from django.conf import settings
from django.core.management.base import BaseCommand
@@ -25,7 +26,6 @@ Usage: python manage.py print_email_delivery_backlog
"""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
print(len(ScheduledJob.objects.filter(type=ScheduledJob.EMAIL,
scheduled_timestamp__lte=datetime.utcnow()-timedelta(minutes=1))))
return

View File

@@ -1,5 +1,8 @@
from __future__ import absolute_import
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from django.core.management import CommandError
from django.conf import settings
@@ -11,6 +14,7 @@ import threading
class Command(BaseCommand):
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('--queue_name', metavar='<queue name>', type=str,
help="queue to process")
parser.add_argument('--worker_num', metavar='<worker number>', type=int, nargs='?', default=0,
@@ -20,6 +24,7 @@ class Command(BaseCommand):
help = "Runs a queue processing worker"
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
logging.basicConfig()
logger = logging.getLogger('process_queue')
@@ -51,10 +56,12 @@ class Command(BaseCommand):
class Threaded_worker(threading.Thread):
def __init__(self, queue_name):
# type: (str) -> None
threading.Thread.__init__(self)
self.worker = get_worker(queue_name)
def run(self):
# type: () -> None
self.worker.setup()
logging.debug('starting consuming ' + self.worker.queue_name)
self.worker.start()

View File

@@ -1,17 +1,22 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from django.core.management import CommandError
from zerver.lib.queue import SimpleQueueClient
class Command(BaseCommand):
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('queue_name', metavar='<queue name>', type=str,
help="queue to purge")
help = "Discards all messages from the given queue"
def handle(self, *args, **options):
# type: (*Any, **str) -> None
queue_name = options['queue_name']
queue = SimpleQueueClient()
queue.ensure_queue(queue_name, lambda: None)

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
import sys
from django.contrib.auth import authenticate, login, get_backends
@@ -12,6 +15,7 @@ from django_auth_ldap.backend import LDAPBackend, _LDAPUser
# Quick tool to test whether you're correctly authenticating to LDAP
def query_ldap(**options):
# type: (**str) -> None
email = options['email']
for backend in get_backends():
if isinstance(backend, LDAPBackend):
@@ -24,8 +28,10 @@ def query_ldap(**options):
class Command(BaseCommand):
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('email', metavar='<email>', type=str,
help="email of user to query")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
query_ldap(**options)

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from zerver.models import UserProfile, get_user_profile_by_email
from zerver.lib.rate_limiter import block_user, unblock_user
@@ -11,6 +14,7 @@ class Command(BaseCommand):
help = """Manually block or unblock a user from accessing the API"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-e', '--email',
dest='email',
help="Email account of user.")
@@ -35,6 +39,7 @@ class Command(BaseCommand):
help="operation to perform (block or unblock)")
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if (not options['api_key'] and not options['email']) or \
(options['api_key'] and options['email']):
print("Please enter either an email or API key to manage")

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
import sys
@@ -12,10 +15,12 @@ class Command(BaseCommand):
help = """Script to reactivate a deactivated realm."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('domain', metavar='<domain>', type=str,
help='domain of realm to reactivate')
def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"])
if realm is None:
print("Could not find realm %s" % (options["domain"],))

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.models import Realm, RealmAlias, get_realm
from zerver.lib.actions import realm_aliases
@@ -10,6 +13,7 @@ class Command(BaseCommand):
help = """Manage aliases for the specified realm"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-r', '--realm',
dest='domain',
type=str,
@@ -24,6 +28,7 @@ class Command(BaseCommand):
help="alias to add or remove")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"])
if options["op"] == "show":
print("Aliases for %s:" % (realm.domain,))

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.models import Realm, get_realm
from zerver.lib.actions import check_add_realm_emoji, do_remove_realm_emoji
@@ -16,6 +19,7 @@ Example: python manage.py realm_emoji --realm=zulip.com --op=show
"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-r', '--realm',
dest='domain',
type=str,
@@ -32,6 +36,7 @@ Example: python manage.py realm_emoji --realm=zulip.com --op=show
help="URL of image to display for the emoji")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"])
if options["op"] == "show":
for name, url in six.iteritems(realm.get_emoji()):

View File

@@ -1,5 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from optparse import make_option
from django.core.management.base import BaseCommand
@@ -22,6 +26,7 @@ Example: python manage.py realm_filters --realm=zulip.com --op=show
"""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-r', '--realm',
dest='domain',
type=str,
@@ -38,6 +43,7 @@ Example: python manage.py realm_filters --realm=zulip.com --op=show
help="format string to substitute")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"])
if options["op"] == "show":
print("%s: %s" % (realm.domain, all_realm_filters().get(realm.domain, [])))

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from optparse import make_option
from django.core.management.base import BaseCommand
@@ -33,6 +36,7 @@ class Command(BaseCommand):
)
def handle(self, **options):
# type: (*Any, **Any) -> None
if options["domain"] is None or options["stream"] is None or \
(options["users"] is None and options["all_users"] is None):
self.print_help("python manage.py", "remove_users_from_stream")

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.actions import do_rename_stream
@@ -12,6 +15,7 @@ class Command(BaseCommand):
help = """Change the stream name for a realm."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('domain', metavar='<domain>', type=str,
help="domain to operate on")
parser.add_argument('old_name', metavar='<old name>', type=str,
@@ -20,6 +24,7 @@ class Command(BaseCommand):
help='new name to rename the stream to')
def handle(self, *args, **options):
# type: (*Any, **str) -> None
domain = options['domain']
old_name = options['old_name']
new_name = options['new_name']

View File

@@ -1,8 +1,11 @@
# Wrapper around Django's runserver to allow filtering logs.
from typing import Any
from django.core.servers.basehttp import WSGIRequestHandler
orig_log_message = WSGIRequestHandler.log_message
def log_message_monkey(self, format, *args):
# type: (Any, str, *Any) -> None
# Filter output for 200 or 304 responses.
if args[1] == '200' or args[1] == '304':
return

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from django.conf import settings
settings.RUNNING_INSIDE_TORNADO = True
# We must call zerver.lib.tornado_ioloop_logging.instrument_tornado_ioloop
@@ -12,6 +14,7 @@ from zerver.lib.tornado_ioloop_logging import instrument_tornado_ioloop
instrument_tornado_ioloop()
from django.core.management.base import BaseCommand, CommandError
from django.http import HttpRequest, HttpResponse
from optparse import make_option
import os
import sys
@@ -55,6 +58,7 @@ class Command(BaseCommand):
args = '[optional port number or ipaddr:port]\n (use multiple ports to start multiple servers)'
def handle(self, addrport, **options):
# type: (str, **bool) -> None
# setup unbuffered I/O
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0)
@@ -83,6 +87,7 @@ class Command(BaseCommand):
format='%(asctime)s %(levelname)-8s %(message)s')
def inner_run():
# type: () -> None
from django.conf import settings
from django.utils import translation
translation.activate(settings.LANGUAGE_CODE)
@@ -139,6 +144,7 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
initLock = Lock()
def __init__(self, *args, **kwargs):
# type: (*Any, **Any) -> None
super(AsyncDjangoHandler, self).__init__(*args, **kwargs)
# Set up middleware if needed. We couldn't do this earlier, because
@@ -155,9 +161,11 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
allocate_handler_id(self)
def __repr__(self):
# type: () -> str
return "AsyncDjangoHandler<%s, %s>" % (self.handler_id, get_descriptor_by_handler_id(self.handler_id))
def get(self, *args, **kwargs):
# type: (*Any, **Any) -> None
environ = WSGIContainer.environ(self.request)
environ['PATH_INFO'] = urllib.parse.unquote(environ['PATH_INFO'])
request = WSGIRequest(environ)
@@ -186,21 +194,26 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
def head(self, *args, **kwargs):
# type: (*Any, **Any) -> None
self.get(*args, **kwargs)
def post(self, *args, **kwargs):
# type: (*Any, **Any) -> None
self.get(*args, **kwargs)
def delete(self, *args, **kwargs):
# type: (*Any, **Any) -> None
self.get(*args, **kwargs)
def on_connection_close(self):
# type: () -> None
client_descriptor = get_descriptor_by_handler_id(self.handler_id)
if client_descriptor is not None:
client_descriptor.disconnect_handler(client_closed=True)
# Based on django.core.handlers.base: get_response
def get_response(self, request):
# type: (HttpRequest) -> HttpResponse
"Returns an HttpResponse object for the given HttpRequest"
try:
try:
@@ -325,6 +338,7 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
### Copied from get_response (above in this file)
def apply_response_middleware(self, request, response, resolver):
# type: (HttpRequest, HttpResponse, urlresolvers.RegexURLResolver) -> HttpResponse
try:
# Apply response middleware, regardless of the response
for middleware_method in self._response_middleware:
@@ -337,6 +351,7 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
return response
def zulip_finish(self, response, request, apply_markdown):
# type: (HttpResponse, HttpRequest, bool) -> None
# Make sure that Markdown rendering really happened, if requested.
# This is a security issue because it's where we escape HTML.
# c.f. ticket #64

View File

@@ -1,5 +1,8 @@
from __future__ import absolute_import
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from django.conf import settings
@@ -7,6 +10,7 @@ class Command(BaseCommand):
help = """Send some stats to statsd."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('operation', metavar='<operation>', type=str,
choices=['incr', 'decr', 'timing', 'timer', 'gauge'],
help="incr|decr|timing|timer|gauge")
@@ -14,6 +18,7 @@ class Command(BaseCommand):
parser.add_argument('val', metavar='<val>', type=str)
def handle(self, *args, **options):
# type: (*Any, **str) -> None
operation = options['operation']
name = options['name']
val = options['val']

View File

@@ -1,5 +1,8 @@
from __future__ import absolute_import
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from django.core.mail import send_mail, BadHeaderError
@@ -8,10 +11,12 @@ class Command(BaseCommand):
help = """Send email to specified email address."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('to', metavar='<to>', type=str,
help="email of user to send the email")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
subject = "Zulip Test email"
message = "Success! If you receive this message, you've successfully " + \
"configured sending email from your Zulip server."

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
import os
import ujson
from optparse import make_option
@@ -32,6 +34,7 @@ Example:
)
def handle(self, **options):
# type: (*Any, **str) -> None
if options['fixture'] is None or options['url'] is None:
self.print_help('python manage.py', 'send_webhook_fixture_message')
exit(1)
@@ -47,7 +50,9 @@ Example:
client.post(options['url'], json, content_type="application/json")
def _does_fixture_path_exist(self, fixture_path):
# type: (str) -> bool
return os.path.exists(fixture_path)
def _get_fixture_as_json(self, fixture_path):
# type: (str) -> str
return ujson.dumps(ujson.loads(open(fixture_path).read()))

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from django.core.management.base import BaseCommand
from zerver.models import get_realm
@@ -36,6 +38,7 @@ python manage.py set_default_streams --domain=foo.com --streams=
)
def handle(self, **options):
# type: (*Any, **str) -> None
if options["domain"] is None or options["streams"] is None:
print("Please provide both a domain name and a default \
set of streams (which can be empty, with `--streams=`).", file=sys.stderr)

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any, Iterable
from optparse import make_option
import logging
import sys
@@ -41,6 +43,7 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if not options["flag"] or not options["op"] or not options["email"]:
print("Please specify an operation, a flag and an email")
exit(1)
@@ -65,6 +68,7 @@ class Command(BaseCommand):
sys.stderr.close()
def do_update(batch):
# type: (Iterable[int]) -> None
msgs = UserMessage.objects.filter(id__in=batch)
if op == 'add':
msgs.update(flags=models.F('flags').bitor(flag))

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.models import get_realm, Realm
import sys
@@ -9,16 +12,18 @@ class Command(BaseCommand):
help = """Show the admins in a realm."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('realm', metavar='<realm>', type=str,
help="realm to show admins for")
def handle(self, *args, **options):
realm = options['realm']
# type: (*Any, **str) -> None
realm_name = options['realm']
try:
realm = get_realm(realm)
realm = get_realm(realm_name)
except Realm.DoesNotExist:
print('There is no realm called %s.' % (realm,))
print('There is no realm called %s.' % (realm_name,))
sys.exit(1)
users = realm.get_admin_users()

View File

@@ -1,6 +1,7 @@
from __future__ import absolute_import
import logging
from typing import Any
from django.core.management.base import BaseCommand
from django.db.utils import IntegrityError
@@ -24,6 +25,7 @@ logger.addHandler(file_handler)
# Run this on a cronjob to pick up on name changes.
def sync_ldap_user_data():
# type: () -> None
logger.info("Starting update.")
backend = ZulipLDAPUserPopulator()
for u in UserProfile.objects.select_related().filter(is_active=True, is_bot=False).all():
@@ -40,4 +42,5 @@ def sync_ldap_user_data():
class Command(BaseCommand):
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
sync_ldap_user_data()

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from optparse import make_option
from django.core.management.base import BaseCommand
@@ -23,6 +25,7 @@ class Command(BaseCommand):
)
def handle(self, **options):
# type: (**str) -> None
if options["domain"] is None and options["users"] is None:
self.print_help("python manage.py", "turn_off_digests")
exit(1)

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import
from typing import Any
from django.core.management.base import BaseCommand
from django.db.models import get_app, get_models
from django.contrib.auth.management import create_permissions
@@ -8,5 +10,6 @@ class Command(BaseCommand):
help = "Sync newly created object permissions to the database"
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
# From http://stackoverflow.com/a/11914435/90777
create_permissions(get_app("zerver"), get_models(), 2)

View File

@@ -1,7 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from optparse import make_option
import sys
from typing import Any
from django.core.management.base import BaseCommand
@@ -31,6 +33,7 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if None in (options["api"], options["web"], options["domain"]):
print("\033[1;31mYou must provide a domain, an API URL, and a web URL.\033[0m\n", file=sys.stderr)
self.print_help("python manage.py", "create_realm")

View File

@@ -1,5 +1,8 @@
from __future__ import absolute_import
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.actions import send_referral_event
@@ -9,12 +12,14 @@ class Command(BaseCommand):
help = """Grants a user invites and resets the number of invites they've used."""
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('email', metavar='<email>', type=str,
help="user to grant invites to")
parser.add_argument('num_invites', metavar='<num invites>', type=int,
help="number of invites to grant")
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
email = options['email']
num_invites = options['num_invites']

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import
from typing import Any
from django.core.management.base import BaseCommand
from zerver.models import Subscription
@@ -7,6 +9,7 @@ class Command(BaseCommand):
help = """One-off script to migration users' stream notification settings."""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
for subscription in Subscription.objects.all():
subscription.desktop_notifications = subscription.notifications
subscription.audible_notifications = subscription.notifications

View File

@@ -31,11 +31,12 @@ import glob
import os
from optparse import make_option
from six.moves import range
from typing import Any, Dict, Set, Tuple
from typing import Any, Dict, List, IO, Iterable, Mapping, Set, Tuple
settings.TORNADO_SERVER = None
def create_users(realms, name_list, bot_type=None):
# type: (Mapping[str, Realm], Iterable[Tuple[str, str]], int) -> None
user_set = set()
for full_name, email in name_list:
short_name = email_to_username(email)
@@ -43,6 +44,7 @@ def create_users(realms, name_list, bot_type=None):
bulk_create_users(realms, user_set, bot_type)
def create_streams(realms, realm, stream_list):
# type: (Mapping[str, Realm], Realm, Iterable[str]) -> None
stream_set = set()
for stream_name in stream_list:
stream_set.add((realm.domain, stream_name))
@@ -109,6 +111,7 @@ class Command(BaseCommand):
)
def handle(self, **options):
# type: (**Any) -> None
if options["percent_huddles"] + options["percent_personals"] > 100:
self.stderr.write("Error! More than 100% of messages allocated.\n")
return
@@ -257,11 +260,13 @@ class Command(BaseCommand):
recipient_hash = {} # type: Dict[int, Recipient]
def get_recipient_by_id(rid):
# type: (int) -> Recipient
if rid in recipient_hash:
return recipient_hash[rid]
return Recipient.objects.get(id=rid)
def restore_saved_messages():
# type: () -> None
old_messages = []
duplicate_suppression_hash = {} # type: Dict[str, bool]
@@ -276,6 +281,7 @@ def restore_saved_messages():
# First, determine all the objects our messages will need.
print(datetime.datetime.now(), "Creating realms/streams/etc...")
def process_line(line):
# type: (str) -> None
old_message_json = line.strip()
# Due to populate_db's shakespeare mode, we have a lot of
@@ -302,6 +308,7 @@ def restore_saved_messages():
# Lower case emails and domains; it will screw up
# deduplication if we don't
def fix_email(email):
# type: (str) -> str
return email.strip().lower()
if message_type in ["stream", "huddle", "personal"]:
@@ -708,6 +715,7 @@ def restore_saved_messages():
# - multiple messages per subject
# - both single and multi-line content
def send_messages(data):
# type: ignore # Should be (Tuple[int, List[UserProfile], Dict[str, Any], Any]) -> int
(tot_messages, personals_pairs, options, output) = data
random.seed(os.getpid())
texts = open("zilencer/management/commands/test_messages.txt", "r").readlines()

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand
from zerver.lib.initial_password import initial_password
from zerver.models import get_user_profile_by_email
@@ -11,10 +14,12 @@ class Command(BaseCommand):
fmt = '%-30s %-16s %-32s'
def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('emails', metavar='<email>', type=str, nargs='*',
help="email of user to show password and API key for")
def handle(self, *args, **options):
# type: (*Any, **str) -> None
print(self.fmt % ('email', 'password', 'API key'))
for email in options['emails']:
if '@' not in email:

View File

@@ -1,6 +1,7 @@
from __future__ import absolute_import
from typing import Any, Dict
from django.http import HttpRequest, HttpResponse
from optparse import make_option
from django.core.management.base import BaseCommand
from zerver.models import get_user_profile_by_email, UserMessage
@@ -13,10 +14,12 @@ request_logger = LogRequests()
class MockSession(object):
def __init__(self):
# type: () -> None
self.modified = False
class MockRequest(object):
class MockRequest(HttpRequest):
def __init__(self, email):
# type: (str) -> None
self.user = get_user_profile_by_email(email)
self.path = '/'
self.method = "POST"
@@ -28,9 +31,11 @@ class MockRequest(object):
self.session = MockSession()
def get_full_path(self):
# type: () -> str
return self.path
def profile_request(request):
# type: (HttpRequest) -> HttpResponse
request_logger.process_request(request)
prof = cProfile.Profile()
prof.enable()
@@ -48,4 +53,5 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
profile_request(MockRequest(options["email"]))

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from django.core.management.base import BaseCommand
from zerver.models import Message
@@ -13,6 +15,7 @@ class Command(BaseCommand):
Usage: python manage.py render_old_messages"""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
total_rendered = 0
while True:
messages = Message.objects.filter(rendered_content_version=None)[0:100]

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import
from __future__ import print_function
from typing import Any
from django.core.management.base import BaseCommand
from zerver.models import get_user_profile_by_email, UserProfile
import os
@@ -10,6 +12,7 @@ class Command(BaseCommand):
help = """Sync your API key from ~/.zuliprc into your development instance"""
def handle(self, *args, **options):
# type: (*Any, **Any) -> None
config_file = os.path.join(os.environ["HOME"], ".zuliprc")
if not os.path.exists(config_file):
raise RuntimeError("No ~/.zuliprc found")