Skip to content

Commit

Permalink
Merge pull request #963 from rommapp/feature/fav_for_siblings
Browse files Browse the repository at this point in the history
Select main sibling
  • Loading branch information
zurdi15 authored Jul 2, 2024
2 parents 9a32f6b + 8ef52b8 commit 31fbb1b
Show file tree
Hide file tree
Showing 31 changed files with 846 additions and 475 deletions.
97 changes: 97 additions & 0 deletions backend/alembic/versions/0021_rom_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""empty message
Revision ID: 0021_rom_user
Revises: 0020_created_and_updated
Create Date: 2024-06-29 00:11:51.800988
"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "0021_rom_user"
down_revision = "0020_created_and_updated"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"rom_user",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column("note_raw_markdown", sa.Text(), nullable=False),
sa.Column("note_is_public", sa.Boolean(), nullable=True),
sa.Column("is_main_sibling", sa.Boolean(), nullable=False),
sa.Column("rom_id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(["rom_id"], ["roms.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("rom_id", "user_id", name="unique_rom_user_props"),
)

op.execute(
"""
INSERT INTO rom_user (id, updated_at, note_raw_markdown, note_is_public, is_main_sibling, rom_id, user_id)
SELECT id, updated_at, raw_markdown, is_public, FALSE, rom_id, user_id
FROM rom_notes
"""
)

