Merge 2550f4d765
into 3187a372fe
This commit is contained in:
commit
394c62b66d
31 changed files with 422 additions and 368 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -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})>"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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?)")
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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...
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import pprint
|
||||
|
||||
import sqlalchemy
|
||||
import sqlalchemy.orm
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from alive_progress import alive_it
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
123
poetry.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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 = "*"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue