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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,9 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
import datetime import datetime
import pytz import pytz
@@ -19,10 +22,12 @@ class Command(BaseCommand):
help = "Generate statistics on realm activity." help = "Generate statistics on realm activity."
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('realms', metavar='<realm>', type=str, nargs='*', parser.add_argument('realms', metavar='<realm>', type=str, nargs='*',
help="realm to generate statistics for") help="realm to generate statistics for")
def active_users(self, realm): def active_users(self, realm):
# type: (Realm) -> List[UserProfile]
# Has been active (on the website, for now) in the last 7 days. # 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) activity_cutoff = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=7)
return [activity.user_profile for activity in \ return [activity.user_profile for activity in \
@@ -33,36 +38,44 @@ class Command(BaseCommand):
client__name="website")] client__name="website")]
def messages_sent_by(self, user, days_ago): 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) 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() return human_messages.filter(sender=user, pub_date__gt=sent_time_cutoff).count()
def total_messages(self, realm, days_ago): 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) 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() return Message.objects.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
def human_messages(self, realm, days_ago): 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) 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() return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).count()
def api_messages(self, realm, days_ago): def api_messages(self, realm, days_ago):
# type: (Realm, int) -> int
return (self.total_messages(realm, days_ago) - self.human_messages(realm, days_ago)) return (self.total_messages(realm, days_ago) - self.human_messages(realm, days_ago))
def stream_messages(self, 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) 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, return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff,
recipient__type=Recipient.STREAM).count() recipient__type=Recipient.STREAM).count()
def private_messages(self, realm, days_ago): 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) 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( return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.HUDDLE).count() recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.HUDDLE).count()
def group_private_messages(self, realm, days_ago): 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) 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( return human_messages.filter(sender__realm=realm, pub_date__gt=sent_time_cutoff).exclude(
recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.PERSONAL).count() recipient__type=Recipient.STREAM).exclude(recipient__type=Recipient.PERSONAL).count()
def report_percentage(self, numerator, denominator, text): def report_percentage(self, numerator, denominator, text):
# type: (float, float, str) -> None
if not denominator: if not denominator:
fraction = 0.0 fraction = 0.0
else: else:
@@ -70,6 +83,7 @@ class Command(BaseCommand):
print("%.2f%% of" % (fraction * 100,), text) print("%.2f%% of" % (fraction * 100,), text)
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options['realms']: if options['realms']:
try: try:
realms = [get_realm(domain) for domain in options['realms']] realms = [get_realm(domain) for domain in options['realms']]

View File

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

View File

