2020-08-29 09:11:16 +02:00
|
|
|
import random
|
|
|
|
from collections import defaultdict
|
|
|
|
from typing import Any, Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
|
2013-04-23 18:51:17 +02:00
|
|
|
|
2020-06-11 00:54:34 +02:00
|
|
|
from django.db.models import Model
|
|
|
|
|
|
|
|
from zerver.lib.create_user import create_user_profile, get_display_email_address
|
2013-07-29 23:03:31 +02:00
|
|
|
from zerver.lib.initial_password import initial_password
|
2020-03-24 14:47:41 +01:00
|
|
|
from zerver.lib.streams import render_stream_description
|
2020-08-29 09:11:16 +02:00
|
|
|
from zerver.models import (
|
|
|
|
Message,
|
|
|
|
Reaction,
|
|
|
|
Realm,
|
|
|
|
RealmAuditLog,
|
|
|
|
Recipient,
|
|
|
|
Stream,
|
|
|
|
Subscription,
|
|
|
|
UserMessage,
|
|
|
|
UserProfile,
|
|
|
|
)
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2017-11-05 11:15:10 +01:00
|
|
|
def bulk_create_users(realm: Realm,
|
2020-07-16 14:10:43 +02:00
|
|
|
users_raw: Set[Tuple[str, str, bool]],
|
2017-11-05 11:15:10 +01:00
|
|
|
bot_type: Optional[int]=None,
|
|
|
|
bot_owner: Optional[UserProfile]=None,
|
2018-05-11 01:40:23 +02:00
|
|
|
tos_version: Optional[str]=None,
|
|
|
|
timezone: str="") -> None:
|
2013-01-10 21:50:09 +01:00
|
|
|
"""
|
2013-04-01 16:57:50 +02:00
|
|
|
Creates and saves a UserProfile with the given email.
|
2013-01-10 21:50:09 +01:00
|
|
|
Has some code based off of UserManage.create_user, but doesn't .save()
|
|
|
|
"""
|
2017-11-22 05:18:48 +01:00
|
|
|
existing_users = frozenset(UserProfile.objects.filter(
|
|
|
|
realm=realm).values_list('email', flat=True))
|
2020-09-02 06:20:26 +02:00
|
|
|
users = sorted(user_raw for user_raw in users_raw if user_raw[0] not in existing_users)
|
2013-01-10 21:50:09 +01:00
|
|
|
|
|
|
|
# Now create user_profiles
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
profiles_to_create: List[UserProfile] = []
|
2020-07-16 14:10:43 +02:00
|
|
|
for (email, full_name, active) in users:
|
2016-12-15 17:25:59 +01:00
|
|
|
profile = create_user_profile(realm, email,
|
2016-05-18 20:23:03 +02:00
|
|
|
initial_password(email), active, bot_type,
|
2020-07-16 14:10:43 +02:00
|
|
|
full_name, bot_owner, False, tos_version,
|
2017-05-04 15:19:06 +02:00
|
|
|
timezone, tutorial_status=UserProfile.TUTORIAL_FINISHED,
|
2017-03-29 23:21:44 +02:00
|
|
|
enter_sends=True)
|
2013-01-10 21:50:09 +01:00
|
|
|
profiles_to_create.append(profile)
|
2020-03-06 17:58:06 +01:00
|
|
|
|
|
|
|
if realm.email_address_visibility == Realm.EMAIL_ADDRESS_VISIBILITY_EVERYONE:
|
|
|
|
UserProfile.objects.bulk_create(profiles_to_create)
|
|
|
|
else:
|
|
|
|
for user_profile in profiles_to_create:
|
|
|
|
user_profile.email = user_profile.delivery_email
|
|
|
|
|
|
|
|
UserProfile.objects.bulk_create(profiles_to_create)
|
|
|
|
|
|
|
|
for user_profile in profiles_to_create:
|
|
|
|
user_profile.email = get_display_email_address(user_profile, realm)
|
|
|
|
UserProfile.objects.bulk_update(profiles_to_create, ['email'])
|
|
|
|
|
2020-03-06 14:48:49 +01:00
|
|
|
user_ids = {user.id for user in profiles_to_create}
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2017-02-15 04:35:10 +01:00
|
|
|
RealmAuditLog.objects.bulk_create(
|
2020-09-02 06:20:26 +02:00
|
|
|
RealmAuditLog(realm=realm, modified_user=profile_,
|
|
|
|
event_type=RealmAuditLog.USER_CREATED, event_time=profile_.date_joined)
|
|
|
|
for profile_ in profiles_to_create)
|
2017-02-15 04:35:10 +01:00
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
recipients_to_create: List[Recipient] = []
|
2020-03-06 14:48:49 +01:00
|
|
|
for user_id in user_ids:
|
|
|
|
recipient = Recipient(type_id=user_id, type=Recipient.PERSONAL)
|
|
|
|
recipients_to_create.append(recipient)
|
|
|
|
|
2013-03-27 15:58:23 +01:00
|
|
|
Recipient.objects.bulk_create(recipients_to_create)
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2019-11-28 16:56:04 +01:00
|
|
|
bulk_set_users_or_streams_recipient_fields(UserProfile, profiles_to_create, recipients_to_create)
|
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
recipients_by_user_id: Dict[int, Recipient] = {}
|
2017-11-22 06:05:25 +01:00
|
|
|
for recipient in recipients_to_create:
|
2020-03-06 14:48:49 +01:00
|
|
|
recipients_by_user_id[recipient.type_id] = recipient
|
2013-01-10 21:50:09 +01:00
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
subscriptions_to_create: List[Subscription] = []
|
2020-03-06 14:48:49 +01:00
|
|
|
for user_id in user_ids:
|
|
|
|
recipient = recipients_by_user_id[user_id]
|
|
|
|
subscription = Subscription(user_profile_id=user_id, recipient=recipient)
|
|
|
|
subscriptions_to_create.append(subscription)
|
|
|
|
|
2013-03-27 15:58:23 +01:00
|
|
|
Subscription.objects.bulk_create(subscriptions_to_create)
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2019-11-28 16:56:04 +01:00
|
|
|
def bulk_set_users_or_streams_recipient_fields(model: Model,
|
|
|
|
objects: Union[Iterable[UserProfile], Iterable[Stream]],
|
|
|
|
recipients: Optional[Iterable[Recipient]]=None) -> None:
|
|
|
|
assert model in [UserProfile, Stream]
|
|
|
|
for obj in objects:
|
|
|
|
assert isinstance(obj, model)
|
|
|
|
|
|
|
|
if model == UserProfile:
|
|
|
|
recipient_type = Recipient.PERSONAL
|
|
|
|
elif model == Stream:
|
|
|
|
recipient_type = Recipient.STREAM
|
|
|
|
|
|
|
|
if recipients is None:
|
|
|
|
object_ids = [obj.id for obj in objects]
|
|
|
|
recipients = Recipient.objects.filter(type=recipient_type, type_id__in=object_ids)
|
|
|
|
|
2020-04-09 21:51:58 +02:00
|
|
|
objects_dict = {obj.id: obj for obj in objects}
|
2019-11-28 16:56:04 +01:00
|
|
|
|
2020-08-03 19:55:57 +02:00
|
|
|
objects_to_update = set()
|
2019-11-28 16:56:04 +01:00
|
|
|
for recipient in recipients:
|
|
|
|
assert recipient.type == recipient_type
|
|
|
|
result = objects_dict.get(recipient.type_id)
|
|
|
|
if result is not None:
|
|
|
|
result.recipient = recipient
|
2020-08-03 19:55:57 +02:00
|
|
|
objects_to_update.add(result)
|
|
|
|
model.objects.bulk_update(objects_to_update, ['recipient'])
|
2019-11-28 16:56:04 +01:00
|
|
|
|
2020-03-28 01:25:56 +01:00
|
|
|
# This is only sed in populate_db, so doesn't really need tests
|
2017-11-05 11:15:10 +01:00
|
|
|
def bulk_create_streams(realm: Realm,
|
2018-05-11 01:40:23 +02:00
|
|
|
stream_dict: Dict[str, Dict[str, Any]]) -> None: # nocoverage
|
2020-09-02 06:20:26 +02:00
|
|
|
existing_streams = {
|
|
|
|
name.lower()
|
|
|
|
for name in Stream.objects.filter(realm=realm).values_list('name', flat=True)
|
|
|
|
}
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
streams_to_create: List[Stream] = []
|
2016-12-08 00:02:21 +01:00
|
|
|
for name, options in stream_dict.items():
|
2018-05-02 17:46:18 +02:00
|
|
|
if 'history_public_to_subscribers' not in options:
|
|
|
|
options['history_public_to_subscribers'] = (
|
2018-05-16 21:34:43 +02:00
|
|
|
not options.get("invite_only", False) and not realm.is_zephyr_mirror_realm)
|
2016-12-15 16:57:21 +01:00
|
|
|
if name.lower() not in existing_streams:
|
2016-12-08 00:02:21 +01:00
|
|
|
streams_to_create.append(
|
|
|
|
Stream(
|
2018-05-02 17:46:18 +02:00
|
|
|
realm=realm,
|
|
|
|
name=name,
|
|
|
|
description=options["description"],
|
2019-03-01 09:10:40 +01:00
|
|
|
rendered_description=render_stream_description(options["description"]),
|
2018-05-16 21:34:43 +02:00
|
|
|
invite_only=options.get("invite_only", False),
|
2020-02-04 21:50:55 +01:00
|
|
|
stream_post_policy=options.get("stream_post_policy",
|
|
|
|
Stream.STREAM_POST_POLICY_EVERYONE),
|
2018-05-02 17:46:18 +02:00
|
|
|
history_public_to_subscribers=options["history_public_to_subscribers"],
|
2018-05-16 21:54:38 +02:00
|
|
|
is_web_public=options.get("is_web_public", False),
|
2017-10-08 21:16:51 +02:00
|
|
|
is_in_zephyr_realm=realm.is_zephyr_mirror_realm,
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
),
|
2016-12-08 00:02:21 +01:00
|
|
|
)
|
2017-07-16 22:46:34 +02:00
|
|
|
# Sort streams by name before creating them so that we can have a
|
|
|
|
# reliable ordering of `stream_id` across different python versions.
|
|
|
|
# This is required for test fixtures which contain `stream_id`. Prior
|
|
|
|
# to python 3.3 hashes were not randomized but after a security fix
|
|
|
|
# hash randomization was enabled in python 3.3 which made iteration
|
|
|
|
# of dictionaries and sets completely unpredictable. Here the order
|
|
|
|
# of elements while iterating `stream_dict` will be completely random
|
|
|
|
# for python 3.3 and later versions.
|
|
|
|
streams_to_create.sort(key=lambda x: x.name)
|
2013-03-27 15:58:23 +01:00
|
|
|
Stream.objects.bulk_create(streams_to_create)
|
2013-01-10 21:50:09 +01:00
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
recipients_to_create: List[Recipient] = []
|
2016-12-15 02:38:14 +01:00
|
|
|
for stream in Stream.objects.filter(realm=realm).values('id', 'name'):
|
|
|
|
if stream['name'].lower() not in existing_streams:
|
|
|
|
recipients_to_create.append(Recipient(type_id=stream['id'],
|
2013-01-10 21:50:09 +01:00
|
|
|
type=Recipient.STREAM))
|
2013-03-27 15:58:23 +01:00
|
|
|
Recipient.objects.bulk_create(recipients_to_create)
|
2019-11-28 16:56:04 +01:00
|
|
|
|
|
|
|
bulk_set_users_or_streams_recipient_fields(Stream, streams_to_create, recipients_to_create)
|
2020-08-29 09:11:16 +02:00
|
|
|
|
|
|
|
DEFAULT_EMOJIS = [
|
|
|
|
('+1', '1f44d'),
|
|
|
|
('smiley', '1f603'),
|
|
|
|
('eyes', '1f440'),
|
|
|
|
('crying_cat_face', '1f63f'),
|
|
|
|
('arrow_up', '2b06'),
|
|
|
|
('confetti_ball', '1f38a'),
|
|
|
|
('hundred_points', '1f4af'),
|
|
|
|
]
|
|
|
|
|
|
|
|
def bulk_create_reactions(
|
|
|
|
messages: Iterable[Message],
|
|
|
|
users: Optional[List[UserProfile]] = None,
|
|
|
|
emojis: Optional[List[Tuple[str, str]]] = None
|
|
|
|
) -> None:
|
|
|
|
messages = list(messages)
|
|
|
|
if not emojis:
|
|
|
|
emojis = DEFAULT_EMOJIS
|
|
|
|
emojis = list(emojis)
|
|
|
|
|
|
|
|
reactions: List[Reaction] = []
|
|
|
|
for message in messages:
|
|
|
|
reactions.extend(_add_random_reactions_to_message(
|
|
|
|
message, emojis, users))
|
|
|
|
Reaction.objects.bulk_create(reactions)
|
|
|
|
|
|
|
|
def _add_random_reactions_to_message(
|
|
|
|
message: Message,
|
|
|
|
emojis: List[Tuple[str, str]],
|
|
|
|
users: Optional[List[UserProfile]] = None,
|
|
|
|
prob_reaction: float = 0.075,
|
|
|
|
prob_upvote: float = 0.5,
|
|
|
|
prob_repeat: float = 0.5) -> List[Reaction]:
|
|
|
|
'''Randomly add emoji reactions to each message from a list.
|
|
|
|
|
|
|
|
Algorithm:
|
|
|
|
|
|
|
|
Give the message at least one reaction with probability `prob_reaction`.
|
|
|
|
Once the first reaction is added, have another user upvote it with probability
|
|
|
|
`prob_upvote`, provided there is another recipient of the message left to upvote.
|
|
|
|
Repeat the process for a different emoji with probability `prob_repeat`.
|
|
|
|
|
|
|
|
If the number of emojis or users is small, there is a chance the above process
|
|
|
|
will produce multiple reactions with the same user and emoji, so group the
|
|
|
|
reactions by emoji code and user profile and then return one reaction from
|
|
|
|
each group.
|
|
|
|
'''
|
|
|
|
for p in (prob_reaction, prob_repeat, prob_upvote):
|
|
|
|
# Prevent p=1 since for prob_repeat and prob_upvote, this will
|
|
|
|
# lead to an infinite loop.
|
|
|
|
if p >= 1 or p < 0:
|
|
|
|
raise ValueError('Probability argument must be between 0 and 1.')
|
|
|
|
|
|
|
|
# Avoid performing database queries if there will be no reactions.
|
|
|
|
compute_next_reaction: bool = random.random() < prob_reaction
|
|
|
|
if not compute_next_reaction:
|
|
|
|
return []
|
|
|
|
|
|
|
|
if users is None:
|
|
|
|
users = []
|
|
|
|
user_ids: Sequence[int] = [user.id for user in users]
|
|
|
|
if not user_ids:
|
|
|
|
user_ids = UserMessage.objects.filter(message=message) \
|
|
|
|
.values_list("user_profile_id", flat=True)
|
|
|
|
if not user_ids:
|
|
|
|
return []
|
|
|
|
|
|
|
|
emojis = list(emojis)
|
|
|
|
|
|
|
|
reactions = []
|
|
|
|
while compute_next_reaction:
|
|
|
|
# We do this O(users) operation only if we've decided to do a
|
|
|
|
# reaction, to avoid performance issues with large numbers of
|
|
|
|
# users.
|
|
|
|
users_available = set(user_ids)
|
|
|
|
|
|
|
|
(emoji_name, emoji_code) = random.choice(emojis)
|
|
|
|
while True:
|
|
|
|
# Handle corner case where all the users have reacted.
|
|
|
|
if not users_available:
|
|
|
|
break
|
|
|
|
|
|
|
|
user_id = random.choice(list(users_available))
|
|
|
|
reactions.append(Reaction(
|
|
|
|
user_profile_id=user_id,
|
|
|
|
message=message,
|
|
|
|
emoji_name=emoji_name,
|
|
|
|
emoji_code=emoji_code,
|
|
|
|
reaction_type=Reaction.UNICODE_EMOJI
|
|
|
|
))
|
|
|
|
users_available.remove(user_id)
|
|
|
|
|
|
|
|
# Add an upvote with the defined probability.
|
|
|
|
if not random.random() < prob_upvote:
|
|
|
|
break
|
|
|
|
|
|
|
|
# Repeat with a possibly different random emoji with the
|
|
|
|
# defined probability.
|
|
|
|
compute_next_reaction = random.random() < prob_repeat
|
|
|
|
|
|
|
|
# Avoid returning duplicate reactions by deduplicating on
|
|
|
|
# (user_profile_id, emoji_code).
|
|
|
|
grouped_reactions = defaultdict(list)
|
|
|
|
for reaction in reactions:
|
|
|
|
k = (str(reaction.user_profile_id), str(reaction.emoji_code))
|
|
|
|
grouped_reactions[k].append(reaction)
|
|
|
|
return [reactions[0] for reactions in grouped_reactions.values()]
|