import hashlib import os from datetime import datetime, timezone import requests from fastapi import HTTPException from src.fingering import normalize_fingering_state def _score_row_to_payload(row: dict) -> dict: score_data = row.get("score_data") or {} parts = score_data.get("parts") or [] musicxml_source = score_data.get("musicxml_source") or "" fingered_musicxml_source = score_data.get("fingered_musicxml_source") or "" fingered_sheet_html = score_data.get("fingered_sheet_html") or "" right_hand = parts[0]["notes"] if parts else [] left_hand = [] for part in parts[1:]: for event in part.get("notes", []): pitches = event[0] if isinstance(event[0], list) else [event[0]] duration = event[2] if len(event) > 2 else 0.75 merged = [pitches, event[1], duration] if len(event) > 3: merged.append(event[3]) if len(event) > 4: if len(merged) == 3: merged.append(None) merged.extend(event[4:]) left_hand.append(merged) left_hand.sort(key=lambda event: event[1]) sheet_html = row.get("sheet_html") or "" has_sheet = "= 400: raise HTTPException(status_code=500, detail=f"Supabase error: {response.text}") if not response.text: return None return response.json() def list_scores(self, user_id: str): rows = self._request( "GET", "scores", params={ "select": "slug,sheet_html,score_data", "user_id": f"eq.{user_id}", "order": "created_at.desc", }, ) or [] names = [row["slug"] for row in rows if row.get("slug")] return { "scores": names, "items": [ { "name": row["slug"], "has_sheet": "