Refactor code structure for improved readability and maintainability

This commit is contained in:
JSC
2025-07-22 13:21:44 +02:00
parent 11796b1012
commit fefb7f7bf4
26 changed files with 1424 additions and 7 deletions

1
app/models/__init__.py Normal file
View File

@@ -0,0 +1 @@
"""Models package."""

13
app/models/base.py Normal file
View File

@@ -0,0 +1,13 @@
from datetime import UTC, datetime
from sqlmodel import Field, SQLModel
class BaseModel(SQLModel):
"""Base model with common fields for all models."""
id: int | None = Field(primary_key=True, default=None)
# timestamps
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))

21
app/models/plan.py Normal file
View File

@@ -0,0 +1,21 @@
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.user import User
class Plan(BaseModel, table=True):
"""Database model for a plan."""
code: str = Field(index=True, unique=True, nullable=False)
name: str = Field(nullable=False)
description: str | None = Field(default=None)
credits: int = Field(default=0, ge=0, nullable=False)
max_credits: int = Field(default=0, ge=0, nullable=False)
# relationships
users: list["User"] = Relationship(back_populates="plan")

26
app/models/playlist.py Normal file
View File

@@ -0,0 +1,26 @@
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.playlist_sound import PlaylistSound
from app.models.user import User
class Playlist(BaseModel, table=True):
"""Database model for a playlist."""
user_id: int | None = Field(foreign_key="user.id", default=None)
name: str = Field(unique=True, nullable=False)
description: str | None = Field(default=None)
genre: str | None = Field(default=None)
is_main: bool = Field(default=False, nullable=False)
is_current: bool = Field(default=False, nullable=False)
is_deletable: bool = Field(default=True, nullable=False)
# relationships
user: "User" = Relationship(back_populates="playlists")
playlist_sounds: list["PlaylistSound"] = Relationship(back_populates="playlist")

View File

@@ -0,0 +1,37 @@
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship, UniqueConstraint
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.playlist import Playlist
from app.models.sound import Sound
class PlaylistSound(BaseModel, table=True):
"""Database model for a sound in a playlist."""
__tablename__ = "playlist_sound" # pyright: ignore[reportAssignmentType]
playlist_id: int = Field(foreign_key="playlist.id", nullable=False)
sound_id: int = Field(foreign_key="sound.id", nullable=False)
position: int = Field(default=0, ge=0, nullable=False)
# constraints
__table_args__ = (
UniqueConstraint(
"playlist_id",
"sound_id",
name="uq_playlist_sound_playlist_sound",
),
UniqueConstraint(
"playlist_id",
"position",
name="uq_playlist_sound_playlist_position",
),
)
# relationships
playlist: "Playlist" = Relationship(back_populates="playlist_sounds")
sound: "Sound" = Relationship(back_populates="playlist_sounds")

34
app/models/sound.py Normal file
View File

@@ -0,0 +1,34 @@
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.playlist_sound import PlaylistSound
from app.models.stream import Stream
class Sound(BaseModel, table=True):
"""Database model for a sound."""
type: str = Field(nullable=False)
name: str = Field(nullable=False)
filename: str = Field(nullable=False)
duration: int = Field(default=0, ge=0, nullable=False)
size: int = Field(default=0, ge=0, nullable=False)
hash: str = Field(nullable=False)
normalized_filename: str | None = Field(default=None)
normalized_duration: int | None = Field(default=None, ge=0)
normalized_size: int | None = Field(default=None, ge=0)
normalized_hash: str | None = Field(default=None)
thumbnail: str | None = Field(default=None)
play_count: int = Field(default=0, ge=0, nullable=False)
is_normalized: bool = Field(default=False, nullable=False)
is_music: bool = Field(default=False, nullable=False)
is_deletable: bool = Field(default=True, nullable=False)
# relationships
playlist_sounds: list["PlaylistSound"] = Relationship(back_populates="sound")
streams: list["Stream"] = Relationship(back_populates="sound")

View File

@@ -0,0 +1,32 @@
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship, UniqueConstraint
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.sound import Sound
from app.models.user import User
class SoundPlayed(BaseModel, table=True):
"""Database model for a sound played."""
__tablename__ = "sound_played" # pyright: ignore[reportAssignmentType]
user_id: int = Field(foreign_key="user.id", nullable=False)
sound_id: int = Field(foreign_key="sound.id", nullable=False)
# constraints
__table_args__ = (
UniqueConstraint(
"user_id",
"sound_id",
name="uq_sound_played_user_sound",
),
)
# relationships
user: "User" = Relationship(back_populates="sounds_played")
sound: "Sound" = Relationship(back_populates="play_history")

40
app/models/stream.py Normal file
View File

@@ -0,0 +1,40 @@
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship, UniqueConstraint
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.sound import Sound
from app.models.user import User
class Stream(BaseModel, table=True):
"""Database model for a stream."""
service: str = Field(nullable=False)
service_id: str = Field(nullable=False)
user_id: int = Field(foreign_key="user.id", nullable=False)
sound_id: int | None = Field(foreign_key="sound.id", default=None)
url: str = Field(nullable=False)
title: str | None = Field(default=None)
track: str | None = Field(default=None)
artist: str | None = Field(default=None)
album: str | None = Field(default=None)
genre: str | None = Field(default=None)
status: str = Field(nullable=False, default="pending")
error: str | None = Field(default=None)
# constraints
__table_args__ = (
UniqueConstraint(
"service",
"service_id",
name="uq_stream_service_service_id",
),
)
# relationships
sound: "Sound" = Relationship(back_populates="streams")
user: "User" = Relationship(back_populates="streams")

32
app/models/user.py Normal file
View File

@@ -0,0 +1,32 @@
from datetime import datetime
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.plan import Plan
from app.models.playlist import Playlist
from app.models.user_oauth import UserOauth
class User(BaseModel, table=True):
"""Database model for a user."""
plan_id: int = Field(foreign_key="plan.id")
role: str = Field(nullable=False, default="user")
email: str = Field(unique=True, nullable=False)
name: str = Field(nullable=False)
picture: str | None = Field(default=None)
password_hash: str | None = Field(default=None)
is_active: bool = Field(nullable=False, default=True)
credits: int = Field(default=0, ge=0, nullable=False)
api_token: str | None = Field(unique=True, default=None)
api_token_expires_at: datetime | None = Field(default=None)
# relationships
oauths: list["UserOauth"] = Relationship(back_populates="user")
plan: "Plan" = Relationship(back_populates="users")
playlists: list["Playlist"] = Relationship(back_populates="user")

34
app/models/user_oauth.py Normal file
View File

@@ -0,0 +1,34 @@
from typing import TYPE_CHECKING
from sqlmodel import Field, Relationship, UniqueConstraint
from app.models.base import BaseModel
if TYPE_CHECKING:
from app.models.user import User
class UserOauth(BaseModel, table=True):
"""Database model for a user OAuth."""
__tablename__ = "user_oauth" # pyright: ignore[reportAssignmentType]
user_id: int = Field(foreign_key="user.id", nullable=False)
provider: str = Field(nullable=False)
provider_user_id: str = Field(nullable=False)
email: str = Field(nullable=False)
name: str = Field(nullable=False)
picture: str | None = Field(default=None)
# constraints
__table_args__ = (
UniqueConstraint(
"provider",
"provider_user_id",
name="uq_user_oauth_provider_user_id",
),
)
# relationships
user: "User" = Relationship(back_populates="oauths")