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,
|
Mapping,
|
||||||
Protocol,
|
Protocol,
|
||||||
Type,
|
Type,
|
||||||
|
TypeAlias,
|
||||||
|
TypeAliasType,
|
||||||
TypedDict,
|
TypedDict,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Union,
|
Union,
|
||||||
|
|
@ -25,8 +27,9 @@ from sqlalchemy.orm import registry
|
||||||
|
|
||||||
from .types import ULID
|
from .types import ULID
|
||||||
|
|
||||||
JSON = int | float | str | None | list["JSON"] | dict[str, "JSON"]
|
JSONScalar: TypeAlias = int | float | str | None
|
||||||
JSONObject = dict[str, JSON]
|
type JSON = JSONScalar | list["JSON"] | dict[str, "JSON"]
|
||||||
|
type JSONObject = dict[str, JSON]
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
@ -126,6 +129,10 @@ def asplain(
|
||||||
continue
|
continue
|
||||||
|
|
||||||
target: Any = f.type
|
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
|
# XXX this doesn't properly support any kind of nested types
|
||||||
if (otype := optional_type(f.type)) is not None:
|
if (otype := optional_type(f.type)) is not None:
|
||||||
target = otype
|
target = otype
|
||||||
|
|
@ -150,10 +157,13 @@ def asplain(
|
||||||
elif target in {bool, str, int, float}:
|
elif target in {bool, str, int, float}:
|
||||||
assert isinstance(
|
assert isinstance(
|
||||||
v, target
|
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
|
d[f.name] = v
|
||||||
else:
|
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
|
return d
|
||||||
|
|
||||||
|
|
@ -196,18 +206,24 @@ def fromplain(cls: Type[T], d: Mapping, *, serialized: bool = False) -> T:
|
||||||
|
|
||||||
def validate(o: object) -> None:
|
def validate(o: object) -> None:
|
||||||
for f in fields(o):
|
for f in fields(o):
|
||||||
vtype = type(getattr(o, f.name))
|
ftype = f.type
|
||||||
if vtype is 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
|
continue
|
||||||
|
|
||||||
origin = get_origin(f.type)
|
origin = get_origin(ftype)
|
||||||
if origin is vtype:
|
if origin is vtype:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
is_union = isinstance(f.type, UnionType) or origin is Union
|
is_union = isinstance(ftype, UnionType) or origin is Union
|
||||||
if is_union:
|
if is_union:
|
||||||
# Support unioned types.
|
# Support unioned types.
|
||||||
utypes = get_args(f.type)
|
utypes = get_args(ftype)
|
||||||
if vtype in utypes:
|
if vtype in utypes:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -216,7 +232,14 @@ def validate(o: object) -> None:
|
||||||
if any(vtype is gtype for gtype in gtypes):
|
if any(vtype is gtype for gtype in gtypes):
|
||||||
continue
|
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:
|
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
|
The contents of the Relation are ignored or discarded when using
|
||||||
`asplain`, `fromplain`, and `validate`.
|
`asplain`, `fromplain`, and `validate`.
|
||||||
"""
|
"""
|
||||||
Relation = Annotated[T | None, _RelationSentinel]
|
Relation: TypeAlias = Annotated[T | None, _RelationSentinel]
|
||||||
|
|
||||||
|
|
||||||
Access = Literal[
|
type Access = Literal[
|
||||||
"r", # read
|
"r", # read
|
||||||
"i", # index
|
"i", # index
|
||||||
"w", # write
|
"w", # write
|
||||||
|
|
@ -413,6 +436,9 @@ class User:
|
||||||
self.groups.append({"id": group_id, "access": access})
|
self.groups.append({"id": group_id, "access": access})
|
||||||
|
|
||||||
|
|
||||||
|
users = User.__table__
|
||||||
|
|
||||||
|
|
||||||
@mapper_registry.mapped
|
@mapper_registry.mapped
|
||||||
@dataclass
|
@dataclass
|
||||||
class Rating:
|
class Rating:
|
||||||
|
|
@ -477,3 +503,41 @@ class Group:
|
||||||
id: ULID = field(default_factory=ULID)
|
id: ULID = field(default_factory=ULID)
|
||||||
name: str = None
|
name: str = None
|
||||||
users: list[GroupUser] = field(default_factory=list)
|
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