codegraph-engine 3.0.0__2-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.
- codegraph/__init__.py +37 -0
- codegraph/agent.py +26 -0
- codegraph/agent_brain.py +2212 -0
- codegraph/agent_capabilities.py +4058 -0
- codegraph/agent_rules.py +531 -0
- codegraph/api_drift.py +455 -0
- codegraph/architecture.py +371 -0
- codegraph/async_queue/__init__.py +50 -0
- codegraph/async_queue/detectors/__init__.py +48 -0
- codegraph/async_queue/detectors/celery.py +160 -0
- codegraph/async_queue/detectors/kafka.py +168 -0
- codegraph/async_queue/detectors/rabbitmq.py +116 -0
- codegraph/async_queue/detectors/redis.py +130 -0
- codegraph/async_queue/detectors/sqs.py +135 -0
- codegraph/async_queue/models.py +161 -0
- codegraph/async_queue/otel_models.py +74 -0
- codegraph/async_queue/otel_parser.py +337 -0
- codegraph/async_queue/reconciler.py +427 -0
- codegraph/async_queue/scanner.py +291 -0
- codegraph/async_queue/traversal.py +447 -0
- codegraph/audit.py +106 -0
- codegraph/binding_resolver.py +616 -0
- codegraph/branch_comparison.py +197 -0
- codegraph/cache.py +95 -0
- codegraph/change_impact.py +547 -0
- codegraph/cli.py +2436 -0
- codegraph/cli_output.py +205 -0
- codegraph/config.py +45 -0
- codegraph/constraints.py +238 -0
- codegraph/context.py +2182 -0
- codegraph/context_freshness.py +351 -0
- codegraph/database/__init__.py +72 -0
- codegraph/database/extractor.py +3651 -0
- codegraph/database/interrogation.py +1494 -0
- codegraph/database/models.py +310 -0
- codegraph/database/schema_drift.py +862 -0
- codegraph/epistemic.py +200 -0
- codegraph/errors.py +282 -0
- codegraph/evidence/__init__.py +15 -0
- codegraph/evidence/citations.py +418 -0
- codegraph/evidence_contract.py +486 -0
- codegraph/frameworks.py +960 -0
- codegraph/frameworks_nestjs.py +502 -0
- codegraph/frameworks_nextjs.py +337 -0
- codegraph/frameworks_react.py +457 -0
- codegraph/freshness.py +335 -0
- codegraph/frontend_html.py +109 -0
- codegraph/git.py +362 -0
- codegraph/git_state.py +545 -0
- codegraph/graph/__init__.py +46 -0
- codegraph/graph/models.py +56 -0
- codegraph/graph/traversal.py +1993 -0
- codegraph/indexing/__init__.py +24 -0
- codegraph/indexing/classifier.py +564 -0
- codegraph/indexing/indexer.py +2578 -0
- codegraph/indexing/models.py +427 -0
- codegraph/indexing/parser.py +3404 -0
- codegraph/indexing/pool.py +136 -0
- codegraph/indexing/scanner.py +211 -0
- codegraph/indexing/telemetry.py +326 -0
- codegraph/indexing/templates.py +418 -0
- codegraph/indexing/test_framework.py +116 -0
- codegraph/installer.py +1702 -0
- codegraph/interrogation.py +2355 -0
- codegraph/llm/__init__.py +3 -0
- codegraph/llm/base.py +15 -0
- codegraph/llm/context.py +20 -0
- codegraph/mcp/__init__.py +3 -0
- codegraph/mcp/server.py +1476 -0
- codegraph/mcp_diagnostics.py +882 -0
- codegraph/memory/__init__.py +3 -0
- codegraph/memory/store.py +46 -0
- codegraph/models.py +310 -0
- codegraph/monorepo.py +925 -0
- codegraph/observability.py +262 -0
- codegraph/optimizer.py +1014 -0
- codegraph/planner.py +417 -0
- codegraph/process_lifecycle.py +1153 -0
- codegraph/py.typed +1 -0
- codegraph/query_expansion.py +199 -0
- codegraph/ranking.py +408 -0
- codegraph/refactor/__init__.py +36 -0
- codegraph/refactor/models.py +114 -0
- codegraph/refactor/renamer.py +844 -0
- codegraph/refactor/transactions.py +414 -0
- codegraph/resolver.py +3968 -0
- codegraph/resources/__init__.py +46 -0
- codegraph/resources/cache.py +117 -0
- codegraph/resources/coalescer.py +83 -0
- codegraph/resources/debouncer.py +98 -0
- codegraph/resources/governor.py +219 -0
- codegraph/resources/monitor.py +102 -0
- codegraph/resources/policy.py +123 -0
- codegraph/retrieval_policy.py +320 -0
- codegraph/roblox.py +2174 -0
- codegraph/route_composer.py +363 -0
- codegraph/route_topology.py +172 -0
- codegraph/runtime/__init__.py +51 -0
- codegraph/runtime/collector.py +155 -0
- codegraph/runtime/db_watcher.py +191 -0
- codegraph/runtime/ingestor.py +692 -0
- codegraph/runtime/models.py +130 -0
- codegraph/runtime/port_inspector.py +304 -0
- codegraph/runtime/reconciliation.py +492 -0
- codegraph/runtime/runner.py +353 -0
- codegraph/runtime/trace_linker.py +175 -0
- codegraph/search/__init__.py +25 -0
- codegraph/search/hybrid.py +846 -0
- codegraph/search/semantic.py +28 -0
- codegraph/security/__init__.py +51 -0
- codegraph/security/paths.py +189 -0
- codegraph/security/redaction.py +633 -0
- codegraph/semantic_conflicts.py +270 -0
- codegraph/semantic_decorators.py +372 -0
- codegraph/structural_diff.py +542 -0
- codegraph/styles.py +144 -0
- codegraph/symbol_history.py +277 -0
- codegraph/target_resolver.py +348 -0
- codegraph/task.py +637 -0
- codegraph/tool_selection_eval.py +1158 -0
- codegraph/tsconfig.py +236 -0
- codegraph/ui/__init__.py +6 -0
- codegraph/ui/server.py +755 -0
- codegraph/watcher.py +323 -0
- codegraph_engine-3.0.0.dist-info/METADATA +868 -0
- codegraph_engine-3.0.0.dist-info/RECORD +130 -0
- codegraph_engine-3.0.0.dist-info/WHEEL +6 -0
- codegraph_engine-3.0.0.dist-info/entry_points.txt +2 -0
- codegraph_engine-3.0.0.dist-info/licenses/LICENSE +21 -0
- codegraph_engine-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"""Canonical Database Schema & Evidence Models for CodeGraph (Phases 2, 3, 4, 5, 6, 7).
|
|
2
|
+
|
|
3
|
+
Provides deterministic canonical IDs and structured dataclasses for:
|
|
4
|
+
- Database
|
|
5
|
+
- Schema
|
|
6
|
+
- Table
|
|
7
|
+
- Column
|
|
8
|
+
- PrimaryKey
|
|
9
|
+
- ForeignKey
|
|
10
|
+
- Index
|
|
11
|
+
- UniqueConstraint
|
|
12
|
+
- CheckConstraint
|
|
13
|
+
- View
|
|
14
|
+
- Sequence
|
|
15
|
+
- Migration
|
|
16
|
+
- ORMModel
|
|
17
|
+
|
|
18
|
+
Architectural Rules:
|
|
19
|
+
1. Never assume database dialect or schema name when not statically provable; use `UNKNOWN` or `AMBIGUOUS`.
|
|
20
|
+
2. Every database entity and relationship carries source file, line range, parser version, index generation,
|
|
21
|
+
relationship type, evidence class, confidence, and canonical IDs.
|
|
22
|
+
3. All connection strings and defaults pass through secret redaction before model construction.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from dataclasses import dataclass, field
|
|
27
|
+
from enum import StrEnum
|
|
28
|
+
from typing import Any
|
|
29
|
+
|
|
30
|
+
from codegraph.security.redaction import redact_secrets
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DatabaseEntityKind(StrEnum):
|
|
34
|
+
DATABASE = "Database"
|
|
35
|
+
SCHEMA = "Schema"
|
|
36
|
+
TABLE = "Table"
|
|
37
|
+
COLUMN = "Column"
|
|
38
|
+
PRIMARY_KEY = "PrimaryKey"
|
|
39
|
+
FOREIGN_KEY = "ForeignKey"
|
|
40
|
+
INDEX = "Index"
|
|
41
|
+
UNIQUE_CONSTRAINT = "UniqueConstraint"
|
|
42
|
+
CHECK_CONSTRAINT = "CheckConstraint"
|
|
43
|
+
VIEW = "View"
|
|
44
|
+
SEQUENCE = "Sequence"
|
|
45
|
+
MIGRATION = "Migration"
|
|
46
|
+
ORM_MODEL = "ORMModel"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def normalize_dialect(raw_dialect: str | None) -> str:
|
|
50
|
+
"""Normalize database dialect/provider string, defaulting to UNKNOWN when unproven."""
|
|
51
|
+
if not raw_dialect:
|
|
52
|
+
return "UNKNOWN"
|
|
53
|
+
val = raw_dialect.strip().lower()
|
|
54
|
+
if val in ("amg", "ambiguous"):
|
|
55
|
+
return "AMBIGUOUS"
|
|
56
|
+
if val in ("unknown", ""):
|
|
57
|
+
return "UNKNOWN"
|
|
58
|
+
if val.startswith(("postgres", "psycopg", "asyncpg", "pg")):
|
|
59
|
+
return "postgres"
|
|
60
|
+
if val.startswith(("mysql", "pymysql", "mariadb")):
|
|
61
|
+
return "mysql"
|
|
62
|
+
if val.startswith(("sqlite", "aiosqlite")):
|
|
63
|
+
return "sqlite"
|
|
64
|
+
if val.startswith(("mongo", "mongodb")):
|
|
65
|
+
return "mongodb"
|
|
66
|
+
if val.startswith("redis"):
|
|
67
|
+
return "redis"
|
|
68
|
+
if val.startswith(("mssql", "sqlserver")):
|
|
69
|
+
return "mssql"
|
|
70
|
+
return val
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def normalize_schema_name(raw_schema: str | None, dialect: str = "UNKNOWN") -> str:
|
|
74
|
+
"""Normalize schema name without fabricating unproven schemas."""
|
|
75
|
+
if raw_schema and raw_schema.strip():
|
|
76
|
+
s = raw_schema.strip()
|
|
77
|
+
if s.upper() in ("UNKNOWN", "AMBIGUOUS"):
|
|
78
|
+
return s.upper()
|
|
79
|
+
return s.lower()
|
|
80
|
+
norm_dialect = normalize_dialect(dialect)
|
|
81
|
+
if norm_dialect == "postgres":
|
|
82
|
+
return "public"
|
|
83
|
+
if norm_dialect == "sqlite":
|
|
84
|
+
return "main"
|
|
85
|
+
if norm_dialect == "AMBIGUOUS":
|
|
86
|
+
return "AMBIGUOUS"
|
|
87
|
+
return "UNKNOWN"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def build_db_canonical_id(
|
|
91
|
+
kind: DatabaseEntityKind | str,
|
|
92
|
+
table: str = "",
|
|
93
|
+
column: str = "",
|
|
94
|
+
name: str = "",
|
|
95
|
+
dialect: str = "UNKNOWN",
|
|
96
|
+
schema: str = "UNKNOWN",
|
|
97
|
+
module: str = "",
|
|
98
|
+
) -> str:
|
|
99
|
+
"""Build a deterministic canonical ID for any database entity.
|
|
100
|
+
|
|
101
|
+
Examples:
|
|
102
|
+
Table: db.postgres.public.users or db.UNKNOWN.UNKNOWN.users
|
|
103
|
+
Column: db.postgres.public.users.id
|
|
104
|
+
PrimaryKey: db.postgres.public.users.pk.id
|
|
105
|
+
ForeignKey: db.postgres.public.orders.fk.user_id
|
|
106
|
+
Index: db.postgres.public.users.idx.ix_users_email
|
|
107
|
+
Migration: db.migration.alembic.0001_initial
|
|
108
|
+
ORMModel: orm.app.models.User
|
|
109
|
+
"""
|
|
110
|
+
k_str = kind.value if isinstance(kind, DatabaseEntityKind) else str(kind)
|
|
111
|
+
d = normalize_dialect(dialect)
|
|
112
|
+
s = normalize_schema_name(schema, d)
|
|
113
|
+
tbl = table.strip().lower() if table else ""
|
|
114
|
+
col = column.strip().lower() if column else ""
|
|
115
|
+
nm = name.strip() if name else ""
|
|
116
|
+
|
|
117
|
+
if k_str == DatabaseEntityKind.DATABASE.value:
|
|
118
|
+
return f"db.{d}.{nm.lower() or s}"
|
|
119
|
+
if k_str == DatabaseEntityKind.SCHEMA.value:
|
|
120
|
+
return f"db.{d}.{nm.lower() or s}"
|
|
121
|
+
if k_str == DatabaseEntityKind.TABLE.value:
|
|
122
|
+
return f"db.{d}.{s}.{tbl or nm.lower() or 'unknown'}"
|
|
123
|
+
if k_str == DatabaseEntityKind.VIEW.value:
|
|
124
|
+
return f"db.{d}.{s}.view.{tbl or nm.lower() or 'unknown'}"
|
|
125
|
+
if k_str == DatabaseEntityKind.SEQUENCE.value:
|
|
126
|
+
return f"db.{d}.{s}.seq.{nm.lower() or tbl or 'unknown'}"
|
|
127
|
+
if k_str == DatabaseEntityKind.COLUMN.value:
|
|
128
|
+
return f"db.{d}.{s}.{tbl or 'unknown'}.{col or nm.lower() or 'unknown'}"
|
|
129
|
+
if k_str == DatabaseEntityKind.PRIMARY_KEY.value:
|
|
130
|
+
return f"db.{d}.{s}.{tbl or 'unknown'}.pk.{col or nm.lower() or 'pk'}"
|
|
131
|
+
if k_str == DatabaseEntityKind.FOREIGN_KEY.value:
|
|
132
|
+
return f"db.{d}.{s}.{tbl or 'unknown'}.fk.{col or nm.lower() or 'fk'}"
|
|
133
|
+
if k_str == DatabaseEntityKind.INDEX.value:
|
|
134
|
+
return f"db.{d}.{s}.{tbl or 'unknown'}.idx.{nm.lower() or col or 'idx'}"
|
|
135
|
+
if k_str == DatabaseEntityKind.UNIQUE_CONSTRAINT.value:
|
|
136
|
+
return f"db.{d}.{s}.{tbl or 'unknown'}.uq.{nm.lower() or col or 'uq'}"
|
|
137
|
+
if k_str == DatabaseEntityKind.CHECK_CONSTRAINT.value:
|
|
138
|
+
return f"db.{d}.{s}.{tbl or 'unknown'}.ck.{nm.lower() or 'ck'}"
|
|
139
|
+
if k_str == DatabaseEntityKind.MIGRATION.value:
|
|
140
|
+
sys_name = (module or "sql").strip().lower()
|
|
141
|
+
return f"db.migration.{sys_name}.{nm or 'unknown'}"
|
|
142
|
+
if k_str == DatabaseEntityKind.ORM_MODEL.value:
|
|
143
|
+
if module:
|
|
144
|
+
return f"{module}.{nm}"
|
|
145
|
+
return f"orm.{nm}"
|
|
146
|
+
return f"db.{d}.{s}.{nm.lower() or tbl or 'unknown'}"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@dataclass(frozen=True)
|
|
150
|
+
class DatabaseEntity:
|
|
151
|
+
"""Canonical representation of a discovered database schema or ORM entity."""
|
|
152
|
+
|
|
153
|
+
canonical_id: str
|
|
154
|
+
kind: str # DatabaseEntityKind value
|
|
155
|
+
name: str
|
|
156
|
+
dialect: str = "UNKNOWN"
|
|
157
|
+
schema_name: str = "UNKNOWN"
|
|
158
|
+
table_name: str = ""
|
|
159
|
+
column_name: str = ""
|
|
160
|
+
data_type: str = ""
|
|
161
|
+
nullable: bool = True
|
|
162
|
+
default_value: str | None = None
|
|
163
|
+
is_primary_key: bool = False
|
|
164
|
+
is_foreign_key: bool = False
|
|
165
|
+
is_unique: bool = False
|
|
166
|
+
is_indexed: bool = False
|
|
167
|
+
target_table: str | None = None
|
|
168
|
+
target_column: str | None = None
|
|
169
|
+
orm_model_id: str | None = None
|
|
170
|
+
framework: str = ""
|
|
171
|
+
file_path: str = ""
|
|
172
|
+
start_line: int = 1
|
|
173
|
+
end_line: int = 1
|
|
174
|
+
evidence: str = ""
|
|
175
|
+
confidence: str = "HIGH"
|
|
176
|
+
evidence_class: str = "AST_VERIFIED"
|
|
177
|
+
status: str = "FACT"
|
|
178
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
179
|
+
|
|
180
|
+
def __post_init__(self) -> None:
|
|
181
|
+
if self.default_value is not None:
|
|
182
|
+
object.__setattr__(self, "default_value", redact_secrets(str(self.default_value)))
|
|
183
|
+
if self.evidence:
|
|
184
|
+
object.__setattr__(self, "evidence", redact_secrets(self.evidence))
|
|
185
|
+
|
|
186
|
+
def as_dict(self) -> dict[str, Any]:
|
|
187
|
+
return {
|
|
188
|
+
"canonical_id": self.canonical_id,
|
|
189
|
+
"kind": self.kind,
|
|
190
|
+
"name": self.name,
|
|
191
|
+
"dialect": self.dialect,
|
|
192
|
+
"schema_name": self.schema_name,
|
|
193
|
+
"table_name": self.table_name,
|
|
194
|
+
"column_name": self.column_name,
|
|
195
|
+
"data_type": self.data_type,
|
|
196
|
+
"nullable": self.nullable,
|
|
197
|
+
"default_value": self.default_value,
|
|
198
|
+
"is_primary_key": self.is_primary_key,
|
|
199
|
+
"is_foreign_key": self.is_foreign_key,
|
|
200
|
+
"is_unique": self.is_unique,
|
|
201
|
+
"is_indexed": self.is_indexed,
|
|
202
|
+
"target_table": self.target_table,
|
|
203
|
+
"target_column": self.target_column,
|
|
204
|
+
"orm_model_id": self.orm_model_id,
|
|
205
|
+
"framework": self.framework,
|
|
206
|
+
"file_path": self.file_path,
|
|
207
|
+
"file": self.file_path,
|
|
208
|
+
"start_line": self.start_line,
|
|
209
|
+
"end_line": self.end_line,
|
|
210
|
+
"evidence": self.evidence,
|
|
211
|
+
"confidence": self.confidence,
|
|
212
|
+
"evidence_class": self.evidence_class,
|
|
213
|
+
"status": self.status,
|
|
214
|
+
"metadata": dict(self.metadata),
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass(frozen=True)
|
|
219
|
+
class DatabaseQueryFact:
|
|
220
|
+
"""Static database query or ORM operation extracted from source code."""
|
|
221
|
+
|
|
222
|
+
query_id: str
|
|
223
|
+
caller_symbol_id: str
|
|
224
|
+
operation: str # SELECT | INSERT | UPDATE | DELETE | DDL | UNKNOWN
|
|
225
|
+
relationship: str # READS_TABLE | WRITES_TABLE | POSSIBLE_TABLE | UNKNOWN_TABLE | QUERIES_DATABASE
|
|
226
|
+
table_name: str # table name or UNKNOWN / POSSIBLE candidate
|
|
227
|
+
table_canonical_id: str
|
|
228
|
+
columns: tuple[str, ...] = ()
|
|
229
|
+
framework: str = "raw_sql"
|
|
230
|
+
normalized_sql: str = ""
|
|
231
|
+
file_path: str = ""
|
|
232
|
+
start_line: int = 1
|
|
233
|
+
end_line: int = 1
|
|
234
|
+
evidence: str = ""
|
|
235
|
+
confidence: str = "HIGH"
|
|
236
|
+
evidence_class: str = "AST_VERIFIED"
|
|
237
|
+
status: str = "FACT"
|
|
238
|
+
|
|
239
|
+
def as_dict(self) -> dict[str, Any]:
|
|
240
|
+
return {
|
|
241
|
+
"query_id": self.query_id,
|
|
242
|
+
"caller_symbol_id": self.caller_symbol_id,
|
|
243
|
+
"symbol": self.caller_symbol_id,
|
|
244
|
+
"operation": self.operation,
|
|
245
|
+
"relationship": self.relationship,
|
|
246
|
+
"table_name": self.table_name,
|
|
247
|
+
"table_canonical_id": self.table_canonical_id,
|
|
248
|
+
"columns": list(self.columns),
|
|
249
|
+
"framework": self.framework,
|
|
250
|
+
"normalized_sql": self.normalized_sql,
|
|
251
|
+
"file_path": self.file_path,
|
|
252
|
+
"file": self.file_path,
|
|
253
|
+
"start_line": self.start_line,
|
|
254
|
+
"end_line": self.end_line,
|
|
255
|
+
"evidence": self.evidence,
|
|
256
|
+
"confidence": self.confidence,
|
|
257
|
+
"evidence_class": self.evidence_class,
|
|
258
|
+
"status": self.status,
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@dataclass(frozen=True)
|
|
263
|
+
class MigrationFact:
|
|
264
|
+
"""Extracted schema migration record (Alembic, Django migrations, Prisma, raw SQL)."""
|
|
265
|
+
|
|
266
|
+
migration_id: str
|
|
267
|
+
canonical_id: str
|
|
268
|
+
system: str # alembic | django | prisma | sql
|
|
269
|
+
revision: str
|
|
270
|
+
down_revision: str | None = None
|
|
271
|
+
file_path: str = ""
|
|
272
|
+
start_line: int = 1
|
|
273
|
+
end_line: int = 1
|
|
274
|
+
created_tables: tuple[str, ...] = ()
|
|
275
|
+
dropped_tables: tuple[str, ...] = ()
|
|
276
|
+
added_columns: tuple[str, ...] = () # "table.column"
|
|
277
|
+
removed_columns: tuple[str, ...] = () # "table.column"
|
|
278
|
+
renamed_columns: tuple[str, ...] = () # "table.old_col->new_col"
|
|
279
|
+
indexes: tuple[str, ...] = ()
|
|
280
|
+
foreign_keys: tuple[str, ...] = ()
|
|
281
|
+
constraints: tuple[str, ...] = ()
|
|
282
|
+
affected_tables: tuple[str, ...] = ()
|
|
283
|
+
evidence: str = ""
|
|
284
|
+
confidence: str = "HIGH"
|
|
285
|
+
evidence_class: str = "AST_VERIFIED"
|
|
286
|
+
|
|
287
|
+
def as_dict(self) -> dict[str, Any]:
|
|
288
|
+
return {
|
|
289
|
+
"migration_id": self.migration_id,
|
|
290
|
+
"canonical_id": self.canonical_id,
|
|
291
|
+
"system": self.system,
|
|
292
|
+
"revision": self.revision,
|
|
293
|
+
"down_revision": self.down_revision,
|
|
294
|
+
"file_path": self.file_path,
|
|
295
|
+
"file": self.file_path,
|
|
296
|
+
"start_line": self.start_line,
|
|
297
|
+
"end_line": self.end_line,
|
|
298
|
+
"created_tables": list(self.created_tables),
|
|
299
|
+
"dropped_tables": list(self.dropped_tables),
|
|
300
|
+
"added_columns": list(self.added_columns),
|
|
301
|
+
"removed_columns": list(self.removed_columns),
|
|
302
|
+
"renamed_columns": list(self.renamed_columns),
|
|
303
|
+
"indexes": list(self.indexes),
|
|
304
|
+
"foreign_keys": list(self.foreign_keys),
|
|
305
|
+
"constraints": list(self.constraints),
|
|
306
|
+
"affected_tables": list(self.affected_tables),
|
|
307
|
+
"evidence": self.evidence,
|
|
308
|
+
"confidence": self.confidence,
|
|
309
|
+
"evidence_class": self.evidence_class,
|
|
310
|
+
}
|