This commit is contained in:
justcool393 2023-08-24 14:42:48 -07:00 committed by GitHub
commit 394c62b66d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 422 additions and 368 deletions

View file

@ -198,7 +198,6 @@ engine: Engine = create_engine(DATABASE_URL)
db_session_factory: sessionmaker = sessionmaker(
bind=engine,
autoflush=False,
future=True,
)
db_session: scoped_session = scoped_session(db_session_factory)

View file

@ -1,15 +1,23 @@
from sqlalchemy import *
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.sql import text
from files.classes.base import Base
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, nullable=False, default=False)
user1: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
user2: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
is_manual: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
Index('alts_user2_idx', user2)
Index('alts_unique_combination',
text('GREATEST(user1, user2)'),
text('LEAST(user1, user2)'),
unique=True
)
def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id})>"
return f"<{self.__class__.__name__}(user1={self.user1}, user2={self.user2})>"

View file

@ -1,9 +1,11 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import Base
from files.helpers.config.const import AWARDS
from files.helpers.lazy import lazy
class AwardRelationship(Base):
__tablename__ = "award_relationships"
@ -11,11 +13,11 @@ class AwardRelationship(Base):
UniqueConstraint('user_id', 'submission_id', 'comment_id', name='award_constraint'),
)
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
submission_id = Column(Integer, ForeignKey("submissions.id"))
comment_id = Column(Integer, ForeignKey("comments.id"))
kind = Column(String, nullable=False)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
submission_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("submissions.id"))
comment_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("comments.id"))
kind: Mapped[str] = mapped_column(String, nullable=False)
user = relationship("User", primaryjoin="AwardRelationship.user_id==User.id", viewonly=True)
post = relationship("Submission", primaryjoin="AwardRelationship.submission_id==Submission.id", viewonly=True)

View file

@ -3,6 +3,7 @@ from sqlalchemy.orm import relationship
from files.classes.base import Base
from files.helpers.lazy import lazy
from files.helpers.config.const import *
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.helpers.assetcache import assetcache_path
class BadgeDef(Base):
@ -11,9 +12,9 @@ class BadgeDef(Base):
UniqueConstraint('name', name='badge_def_name_unique'),
)
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, nullable=False)
description = Column(String)
id = mapped_column(Integer, primary_key=True, autoincrement=True)
name = mapped_column(String, nullable=False)
description = mapped_column(String)
def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id})>"
@ -22,10 +23,10 @@ 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)
user_id = mapped_column(Integer, ForeignKey('users.id'), primary_key=True)
badge_id = mapped_column(Integer, ForeignKey('badge_defs.id'), primary_key=True)
description = mapped_column(String)
url = mapped_column(String)
Index('badges_badge_id_idx', badge_id)

View file

@ -2,14 +2,16 @@ import time
from datetime import datetime, timedelta, timezone
from sqlalchemy import text
from sqlalchemy.orm import declarative_base, declared_attr
from sqlalchemy.schema import Column
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.orm.decl_api import DeclarativeBase, declared_attr
from sqlalchemy.sql.functions import now
from sqlalchemy.sql.sqltypes import Integer, DateTime
from sqlalchemy.sql.sqltypes import DateTime, Integer
from files.helpers.time import format_age, format_datetime
Base = declarative_base()
class Base(DeclarativeBase):
pass
class CreatedBase(Base):
@ -21,8 +23,8 @@ class CreatedBase(Base):
super().__init__(*args, **kwargs)
@declared_attr
def created_utc(self):
return Column(Integer, nullable=False)
def created_utc(self) -> Mapped[int]:
return mapped_column(Integer, nullable=False)
@property
def created_date(self) -> str:
@ -67,13 +69,13 @@ class CreatedDateTimeBase(Base):
super().__init__(*args, **kwargs)
@declared_attr
def created_datetimez(self):
def created_datetimez(self) -> Mapped[datetime]:
"""
Declare default column for classes/tables inheriting `CreatedDateTimeBase`.
Retrieving `created_datetimez` will return a `datetime` object with `tzinfo` for UTC.
"""
return Column(DateTime(timezone=True), nullable=False, server_default=now())
return mapped_column(DateTime(timezone=True), nullable=False, server_default=now())
@property
def created_utc(self):

View file

@ -1,11 +1,17 @@
from __future__ import annotations
from flask import g
from sqlalchemy import *
from sqlalchemy.orm import relationship
from .submission import Submission
from .comment import Comment
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.schema import ForeignKey, UniqueConstraint
from sqlalchemy.sql.sqltypes import CHAR, Integer, String
from files.classes.base import Base
from files.helpers.lazy import lazy
from files.helpers.config.const import *
from files.helpers.lazy import lazy
from .comment import Comment
from .submission import Submission
class OauthApp(Base):
@ -14,12 +20,12 @@ class OauthApp(Base):
UniqueConstraint('client_id', name='unique_id'),
)
id = Column(Integer, primary_key=True)
client_id = Column(String(length=64))
app_name = Column(String(length=50), nullable=False)
redirect_uri = Column(String(length=50), nullable=False)
description = Column(String(length=256), nullable=False)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
client_id: Mapped[str | None] = mapped_column(CHAR(length=64))
app_name: Mapped[str] = mapped_column(String(length=50), nullable=False)
redirect_uri: Mapped[str] = mapped_column(String(length=4096), nullable=False)
description: Mapped[str] = mapped_column(String(length=256), nullable=False)
author_id: Mapped[str] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
author = relationship("User", viewonly=True)
@ -51,9 +57,9 @@ class ClientAuth(Base):
UniqueConstraint('access_token', name='unique_access'),
)
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(128), nullable=False)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
oauth_client: Mapped[int] = mapped_column(Integer, ForeignKey("oauth_apps.id"), primary_key=True)
access_token: Mapped[str] = mapped_column(CHAR(128), nullable=False)
user = relationship("User", viewonly=True)
application = relationship("OauthApp", viewonly=True)

View file

