semantics
This commit is contained in:
@@ -22,7 +22,7 @@ from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
import hashlib
|
||||
import json
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List, Optional, cast
|
||||
|
||||
from src.core.config_manager import ConfigManager
|
||||
from src.core.logger import belief_scope, logger
|
||||
@@ -72,6 +72,8 @@ from src.services.dataset_review.semantic_resolver import SemanticSourceResolver
|
||||
from src.services.dataset_review.event_logger import SessionEventPayload
|
||||
# [/DEF:DatasetReviewOrchestrator.imports:Block]
|
||||
|
||||
logger = cast(Any, logger)
|
||||
|
||||
|
||||
# [DEF:StartSessionCommand:Class]
|
||||
# @COMPLEXITY: 2
|
||||
@@ -82,6 +84,8 @@ class StartSessionCommand:
|
||||
environment_id: str
|
||||
source_kind: str
|
||||
source_input: str
|
||||
|
||||
|
||||
# [/DEF:StartSessionCommand:Class]
|
||||
|
||||
|
||||
@@ -93,6 +97,8 @@ class StartSessionResult:
|
||||
session: DatasetReviewSession
|
||||
parsed_context: Optional[SupersetParsedContext] = None
|
||||
findings: List[ValidationFinding] = field(default_factory=list)
|
||||
|
||||
|
||||
# [/DEF:StartSessionResult:Class]
|
||||
|
||||
|
||||
@@ -103,6 +109,8 @@ class StartSessionResult:
|
||||
class PreparePreviewCommand:
|
||||
user: User
|
||||
session_id: str
|
||||
|
||||
|
||||
# [/DEF:PreparePreviewCommand:Class]
|
||||
|
||||
|
||||
@@ -114,6 +122,8 @@ class PreparePreviewResult:
|
||||
session: DatasetReviewSession
|
||||
preview: CompiledPreview
|
||||
blocked_reasons: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
# [/DEF:PreparePreviewResult:Class]
|
||||
|
||||
|
||||
@@ -124,6 +134,8 @@ class PreparePreviewResult:
|
||||
class LaunchDatasetCommand:
|
||||
user: User
|
||||
session_id: str
|
||||
|
||||
|
||||
# [/DEF:LaunchDatasetCommand:Class]
|
||||
|
||||
|
||||
@@ -135,6 +147,8 @@ class LaunchDatasetResult:
|
||||
session: DatasetReviewSession
|
||||
run_context: DatasetRunContext
|
||||
blocked_reasons: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
# [/DEF:LaunchDatasetResult:Class]
|
||||
|
||||
|
||||
@@ -168,6 +182,7 @@ class DatasetReviewOrchestrator:
|
||||
self.config_manager = config_manager
|
||||
self.task_manager = task_manager
|
||||
self.semantic_resolver = semantic_resolver or SemanticSourceResolver()
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator.__init__:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator.start_session:Function]
|
||||
@@ -188,7 +203,9 @@ class DatasetReviewOrchestrator:
|
||||
normalized_environment_id = str(command.environment_id or "").strip()
|
||||
|
||||
if not normalized_source_input:
|
||||
logger.explore("Blocked dataset review session start due to empty source input")
|
||||
logger.explore(
|
||||
"Blocked dataset review session start due to empty source input"
|
||||
)
|
||||
raise ValueError("source_input must be non-empty")
|
||||
|
||||
if normalized_source_kind not in {"superset_link", "dataset_selection"}:
|
||||
@@ -196,7 +213,9 @@ class DatasetReviewOrchestrator:
|
||||
"Blocked dataset review session start due to unsupported source kind",
|
||||
extra={"source_kind": normalized_source_kind},
|
||||
)
|
||||
raise ValueError("source_kind must be 'superset_link' or 'dataset_selection'")
|
||||
raise ValueError(
|
||||
"source_kind must be 'superset_link' or 'dataset_selection'"
|
||||
)
|
||||
|
||||
environment = self.config_manager.get_environment(normalized_environment_id)
|
||||
if environment is None:
|
||||
@@ -234,11 +253,15 @@ class DatasetReviewOrchestrator:
|
||||
if parsed_context.partial_recovery:
|
||||
readiness_state = ReadinessState.RECOVERY_REQUIRED
|
||||
recommended_action = RecommendedAction.REVIEW_DOCUMENTATION
|
||||
findings.extend(self._build_partial_recovery_findings(parsed_context))
|
||||
findings.extend(
|
||||
self._build_partial_recovery_findings(parsed_context)
|
||||
)
|
||||
else:
|
||||
readiness_state = ReadinessState.REVIEW_READY
|
||||
else:
|
||||
dataset_ref, dataset_id = self._parse_dataset_selection(normalized_source_input)
|
||||
dataset_ref, dataset_id = self._parse_dataset_selection(
|
||||
normalized_source_input
|
||||
)
|
||||
readiness_state = ReadinessState.REVIEW_READY
|
||||
current_phase = SessionPhase.REVIEW
|
||||
|
||||
@@ -255,17 +278,19 @@ class DatasetReviewOrchestrator:
|
||||
status=SessionStatus.ACTIVE,
|
||||
current_phase=current_phase,
|
||||
)
|
||||
persisted_session = self.repository.create_session(session)
|
||||
persisted_session = cast(Any, self.repository.create_session(session))
|
||||
|
||||
recovered_filters: List[ImportedFilter] = []
|
||||
template_variables: List[TemplateVariable] = []
|
||||
execution_mappings: List[ExecutionMapping] = []
|
||||
if normalized_source_kind == "superset_link" and parsed_context is not None:
|
||||
recovered_filters, template_variables, execution_mappings, findings = self._build_recovery_bootstrap(
|
||||
environment=environment,
|
||||
session=persisted_session,
|
||||
parsed_context=parsed_context,
|
||||
findings=findings,
|
||||
recovered_filters, template_variables, execution_mappings, findings = (
|
||||
self._build_recovery_bootstrap(
|
||||
environment=environment,
|
||||
session=persisted_session,
|
||||
parsed_context=parsed_context,
|
||||
findings=findings,
|
||||
)
|
||||
)
|
||||
|
||||
profile = self._build_initial_profile(
|
||||
@@ -286,7 +311,9 @@ class DatasetReviewOrchestrator:
|
||||
"dataset_ref": persisted_session.dataset_ref,
|
||||
"dataset_id": persisted_session.dataset_id,
|
||||
"dashboard_id": persisted_session.dashboard_id,
|
||||
"partial_recovery": bool(parsed_context and parsed_context.partial_recovery),
|
||||
"partial_recovery": bool(
|
||||
parsed_context and parsed_context.partial_recovery
|
||||
),
|
||||
},
|
||||
)
|
||||
)
|
||||
@@ -327,7 +354,10 @@ class DatasetReviewOrchestrator:
|
||||
)
|
||||
logger.reason(
|
||||
"Linked recovery task to started dataset review session",
|
||||
extra={"session_id": persisted_session.session_id, "task_id": active_task_id},
|
||||
extra={
|
||||
"session_id": persisted_session.session_id,
|
||||
"task_id": active_task_id,
|
||||
},
|
||||
)
|
||||
|
||||
logger.reflect(
|
||||
@@ -347,6 +377,7 @@ class DatasetReviewOrchestrator:
|
||||
parsed_context=parsed_context,
|
||||
findings=findings,
|
||||
)
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator.start_session:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator.prepare_launch_preview:Function]
|
||||
@@ -357,13 +388,20 @@ class DatasetReviewOrchestrator:
|
||||
# @POST: returns preview artifact in pending, ready, failed, or stale state.
|
||||
# @SIDE_EFFECT: persists preview attempt and upstream compilation diagnostics.
|
||||
# @DATA_CONTRACT: Input[PreparePreviewCommand] -> Output[PreparePreviewResult]
|
||||
def prepare_launch_preview(self, command: PreparePreviewCommand) -> PreparePreviewResult:
|
||||
def prepare_launch_preview(
|
||||
self, command: PreparePreviewCommand
|
||||
) -> PreparePreviewResult:
|
||||
with belief_scope("DatasetReviewOrchestrator.prepare_launch_preview"):
|
||||
session = self.repository.load_session_detail(command.session_id, command.user.id)
|
||||
session = self.repository.load_session_detail(
|
||||
command.session_id, command.user.id
|
||||
)
|
||||
if session is None or session.user_id != command.user.id:
|
||||
logger.explore(
|
||||
"Preview preparation rejected because owned session was not found",
|
||||
extra={"session_id": command.session_id, "user_id": command.user.id},
|
||||
extra={
|
||||
"session_id": command.session_id,
|
||||
"user_id": command.user.id,
|
||||
},
|
||||
)
|
||||
raise ValueError("Session not found")
|
||||
|
||||
@@ -451,6 +489,7 @@ class DatasetReviewOrchestrator:
|
||||
preview=persisted_preview,
|
||||
blocked_reasons=[],
|
||||
)
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator.prepare_launch_preview:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator.launch_dataset:Function]
|
||||
@@ -464,11 +503,16 @@ class DatasetReviewOrchestrator:
|
||||
# @INVARIANT: launch remains blocked unless blocking findings are closed, approvals are satisfied, and the latest Superset preview fingerprint matches current execution inputs.
|
||||
def launch_dataset(self, command: LaunchDatasetCommand) -> LaunchDatasetResult:
|
||||
with belief_scope("DatasetReviewOrchestrator.launch_dataset"):
|
||||
session = self.repository.load_session_detail(command.session_id, command.user.id)
|
||||
session = self.repository.load_session_detail(
|
||||
command.session_id, command.user.id
|
||||
)
|
||||
if session is None or session.user_id != command.user.id:
|
||||
logger.explore(
|
||||
"Launch rejected because owned session was not found",
|
||||
extra={"session_id": command.session_id, "user_id": command.user.id},
|
||||
extra={
|
||||
"session_id": command.session_id,
|
||||
"user_id": command.user.id,
|
||||
},
|
||||
)
|
||||
raise ValueError("Session not found")
|
||||
|
||||
@@ -579,6 +623,7 @@ class DatasetReviewOrchestrator:
|
||||
run_context=persisted_run_context,
|
||||
blocked_reasons=[],
|
||||
)
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator.launch_dataset:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._parse_dataset_selection:Function]
|
||||
@@ -601,6 +646,7 @@ class DatasetReviewOrchestrator:
|
||||
return normalized, None
|
||||
|
||||
return normalized, None
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._parse_dataset_selection:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._build_initial_profile:Function]
|
||||
@@ -613,7 +659,9 @@ class DatasetReviewOrchestrator:
|
||||
parsed_context: Optional[SupersetParsedContext],
|
||||
dataset_ref: str,
|
||||
) -> DatasetProfile:
|
||||
dataset_name = dataset_ref.split(".")[-1] if dataset_ref else "Unresolved dataset"
|
||||
dataset_name = (
|
||||
dataset_ref.split(".")[-1] if dataset_ref else "Unresolved dataset"
|
||||
)
|
||||
business_summary = (
|
||||
f"Review session initialized for {dataset_ref}."
|
||||
if dataset_ref
|
||||
@@ -636,9 +684,12 @@ class DatasetReviewOrchestrator:
|
||||
completeness_score=0.25,
|
||||
confidence_state=confidence_state,
|
||||
has_blocking_findings=False,
|
||||
has_warning_findings=bool(parsed_context and parsed_context.partial_recovery),
|
||||
has_warning_findings=bool(
|
||||
parsed_context and parsed_context.partial_recovery
|
||||
),
|
||||
manual_summary_locked=False,
|
||||
)
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._build_initial_profile:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._build_partial_recovery_findings:Function]
|
||||
@@ -670,36 +721,57 @@ class DatasetReviewOrchestrator:
|
||||
)
|
||||
)
|
||||
return findings
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._build_partial_recovery_findings:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._build_recovery_bootstrap:Function]
|
||||
# @COMPLEXITY: 4
|
||||
# @PURPOSE: Recover and materialize initial imported filters, template variables, and draft execution mappings after session creation.
|
||||
# @RELATION: [CALLS] ->[SupersetContextExtractor.recover_imported_filters]
|
||||
# @RELATION: [CALLS] ->[SupersetContextExtractor.discover_template_variables]
|
||||
# @PRE: session belongs to the just-created review aggregate and parsed_context was produced for the same environment scope.
|
||||
# @POST: Returns bootstrap imported filters, template variables, execution mappings, and updated findings without persisting them directly.
|
||||
# @SIDE_EFFECT: Performs Superset reads through the extractor and may append warning findings for incomplete recovery.
|
||||
# @DATA_CONTRACT: Input[Environment, DatasetReviewSession, SupersetParsedContext, List[ValidationFinding]] -> Output[Tuple[List[ImportedFilter], List[TemplateVariable], List[ExecutionMapping], List[ValidationFinding]]]
|
||||
def _build_recovery_bootstrap(
|
||||
self,
|
||||
environment,
|
||||
session: DatasetReviewSession,
|
||||
parsed_context: SupersetParsedContext,
|
||||
findings: List[ValidationFinding],
|
||||
) -> tuple[List[ImportedFilter], List[TemplateVariable], List[ExecutionMapping], List[ValidationFinding]]:
|
||||
) -> tuple[
|
||||
List[ImportedFilter],
|
||||
List[TemplateVariable],
|
||||
List[ExecutionMapping],
|
||||
List[ValidationFinding],
|
||||
]:
|
||||
session_record = cast(Any, session)
|
||||
extractor = SupersetContextExtractor(environment)
|
||||
imported_filters_payload = extractor.recover_imported_filters(parsed_context)
|
||||
if imported_filters_payload is None:
|
||||
imported_filters_payload = []
|
||||
imported_filters = [
|
||||
ImportedFilter(
|
||||
session_id=session.session_id,
|
||||
session_id=session_record.session_id,
|
||||
filter_name=str(item.get("filter_name") or f"imported_filter_{index}"),
|
||||
display_name=item.get("display_name"),
|
||||
raw_value=item.get("raw_value"),
|
||||
normalized_value=item.get("normalized_value"),
|
||||
source=FilterSource(str(item.get("source") or FilterSource.SUPERSET_URL.value)),
|
||||
source=FilterSource(
|
||||
str(item.get("source") or FilterSource.SUPERSET_URL.value)
|
||||
),
|
||||
confidence_state=FilterConfidenceState(
|
||||
str(item.get("confidence_state") or FilterConfidenceState.UNRESOLVED.value)
|
||||
str(
|
||||
item.get("confidence_state")
|
||||
or FilterConfidenceState.UNRESOLVED.value
|
||||
)
|
||||
),
|
||||
requires_confirmation=bool(item.get("requires_confirmation", False)),
|
||||
recovery_status=FilterRecoveryStatus(
|
||||
str(item.get("recovery_status") or FilterRecoveryStatus.PARTIAL.value)
|
||||
str(
|
||||
item.get("recovery_status")
|
||||
or FilterRecoveryStatus.PARTIAL.value
|
||||
)
|
||||
),
|
||||
notes=item.get("notes"),
|
||||
)
|
||||
@@ -711,25 +783,44 @@ class DatasetReviewOrchestrator:
|
||||
|
||||
if session.dataset_id is not None:
|
||||
try:
|
||||
dataset_payload = extractor.client.get_dataset_detail(session.dataset_id)
|
||||
discovered_variables = extractor.discover_template_variables(dataset_payload)
|
||||
dataset_payload = extractor.client.get_dataset_detail(
|
||||
session_record.dataset_id
|
||||
)
|
||||
discovered_variables = extractor.discover_template_variables(
|
||||
dataset_payload
|
||||
)
|
||||
template_variables = [
|
||||
TemplateVariable(
|
||||
session_id=session.session_id,
|
||||
variable_name=str(item.get("variable_name") or f"variable_{index}"),
|
||||
session_id=session_record.session_id,
|
||||
variable_name=str(
|
||||
item.get("variable_name") or f"variable_{index}"
|
||||
),
|
||||
expression_source=str(item.get("expression_source") or ""),
|
||||
variable_kind=VariableKind(str(item.get("variable_kind") or VariableKind.UNKNOWN.value)),
|
||||
variable_kind=VariableKind(
|
||||
str(item.get("variable_kind") or VariableKind.UNKNOWN.value)
|
||||
),
|
||||
is_required=bool(item.get("is_required", True)),
|
||||
default_value=item.get("default_value"),
|
||||
mapping_status=MappingStatus(str(item.get("mapping_status") or MappingStatus.UNMAPPED.value)),
|
||||
mapping_status=MappingStatus(
|
||||
str(
|
||||
item.get("mapping_status")
|
||||
or MappingStatus.UNMAPPED.value
|
||||
)
|
||||
),
|
||||
)
|
||||
for index, item in enumerate(discovered_variables)
|
||||
]
|
||||
except Exception as exc:
|
||||
if "dataset_template_variable_discovery_failed" not in parsed_context.unresolved_references:
|
||||
parsed_context.unresolved_references.append("dataset_template_variable_discovery_failed")
|
||||
if (
|
||||
"dataset_template_variable_discovery_failed"
|
||||
not in parsed_context.unresolved_references
|
||||
):
|
||||
parsed_context.unresolved_references.append(
|
||||
"dataset_template_variable_discovery_failed"
|
||||
)
|
||||
if not any(
|
||||
finding.caused_by_ref == "dataset_template_variable_discovery_failed"
|
||||
finding.caused_by_ref
|
||||
== "dataset_template_variable_discovery_failed"
|
||||
for finding in findings
|
||||
):
|
||||
findings.append(
|
||||
@@ -745,7 +836,11 @@ class DatasetReviewOrchestrator:
|
||||
)
|
||||
logger.explore(
|
||||
"Template variable discovery failed during session bootstrap",
|
||||
extra={"session_id": session.session_id, "dataset_id": session.dataset_id, "error": str(exc)},
|
||||
extra={
|
||||
"session_id": session_record.session_id,
|
||||
"dataset_id": session_record.dataset_id,
|
||||
"error": str(exc),
|
||||
},
|
||||
)
|
||||
|
||||
filter_lookup = {
|
||||
@@ -754,7 +849,9 @@ class DatasetReviewOrchestrator:
|
||||
if str(imported_filter.filter_name or "").strip()
|
||||
}
|
||||
for template_variable in template_variables:
|
||||
matched_filter = filter_lookup.get(str(template_variable.variable_name or "").strip().lower())
|
||||
matched_filter = filter_lookup.get(
|
||||
str(template_variable.variable_name or "").strip().lower()
|
||||
)
|
||||
if matched_filter is None:
|
||||
continue
|
||||
requires_explicit_approval = bool(
|
||||
@@ -763,22 +860,27 @@ class DatasetReviewOrchestrator:
|
||||
)
|
||||
execution_mappings.append(
|
||||
ExecutionMapping(
|
||||
session_id=session.session_id,
|
||||
session_id=session_record.session_id,
|
||||
filter_id=matched_filter.filter_id,
|
||||
variable_id=template_variable.variable_id,
|
||||
mapping_method=MappingMethod.DIRECT_MATCH,
|
||||
raw_input_value=matched_filter.raw_value,
|
||||
effective_value=matched_filter.normalized_value if matched_filter.normalized_value is not None else matched_filter.raw_value,
|
||||
effective_value=matched_filter.normalized_value
|
||||
if matched_filter.normalized_value is not None
|
||||
else matched_filter.raw_value,
|
||||
transformation_note="Bootstrapped from Superset recovery context",
|
||||
warning_level=None if not requires_explicit_approval else None,
|
||||
requires_explicit_approval=requires_explicit_approval,
|
||||
approval_state=ApprovalState.PENDING if requires_explicit_approval else ApprovalState.NOT_REQUIRED,
|
||||
approval_state=ApprovalState.PENDING
|
||||
if requires_explicit_approval
|
||||
else ApprovalState.NOT_REQUIRED,
|
||||
approved_by_user_id=None,
|
||||
approved_at=None,
|
||||
)
|
||||
)
|
||||
|
||||
return imported_filters, template_variables, execution_mappings, findings
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._build_recovery_bootstrap:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._build_execution_snapshot:Function]
|
||||
@@ -789,9 +891,16 @@ class DatasetReviewOrchestrator:
|
||||
# @POST: returns deterministic execution snapshot for current session state without mutating persistence.
|
||||
# @SIDE_EFFECT: none.
|
||||
# @DATA_CONTRACT: Input[DatasetReviewSession] -> Output[Dict[str,Any]]
|
||||
def _build_execution_snapshot(self, session: DatasetReviewSession) -> Dict[str, Any]:
|
||||
filter_lookup = {item.filter_id: item for item in session.imported_filters}
|
||||
variable_lookup = {item.variable_id: item for item in session.template_variables}
|
||||
def _build_execution_snapshot(
|
||||
self, session: DatasetReviewSession
|
||||
) -> Dict[str, Any]:
|
||||
session_record = cast(Any, session)
|
||||
filter_lookup = {
|
||||
item.filter_id: item for item in session_record.imported_filters
|
||||
}
|
||||
variable_lookup = {
|
||||
item.variable_id: item for item in session_record.template_variables
|
||||
}
|
||||
|
||||
effective_filters: List[Dict[str, Any]] = []
|
||||
template_params: Dict[str, Any] = {}
|
||||
@@ -800,14 +909,16 @@ class DatasetReviewOrchestrator:
|
||||
preview_blockers: List[str] = []
|
||||
mapped_filter_ids: set[str] = set()
|
||||
|
||||
for mapping in session.execution_mappings:
|
||||
for mapping in session_record.execution_mappings:
|
||||
imported_filter = filter_lookup.get(mapping.filter_id)
|
||||
template_variable = variable_lookup.get(mapping.variable_id)
|
||||
if imported_filter is None:
|
||||
preview_blockers.append(f"mapping:{mapping.mapping_id}:missing_filter")
|
||||
continue
|
||||
if template_variable is None:
|
||||
preview_blockers.append(f"mapping:{mapping.mapping_id}:missing_variable")
|
||||
preview_blockers.append(
|
||||
f"mapping:{mapping.mapping_id}:missing_variable"
|
||||
)
|
||||
continue
|
||||
|
||||
effective_value = mapping.effective_value
|
||||
@@ -819,7 +930,9 @@ class DatasetReviewOrchestrator:
|
||||
effective_value = template_variable.default_value
|
||||
|
||||
if effective_value is None and template_variable.is_required:
|
||||
preview_blockers.append(f"variable:{template_variable.variable_name}:missing_required_value")
|
||||
preview_blockers.append(
|
||||
f"variable:{template_variable.variable_name}:missing_required_value"
|
||||
)
|
||||
continue
|
||||
|
||||
mapped_filter_ids.add(imported_filter.filter_id)
|
||||
@@ -840,10 +953,13 @@ class DatasetReviewOrchestrator:
|
||||
template_params[template_variable.variable_name] = effective_value
|
||||
if mapping.approval_state == ApprovalState.APPROVED:
|
||||
approved_mapping_ids.append(mapping.mapping_id)
|
||||
if mapping.requires_explicit_approval and mapping.approval_state != ApprovalState.APPROVED:
|
||||
if (
|
||||
mapping.requires_explicit_approval
|
||||
and mapping.approval_state != ApprovalState.APPROVED
|
||||
):
|
||||
open_warning_refs.append(mapping.mapping_id)
|
||||
|
||||
for imported_filter in session.imported_filters:
|
||||
for imported_filter in session_record.imported_filters:
|
||||
if imported_filter.filter_id in mapped_filter_ids:
|
||||
continue
|
||||
effective_value = imported_filter.normalized_value
|
||||
@@ -862,8 +978,10 @@ class DatasetReviewOrchestrator:
|
||||
}
|
||||
)
|
||||
|
||||
mapped_variable_ids = {mapping.variable_id for mapping in session.execution_mappings}
|
||||
for variable in session.template_variables:
|
||||
mapped_variable_ids = {
|
||||
mapping.variable_id for mapping in session_record.execution_mappings
|
||||
}
|
||||
for variable in session_record.template_variables:
|
||||
if variable.variable_id in mapped_variable_ids:
|
||||
continue
|
||||
if variable.default_value is not None:
|
||||
@@ -875,11 +993,13 @@ class DatasetReviewOrchestrator:
|
||||
semantic_decision_refs = [
|
||||
field.field_id
|
||||
for field in session.semantic_fields
|
||||
if field.is_locked or not field.needs_review or field.provenance.value != "unresolved"
|
||||
if field.is_locked
|
||||
or not field.needs_review
|
||||
or field.provenance.value != "unresolved"
|
||||
]
|
||||
preview_fingerprint = self._compute_preview_fingerprint(
|
||||
{
|
||||
"dataset_id": session.dataset_id,
|
||||
"dataset_id": session_record.dataset_id,
|
||||
"template_params": template_params,
|
||||
"effective_filters": effective_filters,
|
||||
}
|
||||
@@ -893,6 +1013,7 @@ class DatasetReviewOrchestrator:
|
||||
"preview_blockers": sorted(set(preview_blockers)),
|
||||
"preview_fingerprint": preview_fingerprint,
|
||||
}
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._build_execution_snapshot:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._build_launch_blockers:Function]
|
||||
@@ -909,16 +1030,21 @@ class DatasetReviewOrchestrator:
|
||||
execution_snapshot: Dict[str, Any],
|
||||
preview: Optional[CompiledPreview],
|
||||
) -> List[str]:
|
||||
session_record = cast(Any, session)
|
||||
blockers = list(execution_snapshot["preview_blockers"])
|
||||
|
||||
for finding in session.findings:
|
||||
for finding in session_record.findings:
|
||||
if (
|
||||
finding.severity == FindingSeverity.BLOCKING
|
||||
and finding.resolution_state not in {ResolutionState.RESOLVED, ResolutionState.APPROVED}
|
||||
and finding.resolution_state
|
||||
not in {ResolutionState.RESOLVED, ResolutionState.APPROVED}
|
||||
):
|
||||
blockers.append(f"finding:{finding.code}:blocking")
|
||||
for mapping in session.execution_mappings:
|
||||
if mapping.requires_explicit_approval and mapping.approval_state != ApprovalState.APPROVED:
|
||||
for mapping in session_record.execution_mappings:
|
||||
if (
|
||||
mapping.requires_explicit_approval
|
||||
and mapping.approval_state != ApprovalState.APPROVED
|
||||
):
|
||||
blockers.append(f"mapping:{mapping.mapping_id}:approval_required")
|
||||
|
||||
if preview is None:
|
||||
@@ -930,23 +1056,28 @@ class DatasetReviewOrchestrator:
|
||||
blockers.append("preview:fingerprint_mismatch")
|
||||
|
||||
return sorted(set(blockers))
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._build_launch_blockers:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._get_latest_preview:Function]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Resolve the current latest preview snapshot for one session aggregate.
|
||||
def _get_latest_preview(self, session: DatasetReviewSession) -> Optional[CompiledPreview]:
|
||||
if not session.previews:
|
||||
def _get_latest_preview(
|
||||
self, session: DatasetReviewSession
|
||||
) -> Optional[CompiledPreview]:
|
||||
session_record = cast(Any, session)
|
||||
if not session_record.previews:
|
||||
return None
|
||||
if session.last_preview_id:
|
||||
for preview in session.previews:
|
||||
if preview.preview_id == session.last_preview_id:
|
||||
if session_record.last_preview_id:
|
||||
for preview in session_record.previews:
|
||||
if preview.preview_id == session_record.last_preview_id:
|
||||
return preview
|
||||
return sorted(
|
||||
session.previews,
|
||||
session_record.previews,
|
||||
key=lambda item: (item.created_at or datetime.min, item.preview_id),
|
||||
reverse=True,
|
||||
)[0]
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._get_latest_preview:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._compute_preview_fingerprint:Function]
|
||||
@@ -955,6 +1086,7 @@ class DatasetReviewOrchestrator:
|
||||
def _compute_preview_fingerprint(self, payload: Dict[str, Any]) -> str:
|
||||
serialized = json.dumps(payload, sort_keys=True, default=str)
|
||||
return hashlib.sha256(serialized.encode("utf-8")).hexdigest()
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._compute_preview_fingerprint:Function]
|
||||
|
||||
# [DEF:DatasetReviewOrchestrator._enqueue_recovery_task:Function]
|
||||
@@ -971,28 +1103,33 @@ class DatasetReviewOrchestrator:
|
||||
session: DatasetReviewSession,
|
||||
parsed_context: Optional[SupersetParsedContext],
|
||||
) -> Optional[str]:
|
||||
session_record = cast(Any, session)
|
||||
if self.task_manager is None:
|
||||
logger.reason(
|
||||
"Dataset review session started without task manager; continuing synchronously",
|
||||
extra={"session_id": session.session_id},
|
||||
extra={"session_id": session_record.session_id},
|
||||
)
|
||||
return None
|
||||
|
||||
task_params: Dict[str, Any] = {
|
||||
"session_id": session.session_id,
|
||||
"session_id": session_record.session_id,
|
||||
"user_id": command.user.id,
|
||||
"environment_id": session.environment_id,
|
||||
"source_kind": session.source_kind,
|
||||
"source_input": session.source_input,
|
||||
"dataset_ref": session.dataset_ref,
|
||||
"dataset_id": session.dataset_id,
|
||||
"dashboard_id": session.dashboard_id,
|
||||
"partial_recovery": bool(parsed_context and parsed_context.partial_recovery),
|
||||
"environment_id": session_record.environment_id,
|
||||
"source_kind": session_record.source_kind,
|
||||
"source_input": session_record.source_input,
|
||||
"dataset_ref": session_record.dataset_ref,
|
||||
"dataset_id": session_record.dataset_id,
|
||||
"dashboard_id": session_record.dashboard_id,
|
||||
"partial_recovery": bool(
|
||||
parsed_context and parsed_context.partial_recovery
|
||||
),
|
||||
}
|
||||
|
||||
create_task = getattr(self.task_manager, "create_task", None)
|
||||
if create_task is None:
|
||||
logger.explore("Task manager has no create_task method; skipping recovery enqueue")
|
||||
logger.explore(
|
||||
"Task manager has no create_task method; skipping recovery enqueue"
|
||||
)
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -1003,13 +1140,16 @@ class DatasetReviewOrchestrator:
|
||||
except TypeError:
|
||||
logger.explore(
|
||||
"Recovery task enqueue skipped because task manager create_task contract is incompatible",
|
||||
extra={"session_id": session.session_id},
|
||||
extra={"session_id": session_record.session_id},
|
||||
)
|
||||
return None
|
||||
|
||||
task_id = getattr(task_object, "id", None)
|
||||
return str(task_id) if task_id else None
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator._enqueue_recovery_task:Function]
|
||||
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator:Class]
|
||||
|
||||
# [/DEF:DatasetReviewOrchestrator:Module]
|
||||
# [/DEF:DatasetReviewOrchestrator:Module]
|
||||
|
||||
Reference in New Issue
Block a user