Add queue APIs
This commit is contained in:
parent
4e74bbb443
commit
3775191fd1
4 changed files with 382 additions and 0 deletions
167
backend/endpoints/queue.py
Normal file
167
backend/endpoints/queue.py
Normal 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)}")
|
|
@ -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")
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue