duckguard 3.1.0__py3-none-any.whl → 3.2.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.
@@ -12,7 +12,7 @@ from typing import TYPE_CHECKING, Any
12
12
  from duckguard.reports.html_reporter import HTMLReporter, ReportConfig
13
13
 
14
14
  if TYPE_CHECKING:
15
- from duckguard.history.storage import StoredRun
15
+ from duckguard.history.storage import StoredRun, TrendDataPoint
16
16
  from duckguard.rules.executor import ExecutionResult
17
17
 
18
18
 
@@ -45,6 +45,9 @@ class PDFReporter(HTMLReporter):
45
45
  output_path: str | Path,
46
46
  *,
47
47
  history: list[StoredRun] | None = None,
48
+ trend_data: list[TrendDataPoint] | None = None,
49
+ row_count: int | None = None,
50
+ column_count: int | None = None,
48
51
  ) -> Path:
49
52
  """Generate a PDF report.
50
53
 
@@ -52,6 +55,9 @@ class PDFReporter(HTMLReporter):
52
55
  result: ExecutionResult to report on
53
56
  output_path: Path to write PDF file
54
57
  history: Optional historical results for trends
58
+ trend_data: Optional trend data points for chart rendering
59
+ row_count: Optional dataset row count for metadata display
60
+ column_count: Optional dataset column count for metadata display
55
61
 
56
62
  Returns:
57
63
  Path to generated PDF report
@@ -63,8 +69,7 @@ class PDFReporter(HTMLReporter):
63
69
  from weasyprint import HTML
64
70
  except ImportError:
65
71
  raise ImportError(
66
- "PDF reports require weasyprint. "
67
- "Install with: pip install duckguard[reports]"
72
+ "PDF reports require weasyprint. " "Install with: pip install duckguard[reports]"
68
73
  )
69
74
 
70
75
  output_path = Path(output_path)
@@ -80,7 +85,14 @@ class PDFReporter(HTMLReporter):
80
85
 
81
86
  try:
82
87
  # Generate HTML report
83
- super().generate(result, html_path, history=history)
88
+ super().generate(
89
+ result,
90
+ html_path,
91
+ history=history,
92
+ trend_data=trend_data,
93
+ row_count=row_count,
94
+ column_count=column_count,
95
+ )
84
96
 
85
97
  # Convert to PDF
86
98
  HTML(filename=str(html_path)).write_pdf(str(output_path))
@@ -97,6 +109,11 @@ class PDFReporter(HTMLReporter):
97
109
  def generate_pdf_report(
98
110
  result: ExecutionResult,
99
111
  output_path: str | Path,
112
+ *,
113
+ history: list[StoredRun] | None = None,
114
+ trend_data: list[TrendDataPoint] | None = None,
115
+ row_count: int | None = None,
116
+ column_count: int | None = None,
100
117
  **kwargs: Any,
101
118
  ) -> Path:
102
119
  """Convenience function to generate PDF report.
