From 3775191fd19cb8340154d75db3ffe2ce57508677 Mon Sep 17 00:00:00 2001 From: Aleeeehh Date: Sat, 2 Aug 2025 04:07:17 +0200 Subject: [PATCH] Add queue APIs --- backend/endpoints/queue.py | 167 ++++++++++++++++++++++++++++++++++++ backend/main.py | 2 + testing/index.html | 43 ++++++++++ testing/script.js | 170 +++++++++++++++++++++++++++++++++++++ 4 files changed, 382 insertions(+) create mode 100644 backend/endpoints/queue.py diff --git a/backend/endpoints/queue.py b/backend/endpoints/queue.py new file mode 100644 index 0000000..2eb6e34 --- /dev/null +++ b/backend/endpoints/queue.py @@ -0,0 +1,167 @@ +from fastapi import APIRouter, HTTPException +from pymongo import MongoClient +from bson import ObjectId +from datetime import datetime +from pydantic import BaseModel +from typing import Optional + +# Router per le API della queue +queue_router = APIRouter(prefix="/queue") + +# Connessione MongoDB +client = MongoClient("mongodb://localhost:27017/") +db = client["simple_db"] +queue_collection = db["queue"] + +# Modelli Pydantic +class QueueItem(BaseModel): + id: str + titolo: str + coverUrl: str + color: str + artista: str + voti: int = 0 + +class QueueItemCreate(BaseModel): + titolo: str + coverUrl: str + color: str + artista: str + voti: int = 0 + +class QueueItemUpdate(BaseModel): + titolo: Optional[str] = None + coverUrl: Optional[str] = None + color: Optional[str] = None + artista: Optional[str] = None + +class VoteUpdate(BaseModel): + increment: bool = True # True per incrementare, False per decrementare + +@queue_router.post("/add") +async def add_item(item: QueueItemCreate): + """POST: Aggiunge un nuovo item alla queue""" + try: + # Genera un ID univoco + import uuid + item_id = str(uuid.uuid4()) + + new_item = { + "id": item_id, + "titolo": item.titolo, + "coverUrl": item.coverUrl, + "color": item.color, + "artista": item.artista, + "voti": item.voti, + "created_at": datetime.now().isoformat() + } + + result = queue_collection.insert_one(new_item) + new_item["_id"] = str(result.inserted_id) + + return {"success": True, "message": "Item aggiunto alla queue", "data": new_item} + except Exception as e: + raise HTTPException(status_code=500, detail=f"Errore aggiunta item: {str(e)}") + +@queue_router.get("/read") +async def read_queue(): + """GET: Legge tutti gli item della queue ordinati per voti""" + try: + # Trova tutti gli item ordinati per voti (decrescente) + items = list(queue_collection.find().sort("voti", -1)) + + # Converti ObjectId in stringa + for item in items: + item["_id"] = str(item["_id"]) + + return {"success": True, "data": items, "count": len(items)} + except Exception as e: + raise HTTPException(status_code=500, detail=f"Errore lettura queue: {str(e)}") + +@queue_router.get("/read/{item_id}") +async def read_item(item_id: str): + """GET: Legge un singolo item per ID""" + try: + item = queue_collection.find_one({"id": item_id}) + if item: + item["_id"] = str(item["_id"]) + return {"success": True, "data": item} + else: + raise HTTPException(status_code=404, detail="Item non trovato") + except HTTPException: + raise + except Exception as e: + raise HTTPException(status_code=500, detail=f"Errore lettura item: {str(e)}") + +@queue_router.delete("/delete/{item_id}") +async def delete_item(item_id: str): + """DELETE: Elimina un item dalla queue""" + try: + result = queue_collection.delete_one({"id": item_id}) + + if result.deleted_count > 0: + return {"success": True, "message": "Item eliminato dalla queue"} + else: + raise HTTPException(status_code=404, detail="Item non trovato") + except HTTPException: + raise + except Exception as e: + raise HTTPException(status_code=500, detail=f"Errore eliminazione item: {str(e)}") + +@queue_router.put("/edit/{item_id}") +async def edit_item(item_id: str, item_update: QueueItemUpdate): + """PUT: Modifica un item nella queue""" + try: + # Prepara i campi da aggiornare + update_data = {} + for field, value in item_update.dict(exclude_unset=True).items(): + if value is not None: + update_data[field] = value + + if not update_data: + raise HTTPException(status_code=400, detail="Nessun campo da aggiornare") + + update_data["updated_at"] = datetime.now().isoformat() + + result = queue_collection.update_one( + {"id": item_id}, + {"$set": update_data} + ) + + if result.modified_count > 0: + return {"success": True, "message": "Item aggiornato"} + else: + raise HTTPException(status_code=404, detail="Item non trovato") + except HTTPException: + raise + except Exception as e: + raise HTTPException(status_code=500, detail=f"Errore aggiornamento item: {str(e)}") + +@queue_router.patch("/vote/{item_id}") +async def update_vote(item_id: str, vote_update: VoteUpdate): + """PATCH: Incrementa/decrementa i voti di un item""" + try: + increment_value = 1 if vote_update.increment else -1 + + result = queue_collection.update_one( + {"id": item_id}, + {"$inc": {"voti": increment_value}} + ) + + if result.modified_count > 0: + # Recupera l'item aggiornato + updated_item = queue_collection.find_one({"id": item_id}) + updated_item["_id"] = str(updated_item["_id"]) + + action = "incrementato" if vote_update.increment else "decrementato" + return { + "success": True, + "message": f"Voti {action} con successo", + "data": updated_item + } + else: + raise HTTPException(status_code=404, detail="Item non trovato") + except HTTPException: + raise + except Exception as e: + raise HTTPException(status_code=500, detail=f"Errore aggiornamento voti: {str(e)}") \ No newline at end of file diff --git a/backend/main.py b/backend/main.py index b44d4ae..4c797e9 100644 --- a/backend/main.py +++ b/backend/main.py @@ -3,6 +3,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from endpoints.geo_access import geo_access_router +from endpoints.queue import queue_router from routes import router load_dotenv() @@ -21,6 +22,7 @@ app.add_middleware( # Includi le route app.include_router(router) app.include_router(geo_access_router) +app.include_router(queue_router) @app.post("/auth") diff --git a/testing/index.html b/testing/index.html index d5dc0f2..0445d23 100644 --- a/testing/index.html +++ b/testing/index.html @@ -72,6 +72,41 @@ .btn-molinella:hover { background-color: #5D4037; } + .btn-queue-add { + background-color: #4CAF50; + color: white; + } + .btn-queue-add:hover { + background-color: #45a049; + } + .btn-queue-read { + background-color: #2196F3; + color: white; + } + .btn-queue-read:hover { + background-color: #1976D2; + } + .btn-queue-vote-up { + background-color: #FF9800; + color: white; + } + .btn-queue-vote-up:hover { + background-color: #F57C00; + } + .btn-queue-vote-down { + background-color: #F44336; + color: white; + } + .btn-queue-vote-down:hover { + background-color: #D32F2F; + } + .btn-queue-delete { + background-color: #9C27B0; + color: white; + } + .btn-queue-delete:hover { + background-color: #7B1FA2; + } .result { background-color: #f9f9f9; border: 1px solid #ddd; @@ -112,6 +147,14 @@ +
+ + + + + +
+
diff --git a/testing/script.js b/testing/script.js index f3de589..3709bcb 100644 --- a/testing/script.js +++ b/testing/script.js @@ -192,6 +192,176 @@ async function sendMolinellaCoordinates() { } } +// Queue CRUD Operations + +// Add song to queue +async function addQueueItem() { + try { + console.log('🎡 Aggiunta canzone alla queue...'); + showStatus('🎡 Aggiunta canzone alla queue...', true); + + // Canzone hardcoded + const song = { + "titolo": "Bohemian Rhapsody", + "coverUrl": "https://i.scdn.co/image/abc123", + "color": "#3b5998", + "artista": "Queen", + "voti": 0 + }; + + const response = await fetch(`${API_BASE_URL}/queue/add`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(song) + }); + + const data = await response.json(); + + if (response.ok) { + showStatus('βœ… Canzone aggiunta alla queue!', true); + showResult(data); + } else { + showStatus(`❌ Errore: ${data.detail || 'Errore sconosciuto'}`, false); + } + } catch (error) { + console.error('Errore aggiunta canzone:', error); + showStatus(`❌ Errore di connessione: ${error.message}`, false); + } +} + +// Read queue +async function readQueue() { + try { + console.log('πŸ“‹ Lettura queue...'); + showStatus('πŸ“‹ Lettura queue...', true); + + const response = await fetch(`${API_BASE_URL}/queue/read`); + const data = await response.json(); + + if (response.ok) { + showStatus(`βœ… Queue letta! ${data.count} canzoni trovate`, true); + showResult(data); + } else { + showStatus(`❌ Errore: ${data.detail || 'Errore sconosciuto'}`, false); + } + } catch (error) { + console.error('Errore lettura queue:', error); + showStatus(`❌ Errore di connessione: ${error.message}`, false); + } +} + +// Vote up (incrementa voti) +async function voteUp() { + try { + console.log('πŸ‘ Voto positivo...'); + showStatus('πŸ‘ Voto positivo...', true); + + // Per semplicitΓ , vota il primo item della queue + const response = await fetch(`${API_BASE_URL}/queue/read`); + const queueData = await response.json(); + + if (response.ok && queueData.data.length > 0) { + const firstItemId = queueData.data[0].id; + + const voteResponse = await fetch(`${API_BASE_URL}/queue/vote/${firstItemId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ "increment": true }) + }); + + const voteData = await voteResponse.json(); + + if (voteResponse.ok) { + showStatus('βœ… Voto positivo aggiunto!', true); + showResult(voteData); + } else { + showStatus(`❌ Errore: ${voteData.detail || 'Errore sconosciuto'}`, false); + } + } else { + showStatus('❌ Nessuna canzone nella queue da votare', false); + } + } catch (error) { + console.error('Errore voto positivo:', error); + showStatus(`❌ Errore di connessione: ${error.message}`, false); + } +} + +// Vote down (decrementa voti) +async function voteDown() { + try { + console.log('πŸ‘Ž Voto negativo...'); + showStatus('πŸ‘Ž Voto negativo...', true); + + // Per semplicitΓ , vota il primo item della queue + const response = await fetch(`${API_BASE_URL}/queue/read`); + const queueData = await response.json(); + + if (response.ok && queueData.data.length > 0) { + const firstItemId = queueData.data[0].id; + + const voteResponse = await fetch(`${API_BASE_URL}/queue/vote/${firstItemId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ "increment": false }) + }); + + const voteData = await voteResponse.json(); + + if (voteResponse.ok) { + showStatus('βœ… Voto negativo aggiunto!', true); + showResult(voteData); + } else { + showStatus(`❌ Errore: ${voteData.detail || 'Errore sconosciuto'}`, false); + } + } else { + showStatus('❌ Nessuna canzone nella queue da votare', false); + } + } catch (error) { + console.error('Errore voto negativo:', error); + showStatus(`❌ Errore di connessione: ${error.message}`, false); + } +} + +// Delete item from queue +async function deleteQueueItem() { + try { + console.log('πŸ—‘οΈ Eliminazione item dalla queue...'); + showStatus('πŸ—‘οΈ Eliminazione item dalla queue...', true); + + // Per semplicitΓ , elimina il primo item della queue + const response = await fetch(`${API_BASE_URL}/queue/read`); + const queueData = await response.json(); + + if (response.ok && queueData.data.length > 0) { + const firstItemId = queueData.data[0].id; + + const deleteResponse = await fetch(`${API_BASE_URL}/queue/delete/${firstItemId}`, { + method: 'DELETE' + }); + + const deleteData = await deleteResponse.json(); + + if (deleteResponse.ok) { + showStatus('βœ… Item eliminato dalla queue!', true); + showResult(deleteData); + } else { + showStatus(`❌ Errore: ${deleteData.detail || 'Errore sconosciuto'}`, false); + } + } else { + showStatus('❌ Nessuna canzone nella queue da eliminare', false); + } + } catch (error) { + console.error('Errore eliminazione item:', error); + showStatus(`❌ Errore di connessione: ${error.message}`, false); + } +} + // Test di connessione all'avvio window.addEventListener('load', async () => { try {