@@ -1,8 +1,10 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from argparse import ArgumentParser
import datetime import datetime
import pytz import pytz
from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zerver.models import UserProfile, Realm, Stream, Message, get_realm from zerver.models import UserProfile, Realm, Stream, Message, get_realm
@@ -12,15 +14,18 @@ class Command(BaseCommand):
help = "Generate statistics on user activity." help = "Generate statistics on user activity."
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('realms', metavar='<realm>', type=str, nargs='*', parser.add_argument('realms', metavar='<realm>', type=str, nargs='*',
help="realm to generate statistics for") help="realm to generate statistics for")
def messages_sent_by(self, user, week): def messages_sent_by(self, user, week):
# type: (UserProfile, int) -> int
start = datetime.datetime.now(tz=pytz.utc) - datetime.timedelta(days=(week + 1)*7) 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) 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() return Message.objects.filter(sender=user, pub_date__gt=start, pub_date__lte=end).count()
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if options['realms']: if options['realms']:
try: try:
realms = [get_realm(domain) for domain in options['realms']] 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 $' __revision__ = '$Id: cleanupconfirmation.py 5 2008-11-18 09:10:12Z jarek.zgoda $'
from typing import Any
from django.core.management.base import NoArgsCommand from django.core.management.base import NoArgsCommand
@@ -14,4 +15,5 @@ class Command(NoArgsCommand):
help = 'Delete expired confirmations from database' help = 'Delete expired confirmations from database'
def handle_noargs(self, **options): def handle_noargs(self, **options):
# type: (**Any) -> None
Confirmation.objects.delete_expired_confirmations() Confirmation.objects.delete_expired_confirmations()

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,10 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function 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.lib.actions import do_change_user_email
from zerver.models import UserProfile, get_user_profile_by_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.""" help = """Change the email address for a user."""
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('old_email', metavar='<old email>', type=str, parser.add_argument('old_email', metavar='<old email>', type=str,
help='email address to change') help='email address to change')
parser.add_argument('new_email', metavar='<new email>', type=str, parser.add_argument('new_email', metavar='<new email>', type=str,
help='new email address') help='new email address')
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **str) -> None
old_email = options['old_email'] old_email = options['old_email']
new_email = options['new_email'] new_email = options['new_email']
try: try:

View File

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

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any, Callable, Optional
from zerver.models import get_user_profile_by_id from zerver.models import get_user_profile_by_id
from zerver.lib.rate_limiter import client, max_api_calls, max_api_window 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): 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]) user_id = int(key.split(':')[1])
try: try:
user = get_user_profile_by_id(user_id) 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) trim_func(key, max_calls)
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if not settings.RATE_LIMITING: if not settings.RATE_LIMITING:
print("This machine is not using redis or rate limiting, aborting") print("This machine is not using redis or rate limiting, aborting")
exit(1) exit(1)

View File

@@ -2,6 +2,7 @@ from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from optparse import make_option from optparse import make_option
from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.conf import settings from django.conf import settings
@@ -11,7 +12,8 @@ class Command(BaseCommand):
help = """Checks your Zulip Voyager Django configuration for issues.""" help = """Checks your Zulip Voyager Django configuration for issues."""
option_list = BaseCommand.option_list + () 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: for (setting_name, default) in settings.REQUIRED_SETTINGS:
try: try:
if settings.__getattr__(setting_name) != default: if settings.__getattr__(setting_name) != default:

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
import sys import sys
import argparse import argparse
@@ -25,6 +27,7 @@ Omit both <email> and <full name> for interactive user creation.
""" """
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (argparse.ArgumentParser) -> None
parser.add_argument('--this-user-has-accepted-the-tos', parser.add_argument('--this-user-has-accepted-the-tos',
dest='tos', dest='tos',
action="store_true", action="store_true",
@@ -40,6 +43,7 @@ Omit both <email> and <full name> for interactive user creation.
help='full name of new user') help='full name of new user')
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if not options["tos"]: if not options["tos"]:
raise CommandError("""You must confirm that this user has accepted the raise CommandError("""You must confirm that this user has accepted the
Terms of Service by passing --this-user-has-accepted-the-tos.""") 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 absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from argparse import ArgumentParser
import sys import sys
from zerver.lib.actions import do_deactivate_realm from zerver.lib.actions import do_deactivate_realm
@@ -12,10 +15,12 @@ class Command(BaseCommand):
help = """Script to deactivate a realm.""" help = """Script to deactivate a realm."""
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('domain', metavar='<domain>', type=str, parser.add_argument('domain', metavar='<domain>', type=str,
help='domain of realm to deactivate') help='domain of realm to deactivate')
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"]) realm = get_realm(options["domain"])
if realm is None: if realm is None:
print("Could not find realm %s" % (options["domain"],)) print("Could not find realm %s" % (options["domain"],))

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
from optparse import make_option from optparse import make_option
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zerver.models import get_realm, Message, Realm, Stream, Recipient from zerver.models import get_realm, Message, Realm, Stream, Recipient
@@ -24,6 +26,7 @@ class Command(BaseCommand):
) )
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
realm = get_realm(options["domain"]) realm = get_realm(options["domain"])
streams = Stream.objects.filter(realm=realm, invite_only=False) streams = Stream.objects.filter(realm=realm, invite_only=False)
recipients = Recipient.objects.filter( 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 absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
import email import email
import os import os
from email.header import decode_header from email.header import decode_header
@@ -123,9 +126,11 @@ def login_failed(failure):
return failure return failure
def done(_): def done(_):
# type: (Any) -> None
reactor.callLater(0, reactor.stop) reactor.callLater(0, reactor.stop)
def main(): def main():
# type: () -> None
imap_client = protocol.ClientCreator(reactor, imap4.IMAP4Client) imap_client = protocol.ClientCreator(reactor, imap4.IMAP4Client)
d = imap_client.connectSSL(settings.EMAIL_GATEWAY_IMAP_SERVER, settings.EMAIL_GATEWAY_IMAP_PORT, ssl.ClientContextFactory()) d = imap_client.connectSSL(settings.EMAIL_GATEWAY_IMAP_SERVER, settings.EMAIL_GATEWAY_IMAP_PORT, ssl.ClientContextFactory())
d.addCallbacks(connected, login_failed) d.addCallbacks(connected, login_failed)
@@ -135,10 +140,12 @@ class Command(BaseCommand):
help = __doc__ help = __doc__
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('recipient', metavar='<recipient>', type=str, nargs='?', default=None, parser.add_argument('recipient', metavar='<recipient>', type=str, nargs='?', default=None,
help="original recipient") help="original recipient")
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **str) -> None
rcpt_to = os.environ.get("ORIGINAL_RECIPIENT", options['recipient']) rcpt_to = os.environ.get("ORIGINAL_RECIPIENT", options['recipient'])
if rcpt_to is not None: if rcpt_to is not None:
if is_missed_message_address(rcpt_to): if is_missed_message_address(rcpt_to):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function 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.models import UserProfile, get_user_profile_by_email
from zerver.lib.rate_limiter import block_user, unblock_user 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""" help = """Manually block or unblock a user from accessing the API"""
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-e', '--email', parser.add_argument('-e', '--email',
dest='email', dest='email',
help="Email account of user.") help="Email account of user.")
@@ -35,6 +39,7 @@ class Command(BaseCommand):
help="operation to perform (block or unblock)") help="operation to perform (block or unblock)")
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if (not options['api_key'] and not options['email']) or \ if (not options['api_key'] and not options['email']) or \
(options['api_key'] and options['email']): (options['api_key'] and options['email']):
print("Please enter either an email or API key to manage") 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 absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
import sys import sys
@@ -12,10 +15,12 @@ class Command(BaseCommand):
help = """Script to reactivate a deactivated realm.""" help = """Script to reactivate a deactivated realm."""
def add_arguments(self, parser): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('domain', metavar='<domain>', type=str, parser.add_argument('domain', metavar='<domain>', type=str,
help='domain of realm to reactivate') help='domain of realm to reactivate')
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"]) realm = get_realm(options["domain"])
if realm is None: if realm is None:
print("Could not find realm %s" % (options["domain"],)) print("Could not find realm %s" % (options["domain"],))

View File

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

View File

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

View File

@@ -1,5 +1,9 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from optparse import make_option from optparse import make_option
from django.core.management.base import BaseCommand 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): def add_arguments(self, parser):
# type: (ArgumentParser) -> None
parser.add_argument('-r', '--realm', parser.add_argument('-r', '--realm',
dest='domain', dest='domain',
type=str, type=str,
@@ -38,6 +43,7 @@ Example: python manage.py realm_filters --realm=zulip.com --op=show
help="format string to substitute") help="format string to substitute")
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **str) -> None
realm = get_realm(options["domain"]) realm = get_realm(options["domain"])
if options["op"] == "show": if options["op"] == "show":
print("%s: %s" % (realm.domain, all_realm_filters().get(realm.domain, []))) 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 absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
from argparse import ArgumentParser
from optparse import make_option from optparse import make_option
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
@@ -33,6 +36,7 @@ class Command(BaseCommand):
) )
def handle(self, **options): def handle(self, **options):
# type: (*Any, **Any) -> None
if options["domain"] is None or options["stream"] is None or \ if options["domain"] is None or options["stream"] is None or \
(options["users"] is None and options["all_users"] is None): (options["users"] is None and options["all_users"] is None):
self.print_help("python manage.py", "remove_users_from_stream") self.print_help("python manage.py", "remove_users_from_stream")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zerver.models import get_realm from zerver.models import get_realm
@@ -36,6 +38,7 @@ python manage.py set_default_streams --domain=foo.com --streams=
) )
def handle(self, **options): def handle(self, **options):
# type: (*Any, **str) -> None
if options["domain"] is None or options["streams"] is None: if options["domain"] is None or options["streams"] is None:
print("Please provide both a domain name and a default \ print("Please provide both a domain name and a default \
set of streams (which can be empty, with `--streams=`).", file=sys.stderr) 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 absolute_import
from __future__ import print_function from __future__ import print_function
from typing import Any, Iterable
from optparse import make_option from optparse import make_option
import logging import logging
import sys import sys
@@ -41,6 +43,7 @@ class Command(BaseCommand):
) )
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if not options["flag"] or not options["op"] or not options["email"]: if not options["flag"] or not options["op"] or not options["email"]:
print("Please specify an operation, a flag and an email") print("Please specify an operation, a flag and an email")
exit(1) exit(1)
@@ -65,6 +68,7 @@ class Command(BaseCommand):
sys.stderr.close() sys.stderr.close()
def do_update(batch): def do_update(batch):
# type: (Iterable[int]) -> None
msgs = UserMessage.objects.filter(id__in=batch) msgs = UserMessage.objects.filter(id__in=batch)
if op == 'add': if op == 'add':
msgs.update(flags=models.F('flags').bitor(flag)) msgs.update(flags=models.F('flags').bitor(flag))

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,9 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from optparse import make_option from optparse import make_option
import sys import sys
from typing import Any
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
@@ -31,6 +33,7 @@ class Command(BaseCommand):
) )
def handle(self, *args, **options): def handle(self, *args, **options):
# type: (*Any, **Any) -> None
if None in (options["api"], options["web"], options["domain"]): 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) 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") self.print_help("python manage.py", "create_realm")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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