import os import requests import streamlit as st from datetime import date ZENODO_API = "https://zenodo.org/api" st.set_page_config( page_title="Zenodo Research Submission Dashboard", page_icon="📄", layout="wide" ) st.title("Zenodo Research Submission Dashboard") st.caption("Create a Zenodo draft deposit, upload your manuscript, save metadata, and publish when ready.") with st.sidebar: st.header("Zenodo Access") token_source = st.radio( "Token source", ["Environment variable", "Paste for this session"], index=0 ) if token_source == "Environment variable": token = os.getenv("ZENODO_ACCESS_TOKEN", "") st.info("Uses ZENODO_ACCESS_TOKEN from your local environment.") else: token = st.text_input("Zenodo access token", type="password") st.warning("Never commit or share your token. If you pasted one into chat or GitHub, revoke it.") st.subheader("1. Manuscript File") uploaded_file = st.file_uploader("Upload manuscript PDF", type=["pdf"]) st.subheader("2. Research Metadata") col1, col2 = st.columns(2) with col1: title = st.text_input( "Title", value="Bearinglessfull Collateral: A Risk Primitive for Distributed Hedge Capacity" ) creator_name = st.text_input("Creator name", value="Skrobynets, Joseph") version = st.text_input("Version", value="0.2") publication_date = st.date_input("Publication date", value=date.today()) with col2: upload_type = st.selectbox("Upload type", ["publication"], index=0) publication_type = st.selectbox( "Publication type", [ "workingpaper", "article", "preprint", "report", "technicalnote", "other" ], index=0 ) access_right = st.selectbox("Access right", ["open", "restricted", "closed", "embargoed"], index=0) license_id = st.text_input("License", value="cc-by-4.0") description = st.text_area( "Description / abstract", value=( "This working paper introduces Bearinglessfull Collateral, a risk primitive " "for distributed hedge-capacity systems. The framework evaluates whether " "collateral is not merely sufficient in nominal value, but structurally " "resilient across venue distribution, liquidation distance, reserve buffers, " "rebalance readiness, and access risk. It also introduces the Bearinglessfull " "Score, AlphaSignalLLM, and a GMX Hedge Adapter as components of a research-only " "architecture for collateral-aware hedge verification." ), height=180 ) keywords_text = st.text_area( "Keywords, one per line", value="""Bearinglessfull Collateral Bearinglessfull Score BFS collateral topology hedge capacity distributed collateral margin risk DeFi market microstructure LLM-assisted research""", height=180 ) st.subheader("3. Submission Controls") if "deposit_id" not in st.session_state: st.session_state.deposit_id = None if "bucket_url" not in st.session_state: st.session_state.bucket_url = None if "record_url" not in st.session_state: st.session_state.record_url = None if "doi" not in st.session_state: st.session_state.doi = None def require_token(): if not token: st.error("Missing Zenodo token. Set ZENODO_ACCESS_TOKEN or paste a token for this session.") return False return True def params(): return {"access_token": token} metadata_payload = { "metadata": { "title": title, "upload_type": upload_type, "publication_type": publication_type, "description": description, "creators": [{"name": creator_name}], "keywords": [k.strip() for k in keywords_text.splitlines() if k.strip()], "version": version, "publication_date": str(publication_date), "language": "eng", "access_right": access_right, } } if access_right == "open": metadata_payload["metadata"]["license"] = license_id with st.expander("Preview metadata JSON", expanded=False): st.json(metadata_payload) c1, c2, c3, c4 = st.columns(4) with c1: if st.button("Create Zenodo Draft", use_container_width=True): if require_token(): try: response = requests.post( f"{ZENODO_API}/deposit/depositions", params=params(), json={} ) response.raise_for_status() deposit = response.json() st.session_state.deposit_id = deposit["id"] st.session_state.bucket_url = deposit["links"]["bucket"] st.success(f"Draft created: {st.session_state.deposit_id}") except Exception as e: st.error(f"Failed to create draft: {e}") with c2: if st.button("Upload PDF", use_container_width=True): if require_token(): if not st.session_state.deposit_id or not st.session_state.bucket_url: st.error("Create a Zenodo draft first.") elif uploaded_file is None: st.error("Upload a PDF first.") else: try: file_name = uploaded_file.name response = requests.put( f"{st.session_state.bucket_url}/{file_name}", params=params(), data=uploaded_file.getvalue() ) response.raise_for_status() st.success(f"Uploaded: {file_name}") except Exception as e: st.error(f"Failed to upload file: {e}") with c3: if st.button("Save Metadata", use_container_width=True): if require_token(): if not st.session_state.deposit_id: st.error("Create a Zenodo draft first.") else: try: response = requests.put( f"{ZENODO_API}/deposit/depositions/{st.session_state.deposit_id}", params=params(), json=metadata_payload ) response.raise_for_status() st.success("Metadata saved.") except Exception as e: st.error(f"Failed to save metadata: {e}") with c4: publish_confirm = st.text_input( "Type PUBLISH-REAL to enable publishing", placeholder="PUBLISH-REAL" ) publish_enabled = publish_confirm == "PUBLISH-REAL" if st.button("Publish DOI", disabled=not publish_enabled, use_container_width=True): if require_token(): if not st.session_state.deposit_id: st.error("Create a Zenodo draft first.") else: try: response = requests.post( f"{ZENODO_API}/deposit/depositions/{st.session_state.deposit_id}/actions/publish", params=params() ) response.raise_for_status() record = response.json() st.session_state.doi = record.get("doi") st.session_state.record_url = record.get("links", {}).get("html") st.success("Published successfully.") except Exception as e: st.error(f"Failed to publish: {e}") st.subheader("4. Current Submission State") state_col1, state_col2, state_col3 = st.columns(3) with state_col1: st.metric("Deposit ID", st.session_state.deposit_id or "Not created") with state_col2: st.metric("DOI", st.session_state.doi or "Not published") with state_col3: if st.session_state.record_url: st.link_button("Open Zenodo Record", st.session_state.record_url) else: st.write("No public record yet.") st.divider() st.subheader("Recommended Flow") st.write( "Create Zenodo Draft → Upload PDF → Save Metadata → inspect metadata → type PUBLISH-REAL → Publish DOI." ) st.caption( "Publishing is permanent-ish: Zenodo records are designed for durable citation. " "Review carefully before minting the DOI." )