dbtk 0.8.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 (70) hide show
  1. dbtk-0.8.0/LICENSE.txt +7 -0
  2. dbtk-0.8.0/PKG-INFO +305 -0
  3. dbtk-0.8.0/README.md +236 -0
  4. dbtk-0.8.0/dbtk/__init__.py +54 -0
  5. dbtk-0.8.0/dbtk/cli.py +169 -0
  6. dbtk-0.8.0/dbtk/config.py +1194 -0
  7. dbtk-0.8.0/dbtk/cursors.py +566 -0
  8. dbtk-0.8.0/dbtk/database.py +959 -0
  9. dbtk-0.8.0/dbtk/dbtk_sample.yml +90 -0
  10. dbtk-0.8.0/dbtk/defaults.py +29 -0
  11. dbtk-0.8.0/dbtk/etl/__init__.py +61 -0
  12. dbtk-0.8.0/dbtk/etl/base_surge.py +257 -0
  13. dbtk-0.8.0/dbtk/etl/bulk_surge.py +783 -0
  14. dbtk-0.8.0/dbtk/etl/config_generators.py +458 -0
  15. dbtk-0.8.0/dbtk/etl/data_surge.py +294 -0
  16. dbtk-0.8.0/dbtk/etl/managers.py +734 -0
  17. dbtk-0.8.0/dbtk/etl/table.py +1215 -0
  18. dbtk-0.8.0/dbtk/etl/transforms/__init__.py +107 -0
  19. dbtk-0.8.0/dbtk/etl/transforms/address.py +560 -0
  20. dbtk-0.8.0/dbtk/etl/transforms/core.py +594 -0
  21. dbtk-0.8.0/dbtk/etl/transforms/database.py +506 -0
  22. dbtk-0.8.0/dbtk/etl/transforms/datetime.py +497 -0
  23. dbtk-0.8.0/dbtk/etl/transforms/email.py +72 -0
  24. dbtk-0.8.0/dbtk/etl/transforms/phone.py +670 -0
  25. dbtk-0.8.0/dbtk/formats/__init__.py +18 -0
  26. dbtk-0.8.0/dbtk/formats/edi.py +182 -0
  27. dbtk-0.8.0/dbtk/logging_utils.py +310 -0
  28. dbtk-0.8.0/dbtk/readers/__init__.py +26 -0
  29. dbtk-0.8.0/dbtk/readers/base.py +644 -0
  30. dbtk-0.8.0/dbtk/readers/csv.py +195 -0
  31. dbtk-0.8.0/dbtk/readers/data_frame.py +119 -0
  32. dbtk-0.8.0/dbtk/readers/excel.py +260 -0
  33. dbtk-0.8.0/dbtk/readers/fixed_width.py +359 -0
  34. dbtk-0.8.0/dbtk/readers/json.py +323 -0
  35. dbtk-0.8.0/dbtk/readers/utils.py +394 -0
  36. dbtk-0.8.0/dbtk/readers/xml.py +260 -0
  37. dbtk-0.8.0/dbtk/record.py +710 -0
  38. dbtk-0.8.0/dbtk/utils.py +537 -0
  39. dbtk-0.8.0/dbtk/writers/__init__.py +46 -0
  40. dbtk-0.8.0/dbtk/writers/base.py +718 -0
  41. dbtk-0.8.0/dbtk/writers/csv.py +107 -0
  42. dbtk-0.8.0/dbtk/writers/database.py +158 -0
  43. dbtk-0.8.0/dbtk/writers/excel.py +1086 -0
  44. dbtk-0.8.0/dbtk/writers/fixed_width.py +290 -0
  45. dbtk-0.8.0/dbtk/writers/json.py +156 -0
  46. dbtk-0.8.0/dbtk/writers/utils.py +41 -0
  47. dbtk-0.8.0/dbtk/writers/xml.py +387 -0
  48. dbtk-0.8.0/dbtk.egg-info/PKG-INFO +305 -0
  49. dbtk-0.8.0/dbtk.egg-info/SOURCES.txt +68 -0
  50. dbtk-0.8.0/dbtk.egg-info/dependency_links.txt +1 -0
  51. dbtk-0.8.0/dbtk.egg-info/entry_points.txt +2 -0
  52. dbtk-0.8.0/dbtk.egg-info/requires.txt +56 -0
  53. dbtk-0.8.0/dbtk.egg-info/top_level.txt +1 -0
  54. dbtk-0.8.0/pyproject.toml +80 -0
  55. dbtk-0.8.0/setup.cfg +4 -0
  56. dbtk-0.8.0/tests/test_bulk.py +905 -0
  57. dbtk-0.8.0/tests/test_config.py +206 -0
  58. dbtk-0.8.0/tests/test_data_bending.py +546 -0
  59. dbtk-0.8.0/tests/test_edi_writer.py +190 -0
  60. dbtk-0.8.0/tests/test_excel_writer.py +54 -0
  61. dbtk-0.8.0/tests/test_fixed_width_record.py +464 -0
  62. dbtk-0.8.0/tests/test_logging_utils.py +173 -0
  63. dbtk-0.8.0/tests/test_readers.py +778 -0
  64. dbtk-0.8.0/tests/test_table.py +1306 -0
  65. dbtk-0.8.0/tests/test_table_new_features.py +195 -0
  66. dbtk-0.8.0/tests/test_transforms_core.py +740 -0
  67. dbtk-0.8.0/tests/test_transforms_datetime.py +463 -0
  68. dbtk-0.8.0/tests/test_transforms_phone.py +479 -0
  69. dbtk-0.8.0/tests/test_validation_collector.py +177 -0
  70. dbtk-0.8.0/tests/test_writers.py +1059 -0
