|
|
| 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." |
| ) |
|
|
|
|