mirror of
https://github.com/283375/arcaea-offline.git
synced 2025-09-13 15:03:09 +00:00
Compare commits
4 Commits
ff248d1363
...
0.3.0-refa
Author | SHA1 | Date | |
---|---|---|---|
2dc96e06aa
|
|||
308b087b94
|
|||
a8164f37e2
|
|||
ab03b27730
|
@ -0,0 +1,31 @@
|
|||||||
|
"""version date
|
||||||
|
|
||||||
|
Revision ID: f82ac2f445a5
|
||||||
|
Revises: 0ca6733e40dc
|
||||||
|
Create Date: 2025-07-19 17:11:27.448574
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
revision: str = "f82ac2f445a5"
|
||||||
|
down_revision: Union[str, None] = "0ca6733e40dc"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.create_table(
|
||||||
|
"version_date",
|
||||||
|
sa.Column("version", sa.String(), nullable=False),
|
||||||
|
sa.Column("songlist_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.Column("published_at", sa.DateTime(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint("version", name=op.f("pk_version_date")),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_table("version_date")
|
@ -4,6 +4,7 @@ from .config import Property
|
|||||||
from .difficulty import Difficulty, DifficultyLocalization
|
from .difficulty import Difficulty, DifficultyLocalization
|
||||||
from .pack import Pack, PackLocalization
|
from .pack import Pack, PackLocalization
|
||||||
from .song import Song, SongLocalization
|
from .song import Song, SongLocalization
|
||||||
|
from .version_date import VersionDate
|
||||||
|
|
||||||
from .chart import Chart # isort: skip
|
from .chart import Chart # isort: skip
|
||||||
from .play_result import (
|
from .play_result import (
|
||||||
@ -29,4 +30,5 @@ __all__ = [
|
|||||||
"Property",
|
"Property",
|
||||||
"Song",
|
"Song",
|
||||||
"SongLocalization",
|
"SongLocalization",
|
||||||
|
"VersionDate",
|
||||||
]
|
]
|
||||||
|
@ -4,10 +4,13 @@ from sqlalchemy import MetaData
|
|||||||
from sqlalchemy.orm import DeclarativeBase
|
from sqlalchemy.orm import DeclarativeBase
|
||||||
from sqlalchemy.orm.exc import DetachedInstanceError
|
from sqlalchemy.orm.exc import DetachedInstanceError
|
||||||
|
|
||||||
from ._types import ForceTimezoneDateTime
|
from arcaea_offline.utils import Version
|
||||||
|
|
||||||
|
from ._types import ForceTimezoneDateTime, VersionDatabaseType
|
||||||
|
|
||||||
TYPE_ANNOTATION_MAP = {
|
TYPE_ANNOTATION_MAP = {
|
||||||
datetime: ForceTimezoneDateTime,
|
datetime: ForceTimezoneDateTime,
|
||||||
|
Version: VersionDatabaseType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from sqlalchemy import DateTime
|
from sqlalchemy import DateTime, String
|
||||||
from sqlalchemy.types import TypeDecorator
|
from sqlalchemy.types import TypeDecorator
|
||||||
|
|
||||||
|
from arcaea_offline.utils import Version
|
||||||
|
|
||||||
|
|
||||||
class ForceTimezoneDateTime(TypeDecorator):
|
class ForceTimezoneDateTime(TypeDecorator):
|
||||||
"""
|
"""
|
||||||
@ -26,3 +28,23 @@ class ForceTimezoneDateTime(TypeDecorator):
|
|||||||
if value is not None:
|
if value is not None:
|
||||||
value = value.replace(tzinfo=timezone.utc)
|
value = value.replace(tzinfo=timezone.utc)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
class VersionDatabaseType(TypeDecorator):
|
||||||
|
impl = String
|
||||||
|
cache_ok = True
|
||||||
|
|
||||||
|
def process_bind_param(self, value: Optional[Version], dialect):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not isinstance(value, Version):
|
||||||
|
raise ValueError("Input is not a Version instance.")
|
||||||
|
|
||||||
|
return str(f"{value.first}.{value.second}.{value.third}")
|
||||||
|
|
||||||
|
def process_result_value(self, value: Optional[str], dialect):
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Version(*(map(int, value.split("."))))
|
||||||
|
@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Optional
|
|||||||
from sqlalchemy import ForeignKeyConstraint, Integer, Numeric, String
|
from sqlalchemy import ForeignKeyConstraint, Integer, Numeric, String
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
from arcaea_offline.utils import Version
|
||||||
|
|
||||||
from ._base import ModelBase, ReprHelper
|
from ._base import ModelBase, ReprHelper
|
||||||
from ._types import ForceTimezoneDateTime
|
from ._types import ForceTimezoneDateTime
|
||||||
|
|
||||||
@ -28,6 +30,6 @@ class ChartInfo(ModelBase, ReprHelper):
|
|||||||
song_id: Mapped[str] = mapped_column(String, primary_key=True)
|
song_id: Mapped[str] = mapped_column(String, primary_key=True)
|
||||||
rating_class: Mapped[int] = mapped_column(Integer, primary_key=True)
|
rating_class: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||||
constant: Mapped[Decimal] = mapped_column(Numeric, nullable=False)
|
constant: Mapped[Decimal] = mapped_column(Numeric, nullable=False)
|
||||||
notes: Mapped[int] = mapped_column(Integer)
|
notes: Mapped[Optional[int]] = mapped_column(Integer)
|
||||||
added_at: Mapped[datetime] = mapped_column(ForceTimezoneDateTime, primary_key=True)
|
added_at: Mapped[datetime] = mapped_column(ForceTimezoneDateTime, primary_key=True)
|
||||||
version: Mapped[Optional[str]] = mapped_column(String)
|
version: Mapped[Optional[Version]]
|
||||||
|
@ -13,6 +13,8 @@ from sqlalchemy import (
|
|||||||
)
|
)
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
from arcaea_offline.utils import Version
|
||||||
|
|
||||||
from ._base import ModelBase, ReprHelper
|
from ._base import ModelBase, ReprHelper
|
||||||
from ._types import ForceTimezoneDateTime
|
from ._types import ForceTimezoneDateTime
|
||||||
|
|
||||||
@ -65,7 +67,7 @@ class Difficulty(ModelBase, ReprHelper):
|
|||||||
bpm: Mapped[Optional[str]] = mapped_column(String)
|
bpm: Mapped[Optional[str]] = mapped_column(String)
|
||||||
bpm_base: Mapped[Optional[Decimal]] = mapped_column(Numeric(asdecimal=True))
|
bpm_base: Mapped[Optional[Decimal]] = mapped_column(Numeric(asdecimal=True))
|
||||||
added_at: Mapped[Optional[datetime]] = mapped_column(ForceTimezoneDateTime)
|
added_at: Mapped[Optional[datetime]] = mapped_column(ForceTimezoneDateTime)
|
||||||
version: Mapped[Optional[str]] = mapped_column(String)
|
version: Mapped[Optional[Version]]
|
||||||
is_legacy11: Mapped[bool] = mapped_column(
|
is_legacy11: Mapped[bool] = mapped_column(
|
||||||
Boolean, nullable=False, insert_default=False, server_default=text("0")
|
Boolean, nullable=False, insert_default=False, server_default=text("0")
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,8 @@ from typing import TYPE_CHECKING, Optional
|
|||||||
from sqlalchemy import Boolean, ForeignKey, Integer, Numeric, String, text
|
from sqlalchemy import Boolean, ForeignKey, Integer, Numeric, String, text
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||||
|
|
||||||
|
from arcaea_offline.utils import Version
|
||||||
|
|
||||||
from ._base import ModelBase, ReprHelper
|
from ._base import ModelBase, ReprHelper
|
||||||
from ._types import ForceTimezoneDateTime
|
from ._types import ForceTimezoneDateTime
|
||||||
|
|
||||||
@ -42,7 +44,7 @@ class Song(ModelBase, ReprHelper):
|
|||||||
added_at: Mapped[datetime] = mapped_column(
|
added_at: Mapped[datetime] = mapped_column(
|
||||||
ForceTimezoneDateTime, nullable=False, index=True
|
ForceTimezoneDateTime, nullable=False, index=True
|
||||||
)
|
)
|
||||||
version: Mapped[Optional[str]] = mapped_column(String)
|
version: Mapped[Optional[Version]]
|
||||||
|
|
||||||
bpm: Mapped[Optional[str]] = mapped_column(String)
|
bpm: Mapped[Optional[str]] = mapped_column(String)
|
||||||
bpm_base: Mapped[Optional[Decimal]] = mapped_column(Numeric(asdecimal=True))
|
bpm_base: Mapped[Optional[Decimal]] = mapped_column(Numeric(asdecimal=True))
|
||||||
|
15
src/arcaea_offline/database/models/version_date.py
Normal file
15
src/arcaea_offline/database/models/version_date.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
|
from arcaea_offline.utils import Version
|
||||||
|
|
||||||
|
from ._base import ModelBase, ReprHelper
|
||||||
|
|
||||||
|
|
||||||
|
class VersionDate(ModelBase, ReprHelper):
|
||||||
|
__tablename__ = "version_date"
|
||||||
|
|
||||||
|
version: Mapped[Version] = mapped_column(primary_key=True)
|
||||||
|
songlist_at: Mapped[datetime]
|
||||||
|
published_at: Mapped[datetime]
|
@ -0,0 +1,5 @@
|
|||||||
|
from .version import Version
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"Version",
|
||||||
|
]
|
||||||
|
27
src/arcaea_offline/utils/version.py
Normal file
27
src/arcaea_offline/utils/version.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
|
||||||
|
class Version(NamedTuple):
|
||||||
|
first: int
|
||||||
|
second: int
|
||||||
|
third: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, version_str: str):
|
||||||
|
version_str = version_str.removesuffix("c")
|
||||||
|
parts = version_str.split(".")
|
||||||
|
if len(parts) not in {2, 3}:
|
||||||
|
raise ValueError(f"Invalid version string {version_str}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
if len(parts) == 2: # noqa: PLR2004
|
||||||
|
parts.append("0")
|
||||||
|
|
||||||
|
first, second, third = map(int, parts)
|
||||||
|
except ValueError as e:
|
||||||
|
raise ValueError(f"Invalid version string {version_str}") from e
|
||||||
|
|
||||||
|
return cls(first, second, third)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.first}.{self.second}.{self.third}"
|
Reference in New Issue
Block a user