Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

requirements: Add django-stubs. #18777

Merged
merged 5 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 7 additions & 8 deletions analytics/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import datetime
from typing import Optional

from django.db import models
from django.db.models import Q, UniqueConstraint
Expand All @@ -9,13 +8,13 @@


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"<FillState: {self.property} {self.end_time} {self.state}>"
Expand All @@ -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
Expand Down
14 changes: 7 additions & 7 deletions confirmation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"<Confirmation: {self.content_object}>"
Expand Down Expand Up @@ -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
82 changes: 38 additions & 44 deletions corporate/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import datetime
from decimal import Decimal
from typing import Any, Dict, Optional, Union

from django.contrib.contenttypes.fields import GenericForeignKey
Expand All @@ -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:
Expand Down Expand Up @@ -96,20 +90,20 @@ 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
RETRY_UPGRADE_WITH_ANOTHER_PAYMENT_METHOD = 10
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]
Expand Down Expand Up @@ -142,8 +136,8 @@ def get_last_associated_event(self) -> Optional[Event]:


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
Expand All @@ -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
Expand Down Expand Up @@ -200,60 +194,60 @@ 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
INITIAL_INVOICE_TO_BE_SENT = 3
# 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
Expand All @@ -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.
Expand Down Expand Up @@ -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="")
8 changes: 6 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ warn_unreachable = true
# with this behavior.
local_partial_types = true

plugins = ["mypy_django_plugin.main"]

[[tool.mypy.overrides]]
module = ["zproject.configured_settings", "zproject.settings"]
module = ["zproject.configured_settings", "zproject.settings", "zproject.default_settings"]
no_implicit_reexport = false

[[tool.mypy.overrides]]
Expand All @@ -59,7 +61,6 @@ module = [
"defusedxml.*", # https://github.com/tiran/defusedxml/issues/46
"digitalocean.*",
"disposable_email_domains.*",
"django.*", # https://github.com/zulip/zulip/issues/11560
"django_auth_ldap.*",
"django_bmemcached.*",
"django_cte.*",
Expand Down Expand Up @@ -93,3 +94,6 @@ module = [
"two_factor.*",
]
ignore_missing_imports = true

[tool.django-stubs]
django_settings_module = "zproject.settings"
3 changes: 3 additions & 0 deletions requirements/common.in
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,6 @@ soupsieve

# Circuit-breaking for outgoing services
circuitbreaker

# Runtime monkeypatching of django-stubs generics
https://github.com/typeddjango/django-stubs/archive/9bd8aed1e19f9da2c7d3a2879367a40847b57dea.zip#egg=django-stubs-ext==0.5.0+git&subdirectory=django_stubs_ext
22 changes: 21 additions & 1 deletion requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,8 @@ django[argon2]==4.0.7 \
# django-phonenumber-field
# django-scim2
# django-sendfile2
# django-stubs
# django-stubs-ext
# django-two-factor-auth
django-auth-ldap==4.1.0 \
--hash=sha256:68870e7921e84b1a9867e268a9c8a3e573e8a0d95ea08bcf31be178f5826ff36 \
Expand Down Expand Up @@ -504,6 +506,14 @@ django-statsd-mozilla==0.4.0 \
--hash=sha256:0d87cb63de8107279cbb748caad9aa74c6a44e7e96ccc5dbf07b89f77285a4b8 \
--hash=sha256:81084f3d426f5184f0a0f1dbfe035cc26b66f041d2184559d916a228d856f0d3
# via -r requirements/common.in
https://github.com/typeddjango/django-stubs/archive/9bd8aed1e19f9da2c7d3a2879367a40847b57dea.zip#egg=django-stubs==1.12.0+git \
--hash=sha256:42340195b7e67a035f2399cf2718b0990ca40addf3ec2f0ac213887d7ef32c7b
# via -r requirements/mypy.in
https://github.com/typeddjango/django-stubs/archive/9bd8aed1e19f9da2c7d3a2879367a40847b57dea.zip#egg=django-stubs-ext==0.5.0+git&subdirectory=django_stubs_ext \
--hash=sha256:42340195b7e67a035f2399cf2718b0990ca40addf3ec2f0ac213887d7ef32c7b
# via
# -r requirements/common.in
# django-stubs
django-two-factor-auth[call,phonenumberslite,sms]==1.14.0 \
--hash=sha256:b414ef51cc84335e0049e3f98ef89ac15a09187efa381f3acd321651afae95b3 \
--hash=sha256:d18290f02766f400537b88937df06aa0a2d6d6d37937fbfb84896c4790a9e59b
Expand Down Expand Up @@ -1049,6 +1059,7 @@ mypy==0.971 \
--hash=sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c
# via
# -r requirements/mypy.in
# django-stubs
# sqlalchemy
mypy-boto3-s3==1.24.36.post1 \
--hash=sha256:30ae59b33c55f8b7b693170f9519ea5b91a2fbf31a73de79cdef57a27d784e5a \
Expand Down Expand Up @@ -2043,6 +2054,7 @@ tomli==2.0.1 \
--hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
# via
# black
# django-stubs
# mypy
# pep517
tornado==6.2 \
Expand Down Expand Up @@ -2156,10 +2168,16 @@ types-python-dateutil==2.8.19 \
--hash=sha256:6284df1e4783d8fc6e587f0317a81333856b872a6669a282f8a325342bce7fa8 \
--hash=sha256:bfd3eb39c7253aea4ba23b10f69b017d30b013662bb4be4ab48b20bbd763f309
# via -r requirements/mypy.in
types-pytz==2022.2.1.0 \
--hash=sha256:47cfb19c52b9f75896440541db392fd312a35b279c6307a531db71152ea63e2b \
--hash=sha256:50ead2254b524a3d4153bc65d00289b66898060d2938e586170dce918dbaf3b3
# via django-stubs
types-pyyaml==6.0.11 \
--hash=sha256:7f7da2fd11e9bc1e5e9eb3ea1be84f4849747017a59fc2eee0ea34ed1147c2e0 \
--hash=sha256:8f890028123607379c63550179ddaec4517dc751f4c527a52bb61934bf495989
# via -r requirements/mypy.in
# via
# -r requirements/mypy.in
# django-stubs
types-redis==4.3.19 \
--hash=sha256:c74262197487a65e3f02db37d3f0e0f3bb2d5d370f7e6ba390814e865a45e56e \
--hash=sha256:c881ffb94bd47dca21ed503328544a56bb315851575eebb27fb371bff735844a
Expand Down Expand Up @@ -2201,6 +2219,8 @@ typing-extensions==4.3.0 \
# -r requirements/common.in
# black
# boto3-stubs
# django-stubs
# django-stubs-ext
# libcst
# mypy
# mypy-boto3-s3
Expand Down
2 changes: 2 additions & 0 deletions requirements/mypy.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,5 @@ types-stripe
types-zxcvbn

importlib-metadata ; python_version < "3.10" # for SQLAlchemy

https://github.com/typeddjango/django-stubs/archive/9bd8aed1e19f9da2c7d3a2879367a40847b57dea.zip#egg=django-stubs==1.12.0+git