op.drop_table("rom_notes")
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.create_table(
"rom_notes",
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column(
"updated_at",
sa.DateTime(timezone=True),
server_default=sa.text("now()"),
nullable=False,
),
sa.Column("raw_markdown", sa.Text(), nullable=False),
sa.Column("is_public", sa.Boolean(), nullable=True),
sa.Column("rom_id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(["rom_id"], ["roms.id"], ondelete="CASCADE"),
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("rom_id", "user_id", name="unique_rom_user_note"),
)

# Copy the data back from the new table to the old table
op.execute(
"""
INSERT INTO rom_notes (id, updated_at, raw_markdown, is_public, rom_id, user_id)
SELECT id, updated_at, note_raw_markdown, note_is_public, rom_id, user_id
FROM rom_user
"""
)

# Drop the new table
op.drop_table("rom_user")
# ### end Alembic commands ###
92 changes: 43 additions & 49 deletions backend/endpoints/feeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,40 +30,37 @@ def platforms_webrcade_feed(request: Request) -> WebrcadeFeedSchema:

platforms = db_platform_handler.get_platforms()

with db_platform_handler.session.begin() as session:
return {
"title": "RomM Feed",
"longTitle": "Custom RomM Feed",
"description": "Custom feed from your RomM library",
"thumbnail": "https://raw.githubusercontent.com/rommapp/romm/f2dd425d87ad8e21bf47f8258ae5dcf90f56fbc2/frontend/assets/isotipo.svg",
"background": "https://raw.githubusercontent.com/rommapp/romm/release/.github/screenshots/gallery.png",
"categories": [
{
"title": p.name,
"longTitle": f"{p.name} Games",
"background": f"{ROMM_HOST}/assets/webrcade/feed/{p.slug.lower()}-background.png",
"thumbnail": f"{ROMM_HOST}/assets/webrcade/feed/{p.slug.lower()}-thumb.png",
"description": "",
"items": [
{
"title": rom.name,
"description": rom.summary,
"type": WEBRCADE_SLUG_TO_TYPE_MAP.get(p.slug, p.slug),
"thumbnail": f"{ROMM_HOST}/assets/romm/resources/{rom.path_cover_s}",
"background": f"{ROMM_HOST}/assets/romm/resources/{rom.path_cover_l}",
"props": {
"rom": f"{ROMM_HOST}/api/roms/{rom.id}/content/{rom.file_name}"
},
}
for rom in session.scalars(
db_rom_handler.get_roms(platform_id=p.id)
).all()
],
}
for p in platforms
if p.slug in WEBRCADE_SUPPORTED_PLATFORM_SLUGS
],
}
return {
"title": "RomM Feed",
"longTitle": "Custom RomM Feed",
"description": "Custom feed from your RomM library",
"thumbnail": "https://raw.githubusercontent.com/rommapp/romm/f2dd425d87ad8e21bf47f8258ae5dcf90f56fbc2/frontend/assets/isotipo.svg",
"background": "https://raw.githubusercontent.com/rommapp/romm/release/.github/screenshots/gallery.png",
"categories": [
{
"title": p.name,
"longTitle": f"{p.name} Games",
"background": f"{ROMM_HOST}/assets/webrcade/feed/{p.slug.lower()}-background.png",
"thumbnail": f"{ROMM_HOST}/assets/webrcade/feed/{p.slug.lower()}-thumb.png",
"description": "",
"items": [
{
"title": rom.name,
"description": rom.summary,
"type": WEBRCADE_SLUG_TO_TYPE_MAP.get(p.slug, p.slug),
"thumbnail": f"{ROMM_HOST}/assets/romm/resources/{rom.path_cover_s}",
"background": f"{ROMM_HOST}/assets/romm/resources/{rom.path_cover_l}",
"props": {
"rom": f"{ROMM_HOST}/api/roms/{rom.id}/content/{rom.file_name}"
},
}
for rom in db_rom_handler.get_roms(platform_id=p.id)
],
}
for p in platforms
if p.slug in WEBRCADE_SUPPORTED_PLATFORM_SLUGS
],
}


@protected_route(router.get, "/tinfoil/feed", ["roms.read"])
Expand All @@ -79,19 +76,16 @@ def tinfoil_index_feed(request: Request, slug: str = "switch") -> TinfoilFeedSch
TinfoilFeedSchema: Tinfoil feed object schema
"""
switch = db_platform_handler.get_platform_by_fs_slug(slug)
with db_rom_handler.session.begin() as session:
files: list[Rom] = session.scalars(
db_rom_handler.get_roms(platform_id=switch.id)
).all()
files: list[Rom] = db_rom_handler.get_roms(platform_id=switch.id)

return {
"files": [
{
"url": f"{ROMM_HOST}/api/roms/{file.id}/content/{file.file_name}",
"size": file.file_size_bytes,
}
for file in files
],
"directories": [],
"success": "RomM Switch Library",
}
return {
"files": [
{
"url": f"{ROMM_HOST}/api/roms/{file.id}/content/{file.file_name}",
"size": file.file_size_bytes,
}
for file in files
],
"directories": [],
"success": "RomM Switch Library",
}
15 changes: 10 additions & 5 deletions backend/endpoints/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
from decorators.auth import protected_route
from endpoints.responses import MessageResponse
from endpoints.responses.platform import PlatformSchema
from exceptions.endpoint_exceptions import PlatformNotFoundInDatabaseException
from exceptions.fs_exceptions import PlatformAlreadyExistsException
from fastapi import APIRouter, HTTPException, Request, status
from fastapi import APIRouter, Request
from handler.database import db_platform_handler
from handler.filesystem import fs_platform_handler
from handler.metadata.igdb_handler import IGDB_PLATFORM_LIST
Expand Down Expand Up @@ -100,7 +101,12 @@ def get_platform(request: Request, id: int) -> PlatformSchema:
PlatformSchema: Platform
"""

return db_platform_handler.get_platform(id)
platform = db_platform_handler.get_platform(id)

if not platform:
raise PlatformNotFoundInDatabaseException(id)

return platform


@protected_route(router.put, "/platforms/{id}", ["platforms.write"])
Expand Down Expand Up @@ -135,10 +141,9 @@ async def delete_platforms(request: Request, id: int) -> MessageResponse:
"""

platform = db_platform_handler.get_platform(id)

if not platform:
error = f"Platform id {id} not found"
log.error(error)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error)
raise PlatformNotFoundInDatabaseException(id)

log.info(f"Deleting {platform.name} [{platform.fs_slug}] from database")
db_platform_handler.delete_platform(id)
Expand Down
58 changes: 48 additions & 10 deletions backend/endpoints/responses/rom.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,39 @@
)


class RomNoteSchema(BaseModel):
class RomUserSchema(BaseModel):
id: int
user_id: int
rom_id: int
created_at: datetime
updated_at: datetime
raw_markdown: str
is_public: bool
note_raw_markdown: str
note_is_public: bool
is_main_sibling: bool
user__username: str

class Config:
from_attributes = True

@classmethod
def for_user(cls, db_rom: Rom, user_id: int) -> list[RomNoteSchema]:
def for_user(cls, db_rom: Rom, user_id: int) -> RomUserSchema | None:
for n in db_rom.rom_users:
if n.user_id == user_id:
return cls.model_validate(n)

return None

@classmethod
def notes_for_user(cls, db_rom: Rom, user_id: int) -> list[UserNotesSchema]:
return [
cls.model_validate(n)
for n in db_rom.notes
{
"user_id": n.user_id,
"username": n.user__username,
"note_raw_markdown": n.note_raw_markdown,
}
for n in db_rom.rom_users
# This is what filters out private notes
if n.user_id == user_id or n.is_public
if n.user_id == user_id or n.note_is_public
]


Expand Down Expand Up @@ -95,13 +109,29 @@ class RomSchema(BaseModel):
multi: bool
files: list[str]
full_path: str

created_at: datetime
updated_at: datetime

rom_user: RomUserSchema | None = Field(default=None)

class Config:
from_attributes = True

@classmethod
def from_orm_with_request(cls, db_rom: Rom, request: Request) -> RomSchema:
rom = cls.model_validate(db_rom)
user_id = request.user.id

rom.rom_user = RomUserSchema.for_user(db_rom, user_id)

return rom

@classmethod
def from_orm_with_request_list(
cls, db_roms: list[Rom], request: Request
) -> list[RomSchema]:
return [cls.from_orm_with_request(rom, request) for rom in db_roms]

@computed_field # type: ignore
@property
def sort_comparator(self) -> str:
Expand All @@ -117,17 +147,20 @@ def sort_comparator(self) -> str:

class DetailedRomSchema(RomSchema):
merged_screenshots: list[str]
rom_user: RomUserSchema | None = Field(default=None)
sibling_roms: list[RomSchema] = Field(default_factory=list)
user_saves: list[SaveSchema] = Field(default_factory=list)
user_states: list[StateSchema] = Field(default_factory=list)
user_screenshots: list[ScreenshotSchema] = Field(default_factory=list)
user_notes: list[RomNoteSchema] = Field(default_factory=list)
user_notes: list[UserNotesSchema] = Field(default_factory=list)

@classmethod
def from_orm_with_request(cls, db_rom: Rom, request: Request) -> DetailedRomSchema:
rom = cls.model_validate(db_rom)
user_id = request.user.id

rom.rom_user = RomUserSchema.for_user(db_rom, user_id)
rom.user_notes = RomUserSchema.notes_for_user(db_rom, user_id)
rom.sibling_roms = [
RomSchema.model_validate(r) for r in db_rom.get_sibling_roms()
]
Expand All @@ -142,11 +175,16 @@ def from_orm_with_request(cls, db_rom: Rom, request: Request) -> DetailedRomSche
for s in db_rom.screenshots
if s.user_id == user_id
]
rom.user_notes = RomNoteSchema.for_user(db_rom, user_id)

return rom


class UserNotesSchema(TypedDict):
user_id: int
username: str
note_raw_markdown: str


class AddRomsResponse(TypedDict):
uploaded_roms: list[str]
skipped_roms: list[str]
Expand Down
Loading

0 comments on commit 31fbb1b

Please sign in to comment.