feat: add a table to store award information
This commit is contained in:
parent
5eb7211b59
commit
f102e07256
2 changed files with 120 additions and 12 deletions
44
alembic/versions/1716050110-62882ef5e3ff_add_awards_table.py
Normal file
44
alembic/versions/1716050110-62882ef5e3ff_add_awards_table.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"""add awards table
|
||||
|
||||
Revision ID: 62882ef5e3ff
|
||||
Revises: c08ae04dc482
|
||||
Create Date: 2024-05-18 16:35:10.145964+00:00
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "62882ef5e3ff"
|
||||
down_revision: str | None = "c08ae04dc482"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table(
|
||||
"awards",
|
||||
sa.Column("id", sa.String(), nullable=False),
|
||||
sa.Column("movie_id", sa.String(), nullable=False),
|
||||
sa.Column("category", sa.String(), nullable=False),
|
||||
sa.Column("details", sa.String(), nullable=False),
|
||||
sa.Column("created", sa.String(), nullable=False),
|
||||
sa.Column("updated", sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["movie_id"],
|
||||
["movies.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table("awards")
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -13,6 +13,8 @@ from typing import (
|
|||
Mapping,
|
||||
Protocol,
|
||||
Type,
|
||||
TypeAlias,
|
||||
TypeAliasType,
|
||||
TypedDict,
|
||||
TypeVar,
|
||||
Union,
|
||||
|
|
@ -25,8 +27,9 @@ from sqlalchemy.orm import registry
|
|||
|
||||
from .types import ULID
|
||||
|
||||
JSON = int | float | str | None | list["JSON"] | dict[str, "JSON"]
|
||||
JSONObject = dict[str, JSON]
|
||||
JSONScalar: TypeAlias = int | float | str | None
|
||||
type JSON = JSONScalar | list["JSON"] | dict[str, "JSON"]
|
||||
type JSONObject = dict[str, JSON]
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
|
@ -126,6 +129,10 @@ def asplain(
|
|||
continue
|
||||
|
||||
target: Any = f.type
|
||||
if isinstance(target, TypeAliasType):
|
||||
# Support type aliases.
|
||||
target = target.__value__
|
||||
|
||||
# XXX this doesn't properly support any kind of nested types
|
||||
if (otype := optional_type(f.type)) is not None:
|
||||
target = otype
|
||||
|
|
@ -150,10 +157,13 @@ def asplain(
|
|||
elif target in {bool, str, int, float}:
|
||||
assert isinstance(
|
||||
v, target
|
||||
), f"Type mismatch: {f.name} ({target} != {type(v)})"
|
||||
), f"Type mismatch: {f.name!a} ({target!a} != {type(v)!a})"
|
||||
d[f.name] = v
|
||||
elif target in {Literal}:
|
||||
assert isinstance(v, JSONScalar)
|
||||
d[f.name] = v
|
||||
else:
|
||||
raise ValueError(f"Unsupported value type: {f.name}: {type(v)}")
|
||||
raise ValueError(f"Unsupported value type: {f.name!a}: {type(v)!a}")
|
||||
|
||||
return d
|
||||
|
||||
|
|
@ -196,18 +206,24 @@ def fromplain(cls: Type[T], d: Mapping, *, serialized: bool = False) -> T:
|
|||
|
||||
def validate(o: object) -> None:
|
||||
for f in fields(o):
|
||||
vtype = type(getattr(o, f.name))
|
||||
if vtype is f.type:
|
||||
ftype = f.type
|
||||
if isinstance(ftype, TypeAliasType):
|
||||
# Support type aliases.
|
||||
ftype = ftype.__value__
|
||||
|
||||
v = getattr(o, f.name)
|
||||
vtype = type(v)
|
||||
if vtype is ftype:
|
||||
continue
|
||||
|
||||
origin = get_origin(f.type)
|
||||
origin = get_origin(ftype)
|
||||
if origin is vtype:
|
||||
continue
|
||||
|
||||
is_union = isinstance(f.type, UnionType) or origin is Union
|
||||
is_union = isinstance(ftype, UnionType) or origin is Union
|
||||
if is_union:
|
||||
# Support unioned types.
|
||||
utypes = get_args(f.type)
|
||||
utypes = get_args(ftype)
|
||||
if vtype in utypes:
|
||||
continue
|
||||
|
||||
|
|
@ -216,7 +232,14 @@ def validate(o: object) -> None:
|
|||
if any(vtype is gtype for gtype in gtypes):
|
||||
continue
|
||||
|
||||
raise ValueError(f"Invalid value type: {f.name}: {vtype}")
|
||||
if origin is Literal:
|
||||
# Support literal types.
|
||||
vals = get_args(ftype)
|
||||
if v in vals:
|
||||
continue
|
||||
raise ValueError(f"Invalid value: {f.name!a}: {v!a}")
|
||||
|
||||
raise ValueError(f"Invalid value type: {f.name!a}: {vtype!a}")
|
||||
|
||||
|
||||
def utcnow() -> datetime:
|
||||
|
|
@ -365,10 +388,10 @@ dataclass containing the ID of the linked data.
|
|||
The contents of the Relation are ignored or discarded when using
|
||||
`asplain`, `fromplain`, and `validate`.
|
||||
"""
|
||||
Relation = Annotated[T | None, _RelationSentinel]
|
||||
Relation: TypeAlias = Annotated[T | None, _RelationSentinel]
|
||||
|
||||
|
||||
Access = Literal[
|
||||
type Access = Literal[
|
||||
"r", # read
|
||||
"i", # index
|
||||
"w", # write
|
||||
|
|
@ -413,6 +436,9 @@ class User:
|
|||
self.groups.append({"id": group_id, "access": access})
|
||||
|
||||
|
||||
users = User.__table__
|
||||
|
||||
|
||||
@mapper_registry.mapped
|
||||
@dataclass
|
||||
class Rating:
|
||||
|
|
@ -477,3 +503,41 @@ class Group:
|
|||
id: ULID = field(default_factory=ULID)
|
||||
name: str = None
|
||||
users: list[GroupUser] = field(default_factory=list)
|
||||
|
||||
|
||||
type AwardCategory = Literal[
|
||||
"imdb-top-250", "imdb-bottom-100", "imdb-pop-100", "oscars"
|
||||
]
|
||||
|
||||
|
||||
@mapper_registry.mapped
|
||||
@dataclass
|
||||
class Award:
|
||||
__table__: ClassVar[Table] = Table(
|
||||
"awards",
|
||||
metadata,
|
||||
Column("id", String, primary_key=True), # ULID
|
||||
Column("movie_id", ForeignKey("movies.id"), nullable=False), # ULID
|
||||
Column(
|
||||
"category", String, nullable=False
|
||||
), # Enum: "imdb-top-250", "imdb-bottom-100", "imdb-pop-100", "oscars", ...
|
||||
Column(
|
||||
"details", String, nullable=False
|
||||
), # e.g. "23" (position in list), "2024, nominee, best director", "1977, winner, best picture", ...
|
||||
Column("created", String, nullable=False), # datetime
|
||||
Column("updated", String, nullable=False), # datetime
|
||||
)
|
||||
|
||||
id: ULID = field(default_factory=ULID)
|
||||
|
||||
movie_id: ULID = None
|
||||
movie: Relation[Movie] = None
|
||||
|
||||
category: AwardCategory = None
|
||||
details: str = None
|
||||
|
||||
created: datetime = field(default_factory=utcnow)
|
||||
updated: datetime = field(default_factory=utcnow)
|
||||
|
||||
|
||||
awards = Award.__table__
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue