contractforge-databricks 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- contractforge_databricks/__init__.py +172 -0
- contractforge_databricks/adapter.py +69 -0
- contractforge_databricks/annotations/__init__.py +10 -0
- contractforge_databricks/annotations/application.py +52 -0
- contractforge_databricks/annotations/audit.py +49 -0
- contractforge_databricks/annotations/sql.py +142 -0
- contractforge_databricks/api.py +65 -0
- contractforge_databricks/bundles/__init__.py +9 -0
- contractforge_databricks/bundles/assets.py +47 -0
- contractforge_databricks/bundles/project.py +213 -0
- contractforge_databricks/bundles/project_config.py +133 -0
- contractforge_databricks/capabilities/__init__.py +17 -0
- contractforge_databricks/capabilities/builders.py +43 -0
- contractforge_databricks/capabilities/evaluate.py +162 -0
- contractforge_databricks/capabilities/mapping.py +36 -0
- contractforge_databricks/capabilities/models.py +44 -0
- contractforge_databricks/capabilities/runtime.py +111 -0
- contractforge_databricks/capabilities/uc.py +47 -0
- contractforge_databricks/cli.py +196 -0
- contractforge_databricks/cli_deploy.py +98 -0
- contractforge_databricks/cli_governance.py +142 -0
- contractforge_databricks/cli_io.py +91 -0
- contractforge_databricks/cli_maintenance.py +69 -0
- contractforge_databricks/coercion.py +31 -0
- contractforge_databricks/contract_extensions.py +70 -0
- contractforge_databricks/cost/__init__.py +11 -0
- contractforge_databricks/cost/model.py +22 -0
- contractforge_databricks/cost/report.py +65 -0
- contractforge_databricks/cost/sql.py +136 -0
- contractforge_databricks/dashboards/__init__.py +15 -0
- contractforge_databricks/dashboards/control_tables.py +150 -0
- contractforge_databricks/diagnostics/__init__.py +7 -0
- contractforge_databricks/diagnostics/explain.py +40 -0
- contractforge_databricks/environment.py +53 -0
- contractforge_databricks/evidence/__init__.py +98 -0
- contractforge_databricks/evidence/ddl.py +35 -0
- contractforge_databricks/evidence/governance_log.py +175 -0
- contractforge_databricks/evidence/helpers.py +29 -0
- contractforge_databricks/evidence/ops_log.py +210 -0
- contractforge_databricks/evidence/records.py +27 -0
- contractforge_databricks/evidence/run_log.py +74 -0
- contractforge_databricks/evidence/schemas.py +7 -0
- contractforge_databricks/evidence/sql.py +144 -0
- contractforge_databricks/evidence/tables.py +20 -0
- contractforge_databricks/evidence/writer.py +118 -0
- contractforge_databricks/execution/__init__.py +70 -0
- contractforge_databricks/execution/delta_basic.py +57 -0
- contractforge_databricks/execution/hash_diff.py +126 -0
- contractforge_databricks/execution/hash_diff_latest.py +142 -0
- contractforge_databricks/execution/replace_partitions.py +40 -0
- contractforge_databricks/execution/results.py +5 -0
- contractforge_databricks/execution/retry.py +36 -0
- contractforge_databricks/execution/scd2.py +213 -0
- contractforge_databricks/execution/scd2_deletes.py +65 -0
- contractforge_databricks/execution/scd2_late.py +30 -0
- contractforge_databricks/execution/snapshot.py +77 -0
- contractforge_databricks/execution/sql_merge.py +85 -0
- contractforge_databricks/execution/tables.py +98 -0
- contractforge_databricks/execution/windows.py +58 -0
- contractforge_databricks/governance/__init__.py +30 -0
- contractforge_databricks/governance/access.py +185 -0
- contractforge_databricks/governance/application.py +93 -0
- contractforge_databricks/governance/drift.py +49 -0
- contractforge_databricks/governance/runtime.py +60 -0
- contractforge_databricks/governance/sql.py +31 -0
- contractforge_databricks/governance/validation.py +135 -0
- contractforge_databricks/lakeflow/__init__.py +21 -0
- contractforge_databricks/lakeflow/compatibility.py +194 -0
- contractforge_databricks/lakeflow/rendering.py +175 -0
- contractforge_databricks/lineage/__init__.py +7 -0
- contractforge_databricks/lineage/openlineage.py +182 -0
- contractforge_databricks/maintenance/__init__.py +27 -0
- contractforge_databricks/maintenance/retention.py +90 -0
- contractforge_databricks/maintenance/sql.py +68 -0
- contractforge_databricks/metrics/__init__.py +19 -0
- contractforge_databricks/metrics/history.py +21 -0
- contractforge_databricks/metrics/write.py +63 -0
- contractforge_databricks/operations/__init__.py +4 -0
- contractforge_databricks/operations/application.py +38 -0
- contractforge_databricks/operations/sql.py +95 -0
- contractforge_databricks/parity/__init__.py +18 -0
- contractforge_databricks/parity/catalog.py +59 -0
- contractforge_databricks/parity/models.py +7 -0
- contractforge_databricks/parity/scenarios.py +111 -0
- contractforge_databricks/partitioning/__init__.py +3 -0
- contractforge_databricks/partitioning/predicates.py +28 -0
- contractforge_databricks/preparation/__init__.py +47 -0
- contractforge_databricks/preparation/deduplicate.py +87 -0
- contractforge_databricks/preparation/encoding.py +37 -0
- contractforge_databricks/preparation/hashing.py +18 -0
- contractforge_databricks/preparation/pyspark.py +178 -0
- contractforge_databricks/preparation/pyspark_staging.py +70 -0
- contractforge_databricks/preparation/shape.py +209 -0
- contractforge_databricks/preparation/shape_validation.py +94 -0
- contractforge_databricks/preparation/staging.py +17 -0
- contractforge_databricks/preparation/zip_arrays.py +51 -0
- contractforge_databricks/presets/__init__.py +3 -0
- contractforge_databricks/presets/base.py +24 -0
- contractforge_databricks/presets/bronze.py +57 -0
- contractforge_databricks/presets/catalog.py +22 -0
- contractforge_databricks/presets/core.py +134 -0
- contractforge_databricks/presets/gold.py +62 -0
- contractforge_databricks/presets/modifiers.py +51 -0
- contractforge_databricks/presets/runtime.py +22 -0
- contractforge_databricks/presets/silver.py +101 -0
- contractforge_databricks/presets/write_engine.py +57 -0
- contractforge_databricks/quality/__init__.py +41 -0
- contractforge_databricks/quality/evaluation.py +178 -0
- contractforge_databricks/quality/persistence.py +81 -0
- contractforge_databricks/quality/registry.py +134 -0
- contractforge_databricks/quality/results.py +17 -0
- contractforge_databricks/quality/sql.py +113 -0
- contractforge_databricks/rendering/__init__.py +11 -0
- contractforge_databricks/rendering/bundle.py +93 -0
- contractforge_databricks/rendering/markdown.py +50 -0
- contractforge_databricks/rendering/names.py +56 -0
- contractforge_databricks/results.py +15 -0
- contractforge_databricks/runtime/__init__.py +101 -0
- contractforge_databricks/runtime/available_now.py +147 -0
- contractforge_databricks/runtime/bundles.py +211 -0
- contractforge_databricks/runtime/cache.py +20 -0
- contractforge_databricks/runtime/control_tables.py +19 -0
- contractforge_databricks/runtime/deploy.py +197 -0
- contractforge_databricks/runtime/detection.py +114 -0
- contractforge_databricks/runtime/dry_run.py +46 -0
- contractforge_databricks/runtime/errors.py +54 -0
- contractforge_databricks/runtime/file_selection.py +109 -0
- contractforge_databricks/runtime/finalization.py +168 -0
- contractforge_databricks/runtime/governance.py +37 -0
- contractforge_databricks/runtime/hooks.py +45 -0
- contractforge_databricks/runtime/http_file.py +37 -0
- contractforge_databricks/runtime/http_retry.py +15 -0
- contractforge_databricks/runtime/http_safety.py +9 -0
- contractforge_databricks/runtime/json_materialization.py +97 -0
- contractforge_databricks/runtime/lineage.py +164 -0
- contractforge_databricks/runtime/maintenance.py +43 -0
- contractforge_databricks/runtime/merge_validation.py +98 -0
- contractforge_databricks/runtime/metadata.py +21 -0
- contractforge_databricks/runtime/metrics.py +34 -0
- contractforge_databricks/runtime/models.py +32 -0
- contractforge_databricks/runtime/options.py +33 -0
- contractforge_databricks/runtime/orchestration_context.py +185 -0
- contractforge_databricks/runtime/orchestrator.py +147 -0
- contractforge_databricks/runtime/partitioning.py +93 -0
- contractforge_databricks/runtime/quality_quarantine.py +92 -0
- contractforge_databricks/runtime/rest_api.py +46 -0
- contractforge_databricks/runtime/rest_auth.py +21 -0
- contractforge_databricks/runtime/rest_pagination.py +21 -0
- contractforge_databricks/runtime/run_payload.py +177 -0
- contractforge_databricks/runtime/schema.py +106 -0
- contractforge_databricks/runtime/source_metadata.py +30 -0
- contractforge_databricks/runtime/source_registry.py +43 -0
- contractforge_databricks/runtime/source_schema.py +24 -0
- contractforge_databricks/runtime/sources.py +208 -0
- contractforge_databricks/runtime/spark.py +183 -0
- contractforge_databricks/runtime/spark_defaults.py +35 -0
- contractforge_databricks/runtime/storage_auth.py +132 -0
- contractforge_databricks/runtime/streaming.py +131 -0
- contractforge_databricks/runtime/success.py +104 -0
- contractforge_databricks/runtime/utils.py +52 -0
- contractforge_databricks/runtime/watermark.py +71 -0
- contractforge_databricks/runtime/windows.py +184 -0
- contractforge_databricks/runtime/write.py +66 -0
- contractforge_databricks/runtime/write_flow.py +146 -0
- contractforge_databricks/runtime/write_strategy.py +40 -0
- contractforge_databricks/schema/__init__.py +21 -0
- contractforge_databricks/schema/diff.py +11 -0
- contractforge_databricks/schema/policy.py +33 -0
- contractforge_databricks/schema/sync.py +23 -0
- contractforge_databricks/security/__init__.py +21 -0
- contractforge_databricks/security/errors.py +5 -0
- contractforge_databricks/security/redaction.py +5 -0
- contractforge_databricks/security/secrets.py +114 -0
- contractforge_databricks/security/source_policy.py +17 -0
- contractforge_databricks/shapes/__init__.py +3 -0
- contractforge_databricks/shapes/sql.py +123 -0
- contractforge_databricks/sources/__init__.py +67 -0
- contractforge_databricks/sources/artifacts.py +100 -0
- contractforge_databricks/sources/autoloader.py +48 -0
- contractforge_databricks/sources/bounded_streams.py +44 -0
- contractforge_databricks/sources/classification.py +115 -0
- contractforge_databricks/sources/delta_share.py +21 -0
- contractforge_databricks/sources/files.py +48 -0
- contractforge_databricks/sources/http_file.py +46 -0
- contractforge_databricks/sources/interpret.py +76 -0
- contractforge_databricks/sources/jdbc.py +32 -0
- contractforge_databricks/sources/metadata.py +18 -0
- contractforge_databricks/sources/native_passthrough.py +33 -0
- contractforge_databricks/sources/rds_iam.py +15 -0
- contractforge_databricks/sources/rds_iam_runtime.py +191 -0
- contractforge_databricks/sources/rest_api.py +33 -0
- contractforge_databricks/sources/support.py +50 -0
- contractforge_databricks/sources/table_refs.py +65 -0
- contractforge_databricks/sql/__init__.py +4 -0
- contractforge_databricks/sql/identifiers.py +17 -0
- contractforge_databricks/sql/literals.py +36 -0
- contractforge_databricks/state/__init__.py +39 -0
- contractforge_databricks/state/ddl.py +24 -0
- contractforge_databricks/state/migrations.py +146 -0
- contractforge_databricks/state/queries.py +149 -0
- contractforge_databricks/state/sql.py +116 -0
- contractforge_databricks/state/tables.py +9 -0
- contractforge_databricks/state/writer.py +83 -0
- contractforge_databricks/templates/__init__.py +15 -0
- contractforge_databricks/templates/catalog.py +205 -0
- contractforge_databricks/templates/catalog_parity.py +85 -0
- contractforge_databricks/templates/core.py +83 -0
- contractforge_databricks/templates/enrichment.py +175 -0
- contractforge_databricks/transforms/__init__.py +3 -0
- contractforge_databricks/transforms/sql.py +118 -0
- contractforge_databricks/watermark/__init__.py +6 -0
- contractforge_databricks/watermark/sql.py +91 -0
- contractforge_databricks/write_modes/__init__.py +20 -0
- contractforge_databricks/write_modes/registry.py +44 -0
- contractforge_databricks/write_modes/sql.py +33 -0
- contractforge_databricks/write_modes/strategy.py +192 -0
- contractforge_databricks-0.1.0.dist-info/METADATA +34 -0
- contractforge_databricks-0.1.0.dist-info/RECORD +220 -0
- contractforge_databricks-0.1.0.dist-info/WHEEL +4 -0
- contractforge_databricks-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Databricks interpretation of the core environment contract."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from contractforge_core.contracts import validate_environment_contract
|
|
9
|
+
from contractforge_databricks.coercion import mapping
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class DatabricksEnvironment:
|
|
14
|
+
name: str = "dev"
|
|
15
|
+
evidence_catalog: str = "main"
|
|
16
|
+
evidence_schema: str = "ops"
|
|
17
|
+
workspace_path: str = "/Workspace/ContractForge"
|
|
18
|
+
bundle_target: str = "dev"
|
|
19
|
+
runtime_kind: str | None = None
|
|
20
|
+
parameters: dict[str, Any] | None = None
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def from_contract(cls, value: dict[str, Any] | None) -> "DatabricksEnvironment":
|
|
24
|
+
if value is None:
|
|
25
|
+
return cls()
|
|
26
|
+
env = validate_environment_contract(value)
|
|
27
|
+
if env["adapter"] != "databricks":
|
|
28
|
+
raise ValueError("Databricks adapter requires environment.adapter='databricks'")
|
|
29
|
+
runtime = mapping(env.get("runtime"))
|
|
30
|
+
deployment = mapping(env.get("deployment"))
|
|
31
|
+
evidence = mapping(env.get("evidence"))
|
|
32
|
+
parameters = mapping(env.get("parameters")).get("databricks", {})
|
|
33
|
+
return cls(
|
|
34
|
+
name=str(env["name"]),
|
|
35
|
+
evidence_catalog=str(evidence.get("catalog", "main")),
|
|
36
|
+
evidence_schema=str(evidence.get("schema", "ops")),
|
|
37
|
+
workspace_path=str(deployment.get("workspace_path", "/Workspace/ContractForge")).rstrip("/"),
|
|
38
|
+
bundle_target=str(deployment.get("target", env["name"])),
|
|
39
|
+
runtime_kind=_first_text(
|
|
40
|
+
runtime.get("kind"),
|
|
41
|
+
runtime.get("runtime_type"),
|
|
42
|
+
parameters.get("runtime_kind"),
|
|
43
|
+
parameters.get("runtime_type"),
|
|
44
|
+
parameters.get("runtime"),
|
|
45
|
+
),
|
|
46
|
+
parameters=dict(parameters) if isinstance(parameters, dict) else {},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def _first_text(*values: object) -> str | None:
|
|
50
|
+
for value in values:
|
|
51
|
+
if isinstance(value, str) and value.strip():
|
|
52
|
+
return value.strip()
|
|
53
|
+
return None
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from contractforge_databricks.evidence.ddl import render_create_evidence_tables_sql
|
|
2
|
+
from contractforge_databricks.evidence.schemas import EVIDENCE_TABLE_COLUMNS, EVIDENCE_TABLE_SCHEMAS
|
|
3
|
+
from contractforge_core.evidence import (
|
|
4
|
+
AccessEvidenceRecord,
|
|
5
|
+
CostEvidenceRecord,
|
|
6
|
+
ErrorEvidenceRecord,
|
|
7
|
+
LineageEvidenceRecord,
|
|
8
|
+
QualityEvidenceRecord,
|
|
9
|
+
QuarantineEvidenceRecord,
|
|
10
|
+
RunEvidenceRecord,
|
|
11
|
+
SchemaChangeEvidenceRecord,
|
|
12
|
+
SourceMetadataEvidenceRecord,
|
|
13
|
+
StreamBatchEvidenceRecord,
|
|
14
|
+
)
|
|
15
|
+
from contractforge_databricks.evidence.ops_log import (
|
|
16
|
+
ERROR_COLUMNS,
|
|
17
|
+
SCHEMA_CHANGE_COLUMNS,
|
|
18
|
+
STREAM_COLUMNS,
|
|
19
|
+
render_error_log_insert_sql,
|
|
20
|
+
render_schema_change_log_insert_sql,
|
|
21
|
+
render_schema_change_log_insert_sqls,
|
|
22
|
+
render_stream_child_run_metrics_sql,
|
|
23
|
+
render_stream_finish_update_sql,
|
|
24
|
+
render_stream_log_insert_sql,
|
|
25
|
+
)
|
|
26
|
+
from contractforge_databricks.evidence.governance_log import (
|
|
27
|
+
ACCESS_COLUMNS,
|
|
28
|
+
ANNOTATION_COLUMNS,
|
|
29
|
+
OPERATION_COLUMNS,
|
|
30
|
+
render_access_log_insert_sql,
|
|
31
|
+
render_access_log_insert_sqls,
|
|
32
|
+
render_annotation_log_insert_sql,
|
|
33
|
+
render_annotation_log_insert_sqls,
|
|
34
|
+
render_operations_log_insert_sql,
|
|
35
|
+
)
|
|
36
|
+
from contractforge_databricks.evidence.run_log import RUN_COLUMNS, render_run_log_insert_sql
|
|
37
|
+
from contractforge_databricks.evidence.sql import (
|
|
38
|
+
render_access_insert_sql,
|
|
39
|
+
render_cost_insert_sql,
|
|
40
|
+
render_error_insert_sql,
|
|
41
|
+
render_lineage_insert_sql,
|
|
42
|
+
render_quality_insert_sql,
|
|
43
|
+
render_quarantine_insert_sql,
|
|
44
|
+
render_run_insert_sql,
|
|
45
|
+
render_schema_change_insert_sql,
|
|
46
|
+
render_source_metadata_insert_sql,
|
|
47
|
+
render_stream_batch_insert_sql,
|
|
48
|
+
)
|
|
49
|
+
from contractforge_databricks.evidence.tables import evidence_table_names, render_evidence_table_notes
|
|
50
|
+
from contractforge_databricks.evidence.writer import EvidenceWriter
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
"AccessEvidenceRecord",
|
|
54
|
+
"ACCESS_COLUMNS",
|
|
55
|
+
"ANNOTATION_COLUMNS",
|
|
56
|
+
"CostEvidenceRecord",
|
|
57
|
+
"ErrorEvidenceRecord",
|
|
58
|
+
"EvidenceWriter",
|
|
59
|
+
"EVIDENCE_TABLE_COLUMNS",
|
|
60
|
+
"EVIDENCE_TABLE_SCHEMAS",
|
|
61
|
+
"ERROR_COLUMNS",
|
|
62
|
+
"LineageEvidenceRecord",
|
|
63
|
+
"QualityEvidenceRecord",
|
|
64
|
+
"QuarantineEvidenceRecord",
|
|
65
|
+
"OPERATION_COLUMNS",
|
|
66
|
+
"RUN_COLUMNS",
|
|
67
|
+
"SCHEMA_CHANGE_COLUMNS",
|
|
68
|
+
"STREAM_COLUMNS",
|
|
69
|
+
"RunEvidenceRecord",
|
|
70
|
+
"SchemaChangeEvidenceRecord",
|
|
71
|
+
"SourceMetadataEvidenceRecord",
|
|
72
|
+
"StreamBatchEvidenceRecord",
|
|
73
|
+
"evidence_table_names",
|
|
74
|
+
"render_access_insert_sql",
|
|
75
|
+
"render_access_log_insert_sql",
|
|
76
|
+
"render_access_log_insert_sqls",
|
|
77
|
+
"render_annotation_log_insert_sql",
|
|
78
|
+
"render_annotation_log_insert_sqls",
|
|
79
|
+
"render_create_evidence_tables_sql",
|
|
80
|
+
"render_cost_insert_sql",
|
|
81
|
+
"render_error_insert_sql",
|
|
82
|
+
"render_error_log_insert_sql",
|
|
83
|
+
"render_evidence_table_notes",
|
|
84
|
+
"render_lineage_insert_sql",
|
|
85
|
+
"render_operations_log_insert_sql",
|
|
86
|
+
"render_quality_insert_sql",
|
|
87
|
+
"render_quarantine_insert_sql",
|
|
88
|
+
"render_run_insert_sql",
|
|
89
|
+
"render_run_log_insert_sql",
|
|
90
|
+
"render_schema_change_insert_sql",
|
|
91
|
+
"render_schema_change_log_insert_sql",
|
|
92
|
+
"render_schema_change_log_insert_sqls",
|
|
93
|
+
"render_source_metadata_insert_sql",
|
|
94
|
+
"render_stream_child_run_metrics_sql",
|
|
95
|
+
"render_stream_finish_update_sql",
|
|
96
|
+
"render_stream_batch_insert_sql",
|
|
97
|
+
"render_stream_log_insert_sql",
|
|
98
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Databricks Delta DDL for ContractForge evidence tables."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from contractforge_databricks.evidence.tables import evidence_table_names
|
|
6
|
+
from contractforge_databricks.evidence.schemas import EVIDENCE_TABLE_COLUMNS, EVIDENCE_TABLE_SCHEMAS
|
|
7
|
+
from contractforge_databricks.sql import quote_table_name
|
|
8
|
+
|
|
9
|
+
_PARTITION_COLUMNS = {
|
|
10
|
+
"runs": "run_date",
|
|
11
|
+
"errors": "error_date",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def render_create_evidence_tables_sql(*, catalog: str = "main", schema: str = "ops") -> str:
|
|
16
|
+
table_names = evidence_table_names(catalog, schema)
|
|
17
|
+
statements = [f"CREATE SCHEMA IF NOT EXISTS {quote_table_name(f'{catalog}.{schema}')};"]
|
|
18
|
+
for name, table in table_names.items():
|
|
19
|
+
columns = EVIDENCE_TABLE_SCHEMAS[name]
|
|
20
|
+
partition = _PARTITION_COLUMNS.get(name)
|
|
21
|
+
partition_sql = f"PARTITIONED BY ({partition})" if partition else ""
|
|
22
|
+
statements.append(
|
|
23
|
+
"\n".join(
|
|
24
|
+
[
|
|
25
|
+
f"CREATE TABLE IF NOT EXISTS {quote_table_name(table)} (",
|
|
26
|
+
f" {columns}",
|
|
27
|
+
")",
|
|
28
|
+
f"USING DELTA{(' ' + partition_sql) if partition_sql else ''};",
|
|
29
|
+
]
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
return "\n\n".join(statements) + "\n"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
__all__ = ["EVIDENCE_TABLE_COLUMNS", "EVIDENCE_TABLE_SCHEMAS", "render_create_evidence_tables_sql"]
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Governance and operations control-table SQL rendering."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from contractforge_core.config import CTRL_SCHEMA_VERSION, FRAMEWORK_VERSION
|
|
9
|
+
from contractforge_databricks.evidence.helpers import TimestampClock, cast_sql, utc_timestamp
|
|
10
|
+
from contractforge_databricks.evidence.schemas import EVIDENCE_TABLE_COLUMNS
|
|
11
|
+
from contractforge_databricks.evidence.tables import evidence_table_names
|
|
12
|
+
from contractforge_databricks.security import redact_text, redact_value
|
|
13
|
+
from contractforge_databricks.sql import quote_table_name, sql_int, sql_string
|
|
14
|
+
|
|
15
|
+
ANNOTATION_COLUMNS = tuple(column.split(" ", 1)[0] for column in EVIDENCE_TABLE_COLUMNS["annotations"])
|
|
16
|
+
ACCESS_COLUMNS = tuple(column.split(" ", 1)[0] for column in EVIDENCE_TABLE_COLUMNS["access"])
|
|
17
|
+
OPERATION_COLUMNS = tuple(column.split(" ", 1)[0] for column in EVIDENCE_TABLE_COLUMNS["operations"])
|
|
18
|
+
INT_COLUMNS = {"ctrl_schema_version", "freshness_sla_minutes"}
|
|
19
|
+
BOOL_COLUMNS = {"revoke_unmanaged", "alert_on_failure", "alert_on_quality_fail"}
|
|
20
|
+
DATE_COLUMNS = {"annotation_date", "access_date"}
|
|
21
|
+
TIMESTAMP_COLUMNS = {"annotation_ts_utc", "access_ts_utc", "applied_at_utc", "recorded_at_utc"}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def render_annotation_log_insert_sql(
|
|
25
|
+
payload: dict[str, Any],
|
|
26
|
+
*,
|
|
27
|
+
catalog: str = "main",
|
|
28
|
+
schema: str = "ops",
|
|
29
|
+
clock: TimestampClock | None = None,
|
|
30
|
+
) -> str:
|
|
31
|
+
table = evidence_table_names(catalog, schema)["annotations"]
|
|
32
|
+
enriched = _annotation_payload(payload, clock=clock)
|
|
33
|
+
values = [_value(column, _alias(enriched, column)) for column in ANNOTATION_COLUMNS]
|
|
34
|
+
return f"INSERT INTO {quote_table_name(table)} ({', '.join(ANNOTATION_COLUMNS)}) VALUES ({', '.join(values)})"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def render_annotation_log_insert_sqls(
|
|
38
|
+
*,
|
|
39
|
+
run_id: str,
|
|
40
|
+
target_table: str,
|
|
41
|
+
entries: tuple[dict[str, Any], ...] | list[dict[str, Any]],
|
|
42
|
+
catalog: str = "main",
|
|
43
|
+
schema: str = "ops",
|
|
44
|
+
clock: TimestampClock | None = None,
|
|
45
|
+
) -> tuple[str, ...]:
|
|
46
|
+
return tuple(
|
|
47
|
+
render_annotation_log_insert_sql(
|
|
48
|
+
_annotation_payload({**entry, "run_id": run_id, "target_table": target_table}, clock=clock),
|
|
49
|
+
catalog=catalog,
|
|
50
|
+
schema=schema,
|
|
51
|
+
clock=clock,
|
|
52
|
+
)
|
|
53
|
+
for entry in entries
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def render_access_log_insert_sql(
|
|
58
|
+
payload: dict[str, Any],
|
|
59
|
+
*,
|
|
60
|
+
catalog: str = "main",
|
|
61
|
+
schema: str = "ops",
|
|
62
|
+
clock: TimestampClock | None = None,
|
|
63
|
+
) -> str:
|
|
64
|
+
table = evidence_table_names(catalog, schema)["access"]
|
|
65
|
+
enriched = _access_payload(payload, clock=clock)
|
|
66
|
+
values = [_value(column, _alias(enriched, column)) for column in ACCESS_COLUMNS]
|
|
67
|
+
return f"INSERT INTO {quote_table_name(table)} ({', '.join(ACCESS_COLUMNS)}) VALUES ({', '.join(values)})"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def render_access_log_insert_sqls(
|
|
71
|
+
*,
|
|
72
|
+
run_id: str,
|
|
73
|
+
target_table: str,
|
|
74
|
+
entries: tuple[dict[str, Any], ...] | list[dict[str, Any]],
|
|
75
|
+
catalog: str = "main",
|
|
76
|
+
schema: str = "ops",
|
|
77
|
+
clock: TimestampClock | None = None,
|
|
78
|
+
) -> tuple[str, ...]:
|
|
79
|
+
return tuple(
|
|
80
|
+
render_access_log_insert_sql(
|
|
81
|
+
_access_payload(
|
|
82
|
+
{**entry, "access_run_id": entry.get("access_run_id") or run_id, "run_id": run_id, "target_table": target_table},
|
|
83
|
+
clock=clock,
|
|
84
|
+
),
|
|
85
|
+
catalog=catalog,
|
|
86
|
+
schema=schema,
|
|
87
|
+
clock=clock,
|
|
88
|
+
)
|
|
89
|
+
for entry in entries
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def render_operations_log_insert_sql(
|
|
94
|
+
payload: dict[str, Any],
|
|
95
|
+
*,
|
|
96
|
+
catalog: str = "main",
|
|
97
|
+
schema: str = "ops",
|
|
98
|
+
clock: TimestampClock | None = None,
|
|
99
|
+
) -> str:
|
|
100
|
+
table = evidence_table_names(catalog, schema)["operations"]
|
|
101
|
+
enriched = _operations_payload(payload, clock=clock)
|
|
102
|
+
values = [_value(column, enriched.get(column)) for column in OPERATION_COLUMNS]
|
|
103
|
+
return f"INSERT INTO {quote_table_name(table)} ({', '.join(OPERATION_COLUMNS)}) VALUES ({', '.join(values)})"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _annotation_payload(payload: dict[str, Any], *, clock: TimestampClock | None = None) -> dict[str, Any]:
|
|
107
|
+
now = _utc_timestamp(clock)
|
|
108
|
+
return {
|
|
109
|
+
**payload,
|
|
110
|
+
"applied_sql": payload.get("applied_sql", payload.get("sql")),
|
|
111
|
+
"annotation_ts_utc": payload.get("annotation_ts_utc") or now,
|
|
112
|
+
"annotation_date": payload.get("annotation_date") or now[:10],
|
|
113
|
+
"framework_version": payload.get("framework_version") or FRAMEWORK_VERSION,
|
|
114
|
+
"ctrl_schema_version": payload.get("ctrl_schema_version") or CTRL_SCHEMA_VERSION,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _access_payload(payload: dict[str, Any], *, clock: TimestampClock | None = None) -> dict[str, Any]:
|
|
119
|
+
now = _utc_timestamp(clock)
|
|
120
|
+
enriched = {
|
|
121
|
+
**payload,
|
|
122
|
+
"applied_sql": payload.get("applied_sql", payload.get("sql")),
|
|
123
|
+
"action": payload.get("action") or payload.get("access_type"),
|
|
124
|
+
"access_ts_utc": payload.get("access_ts_utc") or now,
|
|
125
|
+
"access_date": payload.get("access_date") or now[:10],
|
|
126
|
+
"applied_at_utc": payload.get("applied_at_utc") or now,
|
|
127
|
+
"framework_version": payload.get("framework_version") or FRAMEWORK_VERSION,
|
|
128
|
+
"ctrl_schema_version": payload.get("ctrl_schema_version") or CTRL_SCHEMA_VERSION,
|
|
129
|
+
}
|
|
130
|
+
return {**enriched, "payload_json": payload.get("payload_json", enriched)}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _operations_payload(payload: dict[str, Any], *, clock: TimestampClock | None = None) -> dict[str, Any]:
|
|
134
|
+
return {
|
|
135
|
+
**payload,
|
|
136
|
+
"recorded_at_utc": payload.get("recorded_at_utc") or _utc_timestamp(clock),
|
|
137
|
+
"framework_version": payload.get("framework_version") or FRAMEWORK_VERSION,
|
|
138
|
+
"ctrl_schema_version": payload.get("ctrl_schema_version") or CTRL_SCHEMA_VERSION,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _alias(payload: dict[str, Any], column: str) -> Any:
|
|
143
|
+
if column == "applied_sql":
|
|
144
|
+
return payload.get("applied_sql", payload.get("sql"))
|
|
145
|
+
if column == "payload_json" and "payload_json" not in payload:
|
|
146
|
+
return payload
|
|
147
|
+
return payload.get(column)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _value(column: str, value: Any) -> str:
|
|
151
|
+
if column in INT_COLUMNS:
|
|
152
|
+
return sql_int(value)
|
|
153
|
+
if column in BOOL_COLUMNS:
|
|
154
|
+
return "NULL" if value is None else str(bool(value)).lower()
|
|
155
|
+
if column in DATE_COLUMNS:
|
|
156
|
+
return cast_sql(value, "DATE")
|
|
157
|
+
if column in TIMESTAMP_COLUMNS or column.endswith("_utc"):
|
|
158
|
+
return cast_sql(value, "TIMESTAMP")
|
|
159
|
+
if column.endswith("_json"):
|
|
160
|
+
return sql_string(_json_text(value))
|
|
161
|
+
if column == "error_message":
|
|
162
|
+
return sql_string(redact_text(str(value))[:2000] if value is not None else None)
|
|
163
|
+
return sql_string(redact_value(value))
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _json_text(value: Any) -> str | None:
|
|
167
|
+
if value is None:
|
|
168
|
+
return None
|
|
169
|
+
if isinstance(value, str):
|
|
170
|
+
return redact_text(value)
|
|
171
|
+
return json.dumps(redact_value(value), sort_keys=True, separators=(",", ":"))
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _utc_timestamp(clock: TimestampClock | None = None) -> str:
|
|
175
|
+
return utc_timestamp(clock)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Shared Databricks evidence SQL rendering helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import date, datetime, timezone
|
|
6
|
+
from typing import Any, Callable
|
|
7
|
+
|
|
8
|
+
from contractforge_databricks.sql import sql_string
|
|
9
|
+
|
|
10
|
+
TimestampClock = Callable[[], datetime | str]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def cast_sql(value: Any, sql_type: str) -> str:
|
|
14
|
+
if value is None:
|
|
15
|
+
return f"CAST(NULL AS {sql_type})"
|
|
16
|
+
if isinstance(value, datetime):
|
|
17
|
+
text = value.strftime("%Y-%m-%d %H:%M:%S")
|
|
18
|
+
elif isinstance(value, date):
|
|
19
|
+
text = value.isoformat()
|
|
20
|
+
else:
|
|
21
|
+
text = str(value)
|
|
22
|
+
return f"CAST({sql_string(text)} AS {sql_type})"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def utc_timestamp(clock: TimestampClock | None = None) -> str:
|
|
26
|
+
value = clock() if clock is not None else datetime.now(timezone.utc)
|
|
27
|
+
if isinstance(value, datetime):
|
|
28
|
+
return value.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
|
|
29
|
+
return str(value)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Operational control-table SQL rendering."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from contractforge_core.config import CTRL_SCHEMA_VERSION, FRAMEWORK_VERSION
|
|
9
|
+
from contractforge_databricks.evidence.helpers import TimestampClock, cast_sql, utc_timestamp
|
|
10
|
+
from contractforge_databricks.evidence.schemas import EVIDENCE_TABLE_COLUMNS
|
|
11
|
+
from contractforge_databricks.evidence.tables import evidence_table_names
|
|
12
|
+
from contractforge_databricks.security import redact_text, redact_value
|
|
13
|
+
from contractforge_databricks.sql import quote_identifier, quote_table_name, sql_int, sql_string
|
|
14
|
+
|
|
15
|
+
ERROR_COLUMNS = tuple(column.split(" ", 1)[0] for column in EVIDENCE_TABLE_COLUMNS["errors"])
|
|
16
|
+
SCHEMA_CHANGE_COLUMNS = tuple(column.split(" ", 1)[0] for column in EVIDENCE_TABLE_COLUMNS["schema_changes"])
|
|
17
|
+
STREAM_COLUMNS = tuple(column.split(" ", 1)[0] for column in EVIDENCE_TABLE_COLUMNS["streams"])
|
|
18
|
+
INT_COLUMNS = {
|
|
19
|
+
"ctrl_schema_version",
|
|
20
|
+
"batches_processed",
|
|
21
|
+
"total_rows_read",
|
|
22
|
+
"total_rows_written",
|
|
23
|
+
"total_rows_quarantined",
|
|
24
|
+
}
|
|
25
|
+
FLOAT_COLUMNS = {"duration_seconds"}
|
|
26
|
+
BOOL_COLUMNS = {"applied"}
|
|
27
|
+
DATE_COLUMNS = {"error_date", "access_date", "annotation_date"}
|
|
28
|
+
TIMESTAMP_COLUMNS = {
|
|
29
|
+
"error_ts_utc",
|
|
30
|
+
"occurred_at_utc",
|
|
31
|
+
"change_ts_utc",
|
|
32
|
+
"changed_at_utc",
|
|
33
|
+
"started_at_utc",
|
|
34
|
+
"ended_at_utc",
|
|
35
|
+
"captured_at_utc",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def render_error_log_insert_sql(
|
|
40
|
+
payload: dict[str, Any],
|
|
41
|
+
*,
|
|
42
|
+
catalog: str = "main",
|
|
43
|
+
schema: str = "ops",
|
|
44
|
+
) -> str:
|
|
45
|
+
table = evidence_table_names(catalog, schema)["errors"]
|
|
46
|
+
values = [_value(column, payload.get(column)) for column in ERROR_COLUMNS]
|
|
47
|
+
return f"INSERT INTO {quote_table_name(table)} ({', '.join(ERROR_COLUMNS)}) VALUES ({', '.join(values)})"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def render_schema_change_log_insert_sql(
|
|
51
|
+
payload: dict[str, Any],
|
|
52
|
+
*,
|
|
53
|
+
catalog: str = "main",
|
|
54
|
+
schema: str = "ops",
|
|
55
|
+
) -> str:
|
|
56
|
+
table = evidence_table_names(catalog, schema)["schema_changes"]
|
|
57
|
+
values = [_value(column, payload.get(column)) for column in SCHEMA_CHANGE_COLUMNS]
|
|
58
|
+
return (
|
|
59
|
+
f"INSERT INTO {quote_table_name(table)} ({', '.join(SCHEMA_CHANGE_COLUMNS)}) VALUES "
|
|
60
|
+
f"({', '.join(values)})"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def render_schema_change_log_insert_sqls(
|
|
65
|
+
*,
|
|
66
|
+
run_id: str,
|
|
67
|
+
target_table: str,
|
|
68
|
+
schema_changes: dict[str, Any],
|
|
69
|
+
source_schema: dict[str, str] | None = None,
|
|
70
|
+
catalog: str = "main",
|
|
71
|
+
schema: str = "ops",
|
|
72
|
+
clock: TimestampClock | None = None,
|
|
73
|
+
) -> tuple[str, ...]:
|
|
74
|
+
now = _utc_timestamp(clock)
|
|
75
|
+
rows = []
|
|
76
|
+
for column in schema_changes.get("added_columns") or ():
|
|
77
|
+
source_type = (source_schema or {}).get(column)
|
|
78
|
+
payload = {"column": column, "source_type": source_type}
|
|
79
|
+
rows.append(
|
|
80
|
+
{
|
|
81
|
+
"run_id": run_id,
|
|
82
|
+
"change_ts_utc": now,
|
|
83
|
+
"target_table": target_table,
|
|
84
|
+
"change_type": "add_column",
|
|
85
|
+
"column_name": column,
|
|
86
|
+
"source_type": source_type,
|
|
87
|
+
"applied": True,
|
|
88
|
+
"details_json": {},
|
|
89
|
+
"payload_json": payload,
|
|
90
|
+
"changed_at_utc": now,
|
|
91
|
+
"framework_version": FRAMEWORK_VERSION,
|
|
92
|
+
"ctrl_schema_version": CTRL_SCHEMA_VERSION,
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
for change in schema_changes.get("type_changes") or ():
|
|
96
|
+
rows.append(
|
|
97
|
+
{
|
|
98
|
+
"run_id": run_id,
|
|
99
|
+
"change_ts_utc": now,
|
|
100
|
+
"target_table": target_table,
|
|
101
|
+
"change_type": change.get("change", "type_change"),
|
|
102
|
+
"column_name": change.get("column"),
|
|
103
|
+
"source_type": change.get("source"),
|
|
104
|
+
"target_type": change.get("target"),
|
|
105
|
+
"applied": bool(change.get("applied")),
|
|
106
|
+
"details_json": change,
|
|
107
|
+
"payload_json": change,
|
|
108
|
+
"changed_at_utc": now,
|
|
109
|
+
"framework_version": FRAMEWORK_VERSION,
|
|
110
|
+
"ctrl_schema_version": CTRL_SCHEMA_VERSION,
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
return tuple(render_schema_change_log_insert_sql(row, catalog=catalog, schema=schema) for row in rows)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def render_stream_log_insert_sql(
|
|
117
|
+
payload: dict[str, Any],
|
|
118
|
+
*,
|
|
119
|
+
catalog: str = "main",
|
|
120
|
+
schema: str = "ops",
|
|
121
|
+
clock: TimestampClock | None = None,
|
|
122
|
+
) -> str:
|
|
123
|
+
table = evidence_table_names(catalog, schema)["streams"]
|
|
124
|
+
enriched = _stream_start_payload(payload, clock=clock)
|
|
125
|
+
values = [_value(column, enriched.get(column)) for column in STREAM_COLUMNS]
|
|
126
|
+
return f"INSERT INTO {quote_table_name(table)} ({', '.join(STREAM_COLUMNS)}) VALUES ({', '.join(values)})"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def render_stream_finish_update_sql(
|
|
130
|
+
*,
|
|
131
|
+
stream_run_id: str,
|
|
132
|
+
payload: dict[str, Any],
|
|
133
|
+
catalog: str = "main",
|
|
134
|
+
schema: str = "ops",
|
|
135
|
+
) -> str | None:
|
|
136
|
+
assignments = [
|
|
137
|
+
f"{quote_identifier(column)} = {_value(column, payload[column])}"
|
|
138
|
+
for column in STREAM_COLUMNS
|
|
139
|
+
if column != "stream_run_id" and column in payload
|
|
140
|
+
]
|
|
141
|
+
if not assignments:
|
|
142
|
+
return None
|
|
143
|
+
table = evidence_table_names(catalog, schema)["streams"]
|
|
144
|
+
return (
|
|
145
|
+
f"UPDATE {quote_table_name(table)} SET {', '.join(assignments)} "
|
|
146
|
+
f"WHERE stream_run_id = {sql_string(stream_run_id)}"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def render_stream_child_run_metrics_sql(
|
|
151
|
+
*,
|
|
152
|
+
stream_run_id: str,
|
|
153
|
+
runs_table: str = "main.ops.ctrl_ingestion_runs",
|
|
154
|
+
) -> str:
|
|
155
|
+
return "\n".join(
|
|
156
|
+
[
|
|
157
|
+
"SELECT",
|
|
158
|
+
" count(1) AS batches_processed,",
|
|
159
|
+
" sum(coalesce(rows_read, 0)) AS total_rows_read,",
|
|
160
|
+
" sum(coalesce(rows_written, 0)) AS total_rows_written,",
|
|
161
|
+
" sum(coalesce(rows_quarantined, 0)) AS total_rows_quarantined",
|
|
162
|
+
f"FROM {quote_table_name(runs_table)}",
|
|
163
|
+
f"WHERE parent_run_id = {sql_string(stream_run_id)}",
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _value(column: str, value: Any) -> str:
|
|
169
|
+
if column in INT_COLUMNS:
|
|
170
|
+
return sql_int(value)
|
|
171
|
+
if column in FLOAT_COLUMNS:
|
|
172
|
+
return "NULL" if value is None else str(float(value))
|
|
173
|
+
if column in BOOL_COLUMNS:
|
|
174
|
+
return "NULL" if value is None else str(bool(value)).lower()
|
|
175
|
+
if column in DATE_COLUMNS:
|
|
176
|
+
return cast_sql(value, "DATE")
|
|
177
|
+
if column in TIMESTAMP_COLUMNS or column.endswith("_utc"):
|
|
178
|
+
return cast_sql(value, "TIMESTAMP")
|
|
179
|
+
if column.endswith("_json"):
|
|
180
|
+
return sql_string(_json_text(value))
|
|
181
|
+
if column in {"error_message", "stack_trace"}:
|
|
182
|
+
return sql_string(redact_text(str(value))[:4000] if value is not None else None)
|
|
183
|
+
return sql_string(redact_value(value))
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _json_text(value: Any) -> str | None:
|
|
187
|
+
if value is None:
|
|
188
|
+
return None
|
|
189
|
+
if isinstance(value, str):
|
|
190
|
+
return redact_text(value)
|
|
191
|
+
return json.dumps(redact_value(value), sort_keys=True, separators=(",", ":"))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _stream_start_payload(payload: dict[str, Any], *, clock: TimestampClock | None = None) -> dict[str, Any]:
|
|
195
|
+
now = _utc_timestamp(clock)
|
|
196
|
+
return {
|
|
197
|
+
**payload,
|
|
198
|
+
"started_at_utc": payload.get("started_at_utc") or now,
|
|
199
|
+
"captured_at_utc": payload.get("captured_at_utc") or now,
|
|
200
|
+
"batches_processed": payload.get("batches_processed", 0),
|
|
201
|
+
"total_rows_read": payload.get("total_rows_read", 0),
|
|
202
|
+
"total_rows_written": payload.get("total_rows_written", 0),
|
|
203
|
+
"total_rows_quarantined": payload.get("total_rows_quarantined", 0),
|
|
204
|
+
"framework_version": payload.get("framework_version") or FRAMEWORK_VERSION,
|
|
205
|
+
"ctrl_schema_version": payload.get("ctrl_schema_version") or CTRL_SCHEMA_VERSION,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _utc_timestamp(clock: TimestampClock | None = None) -> str:
|
|
210
|
+
return utc_timestamp(clock)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Compatibility exports for platform-neutral evidence record models."""
|
|
2
|
+
|
|
3
|
+
from contractforge_core.evidence import (
|
|
4
|
+
AccessEvidenceRecord,
|
|
5
|
+
CostEvidenceRecord,
|
|
6
|
+
ErrorEvidenceRecord,
|
|
7
|
+
LineageEvidenceRecord,
|
|
8
|
+
QualityEvidenceRecord,
|
|
9
|
+
QuarantineEvidenceRecord,
|
|
10
|
+
RunEvidenceRecord,
|
|
11
|
+
SchemaChangeEvidenceRecord,
|
|
12
|
+
SourceMetadataEvidenceRecord,
|
|
13
|
+
StreamBatchEvidenceRecord,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"AccessEvidenceRecord",
|
|
18
|
+
"CostEvidenceRecord",
|
|
19
|
+
"ErrorEvidenceRecord",
|
|
20
|
+
"LineageEvidenceRecord",
|
|
21
|
+
"QualityEvidenceRecord",
|
|
22
|
+
"QuarantineEvidenceRecord",
|
|
23
|
+
"RunEvidenceRecord",
|
|
24
|
+
"SchemaChangeEvidenceRecord",
|
|
25
|
+
"SourceMetadataEvidenceRecord",
|
|
26
|
+
"StreamBatchEvidenceRecord",
|
|
27
|
+
]
|