datasentry-core 0.3.0__tar.gz → 0.5.0__tar.gz

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.
Files changed (83) hide show
  1. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/PKG-INFO +1 -1
  2. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/pyproject.toml +1 -1
  3. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/__init__.py +1 -1
  4. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/__init__.py +2 -0
  5. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/registry.py +3 -1
  6. datasentry_core-0.5.0/src/datasentry_core/connectors/sqlite.py +87 -0
  7. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/storage/schema.py +8 -2
  8. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/.gitignore +0 -0
  9. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/README.md +0 -0
  10. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/base.py +0 -0
  11. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/csv.py +0 -0
  12. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/duckdb.py +0 -0
  13. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/errors.py +0 -0
  14. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/file_based.py +0 -0
  15. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/jsonl.py +0 -0
  16. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/parquet.py +0 -0
  17. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/spec.py +0 -0
  18. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/connectors/xlsx.py +0 -0
  19. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/contracts/__init__.py +0 -0
  20. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/contracts/exporters.py +0 -0
  21. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/__init__.py +0 -0
  22. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/base.py +0 -0
  23. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/common.py +0 -0
  24. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/cross_field.py +0 -0
  25. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/cross_table.py +0 -0
  26. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/datetime.py +0 -0
  27. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/__init__.py +0 -0
  28. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/anomaly_ml.py +0 -0
  29. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/categorical.py +0 -0
  30. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/formula.py +0 -0
  31. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/missing.py +0 -0
  32. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/numeric.py +0 -0
  33. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/textual.py +0 -0
  34. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/initial/uniqueness.py +0 -0
  35. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/missingness.py +0 -0
  36. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/runner.py +0 -0
  37. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/safe_eval.py +0 -0
  38. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/detectors/textual_variants.py +0 -0
  39. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/drift/__init__.py +0 -0
  40. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/drift/engine.py +0 -0
  41. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/engine/__init__.py +0 -0
  42. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/engine/base.py +0 -0
  43. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/engine/duckdb.py +0 -0
  44. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/engine/fusion.py +0 -0
  45. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/engine/profiler.py +0 -0
  46. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/engine/sql_guard.py +0 -0
  47. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/llm/provider.py +0 -0
  48. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/__init__.py +0 -0
  49. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/audit.py +0 -0
  50. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/contract.py +0 -0
  51. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/detector.py +0 -0
  52. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/drift.py +0 -0
  53. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/enums.py +0 -0
  54. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/evidence.py +0 -0
  55. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/feedback.py +0 -0
  56. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/fingerprint.py +0 -0
  57. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/issue.py +0 -0
  58. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/llm.py +0 -0
  59. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/profile.py +0 -0
  60. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/quality.py +0 -0
  61. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/repair.py +0 -0
  62. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/rules.py +0 -0
  63. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/scan.py +0 -0
  64. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/models/validation.py +0 -0
  65. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/plugins.py +0 -0
  66. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/privacy/redactor.py +0 -0
  67. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/repair/__init__.py +0 -0
  68. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/repair/engine.py +0 -0
  69. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/reporting/__init__.py +0 -0
  70. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/reporting/html.py +0 -0
  71. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/reporting/interactive.py +0 -0
  72. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/reporting/junit.py +0 -0
  73. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/reporting/markdown.py +0 -0
  74. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/reporting/sarif.py +0 -0
  75. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/rules/engine.py +0 -0
  76. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/scoring/__init__.py +0 -0
  77. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/scoring/engine.py +0 -0
  78. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/scoring/gate.py +0 -0
  79. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/scoring/quality.py +0 -0
  80. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/scoring/weights.py +0 -0
  81. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/storage/__init__.py +0 -0
  82. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/storage/paths.py +0 -0
  83. {datasentry_core-0.3.0 → datasentry_core-0.5.0}/src/datasentry_core/storage/store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: datasentry-core
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: DataSentry AI core domain models and engine (detectors, fusion, scoring, repairs).
5
5
  License: Apache-2.0