@ -4,7 +4,7 @@ from urllib.parse import parse_qs, urlencode, urlparse
from flask import g
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import CreatedBase
from files.classes.visstate import StateMod, StateReport, VisibilityState
@ -24,35 +24,35 @@ CommentRenderContext = Literal['comments', 'volunteer']
class Comment(CreatedBase):
__tablename__ = "comments"
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
parent_submission = Column(Integer, ForeignKey("submissions.id"))
edited_utc = Column(Integer, default=0, nullable=False)
ghost = Column(Boolean, default=False, nullable=False)
bannedfor = Column(Boolean)
distinguish_level = Column(Integer, default=0, nullable=False)
level = Column(Integer, default=1, nullable=False)
parent_comment_id = Column(Integer, ForeignKey("comments.id"))
top_comment_id = Column(Integer)
over_18 = Column(Boolean, default=False, nullable=False)
is_bot = Column(Boolean, default=False, nullable=False)
is_pinned = Column(String)
is_pinned_utc = Column(Integer)
sentto = Column(Integer, ForeignKey("users.id"))
app_id = Column(Integer, ForeignKey("oauth_apps.id"))
upvotes = Column(Integer, default=1, nullable=False)
downvotes = Column(Integer, default=0, nullable=False)
realupvotes = Column(Integer, default=1, nullable=False)
descendant_count = Column(Integer, default=0, nullable=False)
body = Column(Text)
body_html = Column(Text, nullable=False)
volunteer_janitor_badness = Column(Float, default=0.5, nullable=False)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
parent_submission = mapped_column(Integer, ForeignKey("submissions.id"))
edited_utc: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
ghost: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
bannedfor: Mapped[bool | None] = mapped_column(Boolean)
distinguish_level: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
level: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
parent_comment_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("comments.id"))
top_comment_id: Mapped[int | None] = mapped_column(Integer)
over_18: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_bot: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_pinned: Mapped[str | None] = mapped_column(String)
is_pinned_utc: Mapped[int | None] = mapped_column(Integer)
sentto: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id"))
app_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("oauth_apps.id"))
upvotes: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
downvotes: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
realupvotes: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
descendant_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
body: Mapped[str | None] = mapped_column(Text)
body_html: Mapped[str] = mapped_column(Text, nullable=False)
volunteer_janitor_badness: Mapped[float] = mapped_column(Float, default=0.5, nullable=False)
# Visibility states here
state_user_deleted_utc = Column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user
state_mod = Column(Enum(StateMod), default=StateMod.FILTERED, nullable=False) # default to Filtered just to partially neuter possible exploits
state_mod_set_by = Column(String, nullable=True) # This should *really* be a User.id, but I don't want to mess with the required refactoring at the moment - it's extra hard because it could potentially be a lot of extra either data or queries
state_report = Column(Enum(StateReport), default=StateReport.UNREPORTED, nullable=False)
state_user_deleted_utc = mapped_column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user
state_mod = mapped_column(Enum(StateMod), default=StateMod.FILTERED, nullable=False) # default to Filtered just to partially neuter possible exploits
state_mod_set_by = mapped_column(String, nullable=True) # This should *really* be a User.id, but I don't want to mess with the required refactoring at the moment - it's extra hard because it could potentially be a lot of extra either data or queries
state_report = mapped_column(Enum(StateReport), default=StateReport.UNREPORTED, nullable=False)
Index('comment_parent_index', parent_comment_id)
Index('comment_post_id_index', parent_submission)
@ -61,11 +61,11 @@ class Comment(CreatedBase):
oauth_app = relationship("OauthApp", viewonly=True)
post = relationship("Submission", viewonly=True)
author = relationship("User", primaryjoin="User.id==Comment.author_id")
author: Mapped["User"] = relationship("User", primaryjoin="User.id==Comment.author_id")
senttouser = relationship("User", primaryjoin="User.id==Comment.sentto", viewonly=True)
parent_comment = relationship("Comment", remote_side=[id], viewonly=True)
parent_comment_writable = relationship("Comment", remote_side=[id])
child_comments = relationship("Comment", lazy="dynamic", remote_side=[parent_comment_id], viewonly=True)
parent_comment: Mapped["Comment"] = relationship("Comment", remote_side=[id], viewonly=True)
parent_comment_writable: Mapped["Comment"] = relationship("Comment", remote_side=[id])
child_comments: Mapped[list["Comment"]] = relationship("Comment", lazy="dynamic", remote_side=[parent_comment_id], viewonly=True)
awards = relationship("AwardRelationship",
primaryjoin="AwardRelationship.comment_id == Comment.id",
viewonly=True)

View file

@ -2,7 +2,8 @@ import importlib
from types import ModuleType
from typing import Callable
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.schema import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import Integer, String
from files.classes.cron.tasks import (RepeatableTask, ScheduledTaskType,
@ -40,9 +41,9 @@ class PythonCodeTask(RepeatableTask):
"polymorphic_identity": int(ScheduledTaskType.PYTHON_CALLABLE),
}
id = Column(Integer, ForeignKey(RepeatableTask.id), primary_key=True)
import_path = Column(String, nullable=False)
callable = Column(String, nullable=False)
id: Mapped[int] = mapped_column(Integer, ForeignKey(RepeatableTask.id), primary_key=True)
import_path: Mapped[str] = mapped_column(String, nullable=False)
callable: Mapped[str] = mapped_column(String, nullable=False)
def run_task(self, ctx:TaskRunContext):
self.get_function()(ctx)

View file

@ -1,8 +1,8 @@
import functools
from datetime import datetime, timezone
from sqlalchemy.orm import relationship
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.orm import Mapped, Session, mapped_column, relationship
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql.sqltypes import Boolean, Integer, String, Text
from files.classes.cron.tasks import (RepeatableTask, ScheduledTaskType,
@ -24,18 +24,18 @@ class ScheduledSubmissionTask(RepeatableTask):
"polymorphic_identity": int(ScheduledTaskType.SCHEDULED_SUBMISSION),
}
id = Column(Integer, ForeignKey(RepeatableTask.id), primary_key=True)
author_id_submission = Column(Integer, ForeignKey("users.id"), nullable=False)
ghost = Column(Boolean, default=False, nullable=False)
private = Column(Boolean, default=False, nullable=False)
over_18 = Column(Boolean, default=False, nullable=False)
is_bot = Column(Boolean, default=False, nullable=False)
title = Column(String(SUBMISSION_TITLE_LENGTH_MAXIMUM), nullable=False)
url = Column(String)
body = Column(Text)
body_html = Column(Text)
flair = Column(String)
embed_url = Column(String)
id: Mapped[int] = mapped_column(Integer, ForeignKey(RepeatableTask.id), primary_key=True)
author_id_submission: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
ghost: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
private: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
over_18: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_bot: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
title: Mapped[str] = mapped_column(String(SUBMISSION_TITLE_LENGTH_MAXIMUM), nullable=False)
url: Mapped[str | None] = mapped_column(String)
body: Mapped[str | None] = mapped_column(Text)
body_html: Mapped[str | None] = mapped_column(Text)
flair: Mapped[str | None] = mapped_column(String)
embed_url: Mapped[str | None] = mapped_column(String)
author = relationship("User", foreign_keys=author_id_submission)
task = relationship(RepeatableTask)

View file

@ -4,16 +4,16 @@ import contextlib
import dataclasses
from datetime import date, datetime, timedelta, timezone
from enum import IntEnum, IntFlag
from typing import TYPE_CHECKING, Final, Optional, Union
from typing import TYPE_CHECKING, ClassVar, Final, Optional, Union
import flask
import flask_caching
import flask_mail
import redis
from sqlalchemy.orm import relationship, Session
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.orm import Mapped, Session, mapped_column, relationship
from sqlalchemy.schema import ForeignKey
from sqlalchemy.sql.sqltypes import (Boolean, DateTime, Integer, SmallInteger,
Text, Time, String)
String, Text, Time)
from files.classes.base import CreatedBase
from files.helpers.time import format_age, format_datetime
@ -237,18 +237,18 @@ _TABLE_NAME: Final[str] = "tasks_repeatable"
class RepeatableTask(CreatedBase):
__tablename__ = _TABLE_NAME
id = Column(Integer, primary_key=True, nullable=False)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
type_id = Column(SmallInteger, nullable=False)
enabled = Column(Boolean, default=True, nullable=False)
run_state = Column(SmallInteger, default=int(ScheduledTaskState.WAITING), nullable=False)
run_time_last = Column(DateTime, default=None)
id: Mapped[int] = mapped_column(Integer, primary_key=True, nullable=False)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
type_id: Mapped[int] = mapped_column(SmallInteger, nullable=False)
enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
run_state: Mapped[int] = mapped_column(SmallInteger, default=int(ScheduledTaskState.WAITING), nullable=False)
run_time_last: Mapped[datetime | None] = mapped_column(DateTime, default=None)
frequency_day = Column(SmallInteger, nullable=False)
time_of_day_utc = Column(Time, nullable=False)
frequency_day: Mapped[int] = mapped_column(SmallInteger, nullable=False)
time_of_day_utc = mapped_column(Time, nullable=False)
# used for the cron hardcoding system
label = Column(String, nullable=True, unique=True)
label = mapped_column(String, nullable=True, unique=True)
runs = relationship("RepeatableTaskRun", back_populates="task")
@ -351,16 +351,16 @@ class RepeatableTask(CreatedBase):
class RepeatableTaskRun(CreatedBase):
__tablename__ = "tasks_repeatable_runs"
id = Column(Integer, primary_key=True)
task_id = Column(Integer, ForeignKey(RepeatableTask.id), nullable=False)
manual = Column(Boolean, default=False, nullable=False)
traceback_str = Column(Text, nullable=True)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
task_id: Mapped[int] = mapped_column(Integer, ForeignKey(RepeatableTask.id), nullable=False)
manual: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
traceback_str: Mapped[str | None] = mapped_column(Text, nullable=True)
completed_utc = Column(DateTime)
completed_utc: Mapped[datetime | None] = mapped_column(DateTime)
task = relationship(RepeatableTask, back_populates="runs")
_exception: Optional[Exception] = None # not part of the db model
_exception: ClassVar[Exception | None] = None # not part of the db model
@property
def completed_datetime_py(self) -> datetime | None:

