69 lines
1.8 KiB
Python
69 lines
1.8 KiB
Python
from pathlib import Path
|
||
|
||
from config import STORAGE_DIR, PROJECTS_DIR, PROJECT_ROOT
|
||
|
||
TOPICS_DIR = STORAGE_DIR / "topics"
|
||
|
||
|
||
def _safe(name: str) -> str:
|
||
return name.replace("/", "_").replace("\x00", "")
|
||
|
||
|
||
def topic_dir(topic: str) -> Path:
|
||
return TOPICS_DIR / _safe(topic)
|
||
|
||
|
||
def arbeit_dir(topic: str) -> Path:
|
||
return topic_dir(topic) / "arbeit"
|
||
|
||
|
||
def blocks_path(topic: str) -> Path:
|
||
return topic_dir(topic) / "blocks.md"
|
||
|
||
|
||
def subblocks_path(topic: str) -> Path:
|
||
"""Sidecar: the subblocks with level per block (shared by all guides)."""
|
||
return topic_dir(topic) / "subblocks.json"
|
||
|
||
|
||
def question_pattern_path(topic: str) -> Path:
|
||
"""Sidecar: predefined question patterns per block (subblock × type → example question)."""
|
||
return topic_dir(topic) / "question_pattern.json"
|
||
|
||
|
||
def source_path(topic: str) -> Path:
|
||
"""Persisted source choice per topic: {type, location, spec}."""
|
||
return topic_dir(topic) / "source.json"
|
||
|
||
|
||
def source_crawl_dir(topic: str) -> Path:
|
||
"""Target folder for crawled link sources (pages + PDF .txt)."""
|
||
return topic_dir(topic) / "source"
|
||
|
||
|
||
def safe_folder(location: str) -> Path | None:
|
||
"""Folder path relative to the repo root, sandboxed. None if empty/escaping (../, absolute outside)."""
|
||
if not location or not location.strip():
|
||
return None
|
||
p = (PROJECT_ROOT / location.strip()).resolve()
|
||
try:
|
||
p.relative_to(PROJECT_ROOT)
|
||
except ValueError:
|
||
return None
|
||
return p
|
||
|
||
|
||
def guide_content_path(topic: str, format_name: str) -> Path:
|
||
return topic_dir(topic) / "guides" / f"{format_name}.json"
|
||
|
||
|
||
def blocks_topics() -> list[str]:
|
||
"""Topics for which a topic folder exists."""
|
||
if not TOPICS_DIR.is_dir():
|
||
return []
|
||
return [d.name for d in TOPICS_DIR.iterdir() if d.is_dir()]
|
||
|
||
|
||
def project_dir(name: str) -> Path:
|
||
return PROJECTS_DIR / name
|