| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 |
- from __future__ import annotations
- from collections import defaultdict, deque
- from collections.abc import Iterable, Set
- from app.core.knowledge.contracts import ImpactResult, KnowledgeDependencyDraft
- def _object_key(point_key: str) -> str:
- parts = point_key.split("/", 2)
- return "/".join(parts[:2]) if len(parts) >= 2 else point_key
- def propagate_impact(
- changed_point_keys: Set[str],
- dependencies: Iterable[KnowledgeDependencyDraft],
- *,
- allowed_relations: Set[str],
- max_hops: int,
- max_points: int,
- max_objects: int,
- ) -> ImpactResult:
- if max_hops < 0 or max_points < 0 or max_objects < 1:
- raise ValueError("impact propagation limits are invalid")
- reverse: dict[str, list[str]] = defaultdict(list)
- for dependency in dependencies:
- if dependency.source not in {"governance", "deterministic_rule"}:
- continue
- if dependency.relation_type not in allowed_relations:
- continue
- reverse[dependency.to_point_key].append(dependency.from_point_key)
- for values in reverse.values():
- values.sort()
- roots = sorted(set(changed_point_keys))
- visited = set(roots)
- queue = deque((root, 0, (root,)) for root in roots)
- impacted: set[str] = set()
- paths: dict[str, tuple[str, ...]] = {}
- objects: set[str] = set()
- truncated = False
- while queue:
- current, hop, path = queue.popleft()
- if hop >= max_hops:
- if reverse.get(current):
- truncated = True
- continue
- for dependent in reverse.get(current, ()):
- if dependent in visited:
- continue
- candidate_object = _object_key(dependent)
- if len(impacted) >= max_points or (
- candidate_object not in objects and len(objects) >= max_objects
- ):
- truncated = True
- continue
- visited.add(dependent)
- impacted.add(dependent)
- objects.add(candidate_object)
- dependent_path = (*path, dependent)
- paths[dependent] = dependent_path
- queue.append((dependent, hop + 1, dependent_path))
- return ImpactResult(
- impacted_point_keys=frozenset(impacted),
- paths=paths,
- truncated=truncated,
- )
|