View file

@ -1,10 +1,12 @@
from sqlalchemy import *
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql.sqltypes import String
from sqlalchemy.schema import Index
from files.classes.base import Base
class BannedDomain(Base):
__tablename__ = "banneddomains"
domain = Column(String, primary_key=True)
reason = Column(String, nullable=False)
domain: Mapped[str] = mapped_column(String, primary_key=True)
reason: Mapped[str | None] = mapped_column(String, nullable=False)
Index(
'domains_domain_trgm_idx',
domain,

View file

@ -1,11 +1,13 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import CreatedDateTimeBase
class Follow(CreatedDateTimeBase):
__tablename__ = "follows"
target_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
target_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
Index('follow_user_id_index', user_id)

View file

@ -2,16 +2,18 @@ from dataclasses import dataclass
from typing import Any, Callable, Final, Optional
from sqlalchemy import Column, func
from sqlalchemy.orm import Session, Query
from files.helpers.config.const import LEADERBOARD_LIMIT
from sqlalchemy.orm import Query, Session
from sqlalchemy.orm.attributes import InstrumentedAttribute
from sqlalchemy.sql.expression import text
from files.classes.badges import Badge
from files.classes.marsey import Marsey
from files.classes.user import User
from files.classes.userblock import UserBlock
from files.helpers.config.const import LEADERBOARD_LIMIT
from files.helpers.get import get_accounts_dict
@dataclass(frozen=True, slots=True)
class LeaderboardMeta:
header_name:str
@ -51,11 +53,11 @@ class Leaderboard:
raise NotImplementedError()
class SimpleLeaderboard(Leaderboard):
def __init__(self, v:User, meta:LeaderboardMeta, db:Session, users_query:Query, column:Column):
def __init__(self, v:User, meta:LeaderboardMeta, db:Session, users_query:Query, column: InstrumentedAttribute):
super().__init__(v, meta)
self.db:Session = db
self.users_query:Query = users_query
self.column:Column = column
self.column: InstrumentedAttribute = column
self._calculate()
def _calculate(self) -> None:
@ -101,9 +103,9 @@ class BadgeMarseyLeaderboard(_CountedAndRankedLeaderboard):
def _calculate(self):
sq = self.db.query(self.column, self.count_and_label(self.column), self.rank_filtered_rank_label_by_desc(self.column)).group_by(self.column).subquery()
sq_criteria = None
if self.column == Badge.user_id:
if self.column is Badge.user_id:
sq_criteria = User.id == sq.c.user_id
elif self.column == Marsey.author_id:
elif self.column is Marsey.author_id:
sq_criteria = User.id == sq.c.author_id
else:
raise ValueError("This leaderboard function only supports Badge.user_id and Marsey.author_id")
@ -142,7 +144,7 @@ class UserBlockLeaderboard(_CountedAndRankedLeaderboard):
self._calculate()
def _calculate(self):
if self.column != UserBlock.target_id:
if self.column is not UserBlock.target_id:
raise ValueError("This leaderboard function only supports UserBlock.target_id")
sq = self.db.query(self.column, self.count_and_label(self.column)).group_by(self.column).subquery()
leaderboard = self.db.query(User, sq.c.count).join(User, User.id == sq.c.target_id).order_by(sq.c.count.desc())
@ -179,7 +181,7 @@ class RawSqlLeaderboard(Leaderboard):
self._calculate(query)
def _calculate(self, query:str):
self.result = {result[0]:list(result) for result in self.db.execute(query).all()}
self.result = {result[0]:list(result) for result in self.db.execute(text(query)).all()}
users = get_accounts_dict(self.result.keys(), db=self.db)
if users is None:
raise Exception("Some users don't exist when they should (was a user deleted?)")

View file

@ -1,13 +1,14 @@
from sqlalchemy import *
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import Base
class Marsey(Base):
__tablename__ = "marseys"
name = Column(String, primary_key=True)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
tags = Column(String(length=200), nullable=False)
count = Column(Integer, default=0, nullable=False)
name: Mapped[str] = mapped_column(String, primary_key=True)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
tags: Mapped[str] = mapped_column(String(length=200), nullable=False)
count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
Index('marseys_idx2', author_id)
Index('marseys_idx3', count.desc())

View file

@ -2,7 +2,7 @@ import logging
from copy import deepcopy
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import CreatedDateTimeBase
from files.helpers.config.const import *
@ -11,13 +11,13 @@ from files.helpers.lazy import lazy
class ModAction(CreatedDateTimeBase):
__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_submission_id = Column(Integer, ForeignKey("submissions.id"))
target_comment_id = Column(Integer, ForeignKey("comments.id"))
_note=Column(String)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
user_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id"))
kind: Mapped[str | None] = mapped_column(String)
target_user_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id"))
target_submission_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("submissions.id"))
target_comment_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("comments.id"))
_note: Mapped[str | None] = mapped_column(String)
Index('fki_modactions_user_fkey', target_user_id)
Index('modaction_action_idx', kind)

View file

@ -1,13 +1,15 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import CreatedDateTimeBase
class Notification(CreatedDateTimeBase):
__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, nullable=False)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
comment_id: Mapped[int] = mapped_column(Integer, ForeignKey("comments.id"), primary_key=True)
read: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
Index('notification_read_idx', read)
Index('notifications_comment_idx', comment_id)

View file

@ -1,13 +1,13 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import Base
class SaveRelationship(Base):
__tablename__ = "save_relationship"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
submission_id = Column(Integer, ForeignKey("submissions.id"), primary_key=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
submission_id: Mapped[int] = mapped_column(Integer, ForeignKey("submissions.id"), primary_key=True)
Index('fki_save_relationship_submission_fkey', submission_id)
@ -15,7 +15,7 @@ class SaveRelationship(Base):
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)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
comment_id: Mapped[int] = mapped_column(Integer, ForeignKey("comments.id"), primary_key=True)
Index('fki_comment_save_relationship_comment_fkey', comment_id)

View file

