diff --git a/files/classes/__init__.py b/files/classes/__init__.py index 88f8ab9fc..94a356ec4 100644 --- a/files/classes/__init__.py +++ b/files/classes/__init__.py @@ -1,6 +1,7 @@ # load sqlalchemy's declarative base... -from sqlalchemy.ext.declarative import declarative_base -Base = declarative_base() +from sqlalchemy.orm import DeclarativeBase +class Base(DeclarativeBase): + pass # then load all of our classes :) from .alts import * diff --git a/files/classes/alts.py b/files/classes/alts.py index 2cff4b7c2..d70f76193 100644 --- a/files/classes/alts.py +++ b/files/classes/alts.py @@ -1,17 +1,19 @@ import time +from typing import Optional -from sqlalchemy import Column, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import user_id_fk_pk class Alt(Base): __tablename__ = "alts" - user1 = Column(Integer, ForeignKey("users.id"), primary_key=True) - user2 = Column(Integer, ForeignKey("users.id"), primary_key=True) - is_manual = Column(Boolean, default=False) - created_utc = Column(Integer) + user1: Mapped[user_id_fk_pk] + user2: Mapped[user_id_fk_pk] + is_manual: Mapped[bool] = mapped_column(default=False) + created_utc: Mapped[Optional[int]] def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/award.py b/files/classes/award.py index c7538fadd..440c3a3b9 100644 --- a/files/classes/award.py +++ b/files/classes/award.py @@ -1,30 +1,34 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.config.awards import AWARDS, HOUSE_AWARDS from files.helpers.lazy import lazy +from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk + +if TYPE_CHECKING: + from files.classes import Comment, Post, User class AwardRelationship(Base): __tablename__ = "award_relationships" - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey("users.id")) - post_id = Column(Integer, ForeignKey("posts.id")) - comment_id = Column(Integer, ForeignKey("comments.id")) - kind = Column(String) - awarded_utc = Column(Integer) - created_utc = Column(Integer) - price_paid = Column(Integer, default = 0) - note = Column(String) + id: Mapped[int_pk] + user_id: Mapped[user_id_fk] + post_id: Mapped[Optional[post_id_fk]] + comment_id: Mapped[Optional[comment_id_fk]] + kind: Mapped[str] + awarded_utc: Mapped[Optional[int]] + created_utc: Mapped[Optional[int]] + price_paid: Mapped[int] = mapped_column(default = 0) + note: Mapped[Optional[str]] - user = relationship("User", primaryjoin="AwardRelationship.user_id==User.id", back_populates="awards") - post = relationship("Post", primaryjoin="AwardRelationship.post_id==Post.id", back_populates="awards") - comment = relationship("Comment", primaryjoin="AwardRelationship.comment_id==Comment.id", back_populates="awards") + user: Mapped["User"] = relationship(primaryjoin="AwardRelationship.user_id==User.id", back_populates="awards") + post: Mapped["Post"] = relationship(primaryjoin="AwardRelationship.post_id==Post.id", back_populates="awards") + comment: Mapped["Comment"] = relationship(primaryjoin="AwardRelationship.comment_id==Comment.id", back_populates="awards") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/badges.py b/files/classes/badges.py index c6230c645..a755421a3 100644 --- a/files/classes/badges.py +++ b/files/classes/badges.py @@ -1,20 +1,26 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.config.const import * from files.helpers.lazy import lazy +from files.helpers.types import int_pk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class BadgeDef(Base): __tablename__ = "badge_defs" - id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(String) - description = Column(String) - created_utc = Column(Integer) + id: Mapped[int_pk] = mapped_column(autoincrement=True) + name: Mapped[str] + description: Mapped[Optional[str]] + created_utc: Mapped[int] def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -32,14 +38,14 @@ class Badge(Base): __tablename__ = "badges" - user_id = Column(Integer, ForeignKey('users.id'), primary_key=True) - badge_id = Column(Integer, ForeignKey('badge_defs.id'), primary_key=True) - description = Column(String) - url = Column(String) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + badge_id: Mapped[int] = mapped_column(ForeignKey('badge_defs.id'), primary_key=True) + description: Mapped[Optional[str]] + url: Mapped[Optional[str]] + created_utc: Mapped[int] - user = relationship("User", back_populates="badges") - badge = relationship("BadgeDef", primaryjoin="Badge.badge_id == BadgeDef.id", lazy="joined", innerjoin=True) + user: Mapped["User"] = relationship(back_populates="badges") + badge: Mapped["BadgeDef"] = relationship(primaryjoin="Badge.badge_id == BadgeDef.id", lazy="joined", innerjoin=True) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: diff --git a/files/classes/casino_game.py b/files/classes/casino_game.py index 8864204ea..9d5233619 100644 --- a/files/classes/casino_game.py +++ b/files/classes/casino_game.py @@ -1,29 +1,33 @@ import json import time +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.lazy import lazy +from files.helpers.types import int_pk, user_id_fk + +if TYPE_CHECKING: + from files.classes import User CASINO_GAME_KINDS = ['blackjack', 'slots', 'roulette'] class CasinoGame(Base): __tablename__ = "casino_games" - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey("users.id")) - created_utc = Column(Integer) - active = Column(Boolean, default=True) - currency = Column(String) - wager = Column(Integer) - winnings = Column(Integer) - kind = Column(String) - game_state = Column(JSON) + id: Mapped[int_pk] + user_id: Mapped[user_id_fk] + created_utc: Mapped[int] + active: Mapped[bool] = mapped_column(default=True) + currency: Mapped[str] + wager: Mapped[int] + winnings: Mapped[int] + kind: Mapped[str] + game_state: Mapped[str] = mapped_column(JSON) - user = relationship("User") + user: Mapped["User"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: diff --git a/files/classes/clients.py b/files/classes/clients.py index f4bebecfb..a7ccac0f7 100644 --- a/files/classes/clients.py +++ b/files/classes/clients.py @@ -1,29 +1,35 @@ import time +from typing import Annotated, Optional, TYPE_CHECKING from flask import g -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship, load_only +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, load_only, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.config.const import * from files.helpers.lazy import lazy +from files.helpers.types import int_pk, user_id_fk, user_id_fk_pk from .comment import Comment from .post import Post +if TYPE_CHECKING: + from files.classes import User + + class OauthApp(Base): __tablename__ = "oauth_apps" - id = Column(Integer, primary_key=True) - client_id = Column(String) - app_name = Column(String) - redirect_uri = Column(String) - description = Column(String) - author_id = Column(Integer, ForeignKey("users.id")) - created_utc = Column(Integer) + id: Mapped[int_pk] + client_id: Mapped[Optional[Annotated[str, 64]]] + app_name: Mapped[str] + redirect_uri: Mapped[str] + description: Mapped[str] + author_id: Mapped[user_id_fk] + created_utc: Mapped[Optional[int]] - author = relationship("User", back_populates="apps") + author: Mapped["User"] = relationship(back_populates="apps") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -51,13 +57,13 @@ class OauthApp(Base): class ClientAuth(Base): __tablename__ = "client_auths" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - oauth_client = Column(Integer, ForeignKey("oauth_apps.id"), primary_key=True) - access_token = Column(String) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + oauth_client: Mapped[int] = mapped_column(ForeignKey("oauth_apps.id"), primary_key=True) + access_token: Mapped[str] + created_utc: Mapped[Optional[int]] - user = relationship("User") - application = relationship("OauthApp") + user: Mapped["User"] = relationship() + application: Mapped["OauthApp"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/comment.py b/files/classes/comment.py index 18b775815..ac2a7e785 100644 --- a/files/classes/comment.py +++ b/files/classes/comment.py @@ -1,12 +1,13 @@ import time from math import floor from random import randint +from typing import Optional, TYPE_CHECKING from urllib.parse import parse_qs, urlencode, urlparse from flask import g -from sqlalchemy import Column, ForeignKey +from sqlalchemy import ForeignKey from sqlalchemy.dialects.postgresql import TSVECTOR -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.schema import FetchedValue from sqlalchemy.sql.sqltypes import * @@ -17,10 +18,14 @@ from files.helpers.slurs_and_profanities import * from files.helpers.lazy import lazy from files.helpers.regex import * from files.helpers.sorting_and_time import * +from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk from files.helpers.bleach_body import * from .saves import CommentSaveRelationship +if TYPE_CHECKING: + from files.classes import AwardRelationship, CasinoGame, CommentOption, CommentReport, OauthApp, Post, User + def get_emoji_awards_emojis(obj, v, kind, NSFW_EMOJIS): if g.show_nsfw: emojis = [x.note for x in obj.awards if x.kind == kind] @@ -172,60 +177,60 @@ def add_options(self, body, v): class Comment(Base): __tablename__ = "comments" - id = Column(Integer, primary_key=True) - author_id = Column(Integer, ForeignKey("users.id")) - parent_post = Column(Integer, ForeignKey("posts.id")) - wall_user_id = Column(Integer, ForeignKey("users.id")) - created_utc = Column(Integer) - edited_utc = Column(Integer, default=0) - is_banned = Column(Boolean, default=False) - ghost = Column(Boolean, default=False) - bannedfor = Column(String) - chuddedfor = Column(String) - distinguished = Column(Boolean, default=False) - deleted_utc = Column(Integer, default=0) - is_approved = Column(Integer, ForeignKey("users.id")) - level = Column(Integer, default=1) - parent_comment_id = Column(Integer, ForeignKey("comments.id")) - top_comment_id = Column(Integer) - is_bot = Column(Boolean, default=False) - stickied = Column(String) - stickied_utc = Column(Integer) - num_of_pinned_children = Column(Integer, default=0) - sentto = Column(Integer, ForeignKey("users.id")) - app_id = Column(Integer, ForeignKey("oauth_apps.id")) - upvotes = Column(Integer, default=1) - downvotes = Column(Integer, default=0) - realupvotes = Column(Integer, default=1) - body = Column(String) - body_html = Column(String) - body_ts = Column(TSVECTOR(), server_default=FetchedValue()) - ban_reason = Column(String) - treasure_amount = Column(String) - slots_result = Column(String) - ping_cost = Column(Integer, default=0) - blackjack_result = Column(String) - casino_game_id = Column(Integer, ForeignKey("casino_games.id")) - chudded = Column(Boolean, default=False) - rainbowed = Column(Boolean, default=False) - queened = Column(Boolean, default=False) - sharpened = Column(Boolean, default=False) + id: Mapped[int_pk] + author_id: Mapped[user_id_fk] + parent_post: Mapped[Optional[post_id_fk]] + wall_user_id: Mapped[Optional[user_id_fk]] + created_utc: Mapped[int] + edited_utc: Mapped[int] = mapped_column(default=0) + is_banned: Mapped[bool] = mapped_column(default=False) + ghost: Mapped[bool] = mapped_column(default=False) + bannedfor: Mapped[Optional[str]] + chuddedfor: Mapped[Optional[str]] + distinguished: Mapped[bool] = mapped_column(default=False) + deleted_utc: Mapped[int] = mapped_column(default=0) + is_approved: Mapped[Optional[user_id_fk]] + level: Mapped[int] = mapped_column(default=1) + parent_comment_id: Mapped[Optional[comment_id_fk]] + top_comment_id: Mapped[Optional[int]] + is_bot: Mapped[bool] = mapped_column(default=False) + stickied: Mapped[Optional[str]] + stickied_utc: Mapped[Optional[int]] + num_of_pinned_children: Mapped[int] = mapped_column(default=0) + sentto: Mapped[Optional[user_id_fk]] + app_id: Mapped[Optional[int]] = mapped_column(ForeignKey("oauth_apps.id")) + upvotes: Mapped[int] = mapped_column(default=1) + downvotes: Mapped[int] = mapped_column(default=0) + realupvotes: Mapped[int] = mapped_column(default=1) + body: Mapped[Optional[str]] + body_html: Mapped[Optional[str]] + body_ts: Mapped[str] = mapped_column(TSVECTOR(), server_default=FetchedValue()) + ban_reason: Mapped[Optional[str]] + treasure_amount: Mapped[Optional[str]] + slots_result: Mapped[Optional[str]] + ping_cost: Mapped[int] = mapped_column(default=0) + blackjack_result: Mapped[Optional[str]] + casino_game_id: Mapped[Optional[int]] = mapped_column(ForeignKey("casino_games.id")) + chudded: Mapped[bool] = mapped_column(default=False) + rainbowed: Mapped[bool] = mapped_column(default=False) + queened: Mapped[bool] = mapped_column(default=False) + sharpened: Mapped[bool] = mapped_column(default=False) if FEATURES['NSFW_MARKING']: - nsfw = Column(Boolean, default=False) + nsfw: Mapped[bool] = mapped_column(default=False) else: nsfw = False - oauth_app = relationship("OauthApp") - post = relationship("Post", back_populates="comments") - author = relationship("User", primaryjoin="User.id==Comment.author_id") - senttouser = relationship("User", primaryjoin="User.id==Comment.sentto") - parent_comment = relationship("Comment", remote_side=[id]) - awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", back_populates="comment") - reports = relationship("CommentReport", order_by="CommentReport.created_utc") - options = relationship("CommentOption", order_by="CommentOption.id") - casino_game = relationship("CasinoGame") - wall_user = relationship("User", primaryjoin="User.id==Comment.wall_user_id") + oauth_app: Mapped["OauthApp"] = relationship() + post: Mapped["Post"] = relationship(back_populates="comments") + author: Mapped["User"] = relationship(primaryjoin="User.id==Comment.author_id") + senttouser: Mapped["User"] = relationship(primaryjoin="User.id==Comment.sentto") + parent_comment: Mapped["Comment"] = relationship(remote_side="Comment.id") + awards: Mapped[list["AwardRelationship"]] = relationship(order_by="AwardRelationship.awarded_utc.desc()", back_populates="comment") + reports: Mapped[list["CommentReport"]] = relationship(order_by="CommentReport.created_utc") + options: Mapped[list["CommentOption"]] = relationship(order_by="CommentOption.id") + casino_game: Mapped["CasinoGame"] = relationship() + wall_user: Mapped["User"] = relationship(primaryjoin="User.id==Comment.wall_user_id") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: diff --git a/files/classes/domains.py b/files/classes/domains.py index 4c6bf820c..af586a55d 100644 --- a/files/classes/domains.py +++ b/files/classes/domains.py @@ -1,15 +1,17 @@ import time +from typing import Optional -from sqlalchemy import Column +from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import str_pk class BannedDomain(Base): __tablename__ = "banneddomains" - domain = Column(String, primary_key=True) - reason = Column(String) - created_utc = Column(Integer) + domain: Mapped[str_pk] + reason: Mapped[str] + created_utc: Mapped[Optional[int]] def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/emoji.py b/files/classes/emoji.py index 1b43d5719..04f95e40c 100644 --- a/files/classes/emoji.py +++ b/files/classes/emoji.py @@ -1,21 +1,23 @@ import time +from typing import Optional -from sqlalchemy import Column, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import str_pk, user_id_fk class Emoji(Base): __tablename__ = "emojis" - name = Column(String, primary_key=True) - kind = Column(String) - author_id = Column(Integer, ForeignKey("users.id")) - tags = Column(String) - count = Column(Integer, default=0) - submitter_id = Column(Integer, ForeignKey("users.id")) - created_utc = Column(Integer) - nsfw = Column(Boolean, default=False) + name: Mapped[str_pk] + kind: Mapped[str] + author_id: Mapped[user_id_fk] + tags: Mapped[str] + count: Mapped[int] = mapped_column(default=0) + submitter_id: Mapped[Optional[user_id_fk]] + created_utc: Mapped[int] + nsfw: Mapped[bool] = mapped_column(default=False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/follows.py b/files/classes/follows.py index 67e253eec..ba9b6169d 100644 --- a/files/classes/follows.py +++ b/files/classes/follows.py @@ -1,19 +1,24 @@ import time +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class Follow(Base): __tablename__ = "follows" - target_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - created_utc = Column(Integer) + target_id: Mapped[user_id_fk_pk] + user_id: Mapped[user_id_fk_pk] + created_utc: Mapped[int] - user = relationship("User", uselist=False, primaryjoin="User.id==Follow.user_id", back_populates="following") - target = relationship("User", uselist=False, primaryjoin="User.id==Follow.target_id", back_populates="followers") + user: Mapped["User"] = relationship(uselist=False, primaryjoin="User.id==Follow.user_id", back_populates="following") + target: Mapped["User"] = relationship(uselist=False, primaryjoin="User.id==Follow.target_id", back_populates="followers") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/group.py b/files/classes/group.py index 525e607f2..a7ea4f3c3 100644 --- a/files/classes/group.py +++ b/files/classes/group.py @@ -1,25 +1,29 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column -from sqlalchemy.orm import relationship -from sqlalchemy.types import Integer +from sqlalchemy.orm import Mapped, relationship from files.classes import Base from files.helpers.lazy import lazy from files.helpers.config.const import * +from files.helpers.types import str_pk, user_id_fk from .group_membership import * +if TYPE_CHECKING: + from files.classes import User + + class Group(Base): __tablename__ = "groups" - name = Column(String, primary_key=True) - created_utc = Column(Integer) - owner_id = Column(Integer, ForeignKey("users.id")) - description = Column(String) - description_html = Column(String) + name: Mapped[str_pk] + created_utc: Mapped[int] + owner_id: Mapped[Optional[user_id_fk]] + description: Mapped[Optional[str]] + description_html: Mapped[Optional[str]] - memberships = relationship("GroupMembership", primaryjoin="GroupMembership.group_name==Group.name", order_by="GroupMembership.approved_utc") - owner = relationship("User", primaryjoin="Group.owner_id==User.id") + memberships: Mapped[list["GroupMembership"]] = relationship(primaryjoin="GroupMembership.group_name==Group.name", order_by="GroupMembership.approved_utc") + owner: Mapped["User"] = relationship(primaryjoin="Group.owner_id==User.id") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/group_membership.py b/files/classes/group_membership.py index e0adf493d..c50b74eae 100644 --- a/files/classes/group_membership.py +++ b/files/classes/group_membership.py @@ -1,20 +1,25 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship -from sqlalchemy.types import Integer, String, Boolean +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship from files.classes import Base +from files.helpers.types import user_id_fk_pk + +if TYPE_CHECKING: + from files.classes.user import User + class GroupMembership(Base): __tablename__ = "group_memberships" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - group_name = Column(String, ForeignKey("groups.name"), primary_key=True) - created_utc = Column(Integer) - approved_utc = Column(Integer) - is_mod = Column(Boolean, default=False) + user_id: Mapped[user_id_fk_pk] + group_name: Mapped[str] = mapped_column(ForeignKey("groups.name"), primary_key=True) + created_utc: Mapped[int] + approved_utc: Mapped[Optional[int]] + is_mod: Mapped[bool] = mapped_column(default=False) - user = relationship("User", uselist=False) + user: Mapped["User"] = relationship(uselist=False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/hats.py b/files/classes/hats.py index 746ffc73f..a24e3c556 100644 --- a/files/classes/hats.py +++ b/files/classes/hats.py @@ -1,27 +1,33 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from flask import g from files.classes import Base from files.helpers.lazy import lazy from files.helpers.slurs_and_profanities import censor_slurs_profanities +from files.helpers.types import int_pk, user_id_fk + +if TYPE_CHECKING: + from files.classes import User + class HatDef(Base): __tablename__ = "hat_defs" - id = Column(Integer, primary_key=True) - name = Column(String) - description = Column(String) - author_id = Column(Integer, ForeignKey('users.id')) - price = Column(Integer) - submitter_id = Column(Integer, ForeignKey("users.id")) - created_utc = Column(Integer) + id: Mapped[int_pk] + name: Mapped[str] + description: Mapped[str] + author_id: Mapped[int] = mapped_column(ForeignKey('users.id')) + price: Mapped[int] + submitter_id: Mapped[Optional[user_id_fk]] + created_utc: Mapped[int] - author = relationship("User", primaryjoin="HatDef.author_id == User.id", back_populates="designed_hats") - submitter = relationship("User", primaryjoin="HatDef.submitter_id == User.id") + author: Mapped["User"] = relationship(primaryjoin="HatDef.author_id == User.id", back_populates="designed_hats") + submitter: Mapped["User"] = relationship(primaryjoin="HatDef.submitter_id == User.id") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -48,13 +54,13 @@ class HatDef(Base): class Hat(Base): __tablename__ = "hats" - user_id = Column(Integer, ForeignKey('users.id'), primary_key=True) - hat_id = Column(Integer, ForeignKey('hat_defs.id'), primary_key=True) - equipped = Column(Boolean, default=False) - created_utc = Column(Integer) + user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), primary_key=True) + hat_id: Mapped[int] = mapped_column(ForeignKey('hat_defs.id'), primary_key=True) + equipped: Mapped[Optional[bool]] = mapped_column(default=False) + created_utc: Mapped[Optional[int]] - hat_def = relationship("HatDef") - owners = relationship("User", back_populates="owned_hats") + hat_def: Mapped["HatDef"] = relationship() + owners: Mapped[list["User"]] = relationship(back_populates="owned_hats") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/hole.py b/files/classes/hole.py index b4cc4200c..aade37b6b 100644 --- a/files/classes/hole.py +++ b/files/classes/hole.py @@ -1,10 +1,9 @@ import random import time +from typing import Annotated, Optional -from sqlalchemy import Column from sqlalchemy.ext.mutable import MutableList -from sqlalchemy.orm import relationship, deferred -from sqlalchemy.types import * +from sqlalchemy.orm import DynamicMapped, Mapped, mapped_column, relationship from sqlalchemy.dialects.postgresql import ARRAY from files.classes import Base @@ -15,24 +14,24 @@ from .hole_relationship import * class Hole(Base): __tablename__ = "holes" - name = Column(String, primary_key=True) - sidebar = Column(String) - sidebar_html = Column(String) - sidebarurls = Column(MutableList.as_mutable(ARRAY(String)), default=MutableList([])) - bannerurls = Column(MutableList.as_mutable(ARRAY(String)), default=MutableList([])) - marseyurl = Column(String) - css = deferred(Column(String)) - stealth = Column(Boolean, default=False) - public_use = Column(Boolean, default=False) - created_utc = Column(Integer) + name: Mapped[str] = mapped_column(primary_key=True) + sidebar: Mapped[Optional[str]] + sidebar_html: Mapped[Optional[str]] + sidebarurls: Mapped[list[str]] = mapped_column(MutableList.as_mutable(ARRAY(String)), default=MutableList([])) + bannerurls: Mapped[list[str]] = mapped_column(MutableList.as_mutable(ARRAY(String)), default=MutableList([])) + marseyurl: Mapped[Optional[str]] + css: Mapped[Optional[str]] = mapped_column(deferred=True) + stealth: Mapped[Optional[bool]] = mapped_column(default=False) + public_use: Mapped[bool] = mapped_column(default=False) + created_utc: Mapped[Optional[int]] if SITE_NAME == 'WPD': snappy_quotes = None else: - snappy_quotes = deferred(Column(String)) + snappy_quotes: Mapped[Optional[Annotated[str, HOLE_SNAPPY_QUOTES_LENGTH]]] = mapped_column(deferred=True) - blocks = relationship("HoleBlock", primaryjoin="HoleBlock.hole==Hole.name") - followers = relationship("HoleFollow", primaryjoin="HoleFollow.hole==Hole.name") - stealth_hole_unblocks = relationship("StealthHoleUnblock", lazy="dynamic", primaryjoin="StealthHoleUnblock.hole==Hole.name") + blocks: Mapped[list["HoleBlock"]] = relationship(primaryjoin="HoleBlock.hole==Hole.name") + followers: Mapped[list["HoleFollow"]] = relationship(primaryjoin="HoleFollow.hole==Hole.name") + stealth_hole_unblocks: DynamicMapped["StealthHoleUnblock"] = relationship(primaryjoin="StealthHoleUnblock.hole==Hole.name") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/hole_logs.py b/files/classes/hole_logs.py index aa45dc484..a2e6957ac 100644 --- a/files/classes/hole_logs.py +++ b/files/classes/hole_logs.py @@ -1,7 +1,8 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from flask import g @@ -10,23 +11,28 @@ from files.helpers.config.const import * from files.helpers.lazy import lazy from files.helpers.slurs_and_profanities import censor_slurs_profanities from files.helpers.sorting_and_time import make_age_string +from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk + +if TYPE_CHECKING: + from files.classes import Comment, Post, User + class HoleAction(Base): __tablename__ = "hole_actions" - id = Column(Integer, primary_key=True) - hole = Column(String, ForeignKey("holes.name")) - user_id = Column(Integer, ForeignKey("users.id")) - kind = Column(String) - target_user_id = Column(Integer, ForeignKey("users.id")) - target_post_id = Column(Integer, ForeignKey("posts.id")) - target_comment_id = Column(Integer, ForeignKey("comments.id")) - _note = Column(String) - created_utc = Column(Integer) + id: Mapped[int_pk] + hole: Mapped[str] = mapped_column(ForeignKey("holes.name")) + user_id: Mapped[Optional[user_id_fk]] + kind: Mapped[Optional[str]] + target_user_id: Mapped[Optional[user_id_fk]] + target_post_id: Mapped[Optional[post_id_fk]] + target_comment_id: Mapped[Optional[comment_id_fk]] + _note: Mapped[Optional[str]] + created_utc: Mapped[int] - user = relationship("User", primaryjoin="User.id==HoleAction.user_id") - target_user = relationship("User", primaryjoin="User.id==HoleAction.target_user_id") - target_post = relationship("Post") - target_comment = relationship("Comment") + user: Mapped["User"] = relationship(primaryjoin="User.id==HoleAction.user_id") + target_user: Mapped["User"] = relationship(primaryjoin="User.id==HoleAction.target_user_id") + target_post: Mapped["Post"] = relationship() + target_comment: Mapped["Comment"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/hole_relationship.py b/files/classes/hole_relationship.py index a231ad862..89a9b6971 100644 --- a/files/classes/hole_relationship.py +++ b/files/classes/hole_relationship.py @@ -1,18 +1,23 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, relationship, mapped_column from sqlalchemy.sql.sqltypes import * from files.classes import Base +if TYPE_CHECKING: + from files.classes import User + + class HoleRelationship(Base): __tablename__ = NotImplemented __abstract__ = True - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - hole = Column(String, ForeignKey("holes.name"), primary_key=True) - created_utc = Column(Integer) + user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), primary_key=True) + hole: Mapped[str] = mapped_column(ForeignKey("holes.name"), primary_key=True) + created_utc: Mapped[Optional[int]] def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -35,5 +40,5 @@ class Mod(HoleRelationship): class Exile(HoleRelationship): __tablename__ = "exiles" - exiler_id = Column(Integer, ForeignKey("users.id")) - exiler = relationship("User", primaryjoin="User.id==Exile.exiler_id") + exiler_id: Mapped[int] = mapped_column(ForeignKey("users.id")) + exiler: Mapped["User"] = relationship(primaryjoin="User.id==Exile.exiler_id") diff --git a/files/classes/ip_logs.py b/files/classes/ip_logs.py index 3d6d3db37..a37af0018 100644 --- a/files/classes/ip_logs.py +++ b/files/classes/ip_logs.py @@ -1,19 +1,24 @@ import time +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import str_pk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class IPLog(Base): __tablename__ = "ip_logs" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - ip = Column(String, primary_key=True) - created_utc = Column(Integer) - last_used = Column(Integer) + user_id: Mapped[user_id_fk_pk] + ip: Mapped[str_pk] + created_utc: Mapped[int] + last_used: Mapped[int] - user = relationship("User") + user: Mapped["User"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: diff --git a/files/classes/lottery.py b/files/classes/lottery.py index abde47b0b..0e1850f9d 100644 --- a/files/classes/lottery.py +++ b/files/classes/lottery.py @@ -1,22 +1,24 @@ import time +from typing import Optional -from sqlalchemy import Column, ForeignKey +from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.config.const import * from files.helpers.lazy import lazy +from files.helpers.types import int_pk, user_id_fk class Lottery(Base): __tablename__ = "lotteries" - id = Column(Integer, primary_key=True) - is_active = Column(Boolean, default=False) - ends_at = Column(Integer) - prize = Column(Integer, default=0) - tickets_sold = Column(Integer, default=0) - winner_id = Column(Integer, ForeignKey("users.id")) - created_utc = Column(Integer) + id: Mapped[int_pk] + is_active: Mapped[bool] = mapped_column(default=False) + ends_at: Mapped[int] + prize: Mapped[int] = mapped_column(default=0) + tickets_sold: Mapped[int] = mapped_column(default=0) + winner_id: Mapped[Optional[user_id_fk]] + created_utc: Mapped[Optional[int]] def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/media.py b/files/classes/media.py index 574c0a3c4..939a4a3b4 100644 --- a/files/classes/media.py +++ b/files/classes/media.py @@ -1,16 +1,17 @@ import time -from sqlalchemy import Column, ForeignKey +from sqlalchemy.orm import Mapped from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import str_pk, user_id_fk class Media(Base): __tablename__ = "media" - kind = Column(String, primary_key=True) - filename = Column(String, primary_key=True) - user_id = Column(Integer, ForeignKey("users.id")) - created_utc = Column(Integer) - size = Column(Integer) + kind: Mapped[str_pk] + filename: Mapped[str_pk] + user_id: Mapped[user_id_fk] + created_utc: Mapped[int] + size: Mapped[int] def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/mod_logs.py b/files/classes/mod_logs.py index 94a009ad3..fefe904c3 100644 --- a/files/classes/mod_logs.py +++ b/files/classes/mod_logs.py @@ -1,7 +1,7 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from flask import g @@ -10,22 +10,27 @@ from files.helpers.config.const import * from files.helpers.lazy import lazy from files.helpers.slurs_and_profanities import censor_slurs_profanities from files.helpers.sorting_and_time import make_age_string +from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk + +if TYPE_CHECKING: + from files.classes import Comment, Post, User + class ModAction(Base): __tablename__ = "modactions" - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey("users.id")) - kind = Column(String) - target_user_id = Column(Integer, ForeignKey("users.id")) - target_post_id = Column(Integer, ForeignKey("posts.id")) - target_comment_id = Column(Integer, ForeignKey("comments.id")) - _note = Column(String) - created_utc = Column(Integer) + id: Mapped[int_pk] + user_id: Mapped[Optional[user_id_fk]] + kind: Mapped[Optional[str]] + target_user_id: Mapped[Optional[user_id_fk]] + target_post_id: Mapped[Optional[post_id_fk]] + target_comment_id: Mapped[Optional[comment_id_fk]] + _note: Mapped[Optional[str]] + created_utc: Mapped[int] - user = relationship("User", primaryjoin="User.id==ModAction.user_id") - target_user = relationship("User", primaryjoin="User.id==ModAction.target_user_id") - target_post = relationship("Post") - target_comment = relationship("Comment") + user: Mapped["User"] = relationship(primaryjoin="User.id==ModAction.user_id") + target_user: Mapped["User"] = relationship(primaryjoin="User.id==ModAction.target_user_id") + target_post: Mapped["Post"] = relationship() + target_comment: Mapped["Comment"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/notifications.py b/files/classes/notifications.py index 5ad09c049..5986b106a 100644 --- a/files/classes/notifications.py +++ b/files/classes/notifications.py @@ -1,21 +1,26 @@ import time +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import comment_id_fk_pk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import Comment, User + class Notification(Base): __tablename__ = "notifications" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True) - read = Column(Boolean, default=False) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + comment_id: Mapped[comment_id_fk_pk] + read: Mapped[bool] = mapped_column(default=False) + created_utc: Mapped[int] - comment = relationship("Comment") - user = relationship("User") + comment: Mapped["Comment"] = relationship() + user: Mapped["User"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/orgy.py b/files/classes/orgy.py index 80bee5d2f..6f6b795c5 100644 --- a/files/classes/orgy.py +++ b/files/classes/orgy.py @@ -1,11 +1,13 @@ import time from flask import g, abort import requests +from typing import Optional -from sqlalchemy import Column, or_ +from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import int_pk from files.helpers.lazy import lazy from files.helpers.config.const import * @@ -13,13 +15,13 @@ from files.helpers.config.const import * class Orgy(Base): __tablename__ = "orgies" - created_utc = Column(Integer, primary_key=True) - type = Column(String) - data = Column(String) - title = Column(String) - start_utc = Column(Integer) - end_utc = Column(Integer) - started = Column(Boolean, default=False) + created_utc: Mapped[int_pk] + type: Mapped[str] + data: Mapped[str] + title: Mapped[str] + start_utc: Mapped[int] + end_utc: Mapped[Optional[int]] + started: Mapped[bool] = mapped_column(default=False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/polls.py b/files/classes/polls.py index 3b7923e1e..a5636f3c5 100644 --- a/files/classes/polls.py +++ b/files/classes/polls.py @@ -1,23 +1,29 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.lazy import lazy +from files.helpers.types import comment_id_fk, int_pk, post_id_fk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import Comment, Post, User + class PostOption(Base): __tablename__ = "post_options" - id = Column(Integer, primary_key=True) - parent_id = Column(Integer, ForeignKey("posts.id")) - body_html = Column(Text) - exclusive = Column(Integer) - created_utc = Column(Integer) + id: Mapped[int_pk] + parent_id: Mapped[post_id_fk] + body_html: Mapped[str] = mapped_column(Text) + exclusive: Mapped[int] + created_utc: Mapped[Optional[int]] - votes = relationship("PostOptionVote") - parent = relationship("Post", back_populates="options") + votes: Mapped[list["PostOptionVote"]] = relationship() + parent: Mapped["Post"] = relationship(back_populates="options") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -40,12 +46,12 @@ class PostOptionVote(Base): __tablename__ = "post_option_votes" - option_id = Column(Integer, ForeignKey("post_options.id"), primary_key=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - created_utc = Column(Integer) - post_id = Column(Integer, ForeignKey("posts.id")) + option_id: Mapped[int] = mapped_column(ForeignKey("post_options.id"), primary_key=True) + user_id: Mapped[user_id_fk_pk] + created_utc: Mapped[int] + post_id: Mapped[Optional[post_id_fk]] - user = relationship("User") + user: Mapped["User"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -59,14 +65,14 @@ class CommentOption(Base): __tablename__ = "comment_options" - id = Column(Integer, primary_key=True) - parent_id = Column(Integer, ForeignKey("comments.id")) - body_html = Column(Text) - exclusive = Column(Integer) - created_utc = Column(Integer) + id: Mapped[int_pk] + parent_id: Mapped[comment_id_fk] + body_html: Mapped[str] = mapped_column(Text) + exclusive: Mapped[int] + created_utc: Mapped[Optional[int]] - votes = relationship("CommentOptionVote") - parent = relationship("Comment", back_populates="options") + votes: Mapped[list["CommentOptionVote"]] = relationship() + parent: Mapped["Comment"] = relationship(back_populates="options") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -89,12 +95,12 @@ class CommentOptionVote(Base): __tablename__ = "comment_option_votes" - option_id = Column(Integer, ForeignKey("comment_options.id"), primary_key=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - created_utc = Column(Integer) - comment_id = Column(Integer, ForeignKey("comments.id")) + option_id: Mapped[int] = mapped_column(ForeignKey("comment_options.id"), primary_key=True) + user_id: Mapped[user_id_fk_pk] + created_utc: Mapped[int] + comment_id: Mapped[Optional[comment_id_fk]] - user = relationship("User") + user: Mapped["User"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/post.py b/files/classes/post.py index 5fc1b5a6d..dd09ed11b 100644 --- a/files/classes/post.py +++ b/files/classes/post.py @@ -1,10 +1,11 @@ import random import time +from typing import Optional, TYPE_CHECKING from urllib.parse import urlparse from flask import g -from sqlalchemy import Column, FetchedValue, ForeignKey -from sqlalchemy.orm import deferred, relationship +from sqlalchemy import ForeignKey +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base @@ -14,6 +15,7 @@ from files.helpers.slurs_and_profanities import * from files.helpers.lazy import lazy from files.helpers.regex import * from files.helpers.sorting_and_time import make_age_string +from files.helpers.types import int_pk, user_id_fk from files.helpers.bleach_body import * from .comment import * @@ -22,69 +24,73 @@ from .hole import * from .subscriptions import * from .saves import SaveRelationship +if TYPE_CHECKING: + from files.classes import OauthApp, Report + + class Post(Base): __tablename__ = "posts" - id = Column(Integer, primary_key=True) - author_id = Column(Integer, ForeignKey("users.id")) - edited_utc = Column(Integer, default=0) - created_utc = Column(Integer) - thumburl = Column(String) - posterurl = Column(String) - is_banned = Column(Boolean, default=False) - bannedfor = Column(String) - chuddedfor = Column(String) - ghost = Column(Boolean, default=False) - effortpost = Column(Boolean, default=False) - views = Column(Integer, default=0) - deleted_utc = Column(Integer, default=0) - distinguished = Column(Boolean, default=False) - stickied = Column(String) - stickied_utc = Column(Integer) - hole_pinned = Column(String) - hole = Column(String, ForeignKey("holes.name")) - is_pinned = Column(Boolean, default=False) - private = Column(Boolean, default=False) - comment_count = Column(Integer, default=0) - is_approved = Column(Integer, ForeignKey("users.id")) - is_bot = Column(Boolean, default=False) - upvotes = Column(Integer, default=1) - downvotes = Column(Integer, default=0) - realupvotes = Column(Integer, default=1) - app_id = Column(Integer, ForeignKey("oauth_apps.id")) - title = Column(String) - title_html = Column(String) - url = Column(String) - body = Column(String) - body_html = Column(String) - flair = Column(String) - ban_reason = Column(String) - embed = Column(String) - new = Column(Boolean) - notify = Column(Boolean) - chudded = Column(Boolean, default=False) - rainbowed = Column(Boolean, default=False) - queened = Column(Boolean, default=False) - sharpened = Column(Boolean, default=False) - ping_cost = Column(Integer, default=0) - bump_utc = Column(Integer) + id: Mapped[int_pk] + author_id: Mapped[user_id_fk] + edited_utc: Mapped[int] = mapped_column(default=0) + created_utc: Mapped[int] + thumburl: Mapped[Optional[str]] + posterurl: Mapped[Optional[str]] + is_banned: Mapped[bool] = mapped_column(default=False) + bannedfor: Mapped[Optional[str]] + chuddedfor: Mapped[Optional[str]] + ghost: Mapped[bool] = mapped_column(default=False) + effortpost: Mapped[bool] = mapped_column(default=False) + views: Mapped[int] = mapped_column(default=0) + deleted_utc: Mapped[int] = mapped_column(default=0) + distinguished: Mapped[bool] = mapped_column(default=False) + stickied: Mapped[Optional[str]] + stickied_utc: Mapped[Optional[int]] + hole_pinned: Mapped[Optional[str]] + hole: Mapped[Optional[str]] = mapped_column(ForeignKey("holes.name")) + is_pinned: Mapped[bool] = mapped_column(default=False) + private: Mapped[bool] = mapped_column(default=False) + comment_count: Mapped[int] = mapped_column(default=0) + is_approved: Mapped[Optional[user_id_fk]] + is_bot: Mapped[bool] = mapped_column(default=False) + upvotes: Mapped[int] = mapped_column(default=1) + downvotes: Mapped[int] = mapped_column(default=0) + realupvotes: Mapped[Optional[int]] = mapped_column(default=1) + app_id: Mapped[Optional[int]] = mapped_column(ForeignKey("oauth_apps.id")) + title: Mapped[str] + title_html: Mapped[str] + url: Mapped[Optional[str]] + body: Mapped[Optional[str]] + body_html: Mapped[Optional[str]] + flair: Mapped[Optional[str]] + ban_reason: Mapped[Optional[str]] + embed: Mapped[Optional[str]] + new: Mapped[Optional[bool]] + notify: Mapped[bool] + chudded: Mapped[bool] = mapped_column(default=False) + rainbowed: Mapped[bool] = mapped_column(default=False) + queened: Mapped[bool] = mapped_column(default=False) + sharpened: Mapped[bool] = mapped_column(default=False) + ping_cost: Mapped[int] = mapped_column(default=0) + bump_utc: Mapped[int] if FEATURES['NSFW_MARKING']: - nsfw = Column(Boolean, default=False) + nsfw: Mapped[bool] = mapped_column(default=False) else: nsfw = False if SITE_NAME == 'WPD': - cw = Column(Boolean, default=False) + cw: Mapped[bool] = mapped_column(default=False) - author = relationship("User", primaryjoin="Post.author_id==User.id") - oauth_app = relationship("OauthApp") - approved_by = relationship("User", uselist=False, primaryjoin="Post.is_approved==User.id") - awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", back_populates="post") - reports = relationship("Report", order_by="Report.created_utc") - comments = relationship("Comment", primaryjoin="Comment.parent_post==Post.id", back_populates="post") - hole_obj = relationship("Hole", primaryjoin="foreign(Post.hole)==remote(Hole.name)") - options = relationship("PostOption", order_by="PostOption.id") + author: Mapped["User"] = relationship(primaryjoin="Post.author_id==User.id") + oauth_app: Mapped["OauthApp"] = relationship() + approved_by: Mapped["User"] = relationship(uselist=False, primaryjoin="Post.is_approved==User.id") + awards: Mapped[list["AwardRelationship"]] = relationship(order_by="AwardRelationship.awarded_utc.desc()", back_populates="post") + reports: Mapped[list["Report"]] = relationship(order_by="Report.created_utc") + comments: Mapped[list["Comment"]] = relationship(primaryjoin="Comment.parent_post==Post.id", back_populates="post") + hole_obj: Mapped["Hole"] = relationship(primaryjoin="foreign(Post.hole)==remote(Hole.name)") + options: Mapped[list["PostOption"]] = relationship(order_by="PostOption.id") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: diff --git a/files/classes/push_subscriptions.py b/files/classes/push_subscriptions.py index 0a34763a9..24eda989f 100644 --- a/files/classes/push_subscriptions.py +++ b/files/classes/push_subscriptions.py @@ -1,15 +1,16 @@ import time -from sqlalchemy import Column, ForeignKey +from sqlalchemy.orm import Mapped from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import str_pk, user_id_fk_pk class PushSubscription(Base): __tablename__ = "push_subscriptions" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - subscription_json = Column(String, primary_key=True) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + subscription_json: Mapped[str_pk] + created_utc: Mapped[int] def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/reports.py b/files/classes/reports.py index f05e391f2..b99e8d5b1 100644 --- a/files/classes/reports.py +++ b/files/classes/reports.py @@ -1,22 +1,27 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.lazy import lazy from files.helpers.slurs_and_profanities import censor_slurs_profanities +from files.helpers.types import comment_id_fk_pk, post_id_fk_pk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class Report(Base): __tablename__ = "reports" - post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - reason = Column(String) - created_utc = Column(Integer) + post_id: Mapped[post_id_fk_pk] + user_id: Mapped[user_id_fk_pk] + reason: Mapped[Optional[str]] + created_utc: Mapped[int] - user = relationship("User", primaryjoin = "Report.user_id == User.id", uselist = False) + user: Mapped["User"] = relationship(primaryjoin = "Report.user_id == User.id", uselist = False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -39,12 +44,12 @@ class Report(Base): class CommentReport(Base): __tablename__ = "commentreports" - comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - reason = Column(String) - created_utc = Column(Integer) + comment_id: Mapped[comment_id_fk_pk] + user_id: Mapped[user_id_fk_pk] + reason: Mapped[Optional[str]] + created_utc: Mapped[int] - user = relationship("User", primaryjoin = "CommentReport.user_id == User.id", uselist = False) + user: Mapped["User"] = relationship(primaryjoin = "CommentReport.user_id == User.id", uselist = False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/saves.py b/files/classes/saves.py index 0eeef40d7..186f8bf4a 100644 --- a/files/classes/saves.py +++ b/files/classes/saves.py @@ -1,19 +1,25 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import comment_id_fk_pk, post_id_fk_pk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes.comment import Comment + from files.classes.post import Post + class SaveRelationship(Base): __tablename__ = "save_relationship" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + post_id: Mapped[post_id_fk_pk] + created_utc: Mapped[Optional[int]] - post = relationship("Post", uselist=False) + post: Mapped["Post"] = relationship(uselist=False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -27,11 +33,11 @@ class CommentSaveRelationship(Base): __tablename__ = "comment_save_relationship" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + comment_id: Mapped[comment_id_fk_pk] + created_utc: Mapped[Optional[int]] - comment = relationship("Comment", uselist=False) + comment: Mapped["Comment"] = relationship(uselist=False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/subscriptions.py b/files/classes/subscriptions.py index 46d746fed..f1d581567 100644 --- a/files/classes/subscriptions.py +++ b/files/classes/subscriptions.py @@ -1,19 +1,24 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import post_id_fk_pk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import Post, User + class Subscription(Base): __tablename__ = "subscriptions" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + post_id: Mapped[post_id_fk_pk] + created_utc: Mapped[Optional[int]] - user = relationship("User", uselist=False) - post = relationship("Post", uselist=False) + user: Mapped["User"] = relationship(uselist=False) + post: Mapped["Post"] = relationship(uselist=False) def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/transactions.py b/files/classes/transactions.py index 4b5457537..1cbed3108 100644 --- a/files/classes/transactions.py +++ b/files/classes/transactions.py @@ -1,16 +1,19 @@ -from sqlalchemy import Column +from typing import Optional + +from sqlalchemy.orm import Mapped from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import str_pk class Transaction(Base): __tablename__ = "transactions" - id = Column(String, primary_key=True) - created_utc = Column(Integer) - type = Column(String) - amount = Column(Integer) - email = Column(String) - claimed = Column(Boolean) + id: Mapped[str_pk] + created_utc: Mapped[int] + type: Mapped[str] + amount: Mapped[int] + email: Mapped[str] + claimed: Mapped[Optional[bool]] def __repr__(self): return f"<{self.__class__.__name__}(id={self.id})>" diff --git a/files/classes/user.py b/files/classes/user.py index a419110c7..e651f5723 100644 --- a/files/classes/user.py +++ b/files/classes/user.py @@ -1,9 +1,9 @@ import random from operator import * +from typing import Optional import pyotp -from sqlalchemy import Column, ForeignKey, FetchedValue -from sqlalchemy.orm import aliased, deferred, Query +from sqlalchemy.orm import DynamicMapped, Mapped, aliased, mapped_column from sqlalchemy.sql import case, func, literal from sqlalchemy.sql.expression import not_, and_, or_ from sqlalchemy.sql.sqltypes import * @@ -20,6 +20,7 @@ from files.helpers.config.awards import AWARDS_ENABLED, HOUSE_AWARDS from files.helpers.media import * from files.helpers.security import * from files.helpers.sorting_and_time import * +from files.helpers.types import int_pk, user_id_fk from files.helpers.can_see import * from .alts import Alt @@ -49,106 +50,106 @@ else: class User(Base): __tablename__ = "users" - id = Column(Integer, primary_key=True) - username = Column(String) - namecolor = Column(String, default=DEFAULT_COLOR) - background = Column(String) - profile_background = Column(String) - flair = deferred(Column(String)) - flair_html = Column(String) - flaircolor = Column(String, default=DEFAULT_COLOR) - theme = Column(String, default=DEFAULT_THEME) - themecolor = Column(String, default=DEFAULT_COLOR) - song = Column(String) - highres = Column(String) - profileurl = Column(String) - bannerurl = Column(String) - house = Column(String, default='') - old_house = Column(String, default='') - patron = Column(Integer, default=0) - patron_utc = Column(Integer, default=0) - verified = Column(String) - verifiedcolor = Column(String) - hieroglyphs = Column(Integer, default=0) - rehab = Column(Integer, default=0) - longpost = Column(Integer, default=0) - bird = Column(Integer, default=0) - email = deferred(Column(String)) - css = Column(String) - profilecss = deferred(Column(String)) - passhash = deferred(Column(String)) - post_count = Column(Integer, default=0) - comment_count = Column(Integer, default=0) - received_award_count = Column(Integer, default=0) - created_utc = Column(Integer) - admin_level = Column(Integer, default=DEFAULT_ADMIN_LEVEL) - last_active = Column(Integer) - coins_spent = Column(Integer, default=0) - coins_spent_on_hats = Column(Integer, default=0) - lootboxes_bought = Column(Integer, default=0) - chud = Column(Integer, default=0) - queen = Column(Integer, default=0) - chud_phrase = Column(String) - email_verified = Column(Boolean, default=False) - shadowbanned = Column(Integer, ForeignKey("users.id")) - chudded_by = Column(Integer, ForeignKey("users.id")) - slurreplacer = Column(Integer, default=1) - profanityreplacer = Column(Integer, default=1) - flairchanged = Column(Integer, default=0) - namechanged = Column(Integer, default=0) - newtab = Column(Boolean, default=False) - newtabexternal = Column(Boolean, default=True) - frontsize = Column(Integer, default=25) - bio = deferred(Column(String)) - bio_html = Column(String) - sig = deferred(Column(String)) - sig_html = Column(String) - show_sigs = Column(Boolean, default=True) - progressivestack = Column(Integer, default=0) - deflector = Column(Integer, default=0) - friends = deferred(Column(String)) - friends_html = deferred(Column(String)) - enemies = deferred(Column(String)) - enemies_html = deferred(Column(String)) - is_banned = Column(Integer, ForeignKey("users.id")) - unban_utc = Column(Integer) - ban_reason = deferred(Column(String)) - shadowban_reason = deferred(Column(String)) - is_muted = Column(Boolean, default=False) - login_nonce = Column(Integer, default=0) - coins = Column(Integer, default=DEFAULT_COINS) - truescore = Column(Integer, default=0) - marseybux = Column(Integer, default=DEFAULT_MARSEYBUX) - mfa_secret = deferred(Column(String)) - is_private = Column(Boolean, default=False) - stored_subscriber_count = Column(Integer, default=0) - defaultsortingcomments = Column(String, default="hot") - defaultsorting = Column(String, default="hot") - defaulttime = Column(String, default=DEFAULT_TIME_FILTER) - custom_filter_list = Column(String) - original_username = Column(String) - extra_username = Column(String) - prelock_username = Column(String) - referred_by = Column(Integer, ForeignKey("users.id")) - currently_held_lottery_tickets = Column(Integer, default=0) - total_held_lottery_tickets = Column(Integer, default=0) - total_lottery_winnings = Column(Integer, default=0) - last_viewed_modmail_notifs = Column(Integer, default=0) - last_viewed_post_notifs = Column(Integer, default=0) - last_viewed_log_notifs = Column(Integer, default=0) - last_viewed_offsite_notifs = Column(Integer, default=0) - bite = Column(Integer, default=0) - owoify = Column(Integer, default=0) - sharpen = Column(Integer, default=0) - marsify = Column(Integer, default=0) - rainbow = Column(Integer, default=0) - spider = Column(Integer, default=0) - lifetimedonated = Column(Integer, default=0) - lifetimedonated_visible = Column(Boolean, default=False) - blacklisted_by = Column(Integer, ForeignKey("users.id")) - grinch = Column(Boolean, default=SITE_NAME != 'rDrama') #don't put in an if condition, it will cause an error bc it has a not-null constraint - group_creation_notifs = Column(Boolean, default=False) - effortpost_notifs = Column(Boolean, default=False) + id: Mapped[int_pk] + username: Mapped[str] + namecolor: Mapped[str] = mapped_column(default=DEFAULT_COLOR) + background: Mapped[Optional[str]] + profile_background: Mapped[Optional[str]] + flair: Mapped[Optional[str]] = mapped_column(deferred=True) + flair_html: Mapped[Optional[str]] + flaircolor: Mapped[str] = mapped_column(default=DEFAULT_COLOR) + theme: Mapped[str] = mapped_column(default=DEFAULT_THEME) + themecolor: Mapped[str] = mapped_column(default=DEFAULT_COLOR) + song: Mapped[Optional[str]] + highres: Mapped[Optional[str]] + profileurl: Mapped[Optional[str]] + bannerurl: Mapped[Optional[str]] + house: Mapped[Optional[str]] = mapped_column(default='') + old_house: Mapped[Optional[str]] = mapped_column(default='') + patron: Mapped[int] = mapped_column(default=0) + patron_utc: Mapped[int] = mapped_column(default=0) + verified: Mapped[Optional[str]] + verifiedcolor: Mapped[Optional[str]] + hieroglyphs: Mapped[Optional[int]] = mapped_column(default=0) + rehab: Mapped[Optional[int]] = mapped_column(default=0) + longpost: Mapped[Optional[int]] = mapped_column(default=0) + bird: Mapped[Optional[int]] = mapped_column(default=0) + email: Mapped[Optional[str]] = mapped_column(deferred=True) + css: Mapped[Optional[str]] + profilecss: Mapped[Optional[str]] = mapped_column(deferred=True) + passhash: Mapped[str] = mapped_column(deferred=True) + post_count: Mapped[int] = mapped_column(default=0) + comment_count: Mapped[int] = mapped_column(default=0) + received_award_count: Mapped[int] = mapped_column(default=0) + created_utc: Mapped[int] + admin_level: Mapped[int] = mapped_column(default=DEFAULT_ADMIN_LEVEL) + last_active: Mapped[int] + coins_spent: Mapped[int] = mapped_column(default=0) + coins_spent_on_hats: Mapped[int] = mapped_column(default=0) + lootboxes_bought: Mapped[int] = mapped_column(default=0) + chud: Mapped[int] = mapped_column(default=0) + queen: Mapped[Optional[int]] = mapped_column(default=0) + chud_phrase: Mapped[Optional[str]] + email_verified: Mapped[bool] = mapped_column(default=False) + shadowbanned: Mapped[Optional[user_id_fk]] + chudded_by: Mapped[Optional[user_id_fk]] + slurreplacer: Mapped[int] = mapped_column(default=1) + profanityreplacer: Mapped[int] = mapped_column(default=1) + flairchanged: Mapped[Optional[int]] = mapped_column(default=0) + namechanged: Mapped[Optional[int]] = mapped_column(default=0) + newtab: Mapped[bool] = mapped_column(default=False) + newtabexternal: Mapped[bool] = mapped_column(default=True) + frontsize: Mapped[int] = mapped_column(default=25) + bio: Mapped[Optional[str]] = mapped_column(deferred=True) + bio_html: Mapped[Optional[str]] + sig: Mapped[Optional[str]] = mapped_column(deferred=True) + sig_html: Mapped[Optional[str]] + show_sigs: Mapped[bool] = mapped_column(default=True) + progressivestack: Mapped[Optional[int]] = mapped_column(default=0) + deflector: Mapped[Optional[int]] = mapped_column(default=0) + friends: Mapped[Optional[str]] = mapped_column(deferred=True) + friends_html: Mapped[Optional[str]] = mapped_column(deferred=True) + enemies: Mapped[Optional[str]] = mapped_column(deferred=True) + enemies_html: Mapped[Optional[str]] = mapped_column(deferred=True) + is_banned: Mapped[Optional[user_id_fk]] + unban_utc: Mapped[Optional[int]] + ban_reason: Mapped[Optional[str]] = mapped_column(deferred=True) + shadowban_reason: Mapped[Optional[str]] = mapped_column(deferred=True) + is_muted: Mapped[bool] = mapped_column(default=False) + login_nonce: Mapped[int] = mapped_column(default=0) + coins: Mapped[int] = mapped_column(default=DEFAULT_COINS) + truescore: Mapped[int] = mapped_column(default=0) + marseybux: Mapped[int] = mapped_column(default=DEFAULT_MARSEYBUX) + mfa_secret: Mapped[Optional[str]] = mapped_column(deferred=True) + is_private: Mapped[bool] = mapped_column(default=False) + stored_subscriber_count: Mapped[int] = mapped_column(default=0) + defaultsortingcomments: Mapped[str] = mapped_column(default="hot") + defaultsorting: Mapped[str] = mapped_column(default="hot") + defaulttime: Mapped[str] = mapped_column(default=DEFAULT_TIME_FILTER) + custom_filter_list: Mapped[Optional[str]] + original_username: Mapped[Optional[str]] + extra_username: Mapped[Optional[str]] + prelock_username: Mapped[Optional[str]] + referred_by: Mapped[Optional[user_id_fk]] + currently_held_lottery_tickets: Mapped[int] = mapped_column(default=0) + total_held_lottery_tickets: Mapped[int] = mapped_column(default=0) + total_lottery_winnings: Mapped[int] = mapped_column(default=0) + last_viewed_modmail_notifs: Mapped[int] = mapped_column(default=0) + last_viewed_post_notifs: Mapped[int] = mapped_column(default=0) + last_viewed_log_notifs: Mapped[int] = mapped_column(default=0) + last_viewed_offsite_notifs: Mapped[int] = mapped_column(default=0) + bite: Mapped[Optional[int]] = mapped_column(default=0) + owoify: Mapped[Optional[int]] = mapped_column(default=0) + sharpen: Mapped[Optional[int]] = mapped_column(default=0) + marsify: Mapped[Optional[int]] = mapped_column(default=0) + rainbow: Mapped[Optional[int]] = mapped_column(default=0) + spider: Mapped[Optional[int]] = mapped_column(default=0) + lifetimedonated: Mapped[int] = mapped_column(default=0) + lifetimedonated_visible: Mapped[bool] = mapped_column(default=False) + blacklisted_by: Mapped[Optional[user_id_fk]] + grinch: Mapped[bool] = mapped_column(default=SITE_NAME != 'rDrama') #don't put in an if condition, it will cause an error bc it has a not-null constraint + group_creation_notifs: Mapped[bool] = mapped_column(default=False) + effortpost_notifs: Mapped[bool] = mapped_column(default=False) if SITE_NAME == 'WPD': nitter = False @@ -158,37 +159,37 @@ class User(Base): pronouns = 'they/them' earlylife = 0 hole_creation_notifs = False - hidevotedon = Column(Boolean, default=False) - hide_cw = Column(Boolean, default=False) + hidevotedon: Mapped[bool] = mapped_column(default=False) + hide_cw: Mapped[bool] = mapped_column(default=False) else: - nitter = Column(Boolean, default=False) - imgsed = Column(Boolean, default=False) - controversial = Column(Boolean, default=False) - reddit = Column(String, default='old.reddit.com') - pronouns = Column(String, default='they/them') - earlylife = Column(Integer, default=0) - hole_creation_notifs = Column(Boolean, default=True) + nitter: Mapped[bool] = mapped_column(default=False) + imgsed: Mapped[bool] = mapped_column(default=False) + controversial: Mapped[bool] = mapped_column(default=False) + reddit: Mapped[str] = mapped_column(default='old.reddit.com') + pronouns: Mapped[str] = mapped_column(default='they/them') + earlylife: Mapped[Optional[int]] = mapped_column(default=0) + hole_creation_notifs: Mapped[bool] = mapped_column(default=True) hidevotedon = False hide_cw = False if IS_HOMOWEEN(): - zombie = Column(Integer, default=0) # > 0 vaxxed; < 0 zombie - jumpscare = Column(Integer, default=0) + zombie: Mapped[int] = mapped_column(default=0) # > 0 vaxxed; < 0 zombie + jumpscare: Mapped[int] = mapped_column(default=0) - badges = relationship("Badge", order_by="Badge.created_utc", back_populates="user") - subscriptions = relationship("Subscription", back_populates="user") - following = relationship("Follow", primaryjoin="Follow.user_id==User.id", back_populates="user") - followers = relationship("Follow", primaryjoin="Follow.target_id==User.id", back_populates="target") - blocking = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.user_id", back_populates="user") - blocked = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.target_id", back_populates="target") - authorizations = relationship("ClientAuth", back_populates="user") - apps = relationship("OauthApp", back_populates="author") - awards = relationship("AwardRelationship", primaryjoin="User.id==AwardRelationship.user_id", back_populates="user") - referrals = relationship("User", primaryjoin="User.id==User.referred_by", order_by="User.created_utc") - designed_hats = relationship("HatDef", primaryjoin="User.id==HatDef.author_id", back_populates="author") - owned_hats = relationship("Hat", back_populates="owners") - hats_equipped = relationship("Hat", lazy="raise", viewonly=True) - hole_mods = relationship("Mod", primaryjoin="User.id == Mod.user_id", lazy="raise") + badges: Mapped[list["Badge"]] = relationship(order_by="Badge.created_utc", back_populates="user") + subscriptions: Mapped[list["Subscription"]] = relationship(back_populates="user") + following: Mapped[list["Follow"]] = relationship(primaryjoin="Follow.user_id==User.id", back_populates="user") + followers: Mapped[list["Follow"]] = relationship(primaryjoin="Follow.target_id==User.id", back_populates="target") + blocking: DynamicMapped["UserBlock"] = relationship(primaryjoin="User.id==UserBlock.user_id", back_populates="user") + blocked: DynamicMapped["UserBlock"] = relationship(primaryjoin="User.id==UserBlock.target_id", back_populates="target") + authorizations: Mapped[list["ClientAuth"]] = relationship(back_populates="user") + apps: Mapped[list["OauthApp"]] = relationship(back_populates="author") + awards: Mapped[list["AwardRelationship"]] = relationship(primaryjoin="User.id==AwardRelationship.user_id", back_populates="user") + referrals: Mapped[list["User"]] = relationship(primaryjoin="User.id==User.referred_by", order_by="User.created_utc") + designed_hats: Mapped[list["HatDef"]] = relationship(primaryjoin="User.id==HatDef.author_id", back_populates="author") + owned_hats: Mapped[list["Hat"]] = relationship(back_populates="owners") + hats_equipped: Mapped[list["Hat"]] = relationship(lazy="raise", viewonly=True) + hole_mods: Mapped[list["Mod"]] = relationship(primaryjoin="User.id == Mod.user_id", lazy="raise") def __init__(self, **kwargs): diff --git a/files/classes/userblock.py b/files/classes/userblock.py index 7e17ebf4b..e25694259 100644 --- a/files/classes/userblock.py +++ b/files/classes/userblock.py @@ -1,19 +1,24 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class UserBlock(Base): __tablename__ = "userblocks" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - target_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + target_id: Mapped[user_id_fk_pk] + created_utc: Mapped[Optional[int]] - user = relationship("User", primaryjoin="User.id==UserBlock.user_id", back_populates="blocking") - target = relationship("User", primaryjoin="User.id==UserBlock.target_id", back_populates="blocked") + user: Mapped["User"] = relationship(primaryjoin="User.id==UserBlock.user_id", back_populates="blocking") + target: Mapped["User"] = relationship(primaryjoin="User.id==UserBlock.target_id", back_populates="blocked") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/usermute.py b/files/classes/usermute.py index 10db9750d..af5ad1741 100644 --- a/files/classes/usermute.py +++ b/files/classes/usermute.py @@ -1,19 +1,24 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base +from files.helpers.types import user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class UserMute(Base): __tablename__ = "usermutes" - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - target_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + target_id: Mapped[user_id_fk_pk] + created_utc: Mapped[Optional[int]] - user = relationship("User", primaryjoin="User.id==UserMute.user_id") - target = relationship("User", primaryjoin="User.id==UserMute.target_id") + user: Mapped["User"] = relationship(primaryjoin="User.id==UserMute.user_id") + target: Mapped["User"] = relationship(primaryjoin="User.id==UserMute.target_id") def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/views.py b/files/classes/views.py index b8984db8f..725d64313 100644 --- a/files/classes/views.py +++ b/files/classes/views.py @@ -1,22 +1,27 @@ import time +from typing import Optional, TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.lazy import * from files.helpers.sorting_and_time import make_age_string +from files.helpers.types import user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class ViewerRelationship(Base): __tablename__ = "viewers" - user_id = Column(Integer, ForeignKey('users.id'), primary_key=True) - viewer_id = Column(Integer, ForeignKey('users.id'), primary_key=True) - last_view_utc = Column(Integer) - created_utc = Column(Integer) + user_id: Mapped[user_id_fk_pk] + viewer_id: Mapped[user_id_fk_pk] + last_view_utc: Mapped[int] + created_utc: Mapped[Optional[int]] - viewer = relationship("User", primaryjoin="ViewerRelationship.viewer_id == User.id") + viewer: Mapped["User"] = relationship(primaryjoin="ViewerRelationship.viewer_id == User.id") def __init__(self, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/classes/votes.py b/files/classes/votes.py index a2d96e112..6f987afdc 100644 --- a/files/classes/votes.py +++ b/files/classes/votes.py @@ -1,23 +1,28 @@ import time +from typing import TYPE_CHECKING -from sqlalchemy import Column, ForeignKey -from sqlalchemy.orm import relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql.sqltypes import * from files.classes import Base from files.helpers.lazy import lazy +from files.helpers.types import comment_id_fk_pk, post_id_fk_pk, user_id_fk_pk + +if TYPE_CHECKING: + from files.classes import User + class Vote(Base): __tablename__ = "votes" - post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - vote_type = Column(Integer) - real = Column(Boolean, default=True) - coins = Column(Integer, default=1) - created_utc = Column(Integer) + post_id: Mapped[post_id_fk_pk] + user_id: Mapped[user_id_fk_pk] + vote_type: Mapped[int] + real: Mapped[bool] = mapped_column(default=True) + coins: Mapped[int] = mapped_column(default=1) + created_utc: Mapped[int] - user = relationship("User") + user: Mapped["User"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) @@ -40,14 +45,14 @@ class CommentVote(Base): __tablename__ = "commentvotes" - comment_id = Column(Integer, ForeignKey("comments.id"), primary_key=True) - user_id = Column(Integer, ForeignKey("users.id"), primary_key=True) - vote_type = Column(Integer) - real = Column(Boolean, default=True) - coins = Column(Integer, default=1) - created_utc = Column(Integer) + comment_id: Mapped[comment_id_fk_pk] + user_id: Mapped[user_id_fk_pk] + vote_type: Mapped[int] + real: Mapped[bool] = mapped_column(default=True) + coins: Mapped[int] = mapped_column(default=1) + created_utc: Mapped[int] - user = relationship("User") + user: Mapped["User"] = relationship() def __init__(self, *args, **kwargs): if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time()) diff --git a/files/helpers/types.py b/files/helpers/types.py new file mode 100644 index 000000000..085070483 --- /dev/null +++ b/files/helpers/types.py @@ -0,0 +1,14 @@ +from typing import Annotated + +from sqlalchemy.sql.schema import ForeignKey +from sqlalchemy.orm import mapped_column + +int_pk = Annotated[int, mapped_column(primary_key=True)] +str_pk = Annotated[str, mapped_column(primary_key=True)] + +user_id_fk = Annotated[int, mapped_column(ForeignKey("users.id"))] +user_id_fk_pk = Annotated[int, mapped_column(ForeignKey("users.id"), primary_key=True)] +post_id_fk = Annotated[int, mapped_column(ForeignKey("posts.id"))] +post_id_fk_pk = Annotated[int, mapped_column(ForeignKey("posts.id"), primary_key=True)] +comment_id_fk = Annotated[int, mapped_column(ForeignKey("comments.id"))] +comment_id_fk_pk = Annotated[int, mapped_column(ForeignKey("comments.id"), primary_key=True)] \ No newline at end of file diff --git a/typings/flask/__init__.pyi b/typings/flask/__init__.pyi new file mode 100644 index 000000000..7f142f821 --- /dev/null +++ b/typings/flask/__init__.pyi @@ -0,0 +1,65 @@ +from files.classes.user import User +from flask.ctx import _AppCtxGlobals +from sqlalchemy.orm import Session +from sqlalchemy.schema import Column +import typing as t + +# According to PEP 484, "all objects imported into a stub using `from ... import *` are considered +# exported", but this didn't work with Pyright for some reason, so everything has to be exported +# manually. The alternative is to put `def __getattr__(name: str) -> t.Any: ...` here instead, but +# then any symbol not in this file will be treated as Any, which prevents it from being checked. +from flask import json as json +from flask.app import Flask as Flask +from flask.blueprints import Blueprint as Blueprint +from flask.config import Config as Config +from flask.ctx import after_this_request as after_this_request +from flask.ctx import copy_current_request_context as copy_current_request_context +from flask.ctx import has_app_context as has_app_context +from flask.ctx import has_request_context as has_request_context +from flask.globals import current_app as current_app +from flask.globals import request as request +from flask.globals import session as session +from flask.helpers import abort as abort +from flask.helpers import flash as flash +from flask.helpers import get_flashed_messages as get_flashed_messages +from flask.helpers import get_template_attribute as get_template_attribute +from flask.helpers import make_response as make_response +from flask.helpers import redirect as redirect +from flask.helpers import send_file as send_file +from flask.helpers import send_from_directory as send_from_directory +from flask.helpers import stream_with_context as stream_with_context +from flask.helpers import url_for as url_for +from flask.json import jsonify as jsonify +from flask.signals import appcontext_popped as appcontext_popped +from flask.signals import appcontext_pushed as appcontext_pushed +from flask.signals import appcontext_tearing_down as appcontext_tearing_down +from flask.signals import before_render_template as before_render_template +from flask.signals import got_request_exception as got_request_exception +from flask.signals import message_flashed as message_flashed +from flask.signals import request_finished as request_finished +from flask.signals import request_started as request_started +from flask.signals import request_tearing_down as request_tearing_down +from flask.signals import template_rendered as template_rendered +from flask.templating import render_template as render_template +from flask.templating import render_template_string as render_template_string +from flask.templating import stream_template as stream_template +from flask.templating import stream_template_string as stream_template_string +from flask.wrappers import Request as Request +from flask.wrappers import Response as Response + +class Globals(_AppCtxGlobals): + agent: str + browser: t.Literal["webview", "firefox", "iphone", "mac", "chromium"] + db: Session + desires_auth: bool + is_api_or_xhr: bool + is_tor: bool + loggedin_chat: int + loggedin_counter: int + loggedout_counter: int + nonce: str + show_nsfw: bool + username: Column[str] + v: User | None + +g: Globals