DataExcept 0.1.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 (33) hide show
  1. dataexcept-0.1.0/CHANGELOG.md +42 -0
  2. dataexcept-0.1.0/CITATION.cff +14 -0
  3. dataexcept-0.1.0/LICENSE +22 -0
  4. dataexcept-0.1.0/PKG-INFO +397 -0
  5. dataexcept-0.1.0/README.md +369 -0
  6. dataexcept-0.1.0/dataexcept/__init__.py +81 -0
  7. dataexcept-0.1.0/dataexcept/__main__.py +74 -0
  8. dataexcept-0.1.0/dataexcept/database_exceptions.py +71 -0
  9. dataexcept-0.1.0/dataexcept/dataengineering_exceptions.py +125 -0
  10. dataexcept-0.1.0/dataexcept/datascience_exceptions/__init__.py +85 -0
  11. dataexcept-0.1.0/dataexcept/datascience_exceptions/base.py +17 -0
  12. dataexcept-0.1.0/dataexcept/datascience_exceptions/ingestion.py +312 -0
  13. dataexcept-0.1.0/dataexcept/datascience_exceptions/operations.py +130 -0
  14. dataexcept-0.1.0/dataexcept/datascience_exceptions/training.py +508 -0
  15. dataexcept-0.1.0/dataexcept/exceptions/__init__.py +36 -0
  16. dataexcept-0.1.0/dataexcept/exceptions/authentication.py +21 -0
  17. dataexcept-0.1.0/dataexcept/exceptions/base.py +4 -0
  18. dataexcept-0.1.0/dataexcept/exceptions/configuration.py +10 -0
  19. dataexcept-0.1.0/dataexcept/exceptions/external.py +42 -0
  20. dataexcept-0.1.0/dataexcept/exceptions/lifecycle.py +13 -0
  21. dataexcept-0.1.0/dataexcept/exceptions/notification.py +49 -0
  22. dataexcept-0.1.0/dataexcept/exceptions/parsing.py +31 -0
  23. dataexcept-0.1.0/dataexcept/exceptions/scheduling.py +21 -0
  24. dataexcept-0.1.0/dataexcept/exceptions/validation.py +12 -0
  25. dataexcept-0.1.0/dataexcept/io_exceptions.py +66 -0
  26. dataexcept-0.1.0/dataexcept/job_exceptions.py +59 -0
  27. dataexcept-0.1.0/dataexcept/logging_helpers.py +76 -0
  28. dataexcept-0.1.0/dataexcept/network_exceptions.py +102 -0
  29. dataexcept-0.1.0/dataexcept/pandas_exceptions.py +143 -0
  30. dataexcept-0.1.0/dataexcept/pipeline_exceptions.py +205 -0
  31. dataexcept-0.1.0/dataexcept/py.typed +1 -0
  32. dataexcept-0.1.0/dataexcept/security_exceptions.py +68 -0
  33. dataexcept-0.1.0/pyproject.toml +83 -0
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-08-24
11
+
12
+ First public release.
13
+
14
+ ### Added
15
+
16
+ - Hierarchical exception classes for data science, machine learning and data
17
+ engineering workflows. Catch a specific failure or a broad category, and get
18
+ a message that names the value that caused it rather than a bare
19
+ `ValueError`.
20
+ - Domain modules for validation, configuration, authentication, parsing,
21
+ serialization, scheduling, notification, lifecycle and external-service
22
+ errors, plus dedicated pandas, database, network, I/O, pipeline and security
23
+ exception groups.
24
+ - `dataexcept.logging_helpers` with `log_exception`, `log_and_raise` and
25
+ `log_then_raise`, for logging exceptions with structured context and
26
+ re-raising without losing the traceback.
27
+ - A `dataexcept` command-line entry point that lists the exported exception
28
+ classes and reports the installed version.
29
+ - A `py.typed` marker, backed by a mypy-clean codebase that CI enforces, so
30
+ downstream type checkers get annotations that are actually correct.
31
+ - Documentation at
32
+ [diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/),
33
+ including an API reference generated from the docstrings.
34
+
35
+ ### Notes
36
+
37
+ - Supports Python 3.10 through 3.13.
38
+ - Published to PyPI via OIDC trusted publishing; no long-lived API token is
39
+ involved in a release.
40
+
41
+ [Unreleased]: https://github.com/DiogoRibeiro7/DataExcept/compare/v0.1.0...HEAD
42
+ [0.1.0]: https://github.com/DiogoRibeiro7/DataExcept/releases/tag/v0.1.0
@@ -0,0 +1,14 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it using the following metadata."
3
+ title: "DataExcept"
4
+ version: "0.1.0"
5
+ authors:
6
+ - family-names: "Ribeiro"
7
+ given-names: "Diogo"
8
+ email: "dfr@esmad.ipp.pt"
9
+ orcid: "https://orcid.org/0009-0001-2022-7072"
10
+ affiliation: "ESMAD - Instituto Politécnico do Porto"
11
+ type: software
12
+ url: "https://github.com/DiogoRibeiro7/DataExcept"
13
+ repository-code: "https://github.com/DiogoRibeiro7/DataExcept"
14
+ date-released: "2025-06-16"
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Diogo Ribeiro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,397 @@
1
+ Metadata-Version: 2.4
2
+ Name: DataExcept
3
+ Version: 0.1.0
4
+ Summary: A Python package providing structured, easily-extendable custom exception types.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: exceptions,errors,logging
8
+ Author: Diogo Ribeiro
9
+ Author-email: dfr@esmad.ipp.pt
10
+ Maintainer: Diogo Ribeiro
11
+ Maintainer-email: diogo.debastos.ribeiro@gmail.com
12
+ Requires-Python: >=3.10,<3.14
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Requires-Dist: tomli ; python_version < "3.11"
21
+ Project-URL: Changelog, https://github.com/DiogoRibeiro7/DataExcept/releases
22
+ Project-URL: Documentation, https://diogoribeiro7.github.io/DataExcept/
23
+ Project-URL: Homepage, https://github.com/DiogoRibeiro7/DataExcept
24
+ Project-URL: Issues, https://github.com/DiogoRibeiro7/DataExcept/issues
25
+ Project-URL: Repository, https://github.com/DiogoRibeiro7/DataExcept
26
+ Description-Content-Type: text/markdown
27
+
28
+ # DataExcept
29
+
30
+ [![CI](https://github.com/DiogoRibeiro7/DataExcept/actions/workflows/ci.yml/badge.svg)](https://github.com/DiogoRibeiro7/DataExcept/actions/workflows/ci.yml) [![PyPI version](https://badge.fury.io/py/DataExcept.svg)](https://badge.fury.io/py/DataExcept) [![Python Support](https://img.shields.io/pypi/pyversions/DataExcept.svg)](https://pypi.org/project/DataExcept/) [![Coverage](https://diogoribeiro7.github.io/DataExcept/coverage.svg)](https://diogoribeiro7.github.io/DataExcept/htmlcov/) [![Documentation](https://img.shields.io/badge/docs-GitHub%20Pages-blue)](https://diogoribeiro7.github.io/DataExcept/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/)
31
+
32
+ **DataExcept** is a production-ready Python library that provides **structured, hierarchical exception classes** specifically designed for **data science**, **machine learning**, and **data engineering** workflows. Stop debugging generic `ValueError`s and `RuntimeError`s -- get meaningful, actionable error messages that help you understand exactly what went wrong in your data pipeline.
33
+
34
+ ## 🚀 Why DataExcept?
35
+
36
+ ❌ Without DataExcept | ✅ With DataExcept
37
+ ------------------------------- | -------------------------------------------------------------------------------------
38
+ `ValueError: Invalid value` | `DataValidationError: Invalid value for 'age': -1`
39
+ `RuntimeError: Training failed` | `ConvergenceError: Model 'RandomForest' failed to converge after 100 iterations`
40
+ `Exception: Prediction error` | `ModelInferenceError: Inference failed for model 'CNN': CUDA out of memory`
41
+ `KeyError: column not found` | `MissingColumnError: Missing required column 'customer_id' in DataFrame 'sales_data'`
42
+
43
+ ## 🎯 Key Features
44
+
45
+ - **🏗️ Hierarchical Structure**: Catch specific errors or broad categories
46
+ - **📊 Data Science Focused**: 40+ exceptions covering ML pipelines, feature engineering, model training
47
+ - **🔧 Production Ready**: Comprehensive logging helpers and error context
48
+ - **📚 Academic Quality**: Proper documentation, type hints, and citation support
49
+ - **🐍 Python 3.10+**: Modern Python with full type safety
50
+ - **🧪 Well Tested**: Broad test suite with comprehensive edge case handling (see the coverage badge above)
51
+
52
+ ## 📦 Quick Installation
53
+
54
+ ```bash
55
+ pip install DataExcept
56
+ ```
57
+
58
+ For development:
59
+
60
+ ```bash
61
+ git clone https://github.com/DiogoRibeiro7/DataExcept.git
62
+ cd DataExcept
63
+ poetry install
64
+ ```
65
+
66
+ ## 🏃‍♂️ Quick Start
67
+
68
+ ### Basic Usage
69
+
70
+ ```python
71
+ from dataexcept import ValidationError, ModelTrainingError
72
+ from dataexcept.datascience_exceptions import DataLoadingError
73
+ import pandas as pd
74
+
75
+ # Data validation with context
76
+ def validate_dataframe(df: pd.DataFrame) -> None:
77
+ if 'customer_id' not in df.columns:
78
+ raise ValidationError(
79
+ field='customer_id',
80
+ value=list(df.columns),
81
+ message="Customer ID column is required for processing"
82
+ )
83
+
84
+ # Model training with specific error types
85
+ def train_model(model_type: str, epochs: int) -> None:
86
+ try:
87
+ # Your training code here
88
+ if epochs > 1000:
89
+ raise ModelTrainingError(
90
+ model_type=model_type,
91
+ epoch=epochs,
92
+ message=f"Training {model_type} exceeded reasonable epoch limit"
93
+ )
94
+ except Exception as e:
95
+ # Wrap unknown errors with context
96
+ raise ModelTrainingError(model_type, message=f"Unexpected error: {e}")
97
+
98
+ # File operations with detailed context
99
+ def load_dataset(file_path: str) -> pd.DataFrame:
100
+ try:
101
+ return pd.read_csv(file_path)
102
+ except FileNotFoundError as e:
103
+ raise DataLoadingError(source=file_path, original=e)
104
+ ```
105
+
106
+ ### Exception Hierarchies
107
+
108
+ ```python
109
+ from dataexcept import JobError
110
+ from dataexcept.datascience_exceptions import ModelTrainingError, ConvergenceError
111
+
112
+ try:
113
+ # Your ML pipeline
114
+ train_complex_model()
115
+ except ConvergenceError:
116
+ # Handle specific convergence issues
117
+ logger.warning("Model didn't converge, trying with different parameters")
118
+ train_with_fallback_params()
119
+ except ModelTrainingError:
120
+ # Handle any training-related error
121
+ logger.error("Training failed, falling back to simpler model")
122
+ train_simple_model()
123
+ except JobError:
124
+ # Handle any job-related error
125
+ logger.error("Job failed, notifying administrators")
126
+ send_alert()
127
+ ```
128
+
129
+ ## 🏗️ Exception Categories
130
+
131
+ ### 📊 Data Science & ML
132
+
133
+ ```python
134
+ from dataexcept.datascience_exceptions import *
135
+
136
+ # Data ingestion and validation
137
+ DataLoadingError("data.csv", FileNotFoundError())
138
+ DataValidationError("age", -5, "Age cannot be negative")
139
+ MissingDataError("income", "Required for credit scoring")
140
+
141
+ # Feature engineering and preprocessing
142
+ FeatureEngineeringError("log_transform", "Cannot take log of negative values")
143
+ DataNormalizationError("StandardScaler", "Division by zero in variance calculation")
144
+ DataImbalanceError(ratio=0.05, threshold=0.1)
145
+
146
+ # Model training and evaluation
147
+ ModelTrainingError("RandomForest", epoch=45)
148
+ ConvergenceError("GradientBoosting", iterations=1000)
149
+ OverfittingError(train_metric=0.98, val_metric=0.65)
150
+ BiasDetectionError("gender", bias_score=0.15, threshold=0.1)
151
+
152
+ # Model deployment and inference
153
+ ModelInferenceError("CNN", RuntimeError("CUDA out of memory"))
154
+ ModelCompatibilityError("2.1.0", "1.8.0")
155
+ ```
156
+
157
+ ### 🔧 Data Engineering & ETL
158
+
159
+ ```python
160
+ from dataexcept.dataengineering_exceptions import *
161
+
162
+ ETLJobError("daily_customer_pipeline")
163
+ SchemaEvolutionError("v2.1", reason="Incompatible column type change")
164
+ DataTransformationError("currency_conversion", "Invalid exchange rate")
165
+ BatchProcessingError("batch_2023_11_13", original=TimeoutError())
166
+ ```
167
+
168
+ ### 🐼 Pandas Operations
169
+
170
+ ```python
171
+ from dataexcept.pandas_exceptions import *
172
+
173
+ MissingColumnError("customer_id", dataframe="sales_df")
174
+ DtypeMismatchError("revenue", expected=["float64", "int64"], found="object")
175
+ MergeKeyError(["customer_id"], ["cust_id"])
176
+ ```
177
+
178
+ ### 🔗 Infrastructure & Networking
179
+
180
+ ```python
181
+ from dataexcept.network_exceptions import *
182
+ from dataexcept.database_exceptions import *
183
+
184
+ HostUnreachableError("api.example.com")
185
+ DatabaseConnectionError("postgresql://prod-db:5432/analytics")
186
+ QueryExecutionError("SELECT * FROM large_table", original=TimeoutError())
187
+ ```
188
+
189
+ ## 🔍 Advanced Features
190
+
191
+ ### Smart Logging Integration
192
+
193
+ ```python
194
+ from dataexcept.logging_helpers import log_and_raise, log_exception
195
+ import logging
196
+
197
+ logger = logging.getLogger(__name__)
198
+
199
+ # Context manager for automatic logging
200
+ with log_and_raise(logger=logger, context={"job_id": "ETL_001", "batch": "2023-11-13"}):
201
+ process_daily_batch()
202
+
203
+ # Manual exception logging with context
204
+ try:
205
+ risky_operation()
206
+ except Exception as exc:
207
+ log_exception(
208
+ exc,
209
+ logger=logger,
210
+ context={"user_id": "12345", "operation": "feature_extraction"}
211
+ )
212
+ raise
213
+ ```
214
+
215
+ ### Command Line Interface
216
+
217
+ ```bash
218
+ # List all available exception classes
219
+ $ dataexcept list
220
+ JobError
221
+ ValidationError
222
+ DataScienceError
223
+ ModelTrainingError
224
+ ... (40+ more)
225
+
226
+ # Check version
227
+ $ dataexcept --version
228
+ dataexcept 0.1.0
229
+ ```
230
+
231
+ ## 🎯 Use Cases
232
+
233
+ ### 🏭 Production ML Pipelines
234
+
235
+ - **Model Training**: Distinguish between convergence issues, data problems, and infrastructure failures
236
+ - **Feature Engineering**: Track which transformation steps fail and why
237
+ - **Model Serving**: Provide actionable error messages for inference failures
238
+ - **Data Drift**: Alert when model assumptions are violated
239
+
240
+ ### 📈 Data Engineering
241
+
242
+ - **ETL Pipelines**: Clear error categorization for debugging complex data flows
243
+ - **Data Quality**: Structured validation errors with field-level context
244
+ - **Schema Evolution**: Track migration failures and compatibility issues
245
+ - **Batch Processing**: Identify whether failures are data-related or system-related
246
+
247
+ ### 🔬 Research & Academia
248
+
249
+ - **Reproducible Experiments**: Consistent error handling across research codebases
250
+ - **Citation Support**: Proper academic attribution with CITATION.cff
251
+ - **Documentation**: Auto-generated API docs with comprehensive examples
252
+
253
+ ## 📚 Real-World Example
254
+
255
+ ```python
256
+ """
257
+ Complete ML pipeline with DataExcept error handling
258
+ """
259
+ import pandas as pd
260
+ from sklearn.ensemble import RandomForestClassifier
261
+ from dataexcept import ValidationError
262
+ from dataexcept.datascience_exceptions import *
263
+ from dataexcept.pandas_exceptions import *
264
+ from dataexcept.logging_helpers import log_and_raise
265
+ import logging
266
+
267
+ def ml_pipeline(data_path: str, target_col: str):
268
+ logger = logging.getLogger(__name__)
269
+
270
+ with log_and_raise(logger=logger, context={"pipeline": "customer_churn"}):
271
+ # 1\. Data Loading
272
+ try:
273
+ df = pd.read_csv(data_path)
274
+ except FileNotFoundError as e:
275
+ raise DataLoadingError(source=data_path, original=e)
276
+
277
+ # 2\. Data Validation
278
+ if target_col not in df.columns:
279
+ raise MissingColumnError(target_col, dataframe="training_data")
280
+
281
+ if df[target_col].dtype not in ['int64', 'bool']:
282
+ raise DtypeMismatchError(
283
+ target_col,
284
+ expected=['int64', 'bool'],
285
+ found=str(df[target_col].dtype)
286
+ )
287
+
288
+ # 3\. Data Quality Checks
289
+ missing_ratio = df.isnull().sum().sum() / (df.shape[0] * df.shape[1])
290
+ if missing_ratio > 0.3:
291
+ raise DataValidationError(
292
+ field="missing_data_ratio",
293
+ value=missing_ratio,
294
+ message=f"Dataset has {missing_ratio:.1%} missing values, exceeds 30% threshold"
295
+ )
296
+
297
+ # 4\. Class Imbalance Check
298
+ class_ratio = df[target_col].value_counts().min() / df[target_col].value_counts().max()
299
+ if class_ratio < 0.1:
300
+ raise DataImbalanceError(ratio=class_ratio, threshold=0.1)
301
+
302
+ # 5\. Feature Engineering
303
+ try:
304
+ df['log_revenue'] = np.log(df['revenue'] + 1)
305
+ except Exception as e:
306
+ raise FeatureEngineeringError("log_transform", cause=str(e))
307
+
308
+ # 6\. Model Training
309
+ try:
310
+ model = RandomForestClassifier(n_estimators=100)
311
+ X = df.drop(columns=[target_col])
312
+ y = df[target_col]
313
+ model.fit(X, y)
314
+ except Exception as e:
315
+ raise ModelTrainingError("RandomForest", message=f"Training failed: {e}")
316
+
317
+ # 7\. Model Validation
318
+ train_score = model.score(X, y)
319
+ if train_score < 0.6:
320
+ raise UnderfittingError(train_metric=train_score, threshold=0.6)
321
+
322
+ return model
323
+
324
+ # Usage
325
+ if __name__ == "__main__":
326
+ try:
327
+ model = ml_pipeline("customer_data.csv", "churned")
328
+ print("✅ Pipeline completed successfully!")
329
+ except DataLoadingError as e:
330
+ print(f"❌ Data loading failed: {e}")
331
+ except MissingColumnError as e:
332
+ print(f"❌ Schema validation failed: {e}")
333
+ except DataImbalanceError as e:
334
+ print(f"⚠️ Data quality issue: {e}")
335
+ except ModelTrainingError as e:
336
+ print(f"❌ Model training failed: {e}")
337
+ except Exception as e:
338
+ print(f"💥 Unexpected error: {e}")
339
+ ```
340
+
341
+ ## 🤝 Contributing
342
+
343
+ We welcome contributions! See our [Contributing Guide](CONTRIBUTING.md) for details.
344
+
345
+ ```bash
346
+ # Development setup
347
+ git clone https://github.com/DiogoRibeiro7/DataExcept.git
348
+ cd DataExcept
349
+ make install # poetry install --with dev,docs
350
+ pre-commit install
351
+
352
+ make check # lint, formatting, mypy and tests - everything CI runs
353
+ make help # list all targets
354
+ ```
355
+
356
+ Please also read the [Code of Conduct](CODE_OF_CONDUCT.md). Security issues go
357
+ through [SECURITY.md](SECURITY.md), not the public issue tracker.
358
+
359
+ ## 📖 Documentation
360
+
361
+ - **Full Documentation**: [diogoribeiro7.github.io/DataExcept](https://diogoribeiro7.github.io/DataExcept/)
362
+ - **API Reference**: [API Docs](https://diogoribeiro7.github.io/DataExcept/api/)
363
+ - **Advanced Usage**: [Advanced Guide](https://diogoribeiro7.github.io/DataExcept/advanced_usage/)
364
+ - **CLI Reference**: [CLI Guide](https://diogoribeiro7.github.io/DataExcept/cli/)
365
+ - **Changelog**: [CHANGELOG.md](CHANGELOG.md)
366
+
367
+ ## 🎓 Citation
368
+
369
+ If you use DataExcept in your research, please cite it:
370
+
371
+ ```bibtex
372
+ @software{ribeiro_dataexcept_2025,
373
+ author = {Ribeiro, Diogo},
374
+ title = {DataExcept: Structured Exception Handling for Data Science},
375
+ url = {https://github.com/DiogoRibeiro7/DataExcept},
376
+ version = {0.1.0},
377
+ year = {2025},
378
+ publisher = {GitHub}
379
+ }
380
+ ```
381
+
382
+ ## 📄 License
383
+
384
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
385
+
386
+ ## 🏆 About the Author
387
+
388
+ **Diogo Ribeiro** is a Lead Data Scientist at Mysense.ai and researcher/instructor at ESMAD (Instituto Politécnico do Porto). With expertise in machine learning, statistical analysis, and production ML systems, he created DataExcept to solve real-world error handling challenges in data science workflows.
389
+
390
+ - 🔗 **ORCID**: [0009-0001-2022-7072](https://orcid.org/0009-0001-2022-7072)
391
+ - 🌐 **Website**: [diogoribeiro7.github.io](https://diogoribeiro7.github.io/)
392
+ - 🏢 **Affiliation**: ESMAD - Instituto Politécnico do Porto
393
+
394
+ --------------------------------------------------------------------------------
395
+
396
+ ⭐ **Star this repo** if DataExcept helps you build better data pipelines!
397
+