@ -1,8 +1,10 @@
from datetime import datetime
from urllib.parse import urlparse
from flask import g
from sqlalchemy import *
from sqlalchemy.orm import Session, declared_attr, deferred, relationship
from sqlalchemy.orm import (Mapped, Session, declared_attr, deferred,
mapped_column, relationship)
from files.classes.base import CreatedBase
from files.classes.flags import Flag
@ -20,39 +22,39 @@ from files.helpers.time import format_age, format_datetime
class Submission(CreatedBase):
__tablename__ = "submissions"
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
edited_utc = Column(Integer, default=0, nullable=False)
thumburl = Column(String)
bannedfor = Column(Boolean)
ghost = Column(Boolean, default=False, nullable=False)
views = Column(Integer, default=0, nullable=False)
distinguish_level = Column(Integer, default=0, nullable=False)
stickied = Column(String)
stickied_utc = Column(Integer)
is_pinned = Column(Boolean, default=False, nullable=False)
private = Column(Boolean, default=False, nullable=False)
comment_count = Column(Integer, default=0, nullable=False)
over_18 = Column(Boolean, default=False, nullable=False)
is_bot = Column(Boolean, default=False, nullable=False)
upvotes = Column(Integer, default=1, nullable=False)
downvotes = Column(Integer, default=0, nullable=False)
realupvotes = Column(Integer, default=1)
app_id=Column(Integer, ForeignKey("oauth_apps.id"))
title = Column(String, nullable=False)
title_html = Column(String, nullable=False)
url = Column(String)
body = Column(Text)
body_html = Column(Text)
flair = Column(String)
embed_url = Column(String)
task_id = Column(Integer, ForeignKey("tasks_repeatable_scheduled_submissions.id"))
id: Mapped[int] = mapped_column(Integer, primary_key=True)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
edited_utc: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
thumburl: Mapped[str | None] = mapped_column(String)
bannedfor: Mapped[bool | None] = mapped_column(Boolean)
ghost: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
views: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
distinguish_level: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
stickied: Mapped[str | None] = mapped_column(String)
stickied_utc: Mapped[int | None] = mapped_column(Integer)
is_pinned: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
private: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
comment_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
over_18: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_bot: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
upvotes: Mapped[int] = mapped_column(Integer, default=1, nullable=False)
downvotes: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
realupvotes: Mapped[int | None] = mapped_column(Integer, default=1) # XXX: inconsistent with comments
app_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("oauth_apps.id"))
title: Mapped[str] = mapped_column(String, nullable=False)
title_html: Mapped[str] = mapped_column(String, nullable=False)
url: Mapped[str | None] = mapped_column(String)
body: Mapped[str | None] = mapped_column(Text)
body_html: Mapped[str | None] = mapped_column(Text)
flair: Mapped[str | None] = mapped_column(String)
embed_url: Mapped[str | None] = mapped_column(String)
task_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("tasks_repeatable_scheduled_submissions.id"))
# Visibility states here
state_user_deleted_utc = Column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user
state_mod = Column(Enum(StateMod), default=StateMod.FILTERED, nullable=False) # default to Filtered just to partially neuter possible exploits
state_mod_set_by = Column(String, nullable=True) # This should *really* be a User.id, but I don't want to mess with the required refactoring at the moment - it's extra hard because it could potentially be a lot of extra either data or queries
state_report = Column(Enum(StateReport), default=StateReport.UNREPORTED, nullable=False)
state_user_deleted_utc: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) # null if it hasn't been deleted by the user
state_mod: Mapped[StateMod] = mapped_column(Enum(StateMod), default=StateMod.FILTERED, nullable=False) # default to Filtered just to partially neuter possible exploits
state_mod_set_by: Mapped[str | None] = mapped_column(String, nullable=True) # This should *really* be a User.id, but I don't want to mess with the required refactoring at the moment - it's extra hard because it could potentially be a lot of extra either data or queries
state_report: Mapped[StateReport] = mapped_column(Enum(StateReport), default=StateReport.UNREPORTED, nullable=False)
Index('post_app_id_idx', app_id)
Index('subimssion_binary_group_idx', state_mod, state_user_deleted_utc, over_18)
@ -84,7 +86,7 @@ class Submission(CreatedBase):
notes = relationship("UserNote", back_populates="post")
task = relationship("ScheduledSubmissionTask", back_populates="submissions")
bump_utc = deferred(Column(Integer, server_default=FetchedValue()))
bump_utc = deferred(mapped_column(Integer, server_default=FetchedValue()))
def submit(self, db: Session):
# create submission...

View file

