"""Upgrade pending artist page - add/update Spotify ID on PENDING_ARTIST records.""" import re import streamlit as st from common.db import get_session from common.environment import IS_PROD, SCHEMA_NAME from common.services import ( check_spotify_id_in_global_participant, check_spotify_id_in_virtual_participant_excluding, search_pending_artists, upgrade_pending_artist, ) from common.types import UpgradePendingArtistForm from common.utils import init_session_state, parse_spotify_id st.title("âŦ†ī¸ Upgrade Pending Artist") st.write("Add or update a Spotify ID on a pending artist record") # Get session session = get_session() # Show schema indicator if IS_PROD: st.error("âš ī¸ PRODUCTION MODE - Writes are BLOCKED for safety") else: st.info(f"📊 Using schema: {SCHEMA_NAME}") # Initialize session state init_session_state("selected_pending_artist_id", None) init_session_state("selected_pending_artist_name", None) init_session_state("selected_pending_artist_spotify_id", None) init_session_state("upgrade_spotify_id", "") init_session_state("upgrade_success", False) init_session_state("upgrade_error", None) st.divider() # ── Step 1: Select Pending Artist ────────────────────────────────────────────── st.subheader("Step 1: Select Pending Artist") name_search = st.text_input( "Search by name (leave blank for all)", help="Filter pending artists by name (ILIKE pattern match)", key="upgrade_name_search", ) # Search and display results with st.spinner("Searching pending artists..."): pending_artists = search_pending_artists(session, name_filter=name_search or None) if pending_artists: st.write(f"Found {len(pending_artists)} pending artist(s):") for idx, artist in enumerate(pending_artists): col_info, col_btn = st.columns([4, 1]) with col_info: spotify_status = ( f"Spotify ID: {artist.spotify_id}" if artist.spotify_id else "No Spotify ID" ) st.code(f"{artist.name} | {spotify_status}") with col_btn: if st.button("Select", key=f"select_pending_{artist.id}_{idx}"): st.session_state.selected_pending_artist_id = artist.id st.session_state.selected_pending_artist_name = artist.name st.session_state.selected_pending_artist_spotify_id = artist.spotify_id st.session_state.upgrade_spotify_id = "" st.rerun() else: st.warning("No pending artists found") # Show selected artist if st.session_state.selected_pending_artist_id: current_spotify = st.session_state.selected_pending_artist_spotify_id spotify_display = f" | Current Spotify ID: {current_spotify}" if current_spotify else "" st.success( f"✅ Selected: **{st.session_state.selected_pending_artist_name}**" f" (ID: {st.session_state.selected_pending_artist_id}{spotify_display})" ) st.divider() # ── Step 2: Enter Spotify ID ────────────────────────────────────────────────── st.subheader("Step 2: Enter Spotify ID") step1_done = st.session_state.selected_pending_artist_id is not None if not step1_done: st.warning("âš ī¸ Please select a pending artist first (Step 1)") spotify_id_input = st.text_input( "Spotify ID *", value=st.session_state.upgrade_spotify_id, disabled=not step1_done, help=( "Spotify Artist ID in any format:\n" "â€ĸ URL: https://open.spotify.com/artist/7tNO3vJC9zlHy2IJOx34ga\n" "â€ĸ URI: spotify:artist:4OGiMt96TFUKkKWf7Imlno\n" "â€ĸ Plain ID: 6zyG7x3rPnqitv8h5FATI9" ), key="upgrade_spotify_id_input", ) # Update session state if spotify_id_input != st.session_state.upgrade_spotify_id: st.session_state.upgrade_spotify_id = spotify_id_input # Real-time Spotify ID validation spotify_id_is_valid = False parsed_spotify_id = None if step1_done and spotify_id_input and spotify_id_input.strip(): try: parsed_spotify_id = parse_spotify_id(spotify_id_input) # Check format (22 alphanumeric characters) if not re.match(r"^[a-zA-Z0-9]{22}$", parsed_spotify_id): st.error("❌ Invalid Spotify ID format (must be 22 alphanumeric characters)") else: with st.spinner("Validating Spotify ID..."): # Check uniqueness in VP (excluding current record) in_other_vp = check_spotify_id_in_virtual_participant_excluding( session, parsed_spotify_id, st.session_state.selected_pending_artist_id, ) if in_other_vp: st.error( f"❌ Spotify ID '{parsed_spotify_id}' is already used " "by another virtual participant" ) else: spotify_id_is_valid = True st.success(f"✅ Spotify ID is valid: {parsed_spotify_id}") # Informational check: exists in GLOBAL_PARTICIPANT? in_global = check_spotify_id_in_global_participant( session, parsed_spotify_id ) if in_global: st.info( f"â„šī¸ Artist with Spotify ID '{parsed_spotify_id}' " "exists in GLOBAL_PARTICIPANT" ) else: st.info( f"â„šī¸ Spotify ID '{parsed_spotify_id}' " "not found in GLOBAL_PARTICIPANT" ) except ValueError as e: st.error(f"❌ {str(e)}") elif step1_done: st.warning("âš ī¸ Spotify ID is required") st.divider() # ── Step 3: Submit ───────────────────────────────────────────────────────────── st.subheader("Step 3: Submit") form_is_ready = step1_done and spotify_id_is_valid and parsed_spotify_id is not None if form_is_ready: st.success("✅ Ready to upgrade pending artist!") else: st.warning("âš ī¸ Please complete all steps above") with st.form("upgrade_pending_artist_form", clear_on_submit=False): submitted = st.form_submit_button( "âŦ†ī¸ Upgrade Pending Artist", type="primary" if form_is_ready else "secondary", disabled=not form_is_ready, ) if submitted: st.session_state.upgrade_success = False st.session_state.upgrade_error = None if not st.session_state.selected_pending_artist_id: st.session_state.upgrade_error = "No pending artist selected (Step 1)" elif not parsed_spotify_id: st.session_state.upgrade_error = "Spotify ID is required (Step 2)" else: try: form_data = UpgradePendingArtistForm( virtual_participant_id=st.session_state.selected_pending_artist_id, virtual_participant_name=st.session_state.selected_pending_artist_name, spotify_id=parsed_spotify_id, ) with st.spinner("Upgrading pending artist..."): upgrade_pending_artist(session, form_data) st.session_state.upgrade_success = True # Clear selection st.session_state.selected_pending_artist_id = None st.session_state.selected_pending_artist_name = None st.session_state.selected_pending_artist_spotify_id = None st.session_state.upgrade_spotify_id = "" except ValueError as e: st.session_state.upgrade_error = str(e) except Exception as e: st.session_state.upgrade_error = f"Unexpected error: {str(e)}" st.rerun() # Display messages outside form if st.session_state.upgrade_success: st.success("✅ Pending artist successfully upgraded with Spotify ID!") st.balloons() st.session_state.upgrade_success = False if st.session_state.upgrade_error: st.error(f"❌ Error: {st.session_state.upgrade_error}") st.session_state.upgrade_error = None st.divider() # Help section with st.expander("â„šī¸ Help & Rules"): st.markdown( """ ### Upgrading Pending Artists **What does this page do?** Updates the Spotify ID on an existing PENDING_ARTIST record in VIRTUAL_PARTICIPANT. The VP record stays intact — only the SPOTIFY_ID field is updated. **Steps:** 1. **Select** a pending artist from the search results 2. **Enter** the Spotify ID (URL, URI, or plain 22-char ID) 3. **Submit** to update the record **Validation Rules:** | Rule | Blocking? | |------|-----------| | Spotify ID format (22 alphanumeric chars) | Yes | | Not used by another virtual participant | Yes | | PROD write block | Yes | | VP must be PENDING_ARTIST type | Yes (SQL WHERE) | | Exists in GLOBAL_PARTICIPANT | No (informational) | **Spotify ID Formats:** All formats are supported and automatically parsed: - URL: `https://open.spotify.com/artist/7tNO3vJC9zlHy2IJOx34ga` - URI: `spotify:artist:4OGiMt96TFUKkKWf7Imlno` - Plain ID: `6zyG7x3rPnqitv8h5FATI9` """ )