Initial commit
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
"""领域模型。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
from typing import Any
|
||||
|
||||
|
||||
DIMENSIONS = (
|
||||
"Clarity",
|
||||
"Structure",
|
||||
"Executability",
|
||||
"Completeness",
|
||||
"Constraint Salience",
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SkillUnit:
|
||||
unit_id: str
|
||||
level: str
|
||||
parent_id: str | None
|
||||
heading: str
|
||||
text: str
|
||||
order: int
|
||||
start: int
|
||||
end: int
|
||||
heading_depth: int | None = None
|
||||
|
||||
@dataclass
|
||||
class CellScore:
|
||||
score: float
|
||||
evidence: list[str]
|
||||
reason: str
|
||||
|
||||
@dataclass
|
||||
class Coordinate:
|
||||
unit_id: str
|
||||
dimension: str
|
||||
normalized_gap: float
|
||||
|
||||
@dataclass
|
||||
class LocalEdit:
|
||||
unit_id: str
|
||||
dimension: str
|
||||
new_text: str
|
||||
edit_summary: str
|
||||
reason: str
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, value: dict[str, Any]) -> "LocalEdit":
|
||||
required = {"unit_id", "dimension", "new_text", "edit_summary", "reason"}
|
||||
missing = sorted(required - value.keys())
|
||||
if missing:
|
||||
raise ValueError(f"local edit missing fields: {', '.join(missing)}")
|
||||
if not all(isinstance(value[key], str) for key in required):
|
||||
raise ValueError("local edit fields must be strings")
|
||||
return cls(**{key: value[key] for key in cls.__dataclass_fields__})
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScoreMatrix:
|
||||
level: str
|
||||
units: list[SkillUnit]
|
||||
columns: dict[str, dict[str, CellScore]]
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
"level": self.level,
|
||||
"units": [asdict(unit) for unit in self.units],
|
||||
"columns": {
|
||||
dimension: {unit_id: asdict(cell) for unit_id, cell in column.items()}
|
||||
for dimension, column in self.columns.items()
|
||||
},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, value: dict[str, Any]) -> "ScoreMatrix":
|
||||
return cls(
|
||||
level=str(value["level"]),
|
||||
units=[SkillUnit(**item) for item in value["units"]],
|
||||
columns={
|
||||
dimension: {
|
||||
unit_id: CellScore(float(cell["score"]), list(cell["evidence"]), str(cell["reason"]))
|
||||
for unit_id, cell in column.items()
|
||||
}
|
||||
for dimension, column in value["columns"].items()
|
||||
},
|
||||
)
|
||||
|
||||
def unit(self, unit_id: str) -> SkillUnit:
|
||||
return next(unit for unit in self.units if unit.unit_id == unit_id)
|
||||
|
||||
def normalized_gaps(self) -> dict[str, float]:
|
||||
gaps: dict[str, float] = {}
|
||||
for dimension in DIMENSIONS:
|
||||
values = [self.columns[dimension][unit.unit_id].score for unit in self.units]
|
||||
gaps[dimension] = (max(values) - min(values)) / 4.0 if values else 0.0
|
||||
return gaps
|
||||
|
||||
def select_coordinate(
|
||||
self,
|
||||
threshold: float,
|
||||
dimension: str | None = None,
|
||||
excluded: set[tuple[str, str]] | None = None,
|
||||
) -> Coordinate | None:
|
||||
gaps = self.normalized_gaps()
|
||||
excluded = excluded or set()
|
||||
|
||||
def weak_units(item: str) -> list[SkillUnit]:
|
||||
column = self.columns[item]
|
||||
maximum = max((cell.score for cell in column.values()), default=0.0)
|
||||
return [
|
||||
unit for unit in self.units
|
||||
if (unit.unit_id, item) not in excluded
|
||||
and (maximum - column[unit.unit_id].score) / 4.0 > threshold
|
||||
]
|
||||
|
||||
available = [
|
||||
item for item in ([dimension] if dimension else DIMENSIONS)
|
||||
if item is not None and gaps[item] > threshold and weak_units(item)
|
||||
]
|
||||
if not available:
|
||||
return None
|
||||
dimension = max(available, key=lambda item: gaps[item])
|
||||
target = min(
|
||||
weak_units(dimension),
|
||||
key=lambda unit: (self.columns[dimension][unit.unit_id].score, unit.order),
|
||||
)
|
||||
return Coordinate(
|
||||
target.unit_id,
|
||||
dimension,
|
||||
gaps[dimension],
|
||||
)
|
||||
Reference in New Issue
Block a user