@ -1,11 +1,12 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import Base
class Subscription(Base):
__tablename__ = "subscriptions"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
submission_id = Column(Integer, ForeignKey("submissions.id"), primary_key=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
submission_id: Mapped[int] = mapped_column(Integer, ForeignKey("submissions.id"), primary_key=True)
Index('subscription_user_index', user_id)

View file

@ -6,7 +6,11 @@ from typing import TYPE_CHECKING, Union
import pyotp
from flask import g, session
from sqlalchemy.orm import aliased, declared_attr, deferred, relationship
from sqlalchemy.orm import (Mapped, aliased, declared_attr, mapped_column,
relationship)
from sqlalchemy.sql.operators import and_, or_
from sqlalchemy.sql.schema import ForeignKey, Index
from sqlalchemy.sql.sqltypes import Boolean, DateTime, Float, Integer, String
from files.classes.alts import Alt
from files.classes.award import AwardRelationship
@ -40,77 +44,77 @@ class User(CreatedBase):
UniqueConstraint('original_username', name='users_original_username_key'),
UniqueConstraint('username', name='users_username_key'),
)
id = Column(Integer, primary_key=True)
username = Column(String(length=255), nullable=False)
namecolor = Column(String(length=6), default=DEFAULT_COLOR, nullable=False)
customtitle = Column(String)
customtitleplain = deferred(Column(String))
titlecolor = Column(String(length=6), default=DEFAULT_COLOR, nullable=False)
theme = Column(String, default=defaulttheme, nullable=False)
themecolor = Column(String, default=DEFAULT_COLOR, nullable=False)
cardview = Column(Boolean, default=CARD_VIEW, nullable=False)
highres = Column(String)
profileurl = Column(String)
bannerurl = Column(String)
house = Column(String)
patron = Column(Integer, default=0, nullable=False)
patron_utc = Column(Integer, default=0, nullable=False)
verified = Column(String)
verifiedcolor = Column(String)
winnings = Column(Integer, default=0, nullable=False)
email = deferred(Column(String))
css = deferred(Column(String(CSS_LENGTH_MAXIMUM)))
profilecss = deferred(Column(String(CSS_LENGTH_MAXIMUM)))
passhash = deferred(Column(String, nullable=False))
post_count = Column(Integer, default=0, nullable=False)
comment_count = Column(Integer, default=0, nullable=False)
received_award_count = Column(Integer, default=0, nullable=False)
admin_level = Column(Integer, default=0, nullable=False)
coins_spent = Column(Integer, default=0, nullable=False)
lootboxes_bought = Column(Integer, default=0, nullable=False)
agendaposter = Column(Integer, default=0, nullable=False)
changelogsub = Column(Boolean, default=False, nullable=False)
is_activated = Column(Boolean, default=False, nullable=False)
shadowbanned = Column(String)
over_18 = Column(Boolean, default=False, nullable=False)
hidevotedon = Column(Boolean, default=False, nullable=False)
highlightcomments = Column(Boolean, default=True, nullable=False)
slurreplacer = Column(Boolean, default=True, nullable=False)
flairchanged = Column(Integer)
newtab = Column(Boolean, default=False, nullable=False)
newtabexternal = Column(Boolean, default=True, nullable=False)
reddit = Column(String, default='old.reddit.com', nullable=False)
nitter = Column(Boolean)
frontsize = Column(Integer, default=25, nullable=False)
controversial = Column(Boolean, default=False, nullable=False)
bio = deferred(Column(String))
bio_html = Column(String)
fp = Column(String)
friends = deferred(Column(String))
friends_html = deferred(Column(String))
enemies = deferred(Column(String))
enemies_html = deferred(Column(String))
is_banned = Column(Integer, default=0, nullable=False)
unban_utc = Column(Integer, default=0, nullable=False)
ban_reason = deferred(Column(String))
login_nonce = Column(Integer, default=0, nullable=False)
reserved = deferred(Column(String))
coins = Column(Integer, default=0, nullable=False)
truescore = Column(Integer, default=0, nullable=False)
procoins = Column(Integer, default=0, nullable=False)
mfa_secret = deferred(Column(String))
is_private = Column(Boolean, default=False, nullable=False)
stored_subscriber_count = Column(Integer, default=0, nullable=False)
defaultsortingcomments = Column(String, default="new", nullable=False)
defaultsorting = Column(String, default="new", nullable=False)
defaulttime = Column(String, default=DEFAULT_TIME_FILTER, nullable=False)
is_nofollow = Column(Boolean, default=False, nullable=False)
custom_filter_list = Column(String)
ban_evade = Column(Integer, default=0, nullable=False)
original_username = deferred(Column(String))
referred_by = Column(Integer, ForeignKey("users.id"))
volunteer_last_started_utc = Column(DateTime, nullable=True)
volunteer_janitor_correctness = Column(Float, default=0, nullable=False)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
username: Mapped[str] = mapped_column(String(length=255), nullable=False)
namecolor: Mapped[str] = mapped_column(String(length=6), default=DEFAULT_COLOR, nullable=False)
customtitle: Mapped[str | None] = mapped_column(String)
customtitleplain: Mapped[str | None] = mapped_column(String, deferred=True)
titlecolor = mapped_column(String(length=6), default=DEFAULT_COLOR, nullable=False)
theme: Mapped[str] = mapped_column(String, default=defaulttheme, nullable=False)
themecolor: Mapped[str] = mapped_column(String, default=DEFAULT_COLOR, nullable=False)
cardview: Mapped[bool] = mapped_column(Boolean, default=CARD_VIEW, nullable=False)
highres: Mapped[str | None] = mapped_column(String)
profileurl: Mapped[str | None] = mapped_column(String)
bannerurl: Mapped[str | None] = mapped_column(String)
house: Mapped[str | None] = mapped_column(String)
patron: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
patron_utc: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
verified: Mapped[str | None] = mapped_column(String)
verifiedcolor = mapped_column(String)
winnings: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
email: Mapped[str | None] = mapped_column(String, deferred=True)
css: Mapped[str | None] = mapped_column(String(CSS_LENGTH_MAXIMUM))
profilecss: Mapped[str | None] = mapped_column(String(CSS_LENGTH_MAXIMUM), deferred=True)
passhash: Mapped[str] = mapped_column(String, nullable=False, deferred=True)
post_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
comment_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
received_award_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
admin_level: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
coins_spent: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
lootboxes_bought: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
agendaposter: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
changelogsub: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
is_activated: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
shadowbanned: Mapped[str | None] = mapped_column(String)
over_18: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
hidevotedon: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
highlightcomments: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
slurreplacer: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
flairchanged: Mapped[int | None] = mapped_column(Integer)
newtab: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
newtabexternal: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
reddit: Mapped[str] = mapped_column(String, default='old.reddit.com', nullable=False)
nitter: Mapped[bool | None] = mapped_column(Boolean)
frontsize = mapped_column(Integer, default=25, nullable=False)
controversial = mapped_column(Boolean, default=False, nullable=False)
bio: Mapped[str | None] = mapped_column(String, deferred=True)
bio_html: Mapped[str | None] = mapped_column(String)
fp: Mapped[str | None] = mapped_column(String)
friends: Mapped[str | None] = mapped_column(String, deferred=True)
friends_html: Mapped[str | None] = mapped_column(String, deferred=True)
enemies: Mapped[str | None] = mapped_column(String, deferred=True)
enemies_html: Mapped[str | None] = mapped_column(String, deferred=True)
is_banned: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
unban_utc: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
ban_reason: Mapped[str | None] = mapped_column(String, deferred=True)
login_nonce: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
reserved: Mapped[str | None] = mapped_column(String, deferred=True)
coins: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
truescore: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
procoins: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
mfa_secret: Mapped[str | None] = mapped_column(String, deferred=True)
is_private: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
stored_subscriber_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
defaultsortingcomments = mapped_column(String, default="new", nullable=False)
defaultsorting = mapped_column(String, default="new", nullable=False)
defaulttime: Mapped[str] = mapped_column(String, default=DEFAULT_TIME_FILTER, nullable=False)
is_nofollow: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
custom_filter_list: Mapped[str | None] = mapped_column(String)
ban_evade: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
original_username: Mapped[str | None] = mapped_column(String, deferred=True)
referred_by: Mapped[int | None] = mapped_column(Integer, ForeignKey("users.id"))
volunteer_last_started_utc: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
volunteer_janitor_correctness: Mapped[float] = mapped_column(Float, default=0, nullable=False)
Index(
'users_original_username_trgm_idx',

View file

@ -4,6 +4,7 @@ from files.classes.base import CreatedDateTimeBase
from files.helpers.config.const import *
from enum import Enum
from sqlalchemy import Enum as EnumType
from sqlalchemy.orm import Mapped, mapped_column, relationship
class UserTag(Enum):
Quality = 0
@ -18,13 +19,13 @@ class UserTag(Enum):
class UserNote(CreatedDateTimeBase):
__tablename__ = "usernotes"
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("users.id"), nullable=False)
reference_user = Column(Integer, ForeignKey("users.id", ondelete='CASCADE'), nullable=False)
reference_comment = Column(Integer, ForeignKey("comments.id", ondelete='SET NULL'))
reference_post = Column(Integer, ForeignKey("submissions.id", ondelete='SET NULL'))
note = Column(String, nullable=False)
tag = Column(EnumType(UserTag), nullable=False)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
reference_user: Mapped[int] = mapped_column(Integer, ForeignKey("users.id", ondelete='CASCADE'), nullable=False)
reference_comment: Mapped[int | None] = mapped_column(Integer, ForeignKey("comments.id", ondelete='SET NULL'))
reference_post: Mapped[int | None] = mapped_column(Integer, ForeignKey("submissions.id", ondelete='SET NULL'))
note: Mapped[str] = mapped_column(String, nullable=False)
tag: Mapped[UserTag] = mapped_column(EnumType(UserTag), nullable=False)
author = relationship("User", foreign_keys='UserNote.author_id')
user = relationship("User", foreign_keys='UserNote.reference_user', back_populates="notes")

View file

@ -1,7 +1,7 @@
import time
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import Base
from files.helpers.lazy import lazy
@ -11,9 +11,9 @@ from files.helpers.time import format_age
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, nullable=False)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey('users.id'), primary_key=True)
viewer_id: Mapped[int] = mapped_column(Integer, ForeignKey('users.id'), primary_key=True)
last_view_utc: Mapped[int] = mapped_column(Integer, nullable=False)
Index('fki_view_viewer_fkey', viewer_id)

View file

@ -1,8 +1,12 @@
from datetime import datetime
import enum
from files.classes.base import Base
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import Base
class VolunteerJanitorResult(enum.Enum):
Pending = 0
@ -16,11 +20,11 @@ class VolunteerJanitorResult(enum.Enum):
class VolunteerJanitorRecord(Base):
__tablename__ = "volunteer_janitor"
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
comment_id = Column(Integer, ForeignKey("comments.id"), nullable=False)
recorded_utc = Column(DateTime, default=0, nullable=False)
result = Column(Enum(VolunteerJanitorResult), default=VolunteerJanitorResult.Pending, nullable=False)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False)
comment_id: Mapped[int] = mapped_column(Integer, ForeignKey("comments.id"), nullable=False)
recorded_utc: Mapped[datetime] = mapped_column(DateTime, default=0, nullable=False)
result: Mapped[VolunteerJanitorResult] = mapped_column(Enum(VolunteerJanitorResult), default=VolunteerJanitorResult.Pending, nullable=False)
Index('volunteer_comment_index', user_id, comment_id)

View file

