Phase 2 UI Adjustments/Edits by bonamin
This commit is contained in:
@@ -4,6 +4,7 @@ from config import settings
|
||||
from shared.firebase import init_firebase, firebase_initialized
|
||||
from auth.router import router as auth_router
|
||||
from melodies.router import router as melodies_router
|
||||
from settings.router import router as settings_router
|
||||
|
||||
app = FastAPI(
|
||||
title="BellSystems Admin Panel",
|
||||
@@ -22,6 +23,7 @@ app.add_middleware(
|
||||
|
||||
app.include_router(auth_router)
|
||||
app.include_router(melodies_router)
|
||||
app.include_router(settings_router)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Dict, List, Optional
|
||||
from enum import Enum
|
||||
|
||||
|
||||
@@ -17,13 +17,13 @@ class MelodyTone(str, Enum):
|
||||
|
||||
|
||||
class MelodyInfo(BaseModel):
|
||||
name: str
|
||||
description: str = ""
|
||||
name: Dict[str, str] = {}
|
||||
description: Dict[str, str] = {}
|
||||
melodyTone: MelodyTone = MelodyTone.normal
|
||||
customTags: List[str] = []
|
||||
minSpeed: int = 0
|
||||
maxSpeed: int = 0
|
||||
totalNotes: int = 1
|
||||
totalNotes: int = Field(default=1, ge=1, le=16)
|
||||
steps: int = 0
|
||||
color: str = ""
|
||||
isTrueRing: bool = False
|
||||
@@ -32,7 +32,7 @@ class MelodyInfo(BaseModel):
|
||||
|
||||
|
||||
class MelodyAttributes(BaseModel):
|
||||
speed: int = 0
|
||||
speed: int = 50
|
||||
duration: int = 0
|
||||
totalRunDuration: int = 0
|
||||
pauseDuration: int = 0
|
||||
|
||||
@@ -8,6 +8,13 @@ COLLECTION = "melodies"
|
||||
def _doc_to_melody(doc) -> MelodyInDB:
|
||||
"""Convert a Firestore document snapshot to a MelodyInDB model."""
|
||||
data = doc.to_dict()
|
||||
# Backward compat: if name/description are plain strings, wrap as dict
|
||||
info = data.get("information", {})
|
||||
if isinstance(info.get("name"), str):
|
||||
info["name"] = {"en": info["name"]} if info["name"] else {}
|
||||
if isinstance(info.get("description"), str):
|
||||
info["description"] = {"en": info["description"]} if info["description"] else {}
|
||||
data["information"] = info
|
||||
return MelodyInDB(id=doc.id, **data)
|
||||
|
||||
|
||||
@@ -41,8 +48,16 @@ def list_melodies(
|
||||
continue
|
||||
if search:
|
||||
search_lower = search.lower()
|
||||
name_match = search_lower in melody.information.name.lower()
|
||||
desc_match = search_lower in melody.information.description.lower()
|
||||
name_match = any(
|
||||
search_lower in v.lower()
|
||||
for v in melody.information.name.values()
|
||||
if isinstance(v, str)
|
||||
)
|
||||
desc_match = any(
|
||||
search_lower in v.lower()
|
||||
for v in melody.information.description.values()
|
||||
if isinstance(v, str)
|
||||
)
|
||||
tag_match = any(search_lower in t.lower() for t in melody.information.customTags)
|
||||
if not (name_match or desc_match or tag_match):
|
||||
continue
|
||||
|
||||
@@ -7,3 +7,4 @@ paho-mqtt==2.1.0
|
||||
python-jose[cryptography]==3.3.0
|
||||
passlib[bcrypt]==1.7.4
|
||||
python-multipart==0.0.20
|
||||
bcrypt==4.0.1
|
||||
0
backend/settings/__init__.py
Normal file
0
backend/settings/__init__.py
Normal file
19
backend/settings/models.py
Normal file
19
backend/settings/models.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class MelodySettings(BaseModel):
|
||||
available_languages: List[str] = ["en", "el", "sr"]
|
||||
primary_language: str = "en"
|
||||
quick_colors: List[str] = ["#FF5733", "#33FF57", "#3357FF", "#FFD700", "#FF69B4", "#8B4513"]
|
||||
duration_values: List[int] = [
|
||||
0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165, 180,
|
||||
240, 300, 360, 420, 480, 540, 600, 900,
|
||||
]
|
||||
|
||||
|
||||
class MelodySettingsUpdate(BaseModel):
|
||||
available_languages: Optional[List[str]] = None
|
||||
primary_language: Optional[str] = None
|
||||
quick_colors: Optional[List[str]] = None
|
||||
duration_values: Optional[List[int]] = None
|
||||
22
backend/settings/router.py
Normal file
22
backend/settings/router.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from fastapi import APIRouter, Depends
|
||||
from auth.models import TokenPayload
|
||||
from auth.dependencies import require_melody_access, require_viewer
|
||||
from settings.models import MelodySettings, MelodySettingsUpdate
|
||||
from settings import service
|
||||
|
||||
router = APIRouter(prefix="/api/settings", tags=["settings"])
|
||||
|
||||
|
||||
@router.get("/melody", response_model=MelodySettings)
|
||||
async def get_melody_settings(
|
||||
_user: TokenPayload = Depends(require_viewer),
|
||||
):
|
||||
return service.get_melody_settings()
|
||||
|
||||
|
||||
@router.put("/melody", response_model=MelodySettings)
|
||||
async def update_melody_settings(
|
||||
body: MelodySettingsUpdate,
|
||||
_user: TokenPayload = Depends(require_melody_access),
|
||||
):
|
||||
return service.update_melody_settings(body)
|
||||
39
backend/settings/service.py
Normal file
39
backend/settings/service.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from shared.firebase import get_db
|
||||
from settings.models import MelodySettings, MelodySettingsUpdate
|
||||
|
||||
COLLECTION = "admin_settings"
|
||||
DOC_ID = "melody_settings"
|
||||
|
||||
|
||||
def get_melody_settings() -> MelodySettings:
|
||||
"""Get melody settings from Firestore. Creates defaults if not found."""
|
||||
db = get_db()
|
||||
doc = db.collection(COLLECTION).document(DOC_ID).get()
|
||||
if doc.exists:
|
||||
return MelodySettings(**doc.to_dict())
|
||||
# Create with defaults
|
||||
defaults = MelodySettings()
|
||||
db.collection(COLLECTION).document(DOC_ID).set(defaults.model_dump())
|
||||
return defaults
|
||||
|
||||
|
||||
def update_melody_settings(data: MelodySettingsUpdate) -> MelodySettings:
|
||||
"""Update melody settings. Only provided fields are updated."""
|
||||
db = get_db()
|
||||
doc_ref = db.collection(COLLECTION).document(DOC_ID)
|
||||
doc = doc_ref.get()
|
||||
|
||||
if doc.exists:
|
||||
existing = doc.to_dict()
|
||||
else:
|
||||
existing = MelodySettings().model_dump()
|
||||
|
||||
update_data = data.model_dump(exclude_none=True)
|
||||
existing.update(update_data)
|
||||
|
||||
# Sort duration values
|
||||
if "duration_values" in existing:
|
||||
existing["duration_values"] = sorted(existing["duration_values"])
|
||||
|
||||
doc_ref.set(existing)
|
||||
return MelodySettings(**existing)
|
||||
Reference in New Issue
Block a user