@@ -104,6 +121,10 @@ def generate_pdf_report(
104
121
  Args:
105
122
  result: ExecutionResult to report on
106
123
  output_path: Path to write PDF file
124
+ history: Optional historical results for trends
125
+ trend_data: Optional trend data points for chart rendering
126
+ row_count: Optional dataset row count
127
+ column_count: Optional dataset column count
107
128
  **kwargs: Additional ReportConfig options
108
129
 
109
130
  Returns:
@@ -111,4 +132,11 @@ def generate_pdf_report(
111
132
  """
112
133
  config = ReportConfig(**kwargs) if kwargs else None
113
134
  reporter = PDFReporter(config=config)
114
- return reporter.generate(result, output_path)
135
+ return reporter.generate(
136
+ result,
137
+ output_path,
138
+ history=history,
139
+ trend_data=trend_data,
140
+ row_count=row_count,
141
+ column_count=column_count,
142
+ )
@@ -178,7 +178,9 @@ NAME_PATTERNS: dict[SemanticType, list[str]] = {
178
178
  SemanticType.CURRENCY: [
179
179
  r"amount", r"price", r"cost", r"total", r"subtotal",
180
180
  r"revenue", r"salary", r"fee", r"charge", r"balance",
181
- r"payment", r".*_amt$", r".*_amount$"
181
+ r"payment", r"tax", r"shipping", r"discount", r"tip",
182
+ r".*_amt$", r".*_amount$", r".*_price$", r".*_cost$",
183
+ r"unit_price", r"list_price", r"net_.*", r"gross_.*"
182
184
  ],
183
185
  SemanticType.PERCENTAGE: [
184
186
  r"percent(age)?", r"rate", r"ratio", r"pct", r".*_pct$"
@@ -237,8 +239,8 @@ VALUE_PATTERNS: dict[SemanticType, str] = {
237
239
  SemanticType.TIME: r"^\d{2}:\d{2}(:\d{2})?$",
238
240
  SemanticType.COUNTRY_CODE: r"^[A-Z]{2,3}$",
239
241
  SemanticType.SLUG: r"^[a-z0-9]+(?:-[a-z0-9]+)*$",
240
- SemanticType.LATITUDE: r"^-?([1-8]?\d(\.\d+)?|90(\.0+)?)$",
241
- SemanticType.LONGITUDE: r"^-?(1[0-7]\d(\.\d+)?|180(\.0+)?|\d{1,2}(\.\d+)?)$",
242
+ SemanticType.LATITUDE: r"^-?([1-8]?\d\.\d{4,}|90(\.0+)?)$",
243
+ SemanticType.LONGITUDE: r"^-?(1[0-7]\d\.\d{4,}|180(\.0+)?|\d{1,2}\.\d{4,})$",
242
244
  # Identifier pattern: PREFIX-NUMBER, ABC123, etc. (uppercase or mixed case with numbers)
243
245
  SemanticType.IDENTIFIER: r"^[A-Z][A-Z0-9]*[-_]?\d+$|^[A-Z]{2,}[-_][A-Z0-9]+$",
244
246
  }
@@ -386,12 +388,14 @@ class SemanticTypeDetector:
386
388
  reasons = []
387
389
  candidates: dict[SemanticType, float] = {}
388
390
 
389
- # 1. Check column name patterns
391
+ # 1. Check column name patterns (name is strongest signal)
390
392
  name_lower = column_name.lower().replace("-", "_")
393
+ name_matched_types: set[SemanticType] = set()
391
394
  for sem_type, patterns in self.name_patterns.items():
392
395
  for pattern in patterns:
393
396
  if re.match(pattern, name_lower, re.IGNORECASE):
394
- candidates[sem_type] = candidates.get(sem_type, 0) + 0.4
397
+ candidates[sem_type] = candidates.get(sem_type, 0) + 0.6
398
+ name_matched_types.add(sem_type)
395
399
  reasons.append(f"Column name matches '{sem_type.value}' pattern")
396
400
  break
397
401
 
@@ -408,13 +412,20 @@ class SemanticTypeDetector:
408
412
  )
409
413
  match_rate = match_count / min(len(string_values), 50)
410
414
 
415
+ # Reduce score for ambiguous numeric types (lat/lon)
416
+ # when no name hint supports them
417
+ ambiguous_types = {SemanticType.LATITUDE, SemanticType.LONGITUDE, SemanticType.SLUG}
418
+ penalty = 0.0
419
+ if sem_type in ambiguous_types and sem_type not in name_matched_types:
420
+ penalty = 0.2
421
+
411
422
  if match_rate >= 0.8:
412
- candidates[sem_type] = candidates.get(sem_type, 0) + 0.5
423
+ candidates[sem_type] = candidates.get(sem_type, 0) + 0.5 - penalty
413
424
  reasons.append(
414
425
  f"{match_rate:.0%} of values match {sem_type.value} pattern"
415
426
  )
416
427
  elif match_rate >= 0.5:
417
- candidates[sem_type] = candidates.get(sem_type, 0) + 0.3
428
+ candidates[sem_type] = candidates.get(sem_type, 0) + 0.3 - penalty
418
429
  reasons.append(
419
430
  f"{match_rate:.0%} of values match {sem_type.value} pattern"
420
431
  )
@@ -1,13 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: duckguard
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: A Python-native data quality tool with AI superpowers, built on DuckDB for speed
5
5
  Project-URL: Homepage, https://github.com/XDataHubAI/duckguard
6
- Project-URL: Documentation, https://github.com/XDataHubAI/duckguard
6
+ Project-URL: Documentation, https://xdatahubai.github.io/duckguard/
7
7
  Project-URL: Repository, https://github.com/XDataHubAI/duckguard
8
8
  Author: DuckGuard Team
9
- License-Expression: Elastic-2.0
9
+ License-Expression: Apache-2.0
10
10
  License-File: LICENSE
11
+ License-File: NOTICE
11
12
  Keywords: airflow,anomaly-detection,data-catalog,data-contracts,data-engineering,data-governance,data-lineage,data-observability,data-pipeline,data-profiling,data-quality,data-testing,data-validation,dbt,duckdb,etl,great-expectations,pii-detection,pytest-plugin,schema-validation,soda,testing
12
13
  Classifier: Development Status :: 4 - Beta
13
14
  Classifier: Environment :: Console
@@ -15,7 +16,7 @@ Classifier: Framework :: Pytest
15
16
  Classifier: Intended Audience :: Developers
16
17
  Classifier: Intended Audience :: Information Technology
17
18
  Classifier: Intended Audience :: Science/Research
18
- Classifier: License :: Other/Proprietary License
19
+ Classifier: License :: OSI Approved :: Apache Software License
19
20
  Classifier: Operating System :: OS Independent
20
21
  Classifier: Programming Language :: Python :: 3
21
22
  Classifier: Programming Language :: Python :: 3.10
@@ -70,6 +71,7 @@ Provides-Extra: databricks
70
71
  Requires-Dist: databricks-sql-connector>=2.0.0; extra == 'databricks'
71
72
  Provides-Extra: dev
72
73
  Requires-Dist: black>=23.0.0; extra == 'dev'
74
+ Requires-Dist: jinja2>=3.0.0; extra == 'dev'
73
75
  Requires-Dist: mypy>=1.0.0; extra == 'dev'
74
76
  Requires-Dist: numpy>=1.24.0; extra == 'dev'
75
77
  Requires-Dist: pandas>=2.0.0; extra == 'dev'
@@ -116,8 +118,9 @@ Description-Content-Type: text/markdown
116
118
  [![Downloads](https://static.pepy.tech/badge/duckguard)](https://pepy.tech/project/duckguard)
117
119
  [![GitHub stars](https://img.shields.io/github/stars/XDataHubAI/duckguard?style=social)](https://github.com/XDataHubAI/duckguard/stargazers)
118
120
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
119
- [![License: Elastic-2.0](https://img.shields.io/badge/License-Elastic--2.0-blue.svg)](https://www.elastic.co/licensing/elastic-license)
121
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache--2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)
120
122
  [![CI](https://github.com/XDataHubAI/duckguard/actions/workflows/ci.yml/badge.svg)](https://github.com/XDataHubAI/duckguard/actions/workflows/ci.yml)
123
+ [![Docs](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://xdatahubai.github.io/duckguard/)
121
124
 
122
125
  [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/XDataHubAI/duckguard/blob/main/examples/getting_started.ipynb)
123
126
  [![Kaggle](https://kaggle.com/static/images/open-in-kaggle.svg)](https://kaggle.com/kernels/welcome?src=https://github.com/XDataHubAI/duckguard/blob/main/examples/getting_started.ipynb)
@@ -150,7 +153,7 @@ print(f"Grade: {quality.grade}") # A, B, C, D, or F
150
153
  ## Demo
151
154
 
152
155
  <div align="center">
153
- <img src="docs/assets/demo.gif" alt="DuckGuard Demo" width="750">
156
+ <img src="docs/assets/demo.svg" alt="DuckGuard Demo" width="750">
154
157
  </div>
155
158
 
156
159
  ---
@@ -524,6 +527,48 @@ for g in result.get_failed_groups():
524
527
 
525
528
  ---
526
529
 
530
+ ## 🆕 What's New in 3.2
531
+
532
+ DuckGuard 3.2 adds **AI-powered data quality** — the first data quality library with native LLM integration.
533
+
534
+ ### AI Features (NEW)
535
+
536
+ ```bash
537
+ # Explain quality issues in plain English
538
+ duckguard explain orders.csv
539
+
540
+ # AI suggests validation rules based on your data
541
+ duckguard suggest orders.csv
542
+
543
+ # Get AI-powered fix suggestions for quality issues
544
+ duckguard fix orders.csv
545
+ ```
546
+
547
+ ```python
548
+ from duckguard.ai import explainer, rules_generator, fixer
549
+
550
+ # Natural language quality explanation
551
+ summary = explainer.explain(dataset)
552
+
553
+ # AI-generated validation rules
554
+ rules = rules_generator.suggest_rules(dataset)
555
+
556
+ # Suggest fixes for data quality issues
557
+ fixes = fixer.suggest_fixes(dataset, results)
558
+ ```
559
+
560
+ Supports **OpenAI**, **Anthropic**, and **Ollama** (local models). Configure via environment variables or `AIConfig`.
561
+
562
+ ### Also in 3.2
563
+
564
+ - 🔍 **Improved semantic type detection** — smarter column classification, fewer false positives
565
+ - 📄 **Apache 2.0 license** — OSI-approved, enterprise-friendly
566
+ - 🛡️ **SQL injection prevention** — multi-layer escaping in all string-based checks
567
+ - 📖 **Full documentation site** — [xdatahubai.github.io/duckguard](https://xdatahubai.github.io/duckguard/)
568
+ - 🔒 **PEP 561 typed** — `py.typed` marker for mypy/pyright
569
+
570
+ ---
571
+
527
572
  ## What's New in 3.0
528
573
 
529
574
  DuckGuard 3.0 introduces **conditional checks**, **multi-column validation**, **query-based expectations**, **distributional tests**, and **7 anomaly detection methods**.
@@ -659,19 +704,17 @@ seasonal.fit([10, 12, 11, 13, 9, 14])
659
704
  name: orders_validation
660
705
  description: Quality checks for the orders dataset
661
706
 
662
- columns:
707
+ checks:
663
708
  order_id:
664
- checks:
665
- - type: not_null
666
- - type: unique
709
+ - not_null
710
+ - unique
667
711
  quantity:
668
- checks:
669
- - type: between
670
- value: [1, 1000]
712
+ - between: [1, 1000]
671
713
  status:
672
- checks:
673
- - type: allowed_values
674
- value: [pending, shipped, delivered, cancelled, returned]
714
+ - allowed_values: [pending, shipped, delivered, cancelled, returned]
715
+ email:
716
+ - not_null:
717
+ severity: warning
675
718
  ```
676
719
 
677
720
  ```python
@@ -846,18 +889,48 @@ print(trends.summary())
846
889
 
847
890
  ## Reports & Notifications
848
891
 
892
+ DuckGuard generates self-contained HTML reports with dark mode, trend charts, collapsible sections, sortable tables, and search — all in a single file with zero JavaScript dependencies.
893
+
894
+ > **Live demos:**
895
+ > [Light / Auto Theme](https://htmlpreview.github.io/?https://github.com/XDataHubAI/duckguard/blob/main/examples/reports/demo_report.html)
896
+ > &bull;
897
+ > [Dark Theme](https://htmlpreview.github.io/?https://github.com/XDataHubAI/duckguard/blob/main/examples/reports/demo_report_dark.html)
898
+
899
+ ```python
900
+ from duckguard.reports import HTMLReporter, ReportConfig, generate_html_report
901
+
902
+ # Quick one-liner
903
+ generate_html_report(exec_result, "report.html")
904
+
905
+ # Full-featured report with trends and metadata
906
+ config = ReportConfig(
907
+ title="Orders Quality Report",
908
+ dark_mode="auto", # "auto", "light", or "dark"
909
+ include_trends=True,
910
+ include_metadata=True,
911
+ )
912
+ reporter = HTMLReporter(config=config)
913
+ reporter.generate(
914
+ exec_result,
915
+ "report.html",
916
+ trend_data=trend_data, # from HistoryStorage.get_trend()
917
+ row_count=dataset.row_count,
918
+ column_count=dataset.column_count,
919
+ )
920
+
921
+ # PDF export (requires weasyprint)
922
+ from duckguard.reports import generate_pdf_report
923
+ generate_pdf_report(exec_result, "report.pdf")
924
+ ```
925
+
926
+ ### Notifications
927
+
849
928
  ```python
850
- from duckguard.reports import generate_html_report, generate_pdf_report
851
929
  from duckguard.notifications import (
852
930
  SlackNotifier, TeamsNotifier, EmailNotifier,
853
931
  format_results_text, format_results_markdown,
854
932
  )
855
933
 
856
- # HTML/PDF reports
857
- generate_html_report(exec_result, "report.html")
858
- generate_pdf_report(exec_result, "report.pdf") # requires weasyprint
859
-
860
- # Notifications
861
934
  slack = SlackNotifier(webhook_url="https://hooks.slack.com/services/XXX")
862
935
  teams = TeamsNotifier(webhook_url="https://outlook.office.com/webhook/XXX")
863
936
  email = EmailNotifier(
@@ -942,8 +1015,8 @@ duckguard check orders.csv --config duckguard.yaml
942
1015
  # Auto-discover rules from data
943
1016
  duckguard discover orders.csv > duckguard.yaml
944
1017
 
945
- # Generate reports
946
- duckguard report orders.csv --output report.html
1018
+ # Generate reports (with dark mode and trend charts)
1019
+ duckguard report orders.csv --output report.html --dark-mode auto --trends
947
1020
 
948
1021
  # Anomaly detection
949
1022
  duckguard anomaly orders.csv --method zscore
@@ -1113,7 +1186,7 @@ ruff check src tests # Lint
1113
1186
 
1114
1187
  ## License
1115
1188
 
1116
- Elastic License 2.0 - see [LICENSE](LICENSE)
1189
+ Apache License 2.0 - see [LICENSE](LICENSE)
1117
1190
 
1118
1191
  ---
1119
1192
 
@@ -1,17 +1,24 @@
1
- duckguard/__init__.py,sha256=0sBK1jRnQRtGvRqtEvdIKJ8mWg8KNFrArwDsBw8HKBY,3078
1
+ duckguard/__init__.py,sha256=icO1Zz2ewloEnUT6M5R-1klz3F1Q8zZOtLCRqzRoTmY,3078
2
2
  duckguard/errors.py,sha256=xhQPxCCeB3dCQspTbQf58h_DvwHP1vAb6vKI9fHYAJ0,11493
3
+ duckguard/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ duckguard/ai/__init__.py,sha256=aSUdqK03cqYxMXTlaDPDyyd8mqt5JumoXyDXHyQMpjU,909
5
+ duckguard/ai/config.py,sha256=DGqtsm773vQ-3LM_jUs_x3Xd-GyF8lxr42CkIUG08sg,5834
6
+ duckguard/ai/explainer.py,sha256=9fha2Sc9kRyyAUXMmhWrKINI9t1w4whwkqIyYeYmUe8,3659
7
+ duckguard/ai/fixer.py,sha256=TK0JVJo5gXXXsErzt4HA5vqRImwgSS7n5Kixb80Y6Ek,3224
8
+ duckguard/ai/natural_language.py,sha256=tJi8h9qgXY0149WFgsK_zJ_icMxl4eW2T7v6bpI56dM,3978
9
+ duckguard/ai/rules_generator.py,sha256=g39sGV_lLz2SeUwQp6BDlnPbuPZ1KHvyOLWqkZHQrRk,3675
3
10
  duckguard/anomaly/__init__.py,sha256=mrTyL70cOR5S7_RNc9QLADdnBimIsbAoFTbKlWiIsbw,1353
4
11
  duckguard/anomaly/baselines.py,sha256=k28CjjqBa8IaZxnIgof-wjw_Xdb7NJZImC2OJJkGXQ8,8776
5
12
  duckguard/anomaly/detector.py,sha256=voA7WS2x2p5h5cnwH3C_2ly7HdYpXLwC4jDiPL2Xleo,12443
6
13
  duckguard/anomaly/methods.py,sha256=IRt7_1YWGaQHz2syfEd89lL6kAjOjheSk6ayLRUi58M,15237
7
14
  duckguard/anomaly/ml_methods.py,sha256=Ne8BOULj-bcPmf1_YAqJqnlXDlljfhsxvFbBIjWkJB8,28221
8
15
  duckguard/checks/__init__.py,sha256=aSxO02ZILHnfrGhfomQ5EN69t7NZ4yr61Etwtcv_zIw,847
9
- duckguard/checks/conditional.py,sha256=gYFZD_6M-IUs1MGMZeDYH-qC99dyMJ-u63r1SgcBVs8,26646
16
+ duckguard/checks/conditional.py,sha256=KmKViJ0uEcckQlw0iJHS8QDzuU7xJo8GdOtVgDLmzAc,26758
10
17
  duckguard/checks/distributional.py,sha256=Cy3YlWnSPA5QZdNT_lYuTMRLrwvU1yJGk--RGzOQ5N4,18302
11
18
  duckguard/checks/multicolumn.py,sha256=cZhvW1S9qniQACz11tPtIWsBmcBVmz0kKpEDMnZ9ub0,23623
12
19
  duckguard/checks/query_based.py,sha256=T0shCxdPOQo70KUjV_5OUZTfOm6W2PJDWUUrQzD53-0,22045
13
20
  duckguard/cli/__init__.py,sha256=s5MNXEu_MbRqyV-jeUgCIDlHRQA97a9knM_anJooTl0,87
14
- duckguard/cli/main.py,sha256=kZcisEvn3C6ryQ_1LK_fEv47dytaGTGQ7vVvmM7mdLY,53571
21
+ duckguard/cli/main.py,sha256=62gRiUqkLHfDKVFQiXTrary2EopuY6fBZQt6T4YsKQs,59554
15
22
  duckguard/connectors/__init__.py,sha256=BMbVyyBPI9_GAFcwkQivf2xMvHwVOHvBMuT5qZ558jc,2232
16
23
  duckguard/connectors/base.py,sha256=XzGY6_pUwDJIVNhTfgNMkcGNOBs3xxjbnQ_NeMoz4eM,1864
17
24
  duckguard/connectors/bigquery.py,sha256=b-EHAF90dbyCh387qNirkRGY0sEsPAmvy-hNCbY7ilQ,5327
@@ -34,7 +41,7 @@ duckguard/contracts/loader.py,sha256=iTmg9xjSAlYsBpQeTAJ1-ABQnuXs-qpMh3DH4rfN6qs
34
41
  duckguard/contracts/schema.py,sha256=pLoR4QIXs68Q93DOZqqTmPnPecCeZ4iy9lDXZMNuVmI,7032
35
42
  duckguard/contracts/validator.py,sha256=X972Ns-8UWBL8D4nCCQlNOHJas0Mc4ES8URbKqd0WLw,16432
36
43
  duckguard/core/__init__.py,sha256=pHndzrdehB0GFtlSQ46uvw8XgUQj55dVZQP1ZK-aDso,356
37
- duckguard/core/column.py,sha256=88m3WipKNdNslXNWAk4ofTf0kmNlDDAyhjDUa-Q6UGg,48326
44
+ duckguard/core/column.py,sha256=mX4Lhub8PkDL9vh27_CJpC7t7hRb4vVtSfQFyZhinsU,48738
38
45
  duckguard/core/dataset.py,sha256=kQY2ALTsid5x1NWOM5Wse60mOrLdUj8lKUs1cLK7cCo,44364
39
46
  duckguard/core/engine.py,sha256=ld_NHsWyBkVynmWyvbyQcHdXHhpIoSaRDyqAAtVx8J0,7897
40
47
  duckguard/core/result.py,sha256=Bh3SRf993p0ZYW3902pmgHFhZ-jnimyAXnj9tLL_KcM,15984
@@ -64,8 +71,8 @@ duckguard/reporting/__init__.py,sha256=R7Fm--yEiuOb_II-Qo7MGXYyCNhsGnVsMVuAzZT6r
64
71
  duckguard/reporting/console.py,sha256=GvXFqKLLkU-LQb1FNkS7HI-NQYbHpQCSBYI4FSUDOMw,3026
65
72
  duckguard/reporting/json_report.py,sha256=dqUry9akuPRwNz4ysUM6ZP6ZCXl77nA_Z7mXG-1VGKA,3509
66
73
  duckguard/reports/__init__.py,sha256=JGGZ2IJFVOutcQaZ8kpjDDKJru9e5EsVi91au2VFKsk,1025
67
- duckguard/reports/html_reporter.py,sha256=_8jzHg6WzC4xqXgqzHzYQTjE4vXbQGP-p1FUKmYAtuU,20670
68
- duckguard/reports/pdf_reporter.py,sha256=u6zuV24y9YCBlpDwDObHTSrVE9W9beTIqj-UQyvA8jQ,3094
74
+ duckguard/reports/html_reporter.py,sha256=sSUZdwtaWoxMLQbvWGMOwtC2dZ-koZQbyySXLGThfXA,42492
75
+ duckguard/reports/pdf_reporter.py,sha256=RH6g1FEmPWlMOyNcVDmL-x0NXAqmw0WU5wi6OG_KzYs,4168
69
76
  duckguard/rules/__init__.py,sha256=XYVasAnu8ErJ-Cvsqeh1mX5zxqd1wk-sM4OzuBJn72Y,813
70
77
  duckguard/rules/executor.py,sha256=AL32_0CwLZCg4oP64jIV1a6gL94WT0pjMnYurA3BWx0,43410
71
78
  duckguard/rules/generator.py,sha256=h8NWcRsqBqj4xEddavFRlnWZfCi3eoXsqWyIJmxPGeo,11184
@@ -76,11 +83,12 @@ duckguard/schema_history/analyzer.py,sha256=NRDQCjhPstmp6zD7Co0D4D6jVSJ9SB-iAmv4
76
83
  duckguard/schema_history/tracker.py,sha256=ZuMYX8knruiodXd22KoGaT7MgQBElDjekNz73aSwkqI,8468
77
84
  duckguard/semantic/__init__.py,sha256=FbX60d-Qf7qaVEhnSTy9NzKiXZt66A1G-NZdhvi3TIY,847
78
85
  duckguard/semantic/analyzer.py,sha256=2be1oofe-owBhTg-Dy88-wihaoTQ7DPxf1NuA1sgfR0,8297
79
- duckguard/semantic/detector.py,sha256=MPdb2Rv9VGQBko7nmPk4-Kjga_XVjPZdHCr29gdET0M,15665
86
+ duckguard/semantic/detector.py,sha256=P9mDA8NyIJQ6e_oTlC9FNADsYCCaeiX4Rdwe4zKeTSk,16354
80
87
  duckguard/semantic/validators.py,sha256=8Zu3vwPwh79U09zGf4_PpcwV85_hbNCwRHcxTIQ7G_I,10945
81
88
  duckguard/validators/__init__.py,sha256=g717IM5xlVLCTg1nLRRccLAFHCsbRO-IgjzG4H6K32A,268
82
- duckguard-3.1.0.dist-info/METADATA,sha256=dbd9Oxo_vcJdzb6VQ20xYjDGMSaOsp20TcopAKTK8Ck,34640
83
- duckguard-3.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
84
- duckguard-3.1.0.dist-info/entry_points.txt,sha256=teP6JdXUvY20E9P44TW_Z24xuQtXMgnCyOuWtd_KIYU,108
85
- duckguard-3.1.0.dist-info/licenses/LICENSE,sha256=1Li9P3fainL-epQ9kEHZWKDScWtp4inPd6AkhUTJStk,3841
86
- duckguard-3.1.0.dist-info/RECORD,,
89
+ duckguard-3.2.0.dist-info/METADATA,sha256=ZBzpDSRktEX3MFT9h74Jr3OkRnmworHih5_qmC-GKVg,37165
90
+ duckguard-3.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
91
+ duckguard-3.2.0.dist-info/entry_points.txt,sha256=teP6JdXUvY20E9P44TW_Z24xuQtXMgnCyOuWtd_KIYU,108
92
+ duckguard-3.2.0.dist-info/licenses/LICENSE,sha256=2KMAZ3V5Qp6hrS0LES2Pjzt_x1RpHrY957EhX-Y-M-E,10761
93
+ duckguard-3.2.0.dist-info/licenses/NOTICE,sha256=MokeG17hVmF9AwNg1uib0EgicuyoqTjbWcudXszngm8,174
94
+ duckguard-3.2.0.dist-info/RECORD,,
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to the Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by the Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding any notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2025 XDataHubAI
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.
@@ -0,0 +1,7 @@
1
+ DuckGuard
2
+ Copyright 2025 XDataHubAI
3
+
4
+ This product includes software developed at XDataHubAI
5
+ (https://github.com/XDataHubAI).
6
+
7
+ Licensed under the Apache License, Version 2.0.