@ -1,5 +1,5 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped, mapped_column, relationship
from files.classes.base import CreatedDateTimeBase
from files.helpers.lazy import lazy
@ -8,11 +8,11 @@ from files.helpers.lazy import lazy
class Vote(CreatedDateTimeBase):
__tablename__ = "votes"
submission_id = Column(Integer, ForeignKey("submissions.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
vote_type = Column(Integer, nullable=False)
app_id = Column(Integer, ForeignKey("oauth_apps.id"))
real = Column(Boolean, default=True, nullable=False)
submission_id: Mapped[int] = mapped_column(Integer, ForeignKey("submissions.id"), primary_key=True)
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
vote_type: Mapped[int] = mapped_column(Integer, nullable=False)
app_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("oauth_apps.id"))
real: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
Index('votes_type_index', vote_type)
Index('vote_user_index', user_id)
@ -45,11 +45,11 @@ class Vote(CreatedDateTimeBase):
class CommentVote(CreatedDateTimeBase):
__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, nullable=False)
app_id = Column(Integer, ForeignKey("oauth_apps.id"))
real = Column(Boolean, default=True, nullable=False)
comment_id = mapped_column(Integer, ForeignKey("comments.id"), primary_key=True)
user_id = mapped_column(Integer, ForeignKey("users.id"), primary_key=True)
vote_type = mapped_column(Integer, nullable=False)
app_id = mapped_column(Integer, ForeignKey("oauth_apps.id"))
real = mapped_column(Boolean, default=True, nullable=False)
Index('cvote_user_index', user_id)
Index('commentvotes_comments_type_index', vote_type)

View file

