spMetaTME-Atlas / app.py
Surajv's picture
Change UI
32cf4da
import streamlit as st
import logging
import numpy as np
import os
import base64
# Configure Logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def get_base64_of_bin_file(bin_file):
with open(bin_file, 'rb') as f:
data = f.read()
return base64.b64encode(data).decode()
# Page configuration
sidebar_state = "expanded"
st.set_page_config(
page_title="spMetaTME-Atlas",
page_icon=":material/hub:",
layout="wide",
initial_sidebar_state=sidebar_state,
)
from src.ui.components.header import render_header, load_css
from src.ui.components.footer import render_footer
from src.ui.pages.home import show_overview
from src.ui.pages.analyze import (
page_domain_statistics,
page_spatial_flux,
page_umap_analysis,
page_differential_analysis,
page_metabolic_interactions,
page_metabolite_balance,
page_reset
)
def init_session_state():
"""Initialise global session state."""
if "adata" not in st.session_state:
st.session_state.adata = None
if "metabolic_adata" not in st.session_state:
st.session_state.metabolic_adata = None
if "data_type" not in st.session_state:
st.session_state.data_type = None
if "preprocessing_done" not in st.session_state:
st.session_state.preprocessing_done = False
if "flux_analysis_done" not in st.session_state:
st.session_state.flux_analysis_done = False
if "interaction_scores" not in st.session_state:
st.session_state.interaction_scores = None
if "interaction_type" not in st.session_state:
st.session_state.interaction_type = None
# Pagination States
if "dataset_page" not in st.session_state:
st.session_state.dataset_page = 1
if "umap_page" not in st.session_state:
st.session_state.umap_page = 1
if "spatial_flux_page" not in st.session_state:
st.session_state.spatial_flux_page = 1
# Developer Mode
if "dev_mode" not in st.session_state:
st.session_state.dev_mode = True
def render_branding_header():
"""Display a clean, professional branding header using the network background."""
try:
bg_img_path = "assets/network_bg_red.png"
bg_base64 = get_base64_of_bin_file(bg_img_path) if os.path.exists(bg_img_path) else ""
subtitle = "A spatial atlas of tumour microenvironment metabolism and metabolic interactions inferred by a pretrained self-supervised metabolic hypergraph"
# Load brand logo from npy
brand_text = "spMetaTME-Atlas"
brand_path = "assets/spMetaTME_brand.npy"
if os.path.exists(brand_path):
try:
brand_data = np.load(brand_path, allow_pickle=True)
brand_text = brand_data.item() if hasattr(brand_data, 'item') else str(brand_data)
except Exception:
pass
html_code = f"""
<div style="position: relative; width: 100%; background: transparent; padding: 0.5rem 0; margin-bottom: 1rem; overflow: hidden; border-bottom: 1px solid #f0f0f0; text-align: center; border-radius: 12px;">
<div style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: url('data:image/png;base64,{bg_base64}'); background-size: cover; background-position: center; opacity: 0.05; z-index: 1; pointer-events: none;"></div>
<div style="position: relative; z-index: 10; display: flex; flex-direction: column; align-items: center;">
<pre class="brand-logo-ascii" style="margin-bottom: 5px; zoom: 0.85;">{brand_text}</pre>
<p style="font-size: 0.85rem; color: #666; max-width: 900px; margin: 0 auto; font-weight: 400; line-height: 1.2;">{subtitle}</p>
</div>
</div>
"""
st.html(html_code)
except Exception as e:
try:
st.markdown(html_code, unsafe_allow_html=True)
except:
logger.error(f"Failed to load branding header: {e}")
def render_sidebar_dev():
"""Developer shortcuts in sidebar."""
with st.sidebar:
st.markdown("---")
st.session_state.dev_mode = st.checkbox("Developer Mode", value=st.session_state.dev_mode)
if st.session_state.dev_mode:
st.info("Dev Shortcuts Active")
if st.button("Load Breast Cancer Block A", use_container_width=True):
with st.spinner("Loading example data..."):
for key in ['interaction_scores', 'interaction_type']:
if key in st.session_state:
del st.session_state[key]
import scanpy as sc
adata = sc.read_h5ad(r"example_data/metabolic_Breast_cancer_Block_A.h5ad")
if adata is not None:
st.session_state.metabolic_adata = adata
st.session_state.data_type = "metabolic"
st.session_state.just_loaded = True
if 'domain' not in adata.obs.columns and 'domain_id' in adata.obs.columns:
adata.obs['domain'] = adata.obs['domain_id']
st.success("Loaded Breast Cancer Block A")
st.rerun()
def main():
init_session_state()
load_css()
home_page = st.Page(show_overview, title="Home", icon=":material/home:", url_path="home")
domain_stats_page = st.Page(page_domain_statistics, title="Domain Statistics", icon=":material/pie_chart:", url_path="domain_stats")
analysis_pages = [
st.Page(page_reset, title="Back to Home", icon=":material/home:", url_path="reset_to_home"),
domain_stats_page,
st.Page(page_spatial_flux, title="Spatial Flux Distribution", icon=":material/image:", url_path="spatial_flux"),
st.Page(page_umap_analysis, title="UMAP Analysis", icon=":material/palette:", url_path="umap_analysis"),
st.Page(page_differential_analysis, title="Differential Reactions", icon=":material/bar_chart:", url_path="differential_analysis"),
st.Page(page_metabolic_interactions, title="Spatial Metabolic Interactions", icon=":material/link:", url_path="metabolic_interactions"),
st.Page(page_metabolite_balance, title="Metabolite Balance Analysis", icon=":material/opacity:", url_path="metabolite_balance"),
]
if st.session_state.metabolic_adata is None:
pg = st.navigation([home_page], position="hidden")
render_header()
# render_sidebar_dev()
pg.run()
else:
pg = st.navigation({"Metabolic Analysis": analysis_pages}, position="sidebar")
render_branding_header()
# Developer Shortcuts
# render_sidebar_dev()
if st.session_state.get('just_loaded', False):
st.session_state.just_loaded = False
st.switch_page(domain_stats_page)
pg.run()
render_footer()
if __name__ == "__main__":
main()