From 4c3c97617461d01f57a965bc6ea39f5d7fd9fa02 Mon Sep 17 00:00:00 2001 From: Zixuan James Li Date: Mon, 15 Aug 2022 13:10:58 -0400 Subject: [PATCH] models: Implicitly type model fields with django-stubs. Previously, we type the model fields with explicit type annotations manually with the approximate types. This was because the lack of types for Django. django-stubs provides more specific types for all these fields that incompatible with our previous approximate annotations. So now we can remove the inline type annotations and rely on the types defined in the stubs. This allows mypy to infer the types of the model fields for us. Signed-off-by: Zixuan James Li --- analytics/models.py | 15 +- confirmation/models.py | 14 +- corporate/models.py | 82 ++-- zerver/models.py | 868 ++++++++++++++++++++--------------------- zilencer/models.py | 40 +- 5 files changed, 486 insertions(+), 533 deletions(-) diff --git a/analytics/models.py b/analytics/models.py index db8864ec63..76f7285178 100644 --- a/analytics/models.py +++ b/analytics/models.py @@ -1,5 +1,4 @@ import datetime -from typing import Optional from django.db import models from django.db.models import Q, UniqueConstraint @@ -9,13 +8,13 @@ from zerver.models import Realm, Stream, UserProfile class FillState(models.Model): - property: str = models.CharField(max_length=40, unique=True) - end_time: datetime.datetime = models.DateTimeField() + property = models.CharField(max_length=40, unique=True) + end_time = models.DateTimeField() # Valid states are {DONE, STARTED} DONE = 1 STARTED = 2 - state: int = models.PositiveSmallIntegerField() + state = models.PositiveSmallIntegerField() def __str__(self) -> str: return f"" @@ -34,10 +33,10 @@ class BaseCount(models.Model): # Note: When inheriting from BaseCount, you may want to rearrange # the order of the columns in the migration to make sure they # match how you'd like the table to be arranged. - property: str = models.CharField(max_length=32) - subgroup: Optional[str] = models.CharField(max_length=16, null=True) - end_time: datetime.datetime = models.DateTimeField() - value: int = models.BigIntegerField() + property = models.CharField(max_length=32) + subgroup = models.CharField(max_length=16, null=True) + end_time = models.DateTimeField() + value = models.BigIntegerField() class Meta: abstract = True diff --git a/confirmation/models.py b/confirmation/models.py index 9375a58ee5..b7fb44a047 100644 --- a/confirmation/models.py +++ b/confirmation/models.py @@ -161,12 +161,12 @@ def confirmation_url( class Confirmation(models.Model): content_type = models.ForeignKey(ContentType, on_delete=CASCADE) - object_id: int = models.PositiveIntegerField(db_index=True) + object_id = models.PositiveIntegerField(db_index=True) content_object = GenericForeignKey("content_type", "object_id") - date_sent: datetime.datetime = models.DateTimeField(db_index=True) - confirmation_key: str = models.CharField(max_length=40, db_index=True) - expiry_date: Optional[datetime.datetime] = models.DateTimeField(db_index=True, null=True) - realm: Optional[Realm] = models.ForeignKey(Realm, null=True, on_delete=CASCADE) + date_sent = models.DateTimeField(db_index=True) + confirmation_key = models.CharField(max_length=40, db_index=True) + expiry_date = models.DateTimeField(db_index=True, null=True) + realm = models.ForeignKey(Realm, null=True, on_delete=CASCADE) # The following list is the set of valid types USER_REGISTRATION = 1 @@ -177,7 +177,7 @@ class Confirmation(models.Model): MULTIUSE_INVITE = 6 REALM_CREATION = 7 REALM_REACTIVATION = 8 - type: int = models.PositiveSmallIntegerField() + type = models.PositiveSmallIntegerField() def __str__(self) -> str: return f"" @@ -264,7 +264,7 @@ class RealmCreationKey(models.Model): # True just if we should presume the email address the user enters # is theirs, and skip sending mail to it to confirm that. - presume_email_valid: bool = models.BooleanField(default=False) + presume_email_valid = models.BooleanField(default=False) class Invalid(Exception): pass diff --git a/corporate/models.py b/corporate/models.py index 90958ef1bd..a6b044cac2 100644 --- a/corporate/models.py +++ b/corporate/models.py @@ -1,5 +1,3 @@ -import datetime -from decimal import Decimal from typing import Any, Dict, Optional, Union from django.contrib.contenttypes.fields import GenericForeignKey @@ -18,21 +16,17 @@ class Customer(models.Model): and the active plan, if any. """ - realm: Optional[Realm] = models.OneToOneField(Realm, on_delete=CASCADE, null=True) - remote_server: Optional[RemoteZulipServer] = models.OneToOneField( - RemoteZulipServer, on_delete=CASCADE, null=True - ) - stripe_customer_id: Optional[str] = models.CharField(max_length=255, null=True, unique=True) - sponsorship_pending: bool = models.BooleanField(default=False) + realm = models.OneToOneField(Realm, on_delete=CASCADE, null=True) + remote_server = models.OneToOneField(RemoteZulipServer, on_delete=CASCADE, null=True) + stripe_customer_id = models.CharField(max_length=255, null=True, unique=True) + sponsorship_pending = models.BooleanField(default=False) # A percentage, like 85. - default_discount: Optional[Decimal] = models.DecimalField( - decimal_places=4, max_digits=7, null=True - ) + default_discount = models.DecimalField(decimal_places=4, max_digits=7, null=True) # Some non-profit organizations on manual license management pay # only for their paid employees. We don't prevent these # organizations from adding more users than the number of licenses # they purchased. - exempt_from_from_license_number_check: bool = models.BooleanField(default=False) + exempt_from_from_license_number_check = models.BooleanField(default=False) @property def is_self_hosted(self) -> bool: @@ -96,8 +90,8 @@ def get_last_associated_event_by_type( class Session(models.Model): - customer: Customer = models.ForeignKey(Customer, on_delete=CASCADE) - stripe_session_id: str = models.CharField(max_length=255, unique=True) + customer = models.ForeignKey(Customer, on_delete=CASCADE) + stripe_session_id = models.CharField(max_length=255, unique=True) payment_intent = models.ForeignKey("PaymentIntent", null=True, on_delete=CASCADE) UPGRADE_FROM_BILLING_PAGE = 1 @@ -105,11 +99,11 @@ class Session(models.Model): FREE_TRIAL_UPGRADE_FROM_BILLING_PAGE = 20 FREE_TRIAL_UPGRADE_FROM_ONBOARDING_PAGE = 30 CARD_UPDATE_FROM_BILLING_PAGE = 40 - type: int = models.SmallIntegerField() + type = models.SmallIntegerField() CREATED = 1 COMPLETED = 10 - status: int = models.SmallIntegerField(default=CREATED) + status = models.SmallIntegerField(default=CREATED) def get_status_as_string(self) -> str: return {Session.CREATED: "created", Session.COMPLETED: "completed"}[self.status] @@ -142,8 +136,8 @@ class Session(models.Model): class PaymentIntent(models.Model): - customer: Customer = models.ForeignKey(Customer, on_delete=CASCADE) - stripe_payment_intent_id: str = models.CharField(max_length=255, unique=True) + customer = models.ForeignKey(Customer, on_delete=CASCADE) + stripe_payment_intent_id = models.CharField(max_length=255, unique=True) REQUIRES_PAYMENT_METHOD = 1 REQUIRES_CONFIRMATION = 20 @@ -153,7 +147,7 @@ class PaymentIntent(models.Model): CANCELLED = 60 SUCCEEDED = 70 - status: int = models.SmallIntegerField() + status = models.SmallIntegerField() last_payment_error = models.JSONField(default=None, null=True) @classmethod @@ -200,47 +194,47 @@ class CustomerPlan(models.Model): # A customer can only have one ACTIVE plan, but old, inactive plans # are preserved to allow auditing - so there can be multiple # CustomerPlan objects pointing to one Customer. - customer: Customer = models.ForeignKey(Customer, on_delete=CASCADE) + customer = models.ForeignKey(Customer, on_delete=CASCADE) - automanage_licenses: bool = models.BooleanField(default=False) - charge_automatically: bool = models.BooleanField(default=False) + automanage_licenses = models.BooleanField(default=False) + charge_automatically = models.BooleanField(default=False) # Both of these are in cents. Exactly one of price_per_license or # fixed_price should be set. fixed_price is only for manual deals, and # can't be set via the self-serve billing system. - price_per_license: Optional[int] = models.IntegerField(null=True) - fixed_price: Optional[int] = models.IntegerField(null=True) + price_per_license = models.IntegerField(null=True) + fixed_price = models.IntegerField(null=True) # Discount that was applied. For display purposes only. - discount: Optional[Decimal] = models.DecimalField(decimal_places=4, max_digits=6, null=True) + discount = models.DecimalField(decimal_places=4, max_digits=6, null=True) # Initialized with the time of plan creation. Used for calculating # start of next billing cycle, next invoice date etc. This value # should never be modified. The only exception is when we change # the status of the plan from free trial to active and reset the # billing_cycle_anchor. - billing_cycle_anchor: datetime.datetime = models.DateTimeField() + billing_cycle_anchor = models.DateTimeField() ANNUAL = 1 MONTHLY = 2 - billing_schedule: int = models.SmallIntegerField() + billing_schedule = models.SmallIntegerField() # The next date the billing system should go through ledger # entries and create invoices for additional users or plan # renewal. Since we use a daily cron job for invoicing, the # invoice will be generated the first time the cron job runs after # next_invoice_date. - next_invoice_date: Optional[datetime.datetime] = models.DateTimeField(db_index=True, null=True) + next_invoice_date = models.DateTimeField(db_index=True, null=True) # On next_invoice_date, we go through ledger entries that were # created after invoiced_through and process them by generating # invoices for any additional users and/or plan renewal. Once the # invoice is generated, we update the value of invoiced_through # and set it to the last ledger entry we processed. - invoiced_through: Optional["LicenseLedger"] = models.ForeignKey( + invoiced_through = models.ForeignKey( "LicenseLedger", null=True, on_delete=CASCADE, related_name="+" ) - end_date: Optional[datetime.datetime] = models.DateTimeField(null=True) + end_date = models.DateTimeField(null=True) DONE = 1 STARTED = 2 @@ -248,12 +242,12 @@ class CustomerPlan(models.Model): # This status field helps ensure any errors encountered during the # invoicing process do not leave our invoicing system in a broken # state. - invoicing_status: int = models.SmallIntegerField(default=DONE) + invoicing_status = models.SmallIntegerField(default=DONE) STANDARD = 1 PLUS = 2 # not available through self-serve signup ENTERPRISE = 10 - tier: int = models.SmallIntegerField() + tier = models.SmallIntegerField() ACTIVE = 1 DOWNGRADE_AT_END_OF_CYCLE = 2 @@ -265,7 +259,7 @@ class CustomerPlan(models.Model): LIVE_STATUS_THRESHOLD = 10 ENDED = 11 NEVER_STARTED = 12 - status: int = models.SmallIntegerField(default=ACTIVE) + status = models.SmallIntegerField(default=ACTIVE) # TODO maybe override setattr to ensure billing_cycle_anchor, etc # are immutable. @@ -329,38 +323,38 @@ class LicenseLedger(models.Model): in case of issues. """ - plan: CustomerPlan = models.ForeignKey(CustomerPlan, on_delete=CASCADE) + plan = models.ForeignKey(CustomerPlan, on_delete=CASCADE) # Also True for the initial upgrade. - is_renewal: bool = models.BooleanField(default=False) + is_renewal = models.BooleanField(default=False) - event_time: datetime.datetime = models.DateTimeField() + event_time = models.DateTimeField() # The number of licenses ("seats") purchased by the the organization at the time of ledger # entry creation. Normally, to add a user the organization needs at least one spare license. # Once a license is purchased, it is valid till the end of the billing period, irrespective # of whether the license is used or not. So the value of licenses will never decrease for # subsequent LicenseLedger entries in the same billing period. - licenses: int = models.IntegerField() + licenses = models.IntegerField() # The number of licenses the organization needs in the next billing cycle. The value of # licenses_at_next_renewal can increase or decrease for subsequent LicenseLedger entries in # the same billing period. For plans on automatic license management this value is usually # equal to the number of activated users in the organization. - licenses_at_next_renewal: Optional[int] = models.IntegerField(null=True) + licenses_at_next_renewal = models.IntegerField(null=True) class ZulipSponsorshipRequest(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - requested_by: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.ForeignKey(Realm, on_delete=CASCADE) + requested_by = models.ForeignKey(UserProfile, on_delete=CASCADE) - org_type: int = models.PositiveSmallIntegerField( + org_type = models.PositiveSmallIntegerField( default=Realm.ORG_TYPES["unspecified"]["id"], choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()], ) MAX_ORG_URL_LENGTH: int = 200 - org_website: str = models.URLField(max_length=MAX_ORG_URL_LENGTH, blank=True, null=True) + org_website = models.URLField(max_length=MAX_ORG_URL_LENGTH, blank=True, null=True) - org_description: str = models.TextField(default="") + org_description = models.TextField(default="") diff --git a/zerver/models.py b/zerver/models.py index 99e7499917..a2f7d84717 100644 --- a/zerver/models.py +++ b/zerver/models.py @@ -20,7 +20,7 @@ from typing import ( TypeVar, Union, ) -from uuid import UUID, uuid4 +from uuid import uuid4 import django.contrib.auth import orjson @@ -41,7 +41,7 @@ from django.core.exceptions import ValidationError from django.core.validators import MinLengthValidator, RegexValidator, URLValidator, validate_email from django.db import models, transaction from django.db.backends.base.base import BaseDatabaseWrapper -from django.db.models import CASCADE, Exists, F, Manager, OuterRef, Q, Sum +from django.db.models import CASCADE, Exists, F, OuterRef, Q, Sum from django.db.models.functions import Upper from django.db.models.query import QuerySet from django.db.models.signals import post_delete, post_save, pre_delete @@ -271,35 +271,31 @@ class Realm(models.Model): SUBDOMAIN_FOR_ROOT_DOMAIN = "" WILDCARD_MENTION_THRESHOLD = 15 - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") # User-visible display name and description used on e.g. the organization homepage - name: str = models.CharField(max_length=MAX_REALM_NAME_LENGTH) - description: str = models.TextField(default="") + name = models.CharField(max_length=MAX_REALM_NAME_LENGTH) + description = models.TextField(default="") # A short, identifier-like name for the organization. Used in subdomains; # e.g. on a server at example.com, an org with string_id `foo` is reached # at `foo.example.com`. - string_id: str = models.CharField(max_length=MAX_REALM_SUBDOMAIN_LENGTH, unique=True) + string_id = models.CharField(max_length=MAX_REALM_SUBDOMAIN_LENGTH, unique=True) - date_created: datetime.datetime = models.DateTimeField(default=timezone_now) - demo_organization_scheduled_deletion_date: Optional[datetime.datetime] = models.DateTimeField( - default=None, null=True - ) - deactivated: bool = models.BooleanField(default=False) + date_created = models.DateTimeField(default=timezone_now) + demo_organization_scheduled_deletion_date = models.DateTimeField(default=None, null=True) + deactivated = models.BooleanField(default=False) # Redirect URL if the Realm has moved to another server - deactivated_redirect: Optional[str] = models.URLField( - max_length=MAX_REALM_REDIRECT_URL_LENGTH, null=True - ) + deactivated_redirect = models.URLField(max_length=MAX_REALM_REDIRECT_URL_LENGTH, null=True) # See RealmDomain for the domains that apply for a given organization. - emails_restricted_to_domains: bool = models.BooleanField(default=False) + emails_restricted_to_domains = models.BooleanField(default=False) - invite_required: bool = models.BooleanField(default=True) + invite_required = models.BooleanField(default=True) - _max_invites: Optional[int] = models.IntegerField(null=True, db_column="max_invites") - disallow_disposable_email_addresses: bool = models.BooleanField(default=True) + _max_invites = models.IntegerField(null=True, db_column="max_invites") + disallow_disposable_email_addresses = models.BooleanField(default=True) authentication_methods: BitHandler = BitField( flags=AUTHENTICATION_FLAGS, default=2**31 - 1, @@ -307,31 +303,29 @@ class Realm(models.Model): # Allow users to access web-public streams without login. This # setting also controls API access of web-public streams. - enable_spectator_access: bool = models.BooleanField(default=False) + enable_spectator_access = models.BooleanField(default=False) # Whether organization has given permission to be advertised in the # Zulip communities directory. - want_advertise_in_communities_directory: bool = models.BooleanField( - default=False, db_index=True - ) + want_advertise_in_communities_directory = models.BooleanField(default=False, db_index=True) # Whether the organization has enabled inline image and URL previews. - inline_image_preview: bool = models.BooleanField(default=True) - inline_url_embed_preview: bool = models.BooleanField(default=False) + inline_image_preview = models.BooleanField(default=True) + inline_url_embed_preview = models.BooleanField(default=False) # Whether digest emails are enabled for the organization. - digest_emails_enabled: bool = models.BooleanField(default=False) + digest_emails_enabled = models.BooleanField(default=False) # Day of the week on which the digest is sent (default: Tuesday). - digest_weekday: int = models.SmallIntegerField(default=1) + digest_weekday = models.SmallIntegerField(default=1) - send_welcome_emails: bool = models.BooleanField(default=True) - message_content_allowed_in_email_notifications: bool = models.BooleanField(default=True) + send_welcome_emails = models.BooleanField(default=True) + message_content_allowed_in_email_notifications = models.BooleanField(default=True) - mandatory_topics: bool = models.BooleanField(default=False) + mandatory_topics = models.BooleanField(default=False) - name_changes_disabled: bool = models.BooleanField(default=False) - email_changes_disabled: bool = models.BooleanField(default=False) - avatar_changes_disabled: bool = models.BooleanField(default=False) + name_changes_disabled = models.BooleanField(default=False) + email_changes_disabled = models.BooleanField(default=False) + avatar_changes_disabled = models.BooleanField(default=False) POLICY_MEMBERS_ONLY = 1 POLICY_ADMINS_ONLY = 2 @@ -377,39 +371,35 @@ class Realm(models.Model): DEFAULT_COMMUNITY_TOPIC_EDITING_LIMIT_SECONDS = 259200 # Who in the organization is allowed to add custom emojis. - add_custom_emoji_policy: int = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) + add_custom_emoji_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) # Who in the organization is allowed to create streams. - create_public_stream_policy: int = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) - create_private_stream_policy: int = models.PositiveSmallIntegerField( - default=POLICY_MEMBERS_ONLY - ) - create_web_public_stream_policy: int = models.PositiveSmallIntegerField( - default=POLICY_OWNERS_ONLY - ) + create_public_stream_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) + create_private_stream_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) + create_web_public_stream_policy = models.PositiveSmallIntegerField(default=POLICY_OWNERS_ONLY) # Who in the organization is allowed to delete messages they themselves sent. - delete_own_message_policy: bool = models.PositiveSmallIntegerField(default=POLICY_ADMINS_ONLY) + delete_own_message_policy = models.PositiveSmallIntegerField(default=POLICY_ADMINS_ONLY) # Who in the organization is allowed to edit topics of any message. - edit_topic_policy: int = models.PositiveSmallIntegerField(default=POLICY_EVERYONE) + edit_topic_policy = models.PositiveSmallIntegerField(default=POLICY_EVERYONE) # Who in the organization is allowed to invite other users to organization. - invite_to_realm_policy: int = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) + invite_to_realm_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) # Who in the organization is allowed to invite other users to streams. - invite_to_stream_policy: int = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) + invite_to_stream_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) # Who in the organization is allowed to move messages between streams. - move_messages_between_streams_policy: int = models.PositiveSmallIntegerField( + move_messages_between_streams_policy = models.PositiveSmallIntegerField( default=POLICY_ADMINS_ONLY ) - user_group_edit_policy: int = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) + user_group_edit_policy = models.PositiveSmallIntegerField(default=POLICY_MEMBERS_ONLY) PRIVATE_MESSAGE_POLICY_UNLIMITED = 1 PRIVATE_MESSAGE_POLICY_DISABLED = 2 - private_message_policy: int = models.PositiveSmallIntegerField( + private_message_policy = models.PositiveSmallIntegerField( default=PRIVATE_MESSAGE_POLICY_UNLIMITED ) PRIVATE_MESSAGE_POLICY_TYPES = [ @@ -426,7 +416,7 @@ class Realm(models.Model): WILDCARD_MENTION_POLICY_ADMINS = 5 WILDCARD_MENTION_POLICY_NOBODY = 6 WILDCARD_MENTION_POLICY_MODERATORS = 7 - wildcard_mention_policy: int = models.PositiveSmallIntegerField( + wildcard_mention_policy = models.PositiveSmallIntegerField( default=WILDCARD_MENTION_POLICY_ADMINS, ) WILDCARD_MENTION_POLICY_TYPES = [ @@ -446,7 +436,7 @@ class Realm(models.Model): EMAIL_ADDRESS_VISIBILITY_ADMINS = 3 EMAIL_ADDRESS_VISIBILITY_NOBODY = 4 EMAIL_ADDRESS_VISIBILITY_MODERATORS = 5 - email_address_visibility: int = models.PositiveSmallIntegerField( + email_address_visibility = models.PositiveSmallIntegerField( default=EMAIL_ADDRESS_VISIBILITY_EVERYONE, ) EMAIL_ADDRESS_VISIBILITY_TYPES = [ @@ -460,7 +450,7 @@ class Realm(models.Model): # Threshold in days for new users to create streams, and potentially take # some other actions. - waiting_period_threshold: int = models.PositiveIntegerField(default=0) + waiting_period_threshold = models.PositiveIntegerField(default=0) DEFAULT_MESSAGE_CONTENT_DELETE_LIMIT_SECONDS = ( 600 # if changed, also change in admin.js, setting_org.js @@ -468,35 +458,35 @@ class Realm(models.Model): MESSAGE_CONTENT_EDIT_OR_DELETE_LIMIT_SPECIAL_VALUES_MAP = { "unlimited": None, } - message_content_delete_limit_seconds: Optional[int] = models.PositiveIntegerField( + message_content_delete_limit_seconds = models.PositiveIntegerField( default=DEFAULT_MESSAGE_CONTENT_DELETE_LIMIT_SECONDS, null=True ) - allow_message_editing: bool = models.BooleanField(default=True) + allow_message_editing = models.BooleanField(default=True) DEFAULT_MESSAGE_CONTENT_EDIT_LIMIT_SECONDS = ( 600 # if changed, also change in admin.js, setting_org.js ) - message_content_edit_limit_seconds: Optional[int] = models.PositiveIntegerField( + message_content_edit_limit_seconds = models.PositiveIntegerField( default=DEFAULT_MESSAGE_CONTENT_EDIT_LIMIT_SECONDS, null=True ) # Whether users have access to message edit history - allow_edit_history: bool = models.BooleanField(default=True) + allow_edit_history = models.BooleanField(default=True) # Defaults for new users - default_language: str = models.CharField(default="en", max_length=MAX_LANGUAGE_ID_LENGTH) + default_language = models.CharField(default="en", max_length=MAX_LANGUAGE_ID_LENGTH) DEFAULT_NOTIFICATION_STREAM_NAME = "general" INITIAL_PRIVATE_STREAM_NAME = "core team" STREAM_EVENTS_NOTIFICATION_TOPIC = gettext_lazy("stream events") - notifications_stream: Optional["Stream"] = models.ForeignKey( + notifications_stream = models.ForeignKey( "Stream", related_name="+", null=True, blank=True, on_delete=models.SET_NULL, ) - signup_notifications_stream: Optional["Stream"] = models.ForeignKey( + signup_notifications_stream = models.ForeignKey( "Stream", related_name="+", null=True, @@ -508,14 +498,14 @@ class Realm(models.Model): "unlimited": -1, } # For old messages being automatically deleted - message_retention_days: int = models.IntegerField(null=False, default=-1) + message_retention_days = models.IntegerField(null=False, default=-1) # When non-null, all but the latest this many messages in the organization # are inaccessible to users (but not deleted). - message_visibility_limit: Optional[int] = models.IntegerField(null=True) + message_visibility_limit = models.IntegerField(null=True) # Messages older than this message ID in the organization are inaccessible. - first_visible_message_id: int = models.IntegerField(default=0) + first_visible_message_id = models.IntegerField(default=0) # Valid org types ORG_TYPES: Dict[str, Dict[str, Any]] = { @@ -599,7 +589,7 @@ class Realm(models.Model): }, } - org_type: int = models.PositiveSmallIntegerField( + org_type = models.PositiveSmallIntegerField( default=ORG_TYPES["unspecified"]["id"], choices=[(t["id"], t["name"]) for t in ORG_TYPES.values()], ) @@ -613,14 +603,14 @@ class Realm(models.Model): PLAN_TYPE_STANDARD = 3 PLAN_TYPE_STANDARD_FREE = 4 PLAN_TYPE_PLUS = 10 - plan_type: int = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_HOSTED) + plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_HOSTED) # This value is also being used in static/js/settings_bots.bot_creation_policy_values. # On updating it here, update it there as well. BOT_CREATION_EVERYONE = 1 BOT_CREATION_LIMIT_GENERIC_BOTS = 2 BOT_CREATION_ADMINS_ONLY = 3 - bot_creation_policy: int = models.PositiveSmallIntegerField(default=BOT_CREATION_EVERYONE) + bot_creation_policy = models.PositiveSmallIntegerField(default=BOT_CREATION_EVERYONE) BOT_CREATION_POLICY_TYPES = [ BOT_CREATION_EVERYONE, BOT_CREATION_LIMIT_GENERIC_BOTS, @@ -630,7 +620,7 @@ class Realm(models.Model): # See upload_quota_bytes; don't interpret upload_quota_gb directly. UPLOAD_QUOTA_LIMITED = 5 UPLOAD_QUOTA_STANDARD = 50 - upload_quota_gb: Optional[int] = models.IntegerField(null=True) + upload_quota_gb = models.IntegerField(null=True) VIDEO_CHAT_PROVIDERS = { "disabled": { @@ -655,7 +645,7 @@ class Realm(models.Model): if settings.BIG_BLUE_BUTTON_SECRET is not None and settings.BIG_BLUE_BUTTON_URL is not None: VIDEO_CHAT_PROVIDERS["big_blue_button"] = {"name": "BigBlueButton", "id": 4} - video_chat_provider: int = models.PositiveSmallIntegerField( + video_chat_provider = models.PositiveSmallIntegerField( default=VIDEO_CHAT_PROVIDERS["jitsi_meet"]["id"] ) @@ -688,13 +678,13 @@ class Realm(models.Model): } # maximum rating of the GIFs that will be retrieved from GIPHY - giphy_rating: int = models.PositiveSmallIntegerField(default=GIPHY_RATING_OPTIONS["g"]["id"]) + giphy_rating = models.PositiveSmallIntegerField(default=GIPHY_RATING_OPTIONS["g"]["id"]) - default_code_block_language: Optional[str] = models.TextField(null=True, default=None) + default_code_block_language = models.TextField(null=True, default=None) # Whether read receipts are enabled in the organization. If disabled, # they will not be available regardless of users' personal settings. - enable_read_receipts: bool = models.BooleanField(default=False) + enable_read_receipts = models.BooleanField(default=False) # Define the types of the various automatically managed properties property_types: Dict[str, Union[type, Tuple[type, ...]]] = dict( @@ -751,12 +741,12 @@ class Realm(models.Model): (ICON_FROM_GRAVATAR, "Hosted by Gravatar"), (ICON_UPLOADED, "Uploaded by administrator"), ) - icon_source: str = models.CharField( + icon_source = models.CharField( default=ICON_FROM_GRAVATAR, choices=ICON_SOURCES, max_length=1, ) - icon_version: int = models.PositiveSmallIntegerField(default=1) + icon_version = models.PositiveSmallIntegerField(default=1) # Logo is the horizontal logo we show in top-left of web app navbar UI. LOGO_DEFAULT = "D" @@ -765,19 +755,19 @@ class Realm(models.Model): (LOGO_DEFAULT, "Default to Zulip"), (LOGO_UPLOADED, "Uploaded by administrator"), ) - logo_source: str = models.CharField( + logo_source = models.CharField( default=LOGO_DEFAULT, choices=LOGO_SOURCES, max_length=1, ) - logo_version: int = models.PositiveSmallIntegerField(default=1) + logo_version = models.PositiveSmallIntegerField(default=1) - night_logo_source: str = models.CharField( + night_logo_source = models.CharField( default=LOGO_DEFAULT, choices=LOGO_SOURCES, max_length=1, ) - night_logo_version: int = models.PositiveSmallIntegerField(default=1) + night_logo_version = models.PositiveSmallIntegerField(default=1) def authentication_methods_dict(self) -> Dict[str, bool]: """Returns the mapping from authentication flags to their status, @@ -1050,11 +1040,11 @@ class RealmDomain(models.Model): """For an organization with emails_restricted_to_domains enabled, the list of allowed domains""" - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.ForeignKey(Realm, on_delete=CASCADE) # should always be stored lowercase - domain: str = models.CharField(max_length=80, db_index=True) - allow_subdomains: bool = models.BooleanField(default=False) + domain = models.CharField(max_length=80, db_index=True) + allow_subdomains = models.BooleanField(default=False) class Meta: unique_together = ("realm", "domain") @@ -1082,15 +1072,15 @@ def get_realm_domains(realm: Realm) -> List[RealmDomainDict]: class RealmEmoji(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - author: Optional["UserProfile"] = models.ForeignKey( + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + author = models.ForeignKey( "UserProfile", blank=True, null=True, on_delete=CASCADE, ) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - name: str = models.TextField( + realm = models.ForeignKey(Realm, on_delete=CASCADE) + name = models.TextField( validators=[ MinLengthValidator(1), # The second part of the regex (negative lookbehind) disallows names @@ -1103,12 +1093,12 @@ class RealmEmoji(models.Model): ) # The basename of the custom emoji's filename; see PATH_ID_TEMPLATE for the full path. - file_name: Optional[str] = models.TextField(db_index=True, null=True, blank=True) + file_name = models.TextField(db_index=True, null=True, blank=True) # Whether this custom emoji is an animated image. - is_animated: bool = models.BooleanField(default=False) + is_animated = models.BooleanField(default=False) - deactivated: bool = models.BooleanField(default=False) + deactivated = models.BooleanField(default=False) PATH_ID_TEMPLATE = "{realm_id}/emoji/images/{emoji_file_name}" STILL_PATH_ID_TEMPLATE = "{realm_id}/emoji/images/still/{emoji_filename_without_extension}.png" @@ -1267,10 +1257,10 @@ class RealmFilter(models.Model): strings inside the Markdown processor. See "Custom filters" in the settings UI. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - pattern: str = models.TextField() - url_format_string: str = models.TextField(validators=[filter_format_validator]) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.ForeignKey(Realm, on_delete=CASCADE) + pattern = models.TextField() + url_format_string = models.TextField(validators=[filter_format_validator]) class Meta: unique_together = ("realm", "pattern") @@ -1393,15 +1383,15 @@ class RealmPlayground(models.Model): MAX_PYGMENTS_LANGUAGE_LENGTH = 40 - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - url_prefix: str = models.TextField(validators=[URLValidator()]) + realm = models.ForeignKey(Realm, on_delete=CASCADE) + url_prefix = models.TextField(validators=[URLValidator()]) # User-visible display name used when configuring playgrounds in the settings page and # when displaying them in the playground links popover. - name: str = models.TextField(db_index=True) + name = models.TextField(db_index=True) # This stores the pygments lexer subclass names and not the aliases themselves. - pygments_language: str = models.CharField( + pygments_language = models.CharField( db_index=True, max_length=MAX_PYGMENTS_LANGUAGE_LENGTH, # We validate to see if this conforms to the character set allowed for a @@ -1460,9 +1450,9 @@ class Recipient(models.Model): objects are subscribed to which Recipient objects. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - type_id: int = models.IntegerField(db_index=True) - type: int = models.PositiveSmallIntegerField(db_index=True) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + type_id = models.IntegerField(db_index=True) + type = models.PositiveSmallIntegerField(db_index=True) # Valid types are {personal, stream, huddle} # The type for 1:1 private messages. @@ -1503,27 +1493,27 @@ class UserBaseSettings(models.Model): """ # UI settings - enter_sends: bool = models.BooleanField(default=False) + enter_sends = models.BooleanField(default=False) # display settings - left_side_userlist: bool = models.BooleanField(default=False) - default_language: str = models.CharField(default="en", max_length=MAX_LANGUAGE_ID_LENGTH) + left_side_userlist = models.BooleanField(default=False) + default_language = models.CharField(default="en", max_length=MAX_LANGUAGE_ID_LENGTH) # This setting controls which view is rendered first when Zulip loads. # Values for it are URL suffix after `#`. - default_view: str = models.TextField(default="recent_topics") - escape_navigates_to_default_view: bool = models.BooleanField(default=True) - dense_mode: bool = models.BooleanField(default=True) - fluid_layout_width: bool = models.BooleanField(default=False) - high_contrast_mode: bool = models.BooleanField(default=False) - translate_emoticons: bool = models.BooleanField(default=False) - display_emoji_reaction_users: bool = models.BooleanField(default=True) - twenty_four_hour_time: bool = models.BooleanField(default=False) - starred_message_counts: bool = models.BooleanField(default=True) + default_view = models.TextField(default="recent_topics") + escape_navigates_to_default_view = models.BooleanField(default=True) + dense_mode = models.BooleanField(default=True) + fluid_layout_width = models.BooleanField(default=False) + high_contrast_mode = models.BooleanField(default=False) + translate_emoticons = models.BooleanField(default=False) + display_emoji_reaction_users = models.BooleanField(default=True) + twenty_four_hour_time = models.BooleanField(default=False) + starred_message_counts = models.BooleanField(default=True) COLOR_SCHEME_AUTOMATIC = 1 COLOR_SCHEME_NIGHT = 2 COLOR_SCHEME_LIGHT = 3 COLOR_SCHEME_CHOICES = [COLOR_SCHEME_AUTOMATIC, COLOR_SCHEME_NIGHT, COLOR_SCHEME_LIGHT] - color_scheme: int = models.PositiveSmallIntegerField(default=COLOR_SCHEME_AUTOMATIC) + color_scheme = models.PositiveSmallIntegerField(default=COLOR_SCHEME_AUTOMATIC) # UI setting controlling Zulip's behavior of demoting in the sort # order and graying out streams with no recent traffic. The @@ -1537,9 +1527,7 @@ class UserBaseSettings(models.Model): DEMOTE_STREAMS_ALWAYS, DEMOTE_STREAMS_NEVER, ] - demote_inactive_streams: int = models.PositiveSmallIntegerField( - default=DEMOTE_STREAMS_AUTOMATIC - ) + demote_inactive_streams = models.PositiveSmallIntegerField(default=DEMOTE_STREAMS_AUTOMATIC) # Emoji sets GOOGLE_EMOJISET = "google" @@ -1552,9 +1540,7 @@ class UserBaseSettings(models.Model): (TEXT_EMOJISET, "Plain text"), (GOOGLE_BLOB_EMOJISET, "Google blobs"), ) - emojiset: str = models.CharField( - default=GOOGLE_EMOJISET, choices=EMOJISET_CHOICES, max_length=20 - ) + emojiset = models.CharField(default=GOOGLE_EMOJISET, choices=EMOJISET_CHOICES, max_length=20) # User list style USER_LIST_STYLE_COMPACT = 1 @@ -1565,28 +1551,28 @@ class UserBaseSettings(models.Model): USER_LIST_STYLE_WITH_STATUS, USER_LIST_STYLE_WITH_AVATAR, ] - user_list_style: int = models.PositiveSmallIntegerField(default=USER_LIST_STYLE_WITH_STATUS) + user_list_style = models.PositiveSmallIntegerField(default=USER_LIST_STYLE_WITH_STATUS) ### Notifications settings. ### - email_notifications_batching_period_seconds: int = models.IntegerField(default=120) + email_notifications_batching_period_seconds = models.IntegerField(default=120) # Stream notifications. - enable_stream_desktop_notifications: bool = models.BooleanField(default=False) - enable_stream_email_notifications: bool = models.BooleanField(default=False) - enable_stream_push_notifications: bool = models.BooleanField(default=False) - enable_stream_audible_notifications: bool = models.BooleanField(default=False) - notification_sound: str = models.CharField(max_length=20, default="zulip") - wildcard_mentions_notify: bool = models.BooleanField(default=True) + enable_stream_desktop_notifications = models.BooleanField(default=False) + enable_stream_email_notifications = models.BooleanField(default=False) + enable_stream_push_notifications = models.BooleanField(default=False) + enable_stream_audible_notifications = models.BooleanField(default=False) + notification_sound = models.CharField(max_length=20, default="zulip") + wildcard_mentions_notify = models.BooleanField(default=True) # PM + @-mention notifications. - enable_desktop_notifications: bool = models.BooleanField(default=True) - pm_content_in_desktop_notifications: bool = models.BooleanField(default=True) - enable_sounds: bool = models.BooleanField(default=True) - enable_offline_email_notifications: bool = models.BooleanField(default=True) - message_content_in_email_notifications: bool = models.BooleanField(default=True) - enable_offline_push_notifications: bool = models.BooleanField(default=True) - enable_online_push_notifications: bool = models.BooleanField(default=True) + enable_desktop_notifications = models.BooleanField(default=True) + pm_content_in_desktop_notifications = models.BooleanField(default=True) + enable_sounds = models.BooleanField(default=True) + enable_offline_email_notifications = models.BooleanField(default=True) + message_content_in_email_notifications = models.BooleanField(default=True) + enable_offline_push_notifications = models.BooleanField(default=True) + enable_online_push_notifications = models.BooleanField(default=True) DESKTOP_ICON_COUNT_DISPLAY_MESSAGES = 1 DESKTOP_ICON_COUNT_DISPLAY_NOTIFIABLE = 2 @@ -1596,23 +1582,23 @@ class UserBaseSettings(models.Model): DESKTOP_ICON_COUNT_DISPLAY_NOTIFIABLE, DESKTOP_ICON_COUNT_DISPLAY_NONE, ] - desktop_icon_count_display: int = models.PositiveSmallIntegerField( + desktop_icon_count_display = models.PositiveSmallIntegerField( default=DESKTOP_ICON_COUNT_DISPLAY_MESSAGES ) - enable_digest_emails: bool = models.BooleanField(default=True) - enable_login_emails: bool = models.BooleanField(default=True) - enable_marketing_emails: bool = models.BooleanField(default=True) - realm_name_in_notifications: bool = models.BooleanField(default=False) - presence_enabled: bool = models.BooleanField(default=True) + enable_digest_emails = models.BooleanField(default=True) + enable_login_emails = models.BooleanField(default=True) + enable_marketing_emails = models.BooleanField(default=True) + realm_name_in_notifications = models.BooleanField(default=False) + presence_enabled = models.BooleanField(default=True) # Whether or not the user wants to sync their drafts. - enable_drafts_synchronization: bool = models.BooleanField(default=True) + enable_drafts_synchronization = models.BooleanField(default=True) # Privacy settings - send_stream_typing_notifications: bool = models.BooleanField(default=True) - send_private_typing_notifications: bool = models.BooleanField(default=True) - send_read_receipts: bool = models.BooleanField(default=True) + send_stream_typing_notifications = models.BooleanField(default=True) + send_private_typing_notifications = models.BooleanField(default=True) + send_read_receipts = models.BooleanField(default=True) display_settings_legacy = dict( # Don't add anything new to this legacy dict. @@ -1699,8 +1685,8 @@ class RealmUserDefault(UserBaseSettings): like notification settings, used when creating a new user account. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.OneToOneField(Realm, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.OneToOneField(Realm, on_delete=CASCADE) class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): @@ -1738,7 +1724,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): EMBEDDED_BOT, ] - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") # For historical reasons, Zulip has two email fields. The # `delivery_email` field is the user's email address, where all @@ -1756,10 +1742,10 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): # fashion). Since Django's unique_together is case sensitive, this # is enforced via SQL indexes created by # zerver/migrations/0295_case_insensitive_email_indexes.py. - delivery_email: str = models.EmailField(blank=False, db_index=True) - email: str = models.EmailField(blank=False, db_index=True) + delivery_email = models.EmailField(blank=False, db_index=True) + email = models.EmailField(blank=False, db_index=True) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) + realm = models.ForeignKey(Realm, on_delete=CASCADE) # Foreign key to the Recipient object for PERSONAL type messages to this user. recipient = models.ForeignKey(Recipient, null=True, on_delete=models.SET_NULL) @@ -1769,35 +1755,33 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): # It also allows organizations to encode a bit of non-name data in # the "name" attribute if desired, like gender pronouns, # graduation year, etc. - full_name: str = models.CharField(max_length=MAX_NAME_LENGTH) + full_name = models.CharField(max_length=MAX_NAME_LENGTH) - date_joined: datetime.datetime = models.DateTimeField(default=timezone_now) - tos_version: Optional[str] = models.CharField(null=True, max_length=10) - api_key: str = models.CharField(max_length=API_KEY_LENGTH) + date_joined = models.DateTimeField(default=timezone_now) + tos_version = models.CharField(null=True, max_length=10) + api_key = models.CharField(max_length=API_KEY_LENGTH) # A UUID generated on user creation. Introduced primarily to # provide a unique key for a user for the mobile push # notifications bouncer that will not have collisions after doing # a data export and then import. - uuid: UUID = models.UUIDField(default=uuid4, unique=True) + uuid = models.UUIDField(default=uuid4, unique=True) # Whether the user has access to server-level administrator pages, like /activity - is_staff: bool = models.BooleanField(default=False) + is_staff = models.BooleanField(default=False) # For a normal user, this is True unless the user or an admin has # deactivated their account. The name comes from Django; this field # isn't related to presence or to whether the user has recently used Zulip. # # See also `long_term_idle`. - is_active: bool = models.BooleanField(default=True, db_index=True) + is_active = models.BooleanField(default=True, db_index=True) - is_billing_admin: bool = models.BooleanField(default=False, db_index=True) + is_billing_admin = models.BooleanField(default=False, db_index=True) - is_bot: bool = models.BooleanField(default=False, db_index=True) - bot_type: Optional[int] = models.PositiveSmallIntegerField(null=True, db_index=True) - bot_owner: Optional["UserProfile"] = models.ForeignKey( - "self", null=True, on_delete=models.SET_NULL - ) + is_bot = models.BooleanField(default=False, db_index=True) + bot_type = models.PositiveSmallIntegerField(null=True, db_index=True) + bot_owner = models.ForeignKey("self", null=True, on_delete=models.SET_NULL) # Each role has a superset of the permissions of the next higher # numbered role. When adding new roles, leave enough space for @@ -1809,7 +1793,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): ROLE_MODERATOR = 300 ROLE_MEMBER = 400 ROLE_GUEST = 600 - role: int = models.PositiveSmallIntegerField(default=ROLE_MEMBER, db_index=True) + role = models.PositiveSmallIntegerField(default=ROLE_MEMBER, db_index=True) ROLE_TYPES = [ ROLE_REALM_OWNER, @@ -1822,47 +1806,47 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): # Whether the user has been "soft-deactivated" due to weeks of inactivity. # For these users we avoid doing UserMessage table work, as an optimization # for large Zulip organizations with lots of single-visit users. - long_term_idle: bool = models.BooleanField(default=False, db_index=True) + long_term_idle = models.BooleanField(default=False, db_index=True) # When we last added basic UserMessage rows for a long_term_idle user. - last_active_message_id: Optional[int] = models.IntegerField(null=True) + last_active_message_id = models.IntegerField(null=True) # Mirror dummies are fake (!is_active) users used to provide # message senders in our cross-protocol Zephyr<->Zulip content # mirroring integration, so that we can display mirrored content # like native Zulip messages (with a name + avatar, etc.). - is_mirror_dummy: bool = models.BooleanField(default=False) + is_mirror_dummy = models.BooleanField(default=False) # Users with this flag set are allowed to forge messages as sent by another # user and to send to private streams; also used for Zephyr/Jabber mirroring. - can_forge_sender: bool = models.BooleanField(default=False, db_index=True) + can_forge_sender = models.BooleanField(default=False, db_index=True) # Users with this flag set can create other users via API. - can_create_users: bool = models.BooleanField(default=False, db_index=True) + can_create_users = models.BooleanField(default=False, db_index=True) # Used for rate-limiting certain automated messages generated by bots - last_reminder: Optional[datetime.datetime] = models.DateTimeField(default=None, null=True) + last_reminder = models.DateTimeField(default=None, null=True) # Minutes to wait before warning a bot owner that their bot sent a message # to a nonexistent stream BOT_OWNER_STREAM_ALERT_WAITPERIOD = 1 # API rate limits, formatted as a comma-separated list of range:max pairs - rate_limits: str = models.CharField(default="", max_length=100) + rate_limits = models.CharField(default="", max_length=100) # Default streams for some deprecated/legacy classes of bot users. - default_sending_stream: Optional["Stream"] = models.ForeignKey( + default_sending_stream = models.ForeignKey( "zerver.Stream", null=True, related_name="+", on_delete=models.SET_NULL, ) - default_events_register_stream: Optional["Stream"] = models.ForeignKey( + default_events_register_stream = models.ForeignKey( "zerver.Stream", null=True, related_name="+", on_delete=models.SET_NULL, ) - default_all_public_streams: bool = models.BooleanField(default=False) + default_all_public_streams = models.BooleanField(default=False) # A time zone name from the `tzdata` database, as found in zoneinfo.available_timezones(). # @@ -1872,7 +1856,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): # In Django, the convention is to use an empty string instead of NULL/None # for text-based fields. For more information, see # https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.Field.null. - timezone: str = models.CharField(max_length=40, default="") + timezone = models.CharField(max_length=40, default="") AVATAR_FROM_GRAVATAR = "G" AVATAR_FROM_USER = "U" @@ -1880,11 +1864,11 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): (AVATAR_FROM_GRAVATAR, "Hosted by Gravatar"), (AVATAR_FROM_USER, "Uploaded by user"), ) - avatar_source: str = models.CharField( + avatar_source = models.CharField( default=AVATAR_FROM_GRAVATAR, choices=AVATAR_SOURCES, max_length=1 ) - avatar_version: int = models.PositiveSmallIntegerField(default=1) - avatar_hash: Optional[str] = models.CharField(null=True, max_length=64) + avatar_version = models.PositiveSmallIntegerField(default=1) + avatar_hash = models.CharField(null=True, max_length=64) TUTORIAL_WAITING = "W" TUTORIAL_STARTED = "S" @@ -1894,7 +1878,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): (TUTORIAL_STARTED, "Started"), (TUTORIAL_FINISHED, "Finished"), ) - tutorial_status: str = models.CharField( + tutorial_status = models.CharField( default=TUTORIAL_WAITING, choices=TUTORIAL_STATES, max_length=1 ) @@ -1902,11 +1886,11 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): # [("step 1", true), ("step 2", false)] # where the second element of each tuple is if the step has been # completed. - onboarding_steps: str = models.TextField(default="[]") + onboarding_steps = models.TextField(default="[]") - zoom_token: Optional[object] = models.JSONField(default=None, null=True) + zoom_token = models.JSONField(default=None, null=True) - objects: UserManager = UserManager() + objects = UserManager() ROLE_ID_TO_NAME_MAP = { ROLE_REALM_OWNER: gettext_lazy("Organization owner"), @@ -2178,21 +2162,21 @@ class PasswordTooWeakError(Exception): class UserGroup(models.Model): objects: CTEManager = CTEManager() - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - name: str = models.CharField(max_length=100) - direct_members: Manager = models.ManyToManyField( + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + name = models.CharField(max_length=100) + direct_members = models.ManyToManyField( UserProfile, through="UserGroupMembership", related_name="direct_groups" ) - direct_subgroups: Manager = models.ManyToManyField( + direct_subgroups = models.ManyToManyField( "self", symmetrical=False, through="GroupGroupMembership", through_fields=("supergroup", "subgroup"), related_name="direct_supergroups", ) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - description: str = models.TextField(default="") - is_system_group: bool = models.BooleanField(default=False) + realm = models.ForeignKey(Realm, on_delete=CASCADE) + description = models.TextField(default="") + is_system_group = models.BooleanField(default=False) # Names for system groups. FULL_MEMBERS_GROUP_NAME = "@role:fullmembers" @@ -2234,18 +2218,18 @@ class UserGroup(models.Model): class UserGroupMembership(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_group: UserGroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE, related_name="+") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_group = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE, related_name="+") class Meta: unique_together = (("user_group", "user_profile"),) class GroupGroupMembership(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - supergroup: UserGroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") - subgroup: UserGroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + supergroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") + subgroup = models.ForeignKey(UserGroup, on_delete=CASCADE, related_name="+") class Meta: constraints = [ @@ -2277,31 +2261,29 @@ class PreregistrationUser(models.Model): # from the authentication step and pass it to the registration # form. - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - email: str = models.EmailField() + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + email = models.EmailField() confirmation = GenericRelation("confirmation.Confirmation", related_query_name="prereg_user") # If the pre-registration process provides a suggested full name for this user, # store it here to use it to prepopulate the full name field in the registration form: - full_name: Optional[str] = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH, null=True) - full_name_validated: bool = models.BooleanField(default=False) - referred_by: Optional[UserProfile] = models.ForeignKey( - UserProfile, null=True, on_delete=CASCADE - ) - streams: Manager = models.ManyToManyField("Stream") - invited_at: datetime.datetime = models.DateTimeField(auto_now=True) - realm_creation: bool = models.BooleanField(default=False) + full_name = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH, null=True) + full_name_validated = models.BooleanField(default=False) + referred_by = models.ForeignKey(UserProfile, null=True, on_delete=CASCADE) + streams = models.ManyToManyField("Stream") + invited_at = models.DateTimeField(auto_now=True) + realm_creation = models.BooleanField(default=False) # Indicates whether the user needs a password. Users who were # created via SSO style auth (e.g. GitHub/Google) generally do not. - password_required: bool = models.BooleanField(default=True) + password_required = models.BooleanField(default=True) # status: whether an object has been confirmed. # if confirmed, set to confirmation.settings.STATUS_USED - status: int = models.IntegerField(default=0) + status = models.IntegerField(default=0) # The realm should only ever be None for PreregistrationUser # objects created as part of realm creation. - realm: Optional[Realm] = models.ForeignKey(Realm, null=True, on_delete=CASCADE) + realm = models.ForeignKey(Realm, null=True, on_delete=CASCADE) # These values should be consistent with the values # in settings_config.user_role_values. @@ -2312,15 +2294,13 @@ class PreregistrationUser(models.Model): MEMBER=400, GUEST_USER=600, ) - invited_as: int = models.PositiveSmallIntegerField(default=INVITE_AS["MEMBER"]) + invited_as = models.PositiveSmallIntegerField(default=INVITE_AS["MEMBER"]) - multiuse_invite: Optional["MultiuseInvite"] = models.ForeignKey( - "MultiuseInvite", null=True, on_delete=models.SET_NULL - ) + multiuse_invite = models.ForeignKey("MultiuseInvite", null=True, on_delete=models.SET_NULL) # The UserProfile created upon completion of the registration # for this PregistrationUser - created_user: Optional[UserProfile] = models.ForeignKey( + created_user = models.ForeignKey( UserProfile, null=True, related_name="+", on_delete=models.SET_NULL ) @@ -2359,36 +2339,34 @@ def filter_to_valid_prereg_users( class MultiuseInvite(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - referred_by: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - streams: Manager = models.ManyToManyField("Stream") - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - invited_as: int = models.PositiveSmallIntegerField( - default=PreregistrationUser.INVITE_AS["MEMBER"] - ) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + referred_by = models.ForeignKey(UserProfile, on_delete=CASCADE) + streams = models.ManyToManyField("Stream") + realm = models.ForeignKey(Realm, on_delete=CASCADE) + invited_as = models.PositiveSmallIntegerField(default=PreregistrationUser.INVITE_AS["MEMBER"]) class EmailChangeStatus(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - new_email: str = models.EmailField() - old_email: str = models.EmailField() - updated_at: datetime.datetime = models.DateTimeField(auto_now=True) - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + new_email = models.EmailField() + old_email = models.EmailField() + updated_at = models.DateTimeField(auto_now=True) + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) # status: whether an object has been confirmed. # if confirmed, set to confirmation.settings.STATUS_USED - status: int = models.IntegerField(default=0) + status = models.IntegerField(default=0) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) + realm = models.ForeignKey(Realm, on_delete=CASCADE) class RealmReactivationStatus(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") # status: whether an object has been confirmed. # if confirmed, set to confirmation.settings.STATUS_USED - status: int = models.IntegerField(default=0) + status = models.IntegerField(default=0) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) + realm = models.ForeignKey(Realm, on_delete=CASCADE) class AbstractPushDeviceToken(models.Model): @@ -2400,30 +2378,30 @@ class AbstractPushDeviceToken(models.Model): (GCM, "gcm"), ) - kind: int = models.PositiveSmallIntegerField(choices=KINDS) + kind = models.PositiveSmallIntegerField(choices=KINDS) # The token is a unique device-specific token that is # sent to us from each device: # - APNS token if kind == APNS # - GCM registration id if kind == GCM - token: str = models.CharField(max_length=4096, db_index=True) + token = models.CharField(max_length=4096, db_index=True) # TODO: last_updated should be renamed date_created, since it is # no longer maintained as a last_updated value. - last_updated: datetime.datetime = models.DateTimeField(auto_now=True) + last_updated = models.DateTimeField(auto_now=True) # [optional] Contains the app id of the device if it is an iOS device - ios_app_id: Optional[str] = models.TextField(null=True) + ios_app_id = models.TextField(null=True) class Meta: abstract = True class PushDeviceToken(AbstractPushDeviceToken): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") # The user whose device this is - user: UserProfile = models.ForeignKey(UserProfile, db_index=True, on_delete=CASCADE) + user = models.ForeignKey(UserProfile, db_index=True, on_delete=CASCADE) class Meta: unique_together = ("user", "kind", "token") @@ -2437,13 +2415,13 @@ class Stream(models.Model): MAX_NAME_LENGTH = 60 MAX_DESCRIPTION_LENGTH = 1024 - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - name: str = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True) - realm: Realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE) - date_created: datetime.datetime = models.DateTimeField(default=timezone_now) - deactivated: bool = models.BooleanField(default=False) - description: str = models.CharField(max_length=MAX_DESCRIPTION_LENGTH, default="") - rendered_description: str = models.TextField(default="") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + name = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True) + realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE) + date_created = models.DateTimeField(default=timezone_now) + deactivated = models.BooleanField(default=False) + description = models.CharField(max_length=MAX_DESCRIPTION_LENGTH, default="") + rendered_description = models.TextField(default="") # Foreign key to the Recipient object for STREAM type messages to this stream. recipient = models.ForeignKey(Recipient, null=True, on_delete=models.SET_NULL) @@ -2483,11 +2461,11 @@ class Stream(models.Model): "policy_name": gettext_lazy("Public, protected history"), }, } - invite_only: bool = models.BooleanField(default=False) - history_public_to_subscribers: bool = models.BooleanField(default=True) + invite_only = models.BooleanField(default=False) + history_public_to_subscribers = models.BooleanField(default=True) # Whether this stream's content should be published by the web-public archive features - is_web_public: bool = models.BooleanField(default=False) + is_web_public = models.BooleanField(default=False) STREAM_POST_POLICY_EVERYONE = 1 STREAM_POST_POLICY_ADMINS = 2 @@ -2496,7 +2474,7 @@ class Stream(models.Model): # TODO: Implement policy to restrict posting to a user group or admins. # Who in the organization has permission to send messages to this stream. - stream_post_policy: int = models.PositiveSmallIntegerField(default=STREAM_POST_POLICY_EVERYONE) + stream_post_policy = models.PositiveSmallIntegerField(default=STREAM_POST_POLICY_EVERYONE) POST_POLICIES: Dict[int, "StrPromise"] = { # These strings should match the strings in the # stream_post_policy_values object in stream_data.js. @@ -2519,12 +2497,12 @@ class Stream(models.Model): # is more public in the sense that you don't need a Zulip invite to join. # This field is populated directly from UserProfile.is_zephyr_mirror_realm, # and the reason for denormalizing field is performance. - is_in_zephyr_realm: bool = models.BooleanField(default=False) + is_in_zephyr_realm = models.BooleanField(default=False) # Used by the e-mail forwarder. The e-mail RFC specifies a maximum # e-mail length of 254, and our max stream length is 30, so we # have plenty of room for the token. - email_token: str = models.CharField( + email_token = models.CharField( max_length=32, default=generate_email_token_for_stream, unique=True, @@ -2538,20 +2516,18 @@ class Stream(models.Model): "unlimited": -1, "realm_default": None, } - message_retention_days: Optional[int] = models.IntegerField(null=True, default=None) + message_retention_days = models.IntegerField(null=True, default=None) # on_delete field here is set to RESTRICT because we don't want to allow # deleting a user group in case it is referenced by this settig. # We are not using PROTECT since we want to allow deletion of user groups # when realm itself is deleted. - can_remove_subscribers_group: UserGroup = models.ForeignKey( - UserGroup, on_delete=models.RESTRICT - ) + can_remove_subscribers_group = models.ForeignKey(UserGroup, on_delete=models.RESTRICT) # The very first message ID in the stream. Used to help clients # determine whether they might need to display "more topics" for a # stream based on what messages they have cached. - first_message_id: Optional[int] = models.IntegerField(null=True, db_index=True) + first_message_id = models.IntegerField(null=True, db_index=True) def __str__(self) -> str: return f"" @@ -2621,15 +2597,15 @@ post_delete.connect(flush_stream, sender=Stream) class UserTopic(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - stream: Stream = models.ForeignKey(Stream, on_delete=CASCADE) - recipient: Recipient = models.ForeignKey(Recipient, on_delete=CASCADE) - topic_name: str = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + stream = models.ForeignKey(Stream, on_delete=CASCADE) + recipient = models.ForeignKey(Recipient, on_delete=CASCADE) + topic_name = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH) # The default value for last_updated is a few weeks before tracking # of when topics were muted was first introduced. It's designed # to be obviously incorrect so that one can tell it's backfilled data. - last_updated: datetime.datetime = models.DateTimeField( + last_updated = models.DateTimeField( default=datetime.datetime(2020, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) ) @@ -2656,9 +2632,7 @@ class UserTopic(models.Model): (VISIBILITY_POLICY_INHERIT, "User's default policy for the stream."), ) - visibility_policy: int = models.SmallIntegerField( - choices=visibility_policy_choices, default=MUTED - ) + visibility_policy = models.SmallIntegerField(choices=visibility_policy_choices, default=MUTED) class Meta: unique_together = ("user_profile", "stream", "topic_name") @@ -2685,13 +2659,9 @@ class UserTopic(models.Model): class MutedUser(models.Model): - user_profile: UserProfile = models.ForeignKey( - UserProfile, related_name="muter", on_delete=CASCADE - ) - muted_user: UserProfile = models.ForeignKey( - UserProfile, related_name="muted", on_delete=CASCADE - ) - date_muted: datetime.datetime = models.DateTimeField(default=timezone_now) + user_profile = models.ForeignKey(UserProfile, related_name="muter", on_delete=CASCADE) + muted_user = models.ForeignKey(UserProfile, related_name="muted", on_delete=CASCADE) + date_muted = models.DateTimeField(default=timezone_now) class Meta: unique_together = ("user_profile", "muted_user") @@ -2705,8 +2675,8 @@ post_delete.connect(flush_muting_users_cache, sender=MutedUser) class Client(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - name: str = models.CharField(max_length=30, db_index=True, unique=True) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + name = models.CharField(max_length=30, db_index=True, unique=True) def __str__(self) -> str: return f"" @@ -2856,11 +2826,11 @@ def bulk_get_huddle_user_ids(recipients: List[Recipient]) -> Dict[int, List[int] class AbstractMessage(models.Model): - sender: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) + sender = models.ForeignKey(UserProfile, on_delete=CASCADE) # The target of the message is signified by the Recipient object. # See the Recipient class for details. - recipient: Recipient = models.ForeignKey(Recipient, on_delete=CASCADE) + recipient = models.ForeignKey(Recipient, on_delete=CASCADE) # The message's topic. # @@ -2870,24 +2840,24 @@ class AbstractMessage(models.Model): # new code should generally also say "topic". # # See also the `topic_name` method on `Message`. - subject: str = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH, db_index=True) + subject = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH, db_index=True) - content: str = models.TextField() - rendered_content: Optional[str] = models.TextField(null=True) - rendered_content_version: Optional[int] = models.IntegerField(null=True) + content = models.TextField() + rendered_content = models.TextField(null=True) + rendered_content_version = models.IntegerField(null=True) - date_sent: datetime.datetime = models.DateTimeField("date sent", db_index=True) - sending_client: Client = models.ForeignKey(Client, on_delete=CASCADE) + date_sent = models.DateTimeField("date sent", db_index=True) + sending_client = models.ForeignKey(Client, on_delete=CASCADE) - last_edit_time: Optional[datetime.datetime] = models.DateTimeField(null=True) + last_edit_time = models.DateTimeField(null=True) # A JSON-encoded list of objects describing any past edits to this # message, oldest first. - edit_history: Optional[str] = models.TextField(null=True) + edit_history = models.TextField(null=True) - has_attachment: bool = models.BooleanField(default=False, db_index=True) - has_image: bool = models.BooleanField(default=False, db_index=True) - has_link: bool = models.BooleanField(default=False, db_index=True) + has_attachment = models.BooleanField(default=False, db_index=True) + has_image = models.BooleanField(default=False, db_index=True) + has_link = models.BooleanField(default=False, db_index=True) class Meta: abstract = True @@ -2898,19 +2868,19 @@ class AbstractMessage(models.Model): class ArchiveTransaction(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - timestamp: datetime.datetime = models.DateTimeField(default=timezone_now, db_index=True) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + timestamp = models.DateTimeField(default=timezone_now, db_index=True) # Marks if the data archived in this transaction has been restored: - restored: bool = models.BooleanField(default=False, db_index=True) + restored = models.BooleanField(default=False, db_index=True) - type: int = models.PositiveSmallIntegerField(db_index=True) + type = models.PositiveSmallIntegerField(db_index=True) # Valid types: RETENTION_POLICY_BASED = 1 # Archiving was executed due to automated retention policies MANUAL = 2 # Archiving was run manually, via move_messages_to_archive function # ForeignKey to the realm with which objects archived in this transaction are associated. # If type is set to MANUAL, this should be null. - realm: Optional[Realm] = models.ForeignKey(Realm, null=True, on_delete=CASCADE) + realm = models.ForeignKey(Realm, null=True, on_delete=CASCADE) def __str__(self) -> str: return "ArchiveTransaction id: {id}, type: {type}, realm: {realm}, timestamp: {timestamp}".format( @@ -2927,15 +2897,13 @@ class ArchivedMessage(AbstractMessage): 'message retention' feature. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - archive_transaction: ArchiveTransaction = models.ForeignKey( - ArchiveTransaction, on_delete=CASCADE - ) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + archive_transaction = models.ForeignKey(ArchiveTransaction, on_delete=CASCADE) class Message(AbstractMessage): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - search_tsvector: Optional[str] = SearchVectorField(null=True) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + search_tsvector = SearchVectorField(null=True) def topic_name(self) -> str: """ @@ -3050,17 +3018,17 @@ class AbstractSubMessage(models.Model): # games, surveys, mini threads, etc. These are designed to be pretty # generic in purpose. - sender: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - msg_type: str = models.TextField() - content: str = models.TextField() + sender = models.ForeignKey(UserProfile, on_delete=CASCADE) + msg_type = models.TextField() + content = models.TextField() class Meta: abstract = True class SubMessage(AbstractSubMessage): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - message: Message = models.ForeignKey(Message, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + message = models.ForeignKey(Message, on_delete=CASCADE) @staticmethod def get_raw_db_rows(needed_ids: List[int]) -> List[Dict[str, Any]]: @@ -3071,8 +3039,8 @@ class SubMessage(AbstractSubMessage): class ArchivedSubMessage(AbstractSubMessage): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - message: ArchivedMessage = models.ForeignKey(ArchivedMessage, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + message = models.ForeignKey(ArchivedMessage, on_delete=CASCADE) post_save.connect(flush_submessage, sender=SubMessage) @@ -3083,13 +3051,11 @@ class Draft(models.Model): multiple clients/devices. """ - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=models.CASCADE) - recipient: Optional[Recipient] = models.ForeignKey( - Recipient, null=True, on_delete=models.SET_NULL - ) - topic: str = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH, db_index=True) - content: str = models.TextField() # Length should not exceed MAX_MESSAGE_LENGTH - last_edit_time: datetime.datetime = models.DateTimeField(db_index=True) + user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE) + recipient = models.ForeignKey(Recipient, null=True, on_delete=models.SET_NULL) + topic = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH, db_index=True) + content = models.TextField() # Length should not exceed MAX_MESSAGE_LENGTH + last_edit_time = models.DateTimeField(db_index=True) def __str__(self) -> str: return f"<{self.__class__.__name__}: {self.user_profile.email} / {self.id} / {self.last_edit_time}>" @@ -3129,12 +3095,12 @@ class AbstractEmoji(models.Model): https://zulip.readthedocs.io/en/latest/subsystems/emoji.html """ - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) # The user-facing name for an emoji reaction. With emoji aliases, # there may be multiple accepted names for a given emoji; this # field encodes which one the user selected. - emoji_name: str = models.TextField() + emoji_name = models.TextField() UNICODE_EMOJI = "unicode_emoji" REALM_EMOJI = "realm_emoji" @@ -3144,9 +3110,7 @@ class AbstractEmoji(models.Model): (REALM_EMOJI, gettext_lazy("Custom emoji")), (ZULIP_EXTRA_EMOJI, gettext_lazy("Zulip extra emoji")), ) - reaction_type: str = models.CharField( - default=UNICODE_EMOJI, choices=REACTION_TYPES, max_length=30 - ) + reaction_type = models.CharField(default=UNICODE_EMOJI, choices=REACTION_TYPES, max_length=30) # A string with the property that (realm, reaction_type, # emoji_code) uniquely determines the emoji glyph. @@ -3168,7 +3132,7 @@ class AbstractEmoji(models.Model): # of the RealmEmoji object, computed as `str(realm_emoji.id)`. # # * For "Zulip extra emoji" (like :zulip:), the name of the emoji (e.g. "zulip"). - emoji_code: str = models.TextField() + emoji_code = models.TextField() class Meta: abstract = True @@ -3184,8 +3148,8 @@ class AbstractReaction(AbstractEmoji): class Reaction(AbstractReaction): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - message: Message = models.ForeignKey(Message, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + message = models.ForeignKey(Message, on_delete=CASCADE) @staticmethod def get_raw_db_rows(needed_ids: List[int]) -> List[Dict[str, Any]]: @@ -3208,8 +3172,8 @@ class Reaction(AbstractReaction): class ArchivedReaction(AbstractReaction): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - message: ArchivedMessage = models.ForeignKey(ArchivedMessage, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + message = models.ForeignKey(ArchivedMessage, on_delete=CASCADE) # Whenever a message is sent, for each user subscribed to the @@ -3237,9 +3201,9 @@ class ArchivedReaction(AbstractReaction): # UserMessage is the largest table in many Zulip installations, even # though each row is only 4 integers. class AbstractUserMessage(models.Model): - id: int = models.BigAutoField(primary_key=True) + id = models.BigAutoField(primary_key=True) - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) # The order here is important! It's the order of fields in the bitfield. ALL_FLAGS = [ "read", @@ -3345,7 +3309,7 @@ class AbstractUserMessage(models.Model): class UserMessage(AbstractUserMessage): - message: Message = models.ForeignKey(Message, on_delete=CASCADE) + message = models.ForeignKey(Message, on_delete=CASCADE) class Meta(AbstractUserMessage.Meta): indexes = [ @@ -3432,7 +3396,7 @@ class ArchivedUserMessage(AbstractUserMessage): a robust 'message retention' feature. """ - message: Message = models.ForeignKey(ArchivedMessage, on_delete=CASCADE) + message = models.ForeignKey(ArchivedMessage, on_delete=CASCADE) def __str__(self) -> str: display_recipient = get_display_recipient(self.message.recipient) @@ -3440,21 +3404,21 @@ class ArchivedUserMessage(AbstractUserMessage): class AbstractAttachment(models.Model): - file_name: str = models.TextField(db_index=True) + file_name = models.TextField(db_index=True) # path_id is a storage location agnostic representation of the path of the file. # If the path of a file is http://localhost:9991/user_uploads/a/b/abc/temp_file.py # then its path_id will be a/b/abc/temp_file.py. - path_id: str = models.TextField(db_index=True, unique=True) - owner: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) + path_id = models.TextField(db_index=True, unique=True) + owner = models.ForeignKey(UserProfile, on_delete=CASCADE) + realm = models.ForeignKey(Realm, on_delete=CASCADE) - create_time: datetime.datetime = models.DateTimeField( + create_time = models.DateTimeField( default=timezone_now, db_index=True, ) # Size of the uploaded file, in bytes - size: int = models.IntegerField() + size = models.IntegerField() # The two fields below serve as caches to let us avoid looking up # the corresponding messages/streams to check permissions before @@ -3468,11 +3432,11 @@ class AbstractAttachment(models.Model): # thus should be available to all non-guest users in the # organization (even if they weren't a recipient of a message # linking to it). - is_realm_public: Optional[bool] = models.BooleanField(default=False, null=True) + is_realm_public = models.BooleanField(default=False, null=True) # Whether this attachment has been posted to a web-public stream, # and thus should be available to everyone on the internet, even # if the person isn't logged in. - is_web_public: Optional[bool] = models.BooleanField(default=False, null=True) + is_web_public = models.BooleanField(default=False, null=True) class Meta: abstract = True @@ -3497,15 +3461,15 @@ class ArchivedAttachment(AbstractAttachment): the associated uploaded files from storage. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - messages: Manager = models.ManyToManyField( + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + messages = models.ManyToManyField( ArchivedMessage, related_name="attachment_set", related_query_name="attachment" ) class Attachment(AbstractAttachment): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - messages: Manager = models.ManyToManyField(Message) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + messages = models.ManyToManyField(Message) def is_claimed(self) -> bool: return self.messages.count() > 0 @@ -3678,39 +3642,39 @@ class Subscription(models.Model): fields in this model describe the user's subscription to that stream. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - recipient: Recipient = models.ForeignKey(Recipient, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + recipient = models.ForeignKey(Recipient, on_delete=CASCADE) # Whether the user has since unsubscribed. We mark Subscription # objects as inactive, rather than deleting them, when a user # unsubscribes, so we can preserve user customizations like # notification settings, stream color, etc., if the user later # resubscribes. - active: bool = models.BooleanField(default=True) + active = models.BooleanField(default=True) # This is a denormalization designed to improve the performance of # bulk queries of Subscription objects, Whether the subscribed user # is active tends to be a key condition in those queries. # We intentionally don't specify a default value to promote thinking # about this explicitly, as in some special cases, such as data import, # we may be creating Subscription objects for a user that's deactivated. - is_user_active: bool = models.BooleanField() + is_user_active = models.BooleanField() # Whether this user had muted this stream. - is_muted: bool = models.BooleanField(default=False) + is_muted = models.BooleanField(default=False) DEFAULT_STREAM_COLOR = "#c2c2c2" - color: str = models.CharField(max_length=10, default=DEFAULT_STREAM_COLOR) - pin_to_top: bool = models.BooleanField(default=False) + color = models.CharField(max_length=10, default=DEFAULT_STREAM_COLOR) + pin_to_top = models.BooleanField(default=False) # These fields are stream-level overrides for the user's default # configuration for notification, configured in UserProfile. The # default, None, means we just inherit the user-level default. - desktop_notifications: Optional[bool] = models.BooleanField(null=True, default=None) - audible_notifications: Optional[bool] = models.BooleanField(null=True, default=None) - push_notifications: Optional[bool] = models.BooleanField(null=True, default=None) - email_notifications: Optional[bool] = models.BooleanField(null=True, default=None) - wildcard_mentions_notify: Optional[bool] = models.BooleanField(null=True, default=None) + desktop_notifications = models.BooleanField(null=True, default=None) + audible_notifications = models.BooleanField(null=True, default=None) + push_notifications = models.BooleanField(null=True, default=None) + email_notifications = models.BooleanField(null=True, default=None) + wildcard_mentions_notify = models.BooleanField(null=True, default=None) class Meta: unique_together = ("user_profile", "recipient") @@ -3967,10 +3931,10 @@ class Huddle(models.Model): corresponding Huddle object. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") # TODO: We should consider whether using # CommaSeparatedIntegerField would be better. - huddle_hash: str = models.CharField(max_length=40, db_index=True, unique=True) + huddle_hash = models.CharField(max_length=40, db_index=True, unique=True) # Foreign key to the Recipient object for this Huddle. recipient = models.ForeignKey(Recipient, null=True, on_delete=models.SET_NULL) @@ -4031,13 +3995,13 @@ class UserActivity(models.Model): and database migration purposes. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - client: Client = models.ForeignKey(Client, on_delete=CASCADE) - query: str = models.CharField(max_length=50, db_index=True) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + client = models.ForeignKey(Client, on_delete=CASCADE) + query = models.CharField(max_length=50, db_index=True) - count: int = models.IntegerField() - last_visit: datetime.datetime = models.DateTimeField("last visit") + count = models.IntegerField() + last_visit = models.DateTimeField("last visit") class Meta: unique_together = ("user_profile", "client", "query") @@ -4046,10 +4010,10 @@ class UserActivity(models.Model): class UserActivityInterval(models.Model): MIN_INTERVAL_LENGTH = datetime.timedelta(minutes=15) - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - start: datetime.datetime = models.DateTimeField("start time", db_index=True) - end: datetime.datetime = models.DateTimeField("end time", db_index=True) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + start = models.DateTimeField("start time", db_index=True) + end = models.DateTimeField("end time", db_index=True) class Meta: index_together = [ @@ -4074,13 +4038,13 @@ class UserPresence(models.Model): ("realm", "timestamp"), ] - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - client: Client = models.ForeignKey(Client, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + realm = models.ForeignKey(Realm, on_delete=CASCADE) + client = models.ForeignKey(Client, on_delete=CASCADE) # The time we heard this update from the client. - timestamp: datetime.datetime = models.DateTimeField("presence changed") + timestamp = models.DateTimeField("presence changed") # The user was actively using this Zulip client as of `timestamp` (i.e., # they had interacted with the client recently). When the timestamp is @@ -4098,7 +4062,7 @@ class UserPresence(models.Model): # # There is no "inactive" status, because that is encoded by the # timestamp being old. - status: int = models.PositiveSmallIntegerField(default=ACTIVE) + status = models.PositiveSmallIntegerField(default=ACTIVE) @staticmethod def status_to_string(status: int) -> str: @@ -4148,24 +4112,24 @@ class UserPresence(models.Model): class UserStatus(AbstractEmoji): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_profile: UserProfile = models.OneToOneField(UserProfile, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_profile = models.OneToOneField(UserProfile, on_delete=CASCADE) - timestamp: datetime.datetime = models.DateTimeField() - client: Client = models.ForeignKey(Client, on_delete=CASCADE) + timestamp = models.DateTimeField() + client = models.ForeignKey(Client, on_delete=CASCADE) # Override emoji_name and emoji_code field of (AbstractReaction model) to accept # default value. - emoji_name: str = models.TextField(default="") - emoji_code: str = models.TextField(default="") + emoji_name = models.TextField(default="") + emoji_code = models.TextField(default="") - status_text: str = models.CharField(max_length=255, default="") + status_text = models.CharField(max_length=255, default="") class DefaultStream(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - stream: Stream = models.ForeignKey(Stream, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.ForeignKey(Realm, on_delete=CASCADE) + stream = models.ForeignKey(Stream, on_delete=CASCADE) class Meta: unique_together = ("realm", "stream") @@ -4174,11 +4138,11 @@ class DefaultStream(models.Model): class DefaultStreamGroup(models.Model): MAX_NAME_LENGTH = 60 - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - name: str = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - streams: Manager = models.ManyToManyField("Stream") - description: str = models.CharField(max_length=1024, default="") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + name = models.CharField(max_length=MAX_NAME_LENGTH, db_index=True) + realm = models.ForeignKey(Realm, on_delete=CASCADE) + streams = models.ManyToManyField("Stream") + description = models.CharField(max_length=1024, default="") class Meta: unique_together = ("realm", "name") @@ -4197,10 +4161,10 @@ def get_default_stream_groups(realm: Realm) -> QuerySet[DefaultStreamGroup]: class AbstractScheduledJob(models.Model): - scheduled_timestamp: datetime.datetime = models.DateTimeField(db_index=True) + scheduled_timestamp = models.DateTimeField(db_index=True) # JSON representation of arguments to consumer - data: str = models.TextField() - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) + data = models.TextField() + realm = models.ForeignKey(Realm, on_delete=CASCADE) class Meta: abstract = True @@ -4212,31 +4176,31 @@ class ScheduledEmail(AbstractScheduledJob): # ScheduledEmails for use in clear_scheduled_emails; the # recipients used for actually sending messages are stored in the # data field of AbstractScheduledJob. - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - users: Manager = models.ManyToManyField(UserProfile) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + users = models.ManyToManyField(UserProfile) # Just the address part of a full "name
" email address - address: Optional[str] = models.EmailField(null=True, db_index=True) + address = models.EmailField(null=True, db_index=True) # Valid types are below WELCOME = 1 DIGEST = 2 INVITATION_REMINDER = 3 - type: int = models.PositiveSmallIntegerField() + type = models.PositiveSmallIntegerField() def __str__(self) -> str: return f"" class MissedMessageEmailAddress(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - message: Message = models.ForeignKey(Message, on_delete=CASCADE) - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - email_token: str = models.CharField(max_length=34, unique=True, db_index=True) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + message = models.ForeignKey(Message, on_delete=CASCADE) + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + email_token = models.CharField(max_length=34, unique=True, db_index=True) # Timestamp of when the missed message address generated. - timestamp: datetime.datetime = models.DateTimeField(db_index=True, default=timezone_now) + timestamp = models.DateTimeField(db_index=True, default=timezone_now) # Number of times the missed message address has been used. - times_used: int = models.PositiveIntegerField(default=0, db_index=True) + times_used = models.PositiveIntegerField(default=0, db_index=True) def __str__(self) -> str: return settings.EMAIL_GATEWAY_PATTERN % (self.email_token,) @@ -4262,8 +4226,8 @@ class ScheduledMessageNotificationEmail(models.Model): scheduled_timestamp. """ - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - message: Message = models.ForeignKey(Message, on_delete=CASCADE) + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + message = models.ForeignKey(Message, on_delete=CASCADE) EMAIL_NOTIFICATION_TRIGGER_CHOICES = [ (NotificationTriggers.PRIVATE_MESSAGE, "Private message"), @@ -4272,27 +4236,25 @@ class ScheduledMessageNotificationEmail(models.Model): (NotificationTriggers.STREAM_EMAIL, "Stream notifications enabled"), ] - trigger: str = models.TextField(choices=EMAIL_NOTIFICATION_TRIGGER_CHOICES) - mentioned_user_group: Optional[UserGroup] = models.ForeignKey( - UserGroup, null=True, on_delete=CASCADE - ) + trigger = models.TextField(choices=EMAIL_NOTIFICATION_TRIGGER_CHOICES) + mentioned_user_group = models.ForeignKey(UserGroup, null=True, on_delete=CASCADE) # Timestamp for when the notification should be processed and sent. # Calculated from the time the event was received and the batching period. - scheduled_timestamp: datetime.datetime = models.DateTimeField(db_index=True) + scheduled_timestamp = models.DateTimeField(db_index=True) class ScheduledMessage(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - sender: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - recipient: Recipient = models.ForeignKey(Recipient, on_delete=CASCADE) - subject: str = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH) - content: str = models.TextField() - sending_client: Client = models.ForeignKey(Client, on_delete=CASCADE) - stream: Optional[Stream] = models.ForeignKey(Stream, null=True, on_delete=CASCADE) - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - scheduled_timestamp: datetime.datetime = models.DateTimeField(db_index=True) - delivered: bool = models.BooleanField(default=False) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + sender = models.ForeignKey(UserProfile, on_delete=CASCADE) + recipient = models.ForeignKey(Recipient, on_delete=CASCADE) + subject = models.CharField(max_length=MAX_TOPIC_NAME_LENGTH) + content = models.TextField() + sending_client = models.ForeignKey(Client, on_delete=CASCADE) + stream = models.ForeignKey(Stream, null=True, on_delete=CASCADE) + realm = models.ForeignKey(Realm, on_delete=CASCADE) + scheduled_timestamp = models.DateTimeField(db_index=True) + delivered = models.BooleanField(default=False) SEND_LATER = 1 REMIND = 2 @@ -4302,7 +4264,7 @@ class ScheduledMessage(models.Model): (REMIND, "remind"), ) - delivery_type: int = models.PositiveSmallIntegerField( + delivery_type = models.PositiveSmallIntegerField( choices=DELIVERY_TYPES, default=SEND_LATER, ) @@ -4329,10 +4291,10 @@ EMAIL_TYPES = { class AbstractRealmAuditLog(models.Model): """Defines fields common to RealmAuditLog and RemoteRealmAuditLog.""" - event_time: datetime.datetime = models.DateTimeField(db_index=True) + event_time = models.DateTimeField(db_index=True) # If True, event_time is an overestimate of the true time. Can be used # by migrations when introducing a new event_type. - backfilled: bool = models.BooleanField(default=False) + backfilled = models.BooleanField(default=False) # Keys within extra_data, when extra_data is a json dict. Keys are strings because # json keys must always be strings. @@ -4342,7 +4304,7 @@ class AbstractRealmAuditLog(models.Model): ROLE_COUNT_HUMANS = "11" ROLE_COUNT_BOTS = "12" - extra_data: Optional[str] = models.TextField(null=True) + extra_data = models.TextField(null=True) # Event types USER_CREATED = 101 @@ -4429,7 +4391,7 @@ class AbstractRealmAuditLog(models.Model): REMOTE_SERVER_PLAN_TYPE_CHANGED = 10204 REMOTE_SERVER_DEACTIVATED = 10201 - event_type: int = models.PositiveSmallIntegerField() + event_type = models.PositiveSmallIntegerField() # event_types synced from on-prem installations to Zulip Cloud when # billing for mobile push notifications is enabled. Every billing @@ -4469,21 +4431,21 @@ class RealmAuditLog(AbstractRealmAuditLog): modified_stream will be None. """ - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - acting_user: Optional[UserProfile] = models.ForeignKey( + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.ForeignKey(Realm, on_delete=CASCADE) + acting_user = models.ForeignKey( UserProfile, null=True, related_name="+", on_delete=CASCADE, ) - modified_user: Optional[UserProfile] = models.ForeignKey( + modified_user = models.ForeignKey( UserProfile, null=True, related_name="+", on_delete=CASCADE, ) - modified_stream: Optional[Stream] = models.ForeignKey( + modified_stream = models.ForeignKey( Stream, null=True, on_delete=CASCADE, @@ -4499,10 +4461,10 @@ class RealmAuditLog(AbstractRealmAuditLog): class UserHotspot(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - hotspot: str = models.CharField(max_length=30) - timestamp: datetime.datetime = models.DateTimeField(default=timezone_now) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user = models.ForeignKey(UserProfile, on_delete=CASCADE) + hotspot = models.CharField(max_length=30) + timestamp = models.DateTimeField(default=timezone_now) class Meta: unique_together = ("user", "hotspot") @@ -4542,17 +4504,17 @@ class CustomProfileField(models.Model): NAME_MAX_LENGTH = 40 MAX_DISPLAY_IN_PROFILE_SUMMARY_FIELDS = 2 - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.ForeignKey(Realm, on_delete=CASCADE) - name: str = models.CharField(max_length=NAME_MAX_LENGTH) - hint: str = models.CharField(max_length=HINT_MAX_LENGTH, default="") + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.ForeignKey(Realm, on_delete=CASCADE) + name = models.CharField(max_length=NAME_MAX_LENGTH) + hint = models.CharField(max_length=HINT_MAX_LENGTH, default="") # Sort order for display of custom profile fields. - order: int = models.IntegerField(default=0) + order = models.IntegerField(default=0) # Whether the field should be displayed in smaller summary # sections of a page displaying custom profile fields. - display_in_profile_summary: bool = models.BooleanField(default=False) + display_in_profile_summary = models.BooleanField(default=False) SHORT_TEXT = 1 LONG_TEXT = 2 @@ -4606,7 +4568,7 @@ class CustomProfileField(models.Model): (item[0], item[1]) for item in ALL_FIELD_TYPES ] - field_type: int = models.PositiveSmallIntegerField( + field_type = models.PositiveSmallIntegerField( choices=FIELD_TYPE_CHOICES, default=SHORT_TEXT, ) @@ -4620,7 +4582,7 @@ class CustomProfileField(models.Model): # # Note: There is no performance overhead of using TextField in PostgreSQL. # See https://www.postgresql.org/docs/9.0/static/datatype-character.html - field_data: str = models.TextField(default="") + field_data = models.TextField(default="") class Meta: unique_together = ("realm", "name") @@ -4653,11 +4615,11 @@ def custom_profile_fields_for_realm(realm_id: int) -> QuerySet[CustomProfileFiel class CustomProfileFieldValue(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - field: CustomProfileField = models.ForeignKey(CustomProfileField, on_delete=CASCADE) - value: str = models.TextField() - rendered_value: Optional[str] = models.TextField(null=True, default=None) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + field = models.ForeignKey(CustomProfileField, on_delete=CASCADE) + value = models.TextField() + rendered_value = models.TextField(null=True, default=None) class Meta: unique_together = ("user_profile", "field") @@ -4687,16 +4649,16 @@ SLACK_INTERFACE = "SlackOutgoingWebhookService" # embedded bots with the same name will run the same code # - base_url and token are currently unused class Service(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - name: str = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + name = models.CharField(max_length=UserProfile.MAX_NAME_LENGTH) # Bot user corresponding to the Service. The bot_type of this user # determines the type of service. If non-bot services are added later, # user_profile can also represent the owner of the Service. - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - base_url: str = models.TextField() - token: str = models.TextField() + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + base_url = models.TextField() + token = models.TextField() # Interface / API version of the service. - interface: int = models.PositiveSmallIntegerField(default=1) + interface = models.PositiveSmallIntegerField(default=1) # Valid interfaces are {generic, zulip_bot_service, slack} GENERIC = 1 @@ -4726,20 +4688,20 @@ def get_service_profile(user_profile_id: int, service_name: str) -> Service: class BotStorageData(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - bot_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - key: str = models.TextField(db_index=True) - value: str = models.TextField() + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + bot_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + key = models.TextField(db_index=True) + value = models.TextField() class Meta: unique_together = ("bot_profile", "key") class BotConfigData(models.Model): - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - bot_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) - key: str = models.TextField(db_index=True) - value: str = models.TextField() + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + bot_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) + key = models.TextField(db_index=True) + value = models.TextField() class Meta: unique_together = ("bot_profile", "key") @@ -4774,11 +4736,11 @@ class AlertWord(models.Model): # never move to another realm, so it's static, and having Realm # here optimizes the main query on this table, which is fetching # all the alert words in a realm. - id: int = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") - realm: Realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE) - user_profile: UserProfile = models.ForeignKey(UserProfile, on_delete=CASCADE) + id = models.AutoField(auto_created=True, primary_key=True, verbose_name="ID") + realm = models.ForeignKey(Realm, db_index=True, on_delete=CASCADE) + user_profile = models.ForeignKey(UserProfile, on_delete=CASCADE) # Case-insensitive name for the alert word. - word: str = models.TextField() + word = models.TextField() class Meta: unique_together = ("user_profile", "word") diff --git a/zilencer/models.py b/zilencer/models.py index c77cefabcf..84a5c1121d 100644 --- a/zilencer/models.py +++ b/zilencer/models.py @@ -1,6 +1,4 @@ -import datetime from typing import List, Tuple -from uuid import UUID from django.conf import settings from django.core.exceptions import ValidationError @@ -31,25 +29,25 @@ class RemoteZulipServer(models.Model): # The unique UUID (`zulip_org_id`) and API key (`zulip_org_key`) # for this remote server registration. - uuid: UUID = models.UUIDField(unique=True) - api_key: str = models.CharField(max_length=API_KEY_LENGTH) + uuid = models.UUIDField(unique=True) + api_key = models.CharField(max_length=API_KEY_LENGTH) # The hostname and contact details are not verified/trusted. Thus, # they primarily exist so that we can communicate with the # maintainer of a server about abuse problems. - hostname: str = models.CharField(max_length=HOSTNAME_MAX_LENGTH) - contact_email: str = models.EmailField(blank=True, null=False) - last_updated: datetime.datetime = models.DateTimeField("last updated", auto_now=True) + hostname = models.CharField(max_length=HOSTNAME_MAX_LENGTH) + contact_email = models.EmailField(blank=True, null=False) + last_updated = models.DateTimeField("last updated", auto_now=True) # Whether the server registration has been deactivated. - deactivated: bool = models.BooleanField(default=False) + deactivated = models.BooleanField(default=False) # Plan types for self-hosted customers PLAN_TYPE_SELF_HOSTED = 1 PLAN_TYPE_STANDARD = 102 # The current billing plan for the remote server, similar to Realm.plan_type. - plan_type: int = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_HOSTED) + plan_type = models.PositiveSmallIntegerField(default=PLAN_TYPE_SELF_HOSTED) def __str__(self) -> str: return f"" @@ -61,10 +59,10 @@ class RemoteZulipServer(models.Model): class RemotePushDeviceToken(AbstractPushDeviceToken): """Like PushDeviceToken, but for a device connected to a remote server.""" - server: RemoteZulipServer = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) + server = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) # The user id on the remote server for this device - user_id: int = models.BigIntegerField(null=True) - user_uuid: UUID = models.UUIDField(null=True) + user_id = models.BigIntegerField(null=True) + user_uuid = models.UUIDField(null=True) class Meta: unique_together = [ @@ -90,7 +88,7 @@ class RemoteZulipServerAuditLog(AbstractRealmAuditLog): authoritative storage location for the server's history. """ - server: RemoteZulipServer = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) + server = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) def __str__(self) -> str: return f"" @@ -101,19 +99,19 @@ class RemoteRealmAuditLog(AbstractRealmAuditLog): billing. See RealmAuditLog and AbstractRealmAuditLog for details. """ - server: RemoteZulipServer = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) - realm_id: int = models.IntegerField(db_index=True) + server = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) + realm_id = models.IntegerField(db_index=True) # The remote_id field lets us deduplicate data from the remote server - remote_id: int = models.IntegerField(db_index=True) + remote_id = models.IntegerField(db_index=True) def __str__(self) -> str: return f"" class RemoteInstallationCount(BaseCount): - server: RemoteZulipServer = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) + server = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) # The remote_id field lets us deduplicate data from the remote server - remote_id: int = models.IntegerField(db_index=True) + remote_id = models.IntegerField(db_index=True) class Meta: unique_together = ("server", "property", "subgroup", "end_time") @@ -127,10 +125,10 @@ class RemoteInstallationCount(BaseCount): # We can't subclass RealmCount because we only have a realm_id here, not a foreign key. class RemoteRealmCount(BaseCount): - server: RemoteZulipServer = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) - realm_id: int = models.IntegerField(db_index=True) + server = models.ForeignKey(RemoteZulipServer, on_delete=models.CASCADE) + realm_id = models.IntegerField(db_index=True) # The remote_id field lets us deduplicate data from the remote server - remote_id: int = models.IntegerField(db_index=True) + remote_id = models.IntegerField(db_index=True) class Meta: unique_together = ("server", "realm_id", "property", "subgroup", "end_time")