sync-pilot / app.py
Emre Sarigöl
Deploy sync_pilot dashboard - 2026-05-19 17:12
55956c8
Raw
History Blame Contribute Delete
5.82 kB
"""HuggingFace Space entrypoint for the sync_pilot dashboard.
Wraps the multi-page Streamlit app in ``sync_pilot.dashboard`` with an
``ACCESS_KEY`` gate (matching the rehab-robotics GURMA dashboard's pattern
in ``src/dashboard/app.py``). The wrapper *replicates* the navigation
block from ``sync_pilot/dashboard/app.py:main`` rather than importing the
module — the module calls ``main()`` at top level, which would re-fire
``st.set_page_config`` after the wrapper has already set it.
Required Space secrets (set in HF UI):
ACCESS_KEY — the login password
HF_TOKEN — read token for the private dataset
PRIVATE_DATASET_REPO — e.g. ``emresar/gurma-dashboard-private-data``
SYNC_PILOT_DATA_SOURCE — ``hf`` (default in the Dockerfile)
"""
from __future__ import annotations
import hashlib
import os
import streamlit as st
# ``set_page_config`` must be the first Streamlit call — set it before
# any other ``st.*`` invocation (including ones that might happen inside
# the auth gate's ``show_login`` form).
st.set_page_config(
page_title="sync_pilot — Median Müzik",
page_icon="♪",
layout="wide",
initial_sidebar_state="expanded",
)
# ---------------------------------------------------------------------------
# Access control (mirrors src/dashboard/app.py for the rehab dashboard)
# ---------------------------------------------------------------------------
ACCESS_KEY = os.getenv("ACCESS_KEY", "")
IS_HF_SPACE = bool(os.getenv("HF_SPACE"))
def _auth_token(key: str, salt: str = "sync_pilot") -> str:
"""Short deterministic token kept in the URL so refresh survives."""
return hashlib.sha256(f"{salt}_{key}".encode()).hexdigest()[:16]
def check_access() -> bool:
"""Authenticate via session state, URL token, or pass-through locally."""
if not ACCESS_KEY:
# Local dev (no key configured) — bypass. On HF Space we refuse
# rather than open the dashboard up; treat missing secret as a
# misconfiguration the user can fix from the Settings panel.
return not IS_HF_SPACE
if st.session_state.get("authenticated"):
return True
if st.query_params.get("auth") == _auth_token(ACCESS_KEY):
st.session_state.authenticated = True
return True
return False
def show_login() -> None:
st.markdown(
"""
<style>
.sync-login-container {
max-width: 420px;
margin: 100px auto 0 auto;
padding: 36px 32px;
background: linear-gradient(135deg, #1a1a2e 0%, #2a1a3e 100%);
border-radius: 16px;
color: #f5f5fa;
text-align: center;
}
.sync-login-title { font-size: 1.9em; margin-bottom: 4px; }
.sync-login-subtitle { opacity: 0.75; margin-bottom: 8px; }
</style>
""",
unsafe_allow_html=True,
)
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
st.markdown(
"""
<div class="sync-login-container">
<div class="sync-login-title">sync_pilot</div>
<div class="sync-login-subtitle">Median Müzik · sync-licensing pilot</div>
</div>
""",
unsafe_allow_html=True,
)
st.markdown("")
with st.form("sync_pilot_login_form"):
key = st.text_input(
"Access Key",
type="password",
placeholder="Enter your access key",
)
submitted = st.form_submit_button("Enter", width="stretch", type="primary")
if submitted:
if key == ACCESS_KEY:
st.session_state.authenticated = True
st.query_params["auth"] = _auth_token(ACCESS_KEY)
st.rerun()
else:
st.error("Invalid access key")
if not check_access():
show_login()
st.stop()
# ---------------------------------------------------------------------------
# Navigation — mirrors ``sync_pilot/dashboard/app.py:main`` (kept in sync
# manually; touching one without the other will show different pages
# between local and Space).
# ---------------------------------------------------------------------------
from sync_pilot.dashboard.styles import apply_global_styles # noqa: E402
from sync_pilot.dashboard.views import ( # noqa: E402
gt_review,
overview,
taxonomy,
tracks,
)
apply_global_styles()
def _sidebar_chrome() -> None:
with st.sidebar:
st.markdown("---")
st.markdown("### sync_pilot")
st.caption("Median Müzik · sync-licensing pilot")
source = os.getenv("SYNC_PILOT_DATA_SOURCE", "local")
st.caption(f"data source: `{source}`")
if ACCESS_KEY and st.session_state.get("authenticated"):
if st.button("Logout", width="stretch"):
st.session_state.authenticated = False
st.query_params.pop("auth", None)
st.rerun()
nav = st.navigation(
[
st.Page(
overview.render,
title="Overview",
icon=":material/dashboard:",
url_path="overview",
default=True,
),
st.Page(
taxonomy.render,
title="Taxonomy",
icon=":material/category:",
url_path="taxonomy",
),
st.Page(
tracks.render,
title="Track explorer",
icon=":material/library_music:",
url_path="tracks",
),
st.Page(
gt_review.render,
title="GT review",
icon=":material/fact_check:",
url_path="gt-review",
),
],
position="sidebar",
)
_sidebar_chrome()
nav.run()