@ -5,6 +5,7 @@ from datetime import datetime, timezone
from typing import Final
from sqlalchemy.orm import sessionmaker, Session
from sqlalchemy.sql.expression import text
from files.__main__ import app, db_session_factory
from files.classes.cron.tasks import (DayOfWeek, RepeatableTask,
@ -59,7 +60,7 @@ def _acquire_lock_exclusive(db: Session, table: str):
'''
# TODO: make `table` the type LiteralString once we upgrade to python 3.11
db.begin() # we want to raise an exception if there's a txn in progress
db.execute(f"LOCK TABLE {table} IN ACCESS EXCLUSIVE MODE")
db.execute(text(f"LOCK TABLE {table} IN ACCESS EXCLUSIVE MODE"))
try:
yield
db.commit()

View file

@ -2,6 +2,7 @@
import pprint
import sqlalchemy
import sqlalchemy.orm
from sqlalchemy.orm import Session
from alive_progress import alive_it

View file

@ -142,7 +142,7 @@ def bulk_recompute_descendant_counts(predicate = None, db=None):
.join(
child_comments,
parent_comments.corresponding_column(Comment.id) == child_comments.corresponding_column(Comment.parent_comment_id),
True
isouter=True
)
.group_by(parent_comments.corresponding_column(Comment.id))
.with_only_columns(

View file

@ -5,7 +5,8 @@ from typing import Callable, Iterable, List, Optional, Type, Union
from flask import abort, g
from sqlalchemy import and_, or_, func
from sqlalchemy.orm import Query, scoped_session, selectinload
from sqlalchemy.orm import Query, selectinload
from sqlalchemy.orm.session import Session
from files.classes import *
from files.helpers.config.const import AUTOJANNY_ID
@ -81,7 +82,7 @@ def get_account(
v:Optional[User]=None,
graceful:bool=False,
include_blocks:bool=False,
db:Optional[scoped_session]=None) -> Optional[User]:
db:Optional[Session]=None) -> Optional[User]:
try:
id = int(id)
except:
@ -102,7 +103,7 @@ def get_account(
def get_accounts_dict(ids:Union[Iterable[str], Iterable[int]],
v:Optional[User]=None, graceful=False,
include_shadowbanned=True,
db:Optional[scoped_session]=None) -> Optional[dict[int, User]]:
db:Optional[Session]=None) -> Optional[dict[int, User]]:
if not db: db = g.db
if not ids: return {}
try:

View file

@ -7,7 +7,7 @@ from files.__main__ import app
APP_PATH = app.root_path
BASE_PATH = os.path.join(*os.path.split(APP_PATH)[:-1])
VERSIONS_PATH = migrations.versions.__path__._path[0];
VERSIONS_PATH = migrations.versions.__path__._path[0]
def test_migrations_up_to_date():
def get_versions():
@ -27,26 +27,32 @@ def test_migrations_up_to_date():
return [l.strip() for l in method_lines if not l.strip().startswith('#')][1:]
versions_before = get_versions()
result = subprocess.run(
[
'python3',
'-m',
'flask',
'db',
'revision',
'--autogenerate',
'--rev-id=ci_verify_empty_revision',
'--message=should_be_empty',
],
cwd=BASE_PATH,
env={
**os.environ,
'FLASK_APP': 'files/cli:app',
},
capture_output=True,
text=True,
check=True
)
try:
result = subprocess.run(
[
'python3',
'-m',
'flask',
'db',
'revision',
'--autogenerate',
'--rev-id=ci_verify_empty_revision',
'--message=should_be_empty',
],
cwd=BASE_PATH,
env={
**os.environ,
'FLASK_APP': 'files/cli:app',
},
capture_output=True,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
print("Failed to run migration test...")
print(e.stderr)
raise
versions_after = get_versions()
new_versions = [v for v in versions_after if v not in versions_before]
try:

123
poetry.lock generated
View file

@ -14,19 +14,20 @@ files = [
[[package]]
name = "alembic"
version = "1.8.1"
version = "1.11.2"
description = "A database migration tool for SQLAlchemy."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "alembic-1.8.1-py3-none-any.whl", hash = "sha256:0a024d7f2de88d738d7395ff866997314c837be6104e90c5724350313dee4da4"},
{file = "alembic-1.8.1.tar.gz", hash = "sha256:cd0b5e45b14b706426b833f06369b9a6d5ee03f826ec3238723ce8caaf6e5ffa"},
{file = "alembic-1.11.2-py3-none-any.whl", hash = "sha256:7981ab0c4fad4fe1be0cf183aae17689fe394ff874fd2464adb774396faf0796"},
{file = "alembic-1.11.2.tar.gz", hash = "sha256:678f662130dc540dac12de0ea73de9f89caea9dbea138f60ef6263149bf84657"},
]
[package.dependencies]
Mako = "*"
SQLAlchemy = ">=1.3.0"
typing-extensions = ">=4"
[package.extras]
tz = ["python-dateutil"]
@ -630,18 +631,18 @@ Flask = "*"
[[package]]
name = "flask-migrate"
version = "3.1.0"
version = "4.0.4"
description = "SQLAlchemy database migrations for Flask applications using Alembic."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "Flask-Migrate-3.1.0.tar.gz", hash = "sha256:57d6060839e3a7f150eaab6fe4e726d9e3e7cffe2150fb223d73f92421c6d1d9"},
{file = "Flask_Migrate-3.1.0-py3-none-any.whl", hash = "sha256:a6498706241aba6be7a251078de9cf166d74307bca41a4ca3e403c9d39e2f897"},
{file = "Flask-Migrate-4.0.4.tar.gz", hash = "sha256:73293d40b10ac17736e715b377e7b7bde474cb8105165d77474df4c3619b10b3"},
{file = "Flask_Migrate-4.0.4-py3-none-any.whl", hash = "sha256:77580f27ab39bc68be4906a43c56d7674b45075bc4f883b1d0b985db5164d58f"},
]
[package.dependencies]
alembic = ">=0.7"
alembic = ">=1.9.0"
Flask = ">=0.9"
Flask-SQLAlchemy = ">=1.0"
@ -2014,77 +2015,81 @@ files = [
[[package]]
name = "sqlalchemy"
version = "1.4.43"
version = "2.0.19"
description = "Database Abstraction Library"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
python-versions = ">=3.7"
files = [
{file = "SQLAlchemy-1.4.43-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:491d94879f9ec0dea7e1cb053cd9cc65a28d2467960cf99f7b3c286590406060"},
{file = "SQLAlchemy-1.4.43-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eeb55a555eef1a9607c1635bbdddd0b8a2bb9713bcb5bc8da1e8fae8ee46d1d8"},
{file = "SQLAlchemy-1.4.43-cp27-cp27m-win32.whl", hash = "sha256:7d6293010aa0af8bd3b0c9993259f8979db2422d6abf85a31d70ec69cb2ee4dc"},
{file = "SQLAlchemy-1.4.43-cp27-cp27m-win_amd64.whl", hash = "sha256:27479b5a1e110e64c56b18ffbf8cf99e101572a3d1a43943ea02158f1304108e"},
{file = "SQLAlchemy-1.4.43-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:13ce4f3a068ec4ef7598d2a77f42adc3d90c76981f5a7c198756b25c4f4a22ea"},
{file = "SQLAlchemy-1.4.43-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:aa12e27cb465b4b006ffb777624fc6023363e01cfed2d3f89d33fb6da80f6de2"},
{file = "SQLAlchemy-1.4.43-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d16aca30fad4753aeb4ebde564bbd4a248b9673e4f879b940f4e806a17be87f"},
{file = "SQLAlchemy-1.4.43-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cde363fb5412ab178f1cc1e596e9cfc396464da8a4fe8e733cc6d6b4e2c23aa9"},
{file = "SQLAlchemy-1.4.43-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4abda3e693d24169221ffc7aa0444ccef3dc43dfeab6ad8665d3836751cd6af7"},
{file = "SQLAlchemy-1.4.43-cp310-cp310-win32.whl", hash = "sha256:fa46d86a17cccd48c6762df1a60aecf5aaa2e0c0973efacf146c637694b62ffd"},
{file = "SQLAlchemy-1.4.43-cp310-cp310-win_amd64.whl", hash = "sha256:962c7c80c54a42836c47cb0d8a53016986c8584e8d98e90e2ea723a4ed0ba85b"},
{file = "SQLAlchemy-1.4.43-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6e036714a586f757a3e12ff0798ce9a90aa04a60cff392d8bcacc5ecf79c95e"},
{file = "SQLAlchemy-1.4.43-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d05d7365c2d1df03a69d90157a3e9b3e7b62088cca8ee6686aed2598659a6e14"},
{file = "SQLAlchemy-1.4.43-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59bd0ae166253f7fed8c3f4f6265d2637f25d2f6614d00df34d7ee0d95d29c91"},
{file = "SQLAlchemy-1.4.43-cp311-cp311-win32.whl", hash = "sha256:0c8a174f23bc021aac97bcb27fbe2ae3d4652d3d23e5768bc2ec3d44e386c7eb"},
{file = "SQLAlchemy-1.4.43-cp311-cp311-win_amd64.whl", hash = "sha256:5d5937e1bf7921e4d1acdfad72dd98d9e7f9ea5c52aeb12b3b05b534b527692d"},
{file = "SQLAlchemy-1.4.43-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ed1c950aba723b7a5b702b88f05d883607c587de918d7d8c2014fe7f55cf67e0"},
{file = "SQLAlchemy-1.4.43-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5438f6c768b7e928f0463777b545965648ba0d55877afd14a4e96d2a99702e7"},
{file = "SQLAlchemy-1.4.43-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:41df873cdae1d56fde97a1b4f6ffa118f40e4b2d6a6aa8c25c50eea31ecbeb08"},
{file = "SQLAlchemy-1.4.43-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22f46440e61d90100e0f378faac40335fb5bbf278472df0d83dc15b653b9896"},
{file = "SQLAlchemy-1.4.43-cp36-cp36m-win32.whl", hash = "sha256:529e2cc8af75811114e5ab2eb116fd71b6e252c6bdb32adbfcd5e0c5f6d5ab06"},
{file = "SQLAlchemy-1.4.43-cp36-cp36m-win_amd64.whl", hash = "sha256:c1ced2fae7a1177a36cf94d0a5567452d195d3b4d7d932dd61f123fb15ddf87b"},
{file = "SQLAlchemy-1.4.43-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:736d4e706adb3c95a0a7e660073a5213dfae78ff2df6addf8ff2918c83fbeebe"},
{file = "SQLAlchemy-1.4.43-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23a4569d3db1ce44370d05c5ad79be4f37915fcc97387aef9da232b95db7b695"},
{file = "SQLAlchemy-1.4.43-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:42bff29eaecbb284f614f4bb265bb0c268625f5b93ce6268f8017811e0afbdde"},
{file = "SQLAlchemy-1.4.43-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee9613b0460dce970414cfc990ca40afe518bc139e697243fcdf890285fb30ac"},
{file = "SQLAlchemy-1.4.43-cp37-cp37m-win32.whl", hash = "sha256:dc1e005d490c101d27657481a05765851ab795cc8aedeb8d9425595088b20736"},
{file = "SQLAlchemy-1.4.43-cp37-cp37m-win_amd64.whl", hash = "sha256:c9a6e878e63286392b262d86d21fe16e6eec12b95ccb0a92c392f2b1e0acca03"},
{file = "SQLAlchemy-1.4.43-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:c6de20de7c19b965c007c9da240268dde1451865099ca10f0f593c347041b845"},
{file = "SQLAlchemy-1.4.43-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fef01240d32ada9007387afd8e0b2230f99efdc4b57ca6f1d1192fca4fcf6a5"},
{file = "SQLAlchemy-1.4.43-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b6fd58e25e6cdd2a131d7e97f9713f8f2142360cd40c75af8aa5b83d535f811c"},
{file = "SQLAlchemy-1.4.43-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35dc0a5e934c41e282e019c889069b01ff4cd356b2ea452c9985e1542734cfb1"},
{file = "SQLAlchemy-1.4.43-cp38-cp38-win32.whl", hash = "sha256:fb9a44e7124f72b79023ab04e1c8fcd8f392939ef0d7a75beae8634e15605d30"},
{file = "SQLAlchemy-1.4.43-cp38-cp38-win_amd64.whl", hash = "sha256:4a791e7a1e5ac33f70a3598f8f34fdd3b60c68593bbb038baf58bc50e02d7468"},
{file = "SQLAlchemy-1.4.43-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:c9b59863e2b1f1e1ebf9ee517f86cdfa82d7049c8d81ad71ab58d442b137bbe9"},
{file = "SQLAlchemy-1.4.43-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd80300d81d92661e2488a4bf4383f0c5dc6e7b05fa46d2823e231af4e30539a"},
{file = "SQLAlchemy-1.4.43-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c3dde668edea70dc8d55a74d933d5446e5a97786cdd1c67c8e4971c73bd087ad"},
{file = "SQLAlchemy-1.4.43-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b462c070769f0ef06ea5fe65206b970bcf2b59cb3fda2bec2f4729e1be89c13"},
{file = "SQLAlchemy-1.4.43-cp39-cp39-win32.whl", hash = "sha256:c1f5bfffc3227d05d90c557b10604962f655b4a83c9f3ad507a81ac8d6847679"},
{file = "SQLAlchemy-1.4.43-cp39-cp39-win_amd64.whl", hash = "sha256:a7fa3e57a7b0476fbcba72b231150503d53dbcbdd23f4a86be5152912a923b6e"},
{file = "SQLAlchemy-1.4.43.tar.gz", hash = "sha256:c628697aad7a141da8fc3fd81b4874a711cc84af172e1b1e7bbfadf760446496"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9deaae357edc2091a9ed5d25e9ee8bba98bcfae454b3911adeaf159c2e9ca9e3"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0bf0fd65b50a330261ec7fe3d091dfc1c577483c96a9fa1e4323e932961aa1b5"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d90ccc15ba1baa345796a8fb1965223ca7ded2d235ccbef80a47b85cea2d71a"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4e688f6784427e5f9479d1a13617f573de8f7d4aa713ba82813bcd16e259d1"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:584f66e5e1979a7a00f4935015840be627e31ca29ad13f49a6e51e97a3fb8cae"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c69ce70047b801d2aba3e5ff3cba32014558966109fecab0c39d16c18510f15"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-win32.whl", hash = "sha256:96f0463573469579d32ad0c91929548d78314ef95c210a8115346271beeeaaa2"},
{file = "SQLAlchemy-2.0.19-cp310-cp310-win_amd64.whl", hash = "sha256:22bafb1da60c24514c141a7ff852b52f9f573fb933b1e6b5263f0daa28ce6db9"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d6894708eeb81f6d8193e996257223b6bb4041cb05a17cd5cf373ed836ef87a2"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8f2afd1aafded7362b397581772c670f20ea84d0a780b93a1a1529da7c3d369"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15afbf5aa76f2241184c1d3b61af1a72ba31ce4161013d7cb5c4c2fca04fd6e"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fc05b59142445a4efb9c1fd75c334b431d35c304b0e33f4fa0ff1ea4890f92e"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5831138f0cc06b43edf5f99541c64adf0ab0d41f9a4471fd63b54ae18399e4de"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3afa8a21a9046917b3a12ffe016ba7ebe7a55a6fc0c7d950beb303c735c3c3ad"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-win32.whl", hash = "sha256:c896d4e6ab2eba2afa1d56be3d0b936c56d4666e789bfc59d6ae76e9fcf46145"},
{file = "SQLAlchemy-2.0.19-cp311-cp311-win_amd64.whl", hash = "sha256:024d2f67fb3ec697555e48caeb7147cfe2c08065a4f1a52d93c3d44fc8e6ad1c"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:89bc2b374ebee1a02fd2eae6fd0570b5ad897ee514e0f84c5c137c942772aa0c"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd4d410a76c3762511ae075d50f379ae09551d92525aa5bb307f8343bf7c2c12"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f469f15068cd8351826df4080ffe4cc6377c5bf7d29b5a07b0e717dddb4c7ea2"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cda283700c984e699e8ef0fcc5c61f00c9d14b6f65a4f2767c97242513fcdd84"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:43699eb3f80920cc39a380c159ae21c8a8924fe071bccb68fc509e099420b148"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-win32.whl", hash = "sha256:61ada5831db36d897e28eb95f0f81814525e0d7927fb51145526c4e63174920b"},
{file = "SQLAlchemy-2.0.19-cp37-cp37m-win_amd64.whl", hash = "sha256:57d100a421d9ab4874f51285c059003292433c648df6abe6c9c904e5bd5b0828"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:16a310f5bc75a5b2ce7cb656d0e76eb13440b8354f927ff15cbaddd2523ee2d1"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf7b5e3856cbf1876da4e9d9715546fa26b6e0ba1a682d5ed2fc3ca4c7c3ec5b"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e7b69d9ced4b53310a87117824b23c509c6fc1f692aa7272d47561347e133b6"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9eb4575bfa5afc4b066528302bf12083da3175f71b64a43a7c0badda2be365"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6b54d1ad7a162857bb7c8ef689049c7cd9eae2f38864fc096d62ae10bc100c7d"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5d6afc41ca0ecf373366fd8e10aee2797128d3ae45eb8467b19da4899bcd1ee0"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-win32.whl", hash = "sha256:430614f18443b58ceb9dedec323ecddc0abb2b34e79d03503b5a7579cd73a531"},
{file = "SQLAlchemy-2.0.19-cp38-cp38-win_amd64.whl", hash = "sha256:eb60699de43ba1a1f77363f563bb2c652f7748127ba3a774f7cf2c7804aa0d3d"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a752b7a9aceb0ba173955d4f780c64ee15a1a991f1c52d307d6215c6c73b3a4c"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7351c05db355da112e056a7b731253cbeffab9dfdb3be1e895368513c7d70106"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa51ce4aea583b0c6b426f4b0563d3535c1c75986c4373a0987d84d22376585b"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae7473a67cd82a41decfea58c0eac581209a0aa30f8bc9190926fbf628bb17f7"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:851a37898a8a39783aab603c7348eb5b20d83c76a14766a43f56e6ad422d1ec8"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:539010665c90e60c4a1650afe4ab49ca100c74e6aef882466f1de6471d414be7"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-win32.whl", hash = "sha256:f82c310ddf97b04e1392c33cf9a70909e0ae10a7e2ddc1d64495e3abdc5d19fb"},
{file = "SQLAlchemy-2.0.19-cp39-cp39-win_amd64.whl", hash = "sha256:8e712cfd2e07b801bc6b60fdf64853bc2bd0af33ca8fa46166a23fe11ce0dbb0"},
{file = "SQLAlchemy-2.0.19-py3-none-any.whl", hash = "sha256:314145c1389b021a9ad5aa3a18bac6f5d939f9087d7fc5443be28cba19d2c972"},
{file = "SQLAlchemy-2.0.19.tar.gz", hash = "sha256:77a14fa20264af73ddcdb1e2b9c5a829b8cc6b8304d0f093271980e36c200a3f"},
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and platform_machine == \"aarch64\" or python_version >= \"3\" and platform_machine == \"ppc64le\" or python_version >= \"3\" and platform_machine == \"x86_64\" or python_version >= \"3\" and platform_machine == \"amd64\" or python_version >= \"3\" and platform_machine == \"AMD64\" or python_version >= \"3\" and platform_machine == \"win32\" or python_version >= \"3\" and platform_machine == \"WIN32\""}
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
typing-extensions = ">=4.2.0"
[package.extras]
aiomysql = ["aiomysql", "greenlet (!=0.4.17)"]
aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing-extensions (!=3.10.0.1)"]
asyncio = ["greenlet (!=0.4.17)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"]
mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"]
mssql = ["pyodbc"]
mssql-pymssql = ["pymssql"]
mssql-pyodbc = ["pyodbc"]
mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
mypy = ["mypy (>=0.910)"]
mysql = ["mysqlclient (>=1.4.0)"]
mysql-connector = ["mysql-connector-python"]
oracle = ["cx-oracle (>=7)", "cx-oracle (>=7,<8)"]
oracle = ["cx-oracle (>=7)"]
oracle-oracledb = ["oracledb (>=1.0.1)"]
postgresql = ["psycopg2 (>=2.7)"]
postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
postgresql-pg8000 = ["pg8000 (>=1.29.1)"]
postgresql-psycopg = ["psycopg (>=3.0.7)"]
postgresql-psycopg2binary = ["psycopg2-binary"]
postgresql-psycopg2cffi = ["psycopg2cffi"]
pymysql = ["pymysql", "pymysql (<1)"]
postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"]
pymysql = ["pymysql"]
sqlcipher = ["sqlcipher3-binary"]
[[package]]
@ -2434,4 +2439,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.0"
python-versions = "~3.10" # updating to 3.11 causes instability; see https://github.com/themotte/rDrama/issues/446
content-hash = "b97cdf54a01257851bd007b5f8c52c4805dbd7f5b14b55f31df00bcce5376f51"
content-hash = "655195eb22dcc8772215d908f8fe3bc9ad32318fe7fb60b7c45a9a473c6bb4a1"

View file

@ -14,7 +14,7 @@ Flask-Caching = "*"
Flask-Compress = "*"
Flask-Limiter = "*"
Flask-Mail = "*"
Flask-Migrate = "*"
Flask-Migrate = "^4.0.4"
Flask-Socketio = "*"
flask_profiler = "*"
gevent = "*"
@ -31,7 +31,7 @@ python-dotenv = "*"
qrcode = "*"
redis = "*"
requests = "*"
SQLAlchemy = "^1.4.43"
SQLAlchemy = "^2.0.19"
user-agents = "*"
psycopg2-binary = "*"
pusher_push_notifications = "*"