Files
reTraining/backend/config.py
T
asus dee58e4ae5 fix: resolve model weight paths against DATA_DIR (REQ-187)
- config.py: resolve_data_path (legacy abs + rel) + rel_data_path
- all file-opening reads wrapped: preview, autolabel, training, model
  download, live count, projects.get; training_start_point hack replaced
- new writes store paths relative to data/
- legacy stale rows (/home/asus/reTraining/...) resolve without migration
- requirements: REQ-187 added; REQ-188 (per-class max box) + REQ-186
  copy-line amendment drafted for the next task
2026-10-02 16:59:25 +07:00

93 lines
3.4 KiB
Python

"""Paths and settings. Everything is environment-driven so the same image runs
locally and in Docker without code changes (REQ-072)."""
import os
from dotenv import load_dotenv
load_dotenv()
# huggingface_hub reads HF_TOKEN / HUGGING_FACE_HUB_TOKEN; accept either name in
# .env so pasting a token under the obvious name just works.
if os.environ.get("HF_TOKEN") and not os.environ.get("HUGGING_FACE_HUB_TOKEN"):
os.environ["HUGGING_FACE_HUB_TOKEN"] = os.environ["HF_TOKEN"]
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATA_DIR = os.path.abspath(os.environ.get("APP_DATA_DIR", os.path.join(REPO_ROOT, "data")))
PROJECTS_DIR = os.path.join(DATA_DIR, "projects")
DB_PATH = os.path.join(DATA_DIR, "app.db")
def resolve_data_path(path):
"""Resolve a DB-stored weight path against today's DATA_DIR (REQ-187)."""
if not path:
return path
if os.path.isfile(path):
return path
if not os.path.isabs(path):
candidate = os.path.join(DATA_DIR, path)
return candidate if os.path.isfile(candidate) else path
# legacy absolute path from before the data dir moved: .../data/<rel>
marker = os.sep + "data" + os.sep
idx = path.find(marker)
if idx != -1:
candidate = os.path.join(DATA_DIR, path[idx + len(marker):])
if os.path.isfile(candidate):
return candidate
return path
def rel_data_path(path):
"""Store paths under DATA_DIR relative to it (REQ-187); others unchanged."""
try:
rel = os.path.relpath(path, DATA_DIR)
except ValueError:
return path
return path if rel.startswith(os.pardir) else rel
# Where the video archive is mounted. Projects store a path relative to nothing —
# they store an absolute one — but this is the default the UI starts browsing from.
VIDEO_ROOT = os.path.abspath(os.environ.get("VIDEO_ARCHIVE", os.path.join(DATA_DIR, "archive")))
def archive_host_paths() -> dict:
"""Where the archive sits on the host — for the copy-path buttons (REQ-179).
VIDEO_ARCHIVE_HOST may stay relative (portable across machines): compose
injects the host cwd as ARCHIVE_HOST_PWD (compose always has PWD — a
Windows-side run yields a drive-letter path, handled below), bare-metal
resolves against the process cwd (start.sh cds to the repo root)."""
import re
host = os.environ.get("VIDEO_ARCHIVE_HOST", "")
if host and not os.path.isabs(host):
base = os.environ.get("ARCHIVE_HOST_PWD") or os.getcwd()
host = os.path.abspath(os.path.join(base, host))
if not host and VIDEO_ROOT != "/videos":
host = VIDEO_ROOT # bare-metal with no VIDEO_ARCHIVE_HOST at all
if not host:
return {"linux": None, "windows": None}
linux = host.rstrip("/")
if re.match(r"^[A-Za-z]:[\\/]", linux):
return {"linux": None, "windows": linux.replace("/", "\\")}
distro = os.environ.get("WSL_DISTRO_NAME", "")
windows = f"\\\\wsl.localhost\\{distro}\\" + linux.lstrip("/").replace("/", "\\") if distro else None
return {"linux": linux, "windows": windows}
# Vite dev server needs cross-origin access; in Docker nginx proxies /api and
# this is irrelevant.
CORS_ORIGINS = [
origin.strip()
for origin in os.environ.get("CORS_ORIGINS", "http://localhost:5173").split(",")
if origin.strip()
]
def ensure_dirs() -> None:
os.makedirs(PROJECTS_DIR, exist_ok=True)
def project_dir(slug: str) -> str:
return os.path.join(PROJECTS_DIR, slug)