from __future__ import annotations
import re
from dataclasses import dataclass, asdict
from pathlib import Path
from typing import List
PROJECT_DIR = Path('/opt/obsidian-vault/Agent-Solutions/Kundenprojekte')
@dataclass
class CustomerProject:
project_id: str
title: str
status: str
description: str
target_customer: str
target_industries: List[str]
customer_problem: str
acquisition: str
references: str
potential_score: float | None
confidence: str
source_file: str
def as_dict(self) -> dict:
return asdict(self)
def _frontmatter(text: str) -> dict:
if not text.startswith('---'):
return {}
_, raw, _ = text.split('---', 2)
out = {}
for line in raw.splitlines():
if ':' not in line:
continue
key, value = line.split(':', 1)
out[key.strip()] = value.strip().strip('"')
return out
def _section(text: str, heading: str) -> str:
pattern = rf'^## {re.escape(heading)}\s*$\n(.*?)(?=^## |\Z)'
match = re.search(pattern, text, flags=re.M | re.S)
return match.group(1).strip() if match else ''
def _industries(raw: str) -> List[str]:
first = raw.splitlines()[0] if raw else ''
return [x.strip() for x in re.split(r'[·|,;]', first) if x.strip()]
def load_project(path: Path) -> CustomerProject:
text = path.read_text(encoding='utf-8')
fm = _frontmatter(text)
eval_section = _section(text, 'Bewertungsmodell / Evidenzstärke')
score_match = re.search(r'Gesamtpotenzial:\s*\*\*(\d+(?:[,.]\d+)?)/10', eval_section)
conf_match = re.search(r'Konfidenz:\s*\*\*([^*]+)\*\*', eval_section)
score = float(score_match.group(1).replace(',', '.')) if score_match else None
return CustomerProject(
project_id=fm.get('project_id', path.stem),
title=fm.get('title', path.stem),
status=fm.get('status', ''),
description=_section(text, 'Projektbeschreibung'),
target_customer=_section(text, 'Zielkunde'),
target_industries=_industries(_section(text, 'Interessant für / Zielbranchen')),
customer_problem=_section(text, 'Kundenproblem'),
acquisition=_section(text, 'Kundengewinnung / Akquisewege'),
references=_section(text, 'Vorhandene Demos / Referenzen / Agenten / Tools'),
potential_score=score,
confidence=conf_match.group(1).strip() if conf_match else '',
source_file=str(path),
)
def load_all_projects(project_dir: Path = PROJECT_DIR) -> List[CustomerProject]:
return [load_project(p) for p in sorted(project_dir.glob('*.md'))]