61 lines
1.9 KiB
Python
61 lines
1.9 KiB
Python
"""Research method registry for Phase 1 framework selection."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
import yaml
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
DEFAULT_METHOD_CONFIG = REPO_ROOT / "configs" / "research_methods.yaml"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ResearchMethod:
|
|
key: str
|
|
name: str
|
|
best_for: list[str]
|
|
structure_principle: str
|
|
task_axes: list[str]
|
|
framework_sections: list[str]
|
|
|
|
|
|
class ResearchMethodRegistry:
|
|
def __init__(self, path: Path | None = None) -> None:
|
|
self.path = path or DEFAULT_METHOD_CONFIG
|
|
self._data = self._load()
|
|
|
|
def _load(self) -> dict[str, Any]:
|
|
if not self.path.exists():
|
|
raise FileNotFoundError(f"research method config not found: {self.path}")
|
|
data = yaml.safe_load(self.path.read_text(encoding="utf-8")) or {}
|
|
if not isinstance(data, dict) or "methods" not in data:
|
|
raise ValueError(f"invalid research method config: {self.path}")
|
|
return data
|
|
|
|
@property
|
|
def default_method(self) -> str:
|
|
return (self._data.get("defaults") or {}).get("method", "mckinsey_market")
|
|
|
|
def list_names(self) -> list[str]:
|
|
return sorted((self._data.get("methods") or {}).keys())
|
|
|
|
def get(self, key: str | None = None) -> ResearchMethod:
|
|
selected = key or self.default_method
|
|
methods = self._data.get("methods") or {}
|
|
if selected not in methods:
|
|
raise KeyError(f"unknown research_method: {selected}")
|
|
item = methods[selected] or {}
|
|
return ResearchMethod(
|
|
key=selected,
|
|
name=item.get("name", selected),
|
|
best_for=list(item.get("best_for") or []),
|
|
structure_principle=item.get("structure_principle", ""),
|
|
task_axes=list(item.get("task_axes") or []),
|
|
framework_sections=list(item.get("framework_sections") or []),
|
|
)
|
|
|