|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations |
|
|
import os |
|
|
import json |
|
|
import mimetypes |
|
|
from pathlib import Path |
|
|
import streamlit as st |
|
|
|
|
|
|
|
|
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_DIR = Path("/data") |
|
|
if not ROOT_DIR.exists(): |
|
|
st.error("β οΈ /data directory not found in this environment.") |
|
|
st.stop() |
|
|
|
|
|
|
|
|
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("."): |
|
|
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 |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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() |
|
|
|
|
|
|
|
|
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() |
|
|
|
|
|
|
|
|
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.") |
|
|
|
|
|
|
|
|
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) |
|
|
|