dbtk-0.8.0/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2025 Scott Bailey
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
dbtk-0.8.0/PKG-INFO ADDED
@@ -0,0 +1,305 @@
1
+ Metadata-Version: 2.4
2
+ Name: dbtk
3
+ Version: 0.8.0
4
+ Summary: Data Benders Toolkit - A lightweight toolkit for data integration, ELT and ETL
5
+ Author: Scott Bailey <scottrbailey@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/scottrbailey/dbtk
8
+ Project-URL: Documentation, https://dbtk.readthedocs.io
9
+ Project-URL: Repository, https://github.com/scottrbailey/dbtk.git
10
+ Keywords: database,etl,data integration,postgresql,oracle,mysql,sql server
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Database
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.6
17
+ Classifier: Programming Language :: Python :: 3.7
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Requires-Python: >=3.6
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE.txt
28
+ Requires-Dist: PyYAML>=6.0
29
+ Requires-Dist: cryptography>=3.4.8
30
+ Requires-Dist: importlib-metadata>=1.0; python_version < "3.8"
31
+ Provides-Extra: encryption
32
+ Requires-Dist: keyring>=23.0.0; extra == "encryption"
33
+ Provides-Extra: formats
34
+ Requires-Dist: lxml>=4.6.0; extra == "formats"
35
+ Requires-Dist: openpyxl>=3.0.0; extra == "formats"
36
+ Provides-Extra: recommended
37
+ Requires-Dist: keyring>=23.0.0; extra == "recommended"
38
+ Requires-Dist: lxml>=4.6.0; extra == "recommended"
39
+ Requires-Dist: openpyxl>=3.0.0; extra == "recommended"
40
+ Requires-Dist: phonenumbers>=8.12.0; extra == "recommended"
41
+ Requires-Dist: python-dateutil>=2.8.0; extra == "recommended"
42
+ Requires-Dist: usaddress>=0.5.11; extra == "recommended"
43
+ Provides-Extra: postgresql
44
+ Requires-Dist: psycopg2-binary>=2.9; extra == "postgresql"
45
+ Requires-Dist: psycopg[binary]>=3.1; extra == "postgresql"
46
+ Provides-Extra: oracle
47
+ Requires-Dist: oracledb>=1.0; python_version >= "3.7" and extra == "oracle"
48
+ Requires-Dist: cx_Oracle>=8.0; python_version < "3.7" and extra == "oracle"
49
+ Provides-Extra: mysql
50
+ Requires-Dist: pymysql>=1.0; extra == "mysql"
51
+ Provides-Extra: sqlserver
52
+ Requires-Dist: pyodbc<5.0,>=4.0.21; extra == "sqlserver"
53
+ Provides-Extra: all
54
+ Requires-Dist: keyring>=23.0.0; extra == "all"
55
+ Requires-Dist: lxml>=4.6.0; extra == "all"
56
+ Requires-Dist: openpyxl>=3.0.0; extra == "all"
57
+ Requires-Dist: xlrd>=1.2.0; extra == "all"
58
+ Requires-Dist: phonenumbers>=8.12.0; extra == "all"
59
+ Requires-Dist: python-dateutil>=2.8.0; extra == "all"
60
+ Requires-Dist: usaddress>=0.5.11; extra == "all"
61
+ Provides-Extra: dev
62
+ Requires-Dist: pytest>=6.0; extra == "dev"
63
+ Requires-Dist: pytest-cov>=2.10; extra == "dev"
64
+ Provides-Extra: docs
65
+ Requires-Dist: sphinx>=4.0; extra == "docs"
66
+ Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
67
+ Requires-Dist: myst-parser>=0.18; extra == "docs"
68
+ Dynamic: license-file
69
+
70
+ # DBTK - Data Benders Toolkit
71
+
72
+ <div style="float: right; padding: 20px">
73
+ <img src="/docs/assets/databender.png" height="240" align="right" />
74
+ </div>
75
+
76
+ **Control and Manipulate the Flow of Data** - A lightweight Python toolkit for data integration, transformation, and movement between systems.
77
+
78
+ Like the elemental benders of Avatar, this library gives you precise control over data, the world's most rapidly growing element.
79
+ Extract data from various sources, transform it through powerful operations, and load it exactly where it needs to go.
80
+ This library is designed by and for data integrators.
81
+
82
+ **DBTK aims to be fast and memory-efficient at every turn.** But it was designed to boost your productivity first and foremost.
83
+ You have dozens (possibly hundreds) of interfaces, impossible deadlines, and multiple projects all happening at once. Your
84
+ environment has three or more different relational databases. You just want to get stuff done instead of writing the same
85
+ boilerplate code over and over or stressing over differences on a database server you don't use very often.
86
+
87
+ **Design philosophy:** Modern databases excel at aggregating and transforming data at scale. DBTK embraces
88
+ this by focusing on what Python does well: flexible record-by-record transformations,
89
+ connecting disparate systems, and orchestrating data movement.
90
+
91
+ If you need to pivot, aggregate, or perform complex SQL operations - write SQL and let
92
+ your database handle it. If you need dataframes and heavy analytics - reach for Pandas
93
+ or polars. DBTK sits in between: getting your data where it needs to be, cleaned and
94
+ validated along the way.
95
+
96
+ **Speed and Memory** The primary objective of DBTK is to give data integrators an elegant toolkit to speed up your development.
97
+ But DBTK's throughput and memory usage are very good. BulkSurge streaming from a polars and doing direct loads to PostgreSQL will
98
+ process 1M rows in 3-4 seconds. But even with a standard Python csv reader and numerous column transforms, DataSurge is able to
99
+ write 1M rows to every supported database in 5-10 seconds.
100
+
101
+ ## Features
102
+
103
+ - **Universal Database Connectivity** - Unified interface across PostgreSQL, Oracle, MySQL, SQL Server, and SQLite with intelligent driver auto-detection
104
+ - **Portable SQL Queries** - Write SQL once with named parameters, runs on any database regardless of parameter style
105
+ - **Smart Cursors** - All cursors and readers return Record objects with the speed and efficiency of tuples and the flexibility of dicts
106
+ - **Flexible File Reading** - CSV, Excel (XLS/XLSX), JSON, NDJSON, XML, DataFrame and fixed-width text files with consistent API
107
+ - **Transparent Compression** - Automatic decompression of .gz, .bz2, .xz, and .zip files with smart member selection
108
+ - **Multiple Export Formats** - Write to CSV, Excel, JSON, NDJSON, XML, fixed-width text, or directly between databases
109
+ - **Advanced ETL Framework** - Full-featured Table class for complex data transformations, validations, and upserts
110
+ - **Data Transformations** - Built-in functions for dates, phones, emails, and custom data cleaning with international support
111
+ - **High-Performance Bulk Operations** - DataSurge for blazing-fast batch operations; BulkSurge for even faster direct loading when supported
112
+ - **Integrated Logging** - Timestamped log files with automatic cleanup, split error logs, and zero-config setup
113
+ - **Encrypted Configuration** - YAML-based config with password encryption and environment variable support
114
+
115
+ ## Installation
116
+
117
+ ```bash
118
+ pip install dbtk
119
+
120
+ # installs keyring, lxml, openpyxl, phone address and date helpers
121
+ pip install dbtk[recommended]
122
+
123
+ # For reading/writing XML and Excel files
124
+ pip install dbtk[formats] # lxml and openpyxl
125
+
126
+ # Full functionality
127
+ pip install dbtk[all] # all optional dependencies - database adapters
128
+
129
+ # Database adapters (install as needed)
130
+ pip install psycopg2 # PostgreSQL
131
+ pip install oracledb # Oracle
132
+ pip install mysqlclient # MySQL
133
+ ```
134
+
135
+ ## Quick Start
136
+
137
+ ### Sample Outbound Integration - Export Data
138
+
139
+ Extract data from your database and export to multiple formats with portable SQL queries:
140
+
141
+ ```python
142
+ import dbtk
143
+
144
+ # One-line setup creates timestamped log - all operations automatically logged
145
+ dbtk.setup_logging()
146
+
147
+ with dbtk.connect('fire_nation_db') as db:
148
+ cursor = db.cursor()
149
+
150
+ # SQL with named parameters - works on ANY database
151
+ # Supports both :named and %(pyformat)s parameter styles!
152
+ params = {
153
+ 'min_rank': 'Captain',
154
+ 'start_date': '2024-01-01',
155
+ 'region': 'Western Fleet',
156
+ 'status': 'active'
157
+ }
158
+
159
+ # DBTK transforms the query and parameters to match your database's style
160
+ cursor.execute_file('queries/monthly_report.sql', params)
161
+ monthly_data = cursor.fetchall()
162
+
163
+ cursor.execute_file('queries/officer_summary.sql', params)
164
+ summary_data = cursor.fetchall()
165
+
166
+ # Export to multiple formats trivially
167
+ dbtk.writers.to_csv(monthly_data, 'reports/soldiers_monthly.csv')
168
+ dbtk.writers.to_excel(summary_data, 'reports/officer_summary.xlsx',
169
+ sheet='Officer Stats')
170
+
171
+ # Check for errors
172
+ if dbtk.errors_logged():
173
+ print("⚠️ Export completed with errors - check log file")
174
+ ```
175
+
176
+ **What makes this easy:**
177
+ - Write SQL once with named (`:param`) or pyformat (`%(param)s`) parameters - works on any database
178
+ - Pass the same dict to multiple queries - extra parameters are ignored, missing params are set to NULL
179
+ - DBTK handles parameter conversion automatically - no manual string formatting needed
180
+ - Export to CSV/Excel/JSON/NDJSON/XML with one line of code
181
+
182
+ ### Sample Inbound Integration - Import Data
183
+
184
+ Import data with field mapping, transformations, and validation:
185
+
186
+ ```python
187
+ import dbtk
188
+ from dbtk.etl import Table
189
+ from dbtk.etl.transforms import email_clean
190
+
191
+ dbtk.setup_logging() # Timestamped logs with auto-cleanup
192
+
193
+ with dbtk.connect('fire_nation_db') as db:
194
+ cursor = db.cursor()
195
+
196
+ # Define table schema with field mapping and transformations
197
+ soldier_table = Table('soldiers', {
198
+ 'soldier_id': {'field': 'id', 'key': True}, # Maps CSV 'id' to DB 'soldier_id', marks as primary key
199
+ 'name': {'field': 'full_name', 'nullable': False}, # Required field, will error if missing
200
+ 'rank': {'field': 'officer_rank', 'nullable': False,
201
+ 'fn': 'validate:ranks:rank_code:preload'}, # Validates against 'ranks' table, preloads cache
202
+ 'email': {'field': 'contact_email', 'default': 'intel@firenation.com',
203
+ 'fn': email_clean}, # Cleans/validates email, uses default if missing
204
+ 'enlistment_date': {'field': 'join_date', 'fn': 'date'}, # Parses various date formats
205
+ 'missions_completed': {'field': 'mission_count', 'fn': 'int'}, # Converts to integer, NULL if fails
206
+ 'status': {'default': 'active'} # Sets default, no source field needed
207
+ }, cursor=cursor)
208
+
209
+ # Process incoming compressed CSV
210
+ with dbtk.readers.get_reader('incoming/new_recruits.csv.gz') as reader: # Auto-detects .gz, decompresses
211
+ # DataSurge batches inserts, uses fastest method for this database driver
212
+ surge = dbtk.etl.DataSurge(soldier_table, use_transaction=True) # Wraps in transaction
213
+ surge.insert(reader) # Auto-shows progress bar for large files
214
+
215
+ if dbtk.errors_logged(): # Check global error flag
216
+ # send notification email or call 911
217
+ print("⚠️ Export completed with errors - check log file")
218
+ ```
219
+
220
+ **What makes this easy:**
221
+ - Field mapping separates database schema from source data format - change one without touching the other
222
+ - Built-in transforms (dates, emails, integers) with string shorthand - `'fn': 'date'` instead of importing functions
223
+ - Table class auto-validates required data before operations - no silent failures or cryptic database errors
224
+ - Built-in table lookups and validation with deferred cursor binding and intelligent caching
225
+ - Readers auto-detect file size and show progress on large files - never wonder if your pipeline has stalled
226
+ - Automatic statistics tracking - records processed, skipped, inserted, etc.
227
+ - Automatic logging with sensible global defaults - override per-pipeline when needed
228
+ - Error tracking built-in - `dbtk.errors_logged()` tells you if anything went wrong
229
+
230
+ ## The Record Class
231
+
232
+ Every cursor query and file reader in DBTK returns **Record** objects - a hybrid data structure that works like a dict, tuple, and object simultaneously.
233
+
234
+ **Why not just use dicts?** Dicts are optimized for n=1: one object with many keys you look up dynamically. But ETL pipelines process hundreds of thousands or millions of rows, all with the same columns. Record stores column names once on a shared class, not on every row - giving you dict-like flexibility with tuple-like memory efficiency.
235
+
236
+ ```python
237
+ for row in cursor:
238
+ row['name'] # Dict-style access
239
+ row.name # Attribute access
240
+ row[0] # Index access (dicts can't do this)
241
+ row[1:3] # Slicing (dicts can't do this)
242
+ id, name, email = row # Tuple unpacking (dicts can't do this)
243
+ row.get('phone', '') # Safe access with default
244
+ dict(row) # Convert to dict when needed
245
+ ```
246
+
247
+ **Normalized field names** let you write resilient code. Whether your source column is `Employee_ID`, `EMPLOYEE ID`, or `employee_id`, you can always access it as `row.employee_id`. This means your Table field mappings work regardless of how the source system names its columns.
248
+
249
+ See [Record Objects](docs/04-record.md) for complete documentation.
250
+
251
+ ## Documentation
252
+
253
+ ### Getting Started
254
+ - **[Getting Started Guide](docs/01-getting-started.md)** - 5-minute tutorial with complete examples
255
+ - **[API Reference](docs/11-api-reference.md)** - Complete method and function reference
256
+
257
+ ### Core Features
258
+ - **[Record Objects](docs/04-record.md)** - DBTK's universal data structure with dict, tuple, and attribute access
259
+ - **[Configuration & Security](docs/02-configuration.md)** - Set up encrypted passwords, YAML config files, and command-line tools
260
+ - **[Database Connections](docs/03-database-connections.md)** - Connect to any database, use smart cursors, SQL file execution, manage transactions
261
+ - **[File Readers](docs/05-readers.md)** - Read from CSV, Excel, JSON, XML, and fixed-width files
262
+ - **[Data Writers](docs/06-writers.md)** - Write to CSV, Excel, JSON, XML, fixed-width files, and between databases
263
+
264
+ ### ETL Framework
265
+ - **[ETL: Table & Transforms](docs/07-table.md)** - Field mapping, column config, data transforms, database lookups
266
+ - **[ETL: DataSurge & BulkSurge](docs/08-datasurge.md)** - High-performance bulk loading for any database
267
+ - **[ETL: Tools & Logging](docs/09-etl-tools.md)** - IdentityManager, ValidationCollector, and integration script logging
268
+
269
+ ### Advanced Topics
270
+ - **[Advanced Features](docs/10-advanced.md)** - Custom drivers, multiple config locations, and performance tuning
271
+ - **[Troubleshooting](docs/12-troubleshooting.md)** - Common issues and solutions
272
+
273
+ ## Performance Highlights
274
+
275
+ **Driver optimizations enabled automatically** - If your database driver supports faster batch operations (psycopg2, pyodbc), DBTK detects and uses them automatically.
276
+
277
+ Real-world benchmarks from production systems:
278
+
279
+ - **DataFrameReader**: 1.3M rec/s reading compressed CSV with polars + transforms
280
+ - **BulkSurge (Postgres/Oracle)**: 200-300K rec/s transforming, validating, and bulk loading
281
+ - **DataSurge (Oracle/SQL Server/MySQL)**: 90-150K rec/s with native executemany
282
+ - **IMDB Dataset**: 130K rec/s loading 12M titles with transforms and validation
283
+ - **Examples**: See the Examples folder for scripts you can run against the IMDB Dataset
284
+
285
+ These aren't toy benchmarks - they're real ETL pipelines with field mapping, data validation, type conversions, and database constraints. See the examples in the example folder.
286
+
287
+ ## License
288
+
289
+ MIT License - see LICENSE file for details.
290
+
291
+ ## Acknowledgments
292
+
293
+ Documentation, testing and architectural improvements assisted by [Claude](https://claude.ai) (Anthropic).
294
+ Architectural review and witty banter by [Grok](https://grok.com/) (xAI).
295
+ DataBender imagery [ChatGPT](https://chatgpt.com/) (OpenAI).
296
+
297
+ ## Support
298
+
299
+ - **Issues**: [GitHub Issues](https://github.com/yourusername/dbtk/issues)
300
+ - **Discussions**: [GitHub Discussions](https://github.com/yourusername/dbtk/discussions)
301
+ - **Documentation**: [Full Documentation](https://dbtk.readthedocs.io)
302
+
303
+ ## Contributing
304
+
305
+ Contributions are welcome! Please feel free to submit a Pull Request.
dbtk-0.8.0/README.md ADDED
@@ -0,0 +1,236 @@
1
+ # DBTK - Data Benders Toolkit
2
+
3
+ <div style="float: right; padding: 20px">
4
+ <img src="/docs/assets/databender.png" height="240" align="right" />
5
+ </div>
6
+
7
+ **Control and Manipulate the Flow of Data** - A lightweight Python toolkit for data integration, transformation, and movement between systems.
8
+
9
+ Like the elemental benders of Avatar, this library gives you precise control over data, the world's most rapidly growing element.
10
+ Extract data from various sources, transform it through powerful operations, and load it exactly where it needs to go.
11
+ This library is designed by and for data integrators.
12
+
13
+ **DBTK aims to be fast and memory-efficient at every turn.** But it was designed to boost your productivity first and foremost.
14
+ You have dozens (possibly hundreds) of interfaces, impossible deadlines, and multiple projects all happening at once. Your
15
+ environment has three or more different relational databases. You just want to get stuff done instead of writing the same
16
+ boilerplate code over and over or stressing over differences on a database server you don't use very often.
17
+
18
+ **Design philosophy:** Modern databases excel at aggregating and transforming data at scale. DBTK embraces
19
+ this by focusing on what Python does well: flexible record-by-record transformations,
20
+ connecting disparate systems, and orchestrating data movement.
21
+
22
+ If you need to pivot, aggregate, or perform complex SQL operations - write SQL and let
23
+ your database handle it. If you need dataframes and heavy analytics - reach for Pandas
24
+ or polars. DBTK sits in between: getting your data where it needs to be, cleaned and
25
+ validated along the way.
26
+
27
+ **Speed and Memory** The primary objective of DBTK is to give data integrators an elegant toolkit to speed up your development.
28
+ But DBTK's throughput and memory usage are very good. BulkSurge streaming from a polars and doing direct loads to PostgreSQL will
29
+ process 1M rows in 3-4 seconds. But even with a standard Python csv reader and numerous column transforms, DataSurge is able to
30
+ write 1M rows to every supported database in 5-10 seconds.
31
+
32
+ ## Features
33
+
34
+ - **Universal Database Connectivity** - Unified interface across PostgreSQL, Oracle, MySQL, SQL Server, and SQLite with intelligent driver auto-detection
35
+ - **Portable SQL Queries** - Write SQL once with named parameters, runs on any database regardless of parameter style
36
+ - **Smart Cursors** - All cursors and readers return Record objects with the speed and efficiency of tuples and the flexibility of dicts
37
+ - **Flexible File Reading** - CSV, Excel (XLS/XLSX), JSON, NDJSON, XML, DataFrame and fixed-width text files with consistent API
38
+ - **Transparent Compression** - Automatic decompression of .gz, .bz2, .xz, and .zip files with smart member selection
39
+ - **Multiple Export Formats** - Write to CSV, Excel, JSON, NDJSON, XML, fixed-width text, or directly between databases
40
+ - **Advanced ETL Framework** - Full-featured Table class for complex data transformations, validations, and upserts
41
+ - **Data Transformations** - Built-in functions for dates, phones, emails, and custom data cleaning with international support
42
+ - **High-Performance Bulk Operations** - DataSurge for blazing-fast batch operations; BulkSurge for even faster direct loading when supported
43
+ - **Integrated Logging** - Timestamped log files with automatic cleanup, split error logs, and zero-config setup
44
+ - **Encrypted Configuration** - YAML-based config with password encryption and environment variable support
45
+
46
+ ## Installation
47
+
48
+ ```bash
49
+ pip install dbtk
50
+
51
+ # installs keyring, lxml, openpyxl, phone address and date helpers
52
+ pip install dbtk[recommended]
53
+
54
+ # For reading/writing XML and Excel files
55
+ pip install dbtk[formats] # lxml and openpyxl
56
+
57
+ # Full functionality
58
+ pip install dbtk[all] # all optional dependencies - database adapters
59
+
60
+ # Database adapters (install as needed)
61
+ pip install psycopg2 # PostgreSQL
62
+ pip install oracledb # Oracle
63
+ pip install mysqlclient # MySQL
64
+ ```
65
+
66
+ ## Quick Start
67
+
68
+ ### Sample Outbound Integration - Export Data
69
+
70
+ Extract data from your database and export to multiple formats with portable SQL queries:
71
+
72
+ ```python
73
+ import dbtk
74
+
75
+ # One-line setup creates timestamped log - all operations automatically logged
76
+ dbtk.setup_logging()
77
+
78
+ with dbtk.connect('fire_nation_db') as db:
79
+ cursor = db.cursor()
80
+
81
+ # SQL with named parameters - works on ANY database
82
+ # Supports both :named and %(pyformat)s parameter styles!
83
+ params = {
84
+ 'min_rank': 'Captain',
85
+ 'start_date': '2024-01-01',
86
+ 'region': 'Western Fleet',
87
+ 'status': 'active'
88
+ }
89
+
90
+ # DBTK transforms the query and parameters to match your database's style
91
+ cursor.execute_file('queries/monthly_report.sql', params)
92
+ monthly_data = cursor.fetchall()
93
+
94
+ cursor.execute_file('queries/officer_summary.sql', params)
95
+ summary_data = cursor.fetchall()
96
+
97
+ # Export to multiple formats trivially
98
+ dbtk.writers.to_csv(monthly_data, 'reports/soldiers_monthly.csv')
99
+ dbtk.writers.to_excel(summary_data, 'reports/officer_summary.xlsx',
100
+ sheet='Officer Stats')
101
+
102
+ # Check for errors
103
+ if dbtk.errors_logged():
104
+ print("⚠️ Export completed with errors - check log file")
105
+ ```
106
+
107
+ **What makes this easy:**
108
+ - Write SQL once with named (`:param`) or pyformat (`%(param)s`) parameters - works on any database
109
+ - Pass the same dict to multiple queries - extra parameters are ignored, missing params are set to NULL
110
+ - DBTK handles parameter conversion automatically - no manual string formatting needed
111
+ - Export to CSV/Excel/JSON/NDJSON/XML with one line of code
112
+
113
+ ### Sample Inbound Integration - Import Data
114
+
115
+ Import data with field mapping, transformations, and validation:
116
+
117
+ ```python
118
+ import dbtk
119
+ from dbtk.etl import Table
120
+ from dbtk.etl.transforms import email_clean
121
+
122
+ dbtk.setup_logging() # Timestamped logs with auto-cleanup
123
+
124
+ with dbtk.connect('fire_nation_db') as db:
125
+ cursor = db.cursor()
126
+
127
+ # Define table schema with field mapping and transformations
128
+ soldier_table = Table('soldiers', {
129
+ 'soldier_id': {'field': 'id', 'key': True}, # Maps CSV 'id' to DB 'soldier_id', marks as primary key
130
+ 'name': {'field': 'full_name', 'nullable': False}, # Required field, will error if missing
131
+ 'rank': {'field': 'officer_rank', 'nullable': False,
132
+ 'fn': 'validate:ranks:rank_code:preload'}, # Validates against 'ranks' table, preloads cache
133
+ 'email': {'field': 'contact_email', 'default': 'intel@firenation.com',
134
+ 'fn': email_clean}, # Cleans/validates email, uses default if missing
135
+ 'enlistment_date': {'field': 'join_date', 'fn': 'date'}, # Parses various date formats
136
+ 'missions_completed': {'field': 'mission_count', 'fn': 'int'}, # Converts to integer, NULL if fails
137
+ 'status': {'default': 'active'} # Sets default, no source field needed
138
+ }, cursor=cursor)
139
+
140
+ # Process incoming compressed CSV
141
+ with dbtk.readers.get_reader('incoming/new_recruits.csv.gz') as reader: # Auto-detects .gz, decompresses
142
+ # DataSurge batches inserts, uses fastest method for this database driver
143
+ surge = dbtk.etl.DataSurge(soldier_table, use_transaction=True) # Wraps in transaction
144
+ surge.insert(reader) # Auto-shows progress bar for large files
145
+
146
+ if dbtk.errors_logged(): # Check global error flag
147
+ # send notification email or call 911
148
+ print("⚠️ Export completed with errors - check log file")
149
+ ```
150
+
151
+ **What makes this easy:**
152
+ - Field mapping separates database schema from source data format - change one without touching the other
153
+ - Built-in transforms (dates, emails, integers) with string shorthand - `'fn': 'date'` instead of importing functions
154
+ - Table class auto-validates required data before operations - no silent failures or cryptic database errors
155
+ - Built-in table lookups and validation with deferred cursor binding and intelligent caching
156
+ - Readers auto-detect file size and show progress on large files - never wonder if your pipeline has stalled
157
+ - Automatic statistics tracking - records processed, skipped, inserted, etc.
158
+ - Automatic logging with sensible global defaults - override per-pipeline when needed
159
+ - Error tracking built-in - `dbtk.errors_logged()` tells you if anything went wrong
160
+
161
+ ## The Record Class
162
+
163
+ Every cursor query and file reader in DBTK returns **Record** objects - a hybrid data structure that works like a dict, tuple, and object simultaneously.
164
+
165
+ **Why not just use dicts?** Dicts are optimized for n=1: one object with many keys you look up dynamically. But ETL pipelines process hundreds of thousands or millions of rows, all with the same columns. Record stores column names once on a shared class, not on every row - giving you dict-like flexibility with tuple-like memory efficiency.
166
+
167
+ ```python
168
+ for row in cursor:
169
+ row['name'] # Dict-style access
170
+ row.name # Attribute access
171
+ row[0] # Index access (dicts can't do this)
172
+ row[1:3] # Slicing (dicts can't do this)
173
+ id, name, email = row # Tuple unpacking (dicts can't do this)
174
+ row.get('phone', '') # Safe access with default
175
+ dict(row) # Convert to dict when needed
176
+ ```
177
+
178
+ **Normalized field names** let you write resilient code. Whether your source column is `Employee_ID`, `EMPLOYEE ID`, or `employee_id`, you can always access it as `row.employee_id`. This means your Table field mappings work regardless of how the source system names its columns.
179
+
180
+ See [Record Objects](docs/04-record.md) for complete documentation.
181
+
182
+ ## Documentation
183
+
184
+ ### Getting Started
185
+ - **[Getting Started Guide](docs/01-getting-started.md)** - 5-minute tutorial with complete examples
186
+ - **[API Reference](docs/11-api-reference.md)** - Complete method and function reference
187
+
188
+ ### Core Features
189
+ - **[Record Objects](docs/04-record.md)** - DBTK's universal data structure with dict, tuple, and attribute access
190
+ - **[Configuration & Security](docs/02-configuration.md)** - Set up encrypted passwords, YAML config files, and command-line tools
191
+ - **[Database Connections](docs/03-database-connections.md)** - Connect to any database, use smart cursors, SQL file execution, manage transactions
192
+ - **[File Readers](docs/05-readers.md)** - Read from CSV, Excel, JSON, XML, and fixed-width files
193
+ - **[Data Writers](docs/06-writers.md)** - Write to CSV, Excel, JSON, XML, fixed-width files, and between databases
194
+
195
+ ### ETL Framework
196
+ - **[ETL: Table & Transforms](docs/07-table.md)** - Field mapping, column config, data transforms, database lookups
197
+ - **[ETL: DataSurge & BulkSurge](docs/08-datasurge.md)** - High-performance bulk loading for any database
198
+ - **[ETL: Tools & Logging](docs/09-etl-tools.md)** - IdentityManager, ValidationCollector, and integration script logging
199
+
200
+ ### Advanced Topics
201
+ - **[Advanced Features](docs/10-advanced.md)** - Custom drivers, multiple config locations, and performance tuning
202
+ - **[Troubleshooting](docs/12-troubleshooting.md)** - Common issues and solutions
203
+
204
+ ## Performance Highlights
205
+
206
+ **Driver optimizations enabled automatically** - If your database driver supports faster batch operations (psycopg2, pyodbc), DBTK detects and uses them automatically.
207
+
208
+ Real-world benchmarks from production systems:
209
+
210
+ - **DataFrameReader**: 1.3M rec/s reading compressed CSV with polars + transforms
211
+ - **BulkSurge (Postgres/Oracle)**: 200-300K rec/s transforming, validating, and bulk loading
212
+ - **DataSurge (Oracle/SQL Server/MySQL)**: 90-150K rec/s with native executemany
213
+ - **IMDB Dataset**: 130K rec/s loading 12M titles with transforms and validation
214
+ - **Examples**: See the Examples folder for scripts you can run against the IMDB Dataset
215
+
216
+ These aren't toy benchmarks - they're real ETL pipelines with field mapping, data validation, type conversions, and database constraints. See the examples in the example folder.
217
+
218
+ ## License
219
+
220
+ MIT License - see LICENSE file for details.
221
+
222
+ ## Acknowledgments
223
+
224
+ Documentation, testing and architectural improvements assisted by [Claude](https://claude.ai) (Anthropic).
225
+ Architectural review and witty banter by [Grok](https://grok.com/) (xAI).
226
+ DataBender imagery [ChatGPT](https://chatgpt.com/) (OpenAI).
227
+
228
+ ## Support
229
+
230
+ - **Issues**: [GitHub Issues](https://github.com/yourusername/dbtk/issues)
231
+ - **Discussions**: [GitHub Discussions](https://github.com/yourusername/dbtk/discussions)
232
+ - **Documentation**: [Full Documentation](https://dbtk.readthedocs.io)
233
+
234
+ ## Contributing
235
+
236
+ Contributions are welcome! Please feel free to submit a Pull Request.
@@ -0,0 +1,54 @@
1
+ # dbtk/__init__.py
2
+ """
3
+ DBTK - Data Benders ToolKit
4
+
5
+ A lightweight database integration toolkit that provides:
6
+ - Uniform interface across different databases (PostgreSQL, Oracle, MySQL, SQL Server, SQLite)
7
+ - Flexible cursor types returning different data structures
8
+ - YAML-based configuration with password encryption
9
+ - Writers for CSV, Excel, fixed-width, and database-to-database export
10
+ - Context managers for connections and transactions
11
+
12
+ Basic usage::
13
+
14
+ import dbtk
15
+
16
+ # From YAML config file
17
+ with dbtk.connect('prod_warehouse') as db:
18
+ cursor = db.cursor()
19
+ cursor.execute("SELECT * FROM users")
20
+
21
+ # Export results
22
+ dbtk.writers.to_csv(cursor, 'users.csv')
23
+ dbtk.writers.to_excel(cursor, 'report.xlsx')
24
+
25
+ Direct connections:
26
+ from dbtk.database import postgres, oracle
27
+
28
+ db = postgres(user='user', password='pass', database='db')
29
+ cursor = db.cursor() # Returns Record objects
30
+ """
31
+
32
+ __version__ = '0.8.0'
33
+ __author__ = 'Scott Bailey <scottrbailey@gmail.com>'
34
+
35
+ from .database import Database
36
+ from .config import connect, set_config_file
37
+ from .cursors import Cursor
38
+ from .logging_utils import setup_logging, cleanup_old_logs, errors_logged
39
+ from . import etl
40
+ from . import readers
41
+ from . import writers
42
+
43
+ # Simple, clean exports
44
+ __all__ = [
45
+ 'connect',
46
+ 'Database',
47
+ 'Cursor',
48
+ 'etl',
49
+ 'readers',
50
+ 'writers',
51
+ 'setup_logging',
52
+ 'cleanup_old_logs',
53
+ 'errors_logged'
54
+ ]