ai_econsult_demo / pages /99_File_Explorer.py
Cardiosense-AG's picture
Create 99_File_Explorer.py
338ddc8 verified
# pages/99_File_Explorer.py
# -------------------------------------------------------------------
# Universal File Explorer for the /data directory (and subfolders).
# Allows browsing, viewing JSON/Markdown/text, and downloading files.
# -------------------------------------------------------------------
from __future__ import annotations
import os
import json
import mimetypes
from pathlib import Path
import streamlit as st
# --- Page setup ---
st.set_page_config(page_title="File Explorer", page_icon="πŸ“", layout="wide")
st.title("πŸ“ File Explorer")
st.caption("Browse and download files under the /data directory (e.g., cases, exports, FAISS indexes, etc.)")
# --- Root directory (default /data) ---
ROOT_DIR = Path("/data")
if not ROOT_DIR.exists():
st.error("⚠️ /data directory not found in this environment.")
st.stop()
# --- Utility functions ---
def list_dir(path: Path):
"""Return a sorted list of (name, is_dir) tuples."""
items = []
for entry in sorted(os.listdir(path)):
p = path / entry
if entry.startswith("."): # hide hidden files
continue
items.append((entry, p.is_dir()))
return items
def render_file(path: Path):
"""Render or offer download for the selected file."""
mime, _ = mimetypes.guess_type(str(path))
st.markdown(f"**Path:** `{path}` \n**Size:** {path.stat().st_size:,} bytes")
if path.suffix.lower() in [".json", ".md", ".txt", ".log"]:
try:
text = path.read_text(encoding="utf-8")
if path.suffix.lower() == ".json":
st.json(json.loads(text))
elif path.suffix.lower() == ".md":
st.markdown(text)
else:
st.code(text)
except Exception as e:
st.error(f"Error reading file: {e}")
else:
with open(path, "rb") as f:
st.download_button(
label="⬇️ Download File",
data=f,
file_name=path.name,
mime=mime or "application/octet-stream",
use_container_width=True,
)
def export_metadata_tree(root: Path) -> dict:
"""Recursively export metadata (name, size, type, children)."""
tree = {"name": root.name, "path": str(root), "type": "directory" if root.is_dir() else "file"}
if root.is_dir():
children = []
for entry in sorted(os.listdir(root)):
p = root / entry
if entry.startswith("."):
continue
children.append(export_metadata_tree(p))
tree["children"] = children
else:
try:
tree["size"] = root.stat().st_size
except Exception:
tree["size"] = None
return tree
# --- Sidebar navigation ---
st.sidebar.header("πŸ“‚ Directory Browser")
def browse(path: Path):
st.sidebar.markdown(f"**Current:** `{path}`")
items = list_dir(path)
for name, is_dir in items:
sub_path = path / name
if is_dir:
if st.sidebar.button(f"πŸ“ {name}", key=str(sub_path)):
st.session_state["explorer_path"] = str(sub_path)
else:
if st.sidebar.button(f"πŸ“„ {name}", key=str(sub_path)):
st.session_state["selected_file"] = str(sub_path)
# --- Manage navigation state ---
if "explorer_path" not in st.session_state:
st.session_state["explorer_path"] = str(ROOT_DIR)
if "selected_file" not in st.session_state:
st.session_state["selected_file"] = ""
current_path = Path(st.session_state["explorer_path"])
browse(current_path)
# --- Main view ---
st.divider()
st.markdown(f"### πŸ“‚ {current_path}")
if st.button("⬆️ Up one level", use_container_width=False):
if current_path != ROOT_DIR:
st.session_state["explorer_path"] = str(current_path.parent)
st.session_state["selected_file"] = ""
st.rerun()
# --- Show directory contents ---
entries = list_dir(current_path)
for name, is_dir in entries:
p = current_path / name
icon = "πŸ“" if is_dir else "πŸ“„"
st.write(f"{icon} `{name}`")
st.divider()
# --- Show selected file (if any) ---
selected_path = Path(st.session_state["selected_file"]) if st.session_state["selected_file"] else None
if selected_path and selected_path.exists():
st.subheader(f"πŸ“„ File: {selected_path.name}")
render_file(selected_path)
else:
st.info("Select a file in the sidebar to preview its contents.")
# --- Export metadata tree ---
st.divider()
if st.button("πŸ“¦ Export Metadata Tree", use_container_width=True):
tree = export_metadata_tree(ROOT_DIR)
st.download_button(
"⬇️ Download Metadata (JSON)",
data=json.dumps(tree, indent=2),
file_name="data_directory_tree.json",
mime="application/json",
use_container_width=True,
)
st.json(tree)