6
6
  Keywords: data-observability,data-quality,duckdb
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datasentry-core"
3
- version = "0.3.0"
3
+ version = "0.5.0"
4
4
  description = "DataSentry AI core domain models and engine (detectors, fusion, scoring, repairs)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -108,4 +108,4 @@ __all__ = [
108
108
  "ValidationResult",
109
109
  ]
110
110
 
111
- __version__ = "0.3.0"
111
+ __version__ = "0.5.0"
@@ -22,6 +22,7 @@ from datasentry_core.connectors.jsonl import JsonlConnector
22
22
  from datasentry_core.connectors.parquet import ParquetConnector
23
23
  from datasentry_core.connectors.registry import ConnectorRegistry, default_registry
24
24
  from datasentry_core.connectors.spec import DataSourceSpec, DataSourceType
25
+ from datasentry_core.connectors.sqlite import SqliteConnector
25
26
  from datasentry_core.connectors.xlsx import XlsxConnector
26
27
 
27
28
  __all__ = [
@@ -42,6 +43,7 @@ __all__ = [
42
43
  "ParquetConnector",
43
44
  "SamplingMethod",
44
45
  "SchemaInfo",
46
+ "SqliteConnector",
45
47
  "UnsafeSqlError",
46
48
  "UnsupportedFormatError",
47
49
  "XlsxConnector",
@@ -9,6 +9,7 @@ from datasentry_core.connectors.errors import UnsupportedFormatError
9
9
  from datasentry_core.connectors.jsonl import JsonlConnector
10
10
  from datasentry_core.connectors.parquet import ParquetConnector
11
11
  from datasentry_core.connectors.spec import DataSourceSpec
12
+ from datasentry_core.connectors.sqlite import SqliteConnector
12
13
  from datasentry_core.connectors.xlsx import XlsxConnector
13
14
 
14
15
 
@@ -45,11 +46,12 @@ class ConnectorRegistry:
45
46
 
46
47
 
47
48
  def default_registry() -> ConnectorRegistry:
48
- """默认注册表:文件型连接器 + DuckDB 文件连接器(Step 38,V1 数据库型落地)。"""
49
+ """默认注册表:文件型连接器 + DuckDB/SQLite 文件连接器(Step 38/54)。"""
49
50
  registry = ConnectorRegistry()
50
51
  registry.register(CsvConnector())
51
52
  registry.register(ParquetConnector())
52
53
  registry.register(JsonlConnector())
53
54
  registry.register(XlsxConnector())
54
55
  registry.register(DuckdbConnector())
56
+ registry.register(SqliteConnector())
55
57
  return registry
@@ -0,0 +1,87 @@
1
+ """SQLite 数据源连接器(Step 54:V3 多数据源第一落点)。
2
+
3
+ 对 `.db` / `.sqlite` / `.sqlite3` 文件经 DuckDB 的 sqlite 扩展
4
+ (sqlite_scan 表函数)暴露只读 data 视图,复用 FileDataHandle
5
+ 共享实现(schema/read_sample/sql_aggregate/count_rows/fingerprint/
6
+ warnings)。表名必填(spec.table_name);路径/表名一律字符串
7
+ 字面量转义(防注入)。
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Iterator
13
+
14
+ import pyarrow as pa
15
+
16
+ from datasentry_core.connectors.base import FrameBatch
17
+ from datasentry_core.connectors.errors import DataSourceNotFoundError
18
+ from datasentry_core.connectors.file_based import FileDataHandle
19
+ from datasentry_core.connectors.spec import DataSourceSpec, DataSourceType
20
+
21
+
22
+ def _sql_string_literal(value: str) -> str:
23
+ return "'" + value.replace("'", "''") + "'"
24
+
25
+
26
+ class SQLiteDataHandle(FileDataHandle):
27
+ """SQLite 文件表数据句柄(只读,表名必填)。"""
28
+
29
+ def __init__(self, spec: DataSourceSpec) -> None:
30
+ super().__init__(spec)
31
+ if spec.path is None or not spec.path.is_file():
32
+ raise DataSourceNotFoundError(f"sqlite file not found: {spec.path}")
33
+ if spec.table_name is None:
34
+ raise DataSourceNotFoundError("sqlite connector requires table_name")
35
+ self._table_name = spec.table_name
36
+
37
+ @property
38
+ def table_name(self) -> str | None:
39
+ return self._table_name
40
+
41
+ def _ensure_view(self) -> None:
42
+ if self._view_ready:
43
+ return
44
+ assert self._path is not None
45
+ self._executor.execute_setup("LOAD sqlite")
46
+ self._executor.execute_setup(
47
+ "CREATE OR REPLACE VIEW data AS "
48
+ f"SELECT * FROM sqlite_scan({_sql_string_literal(str(self._path))}, "
49
+ f"{_sql_string_literal(self._table_name)})"
50
+ )
51
+ self._view_ready = True
52
+
53
+ def read_batches(self, batch_size: int = 65536) -> Iterator[FrameBatch]:
54
+ self._check_open()
55
+ if batch_size < 1:
56
+ raise ValueError("batch_size must be >= 1")
57
+ self._ensure_view()
58
+ row_offset = 0
59
+ for idx, batch in enumerate(
60
+ self._executor.execute_stream("SELECT * FROM data", batch_size)
61
+ ):
62
+ table = pa.Table.from_batches([batch])
63
+ yield FrameBatch(
64
+ source=self._spec,
65
+ table=table,
66
+ batch_index=idx,
67
+ row_offset=row_offset,
68
+ )
69
+ row_offset += table.num_rows
70
+
71
+
72
+ class SqliteConnector:
73
+ """SQLite 文件连接器(Step 54,V3 多数据源第一落点)。"""
74
+
75
+ connector_id = "sqlite"
76
+ display_name = "SQLite"
77
+
78
+ def supports(self, source: DataSourceSpec) -> bool:
79
+ return source.source_type == DataSourceType.SQLITE and source.path is not None
80
+
81
+ def open(self, source: DataSourceSpec) -> SQLiteDataHandle:
82
+ if source.table_name is None:
83
+ raise DataSourceNotFoundError("sqlite connector requires table_name")
84
+ return SQLiteDataHandle(source)
85
+
86
+ def close(self) -> None:
87
+ pass
@@ -13,7 +13,7 @@ from __future__ import annotations
13
13
  import sqlite3
14
14
 
15
15
  #: 当前 schema 版本(PRAGMA user_version)
16
- SCHEMA_VERSION = 5
16
+ SCHEMA_VERSION = 6
17
17
 
18
18
  _SCHEMA_DDL = """
19
19
  PRAGMA journal_mode = WAL;
@@ -329,7 +329,9 @@ CREATE TABLE IF NOT EXISTS job_runs (
329
329
  scan_run_id TEXT,
330
330
  summary TEXT,
331
331
  error TEXT,
332
- webhook_at TEXT
332
+ webhook_at TEXT,
333
+ file_hash TEXT,
334
+ skipped INTEGER NOT NULL DEFAULT 0
333
335
  );
334
336
 
335
337
  CREATE INDEX IF NOT EXISTS idx_scan_runs_dataset ON scan_runs(dataset_id);
@@ -387,5 +389,9 @@ def migrate(conn: sqlite3.Connection) -> None:
387
389
  # v4 → v5:调度质量门禁(Step 52):scheduled_jobs.gate_quality_min
388
390
  if version < 5:
389
391
  _ensure_column(conn, "scheduled_jobs", "gate_quality_min", "gate_quality_min REAL")
392
+ # v5 → v6:变更感知增量调度(Step 53):job_runs.file_hash + skipped
393
+ if version < 6:
394
+ _ensure_column(conn, "job_runs", "file_hash", "file_hash TEXT")
395
+ _ensure_column(conn, "job_runs", "skipped", "skipped INTEGER NOT NULL DEFAULT 0")
390
396
  conn.execute(f"PRAGMA user_version = {SCHEMA_VERSION}")
391
397
  conn.commit()