Add queue APIs

This commit is contained in:
Alessio Prato 2025-08-02 04:07:17 +02:00
parent 4e74bbb443
commit 3775191fd1
4 changed files with 382 additions and 0 deletions

167
backend/endpoints/queue.py Normal file
View file

@ -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)}")

View file

@ -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")

View file

@ -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 @@
<button class="btn-molinella" onclick="sendMolinellaCoordinates()">🏘️ Send Molinella coordinates</button>
</div>
<div class="button-group">
<button class="btn-queue-add" onclick="addQueueItem()">🎵 Add Song</button>
<button class="btn-queue-read" onclick="readQueue()">📋 Read Queue</button>
<button class="btn-queue-vote-up" onclick="voteUp()">👍 Vote Up</button>
<button class="btn-queue-vote-down" onclick="voteDown()">👎 Vote Down</button>
<button class="btn-queue-delete" onclick="deleteQueueItem()">🗑️ Delete Item</button>
</div>
<div id="status"></div>
<div id="result" class="result" style="display: none;"></div>
</div>

View file

@ -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 {