ping-dataexport 0.2.2__tar.gz → 0.3.5__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.
- {ping_dataexport-0.2.2/src/ping_dataexport.egg-info → ping_dataexport-0.3.5}/PKG-INFO +414 -399
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/README.md +34 -19
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/pyproject.toml +1 -1
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/setup.cfg +4 -4
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/__init__.py +1 -1
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/cli.py +4 -3
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/job/validator.py +3 -3
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5/src/ping_dataexport.egg-info}/PKG-INFO +414 -399
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/LICENSE +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/MANIFEST.in +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/config.sample.ini +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/job.sample.ini +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/__config__.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/__main__.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/api.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/applogger.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/config.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/db/__init__.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/db/connector.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/db/oracle_connector.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/db/pyodbc_connector.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/db/sqlite_connector.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/db/type_map.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/errors.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/export/__init__.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/export/engine.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/export/options.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/export/runner.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/export/util.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/job/__init__.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/job/executor.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/job/shared_context.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/job/spec.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/paths.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/planner/__init__.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/planner/base.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/planner/datelist.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/planner/groupby.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/planner/monthbydate.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/planner/single.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/planner/watermark.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/writer/__init__.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/writer/arrow_map.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/writer/base.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/writer/delimited.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport/writer/parquet.py +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport.egg-info/SOURCES.txt +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport.egg-info/dependency_links.txt +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport.egg-info/entry_points.txt +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport.egg-info/requires.txt +0 -0
- {ping_dataexport-0.2.2 → ping_dataexport-0.3.5}/src/ping_dataexport.egg-info/top_level.txt +0 -0
|
@@ -1,399 +1,414 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ping-dataexport
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: PING's Data Export Tool - To Parquet/CSV/TEXT as Accelerator for Data & AI Project
|
|
5
|
-
Author: vorapol
|
|
6
|
-
License-Expression: Apache-2.0
|
|
7
|
-
Keywords: export,database,csv,parquet,etl,data-platform,airflow
|
|
8
|
-
Classifier: Development Status :: 4 - Beta
|
|
9
|
-
Classifier: Environment :: Console
|
|
10
|
-
Classifier: Intended Audience :: Developers
|
|
11
|
-
Classifier: Operating System :: OS Independent
|
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Topic :: Database
|
|
14
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
-
Requires-Python: >=3.9
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
License-File: LICENSE
|
|
18
|
-
Requires-Dist: pyarrow>=18
|
|
19
|
-
Provides-Extra: odbc
|
|
20
|
-
Requires-Dist: pyodbc>=5; extra == "odbc"
|
|
21
|
-
Provides-Extra: oracle
|
|
22
|
-
Requires-Dist: oracledb>=2; extra == "oracle"
|
|
23
|
-
Provides-Extra: drivers
|
|
24
|
-
Requires-Dist: pyodbc>=5; extra == "drivers"
|
|
25
|
-
Requires-Dist: oracledb>=2; extra == "drivers"
|
|
26
|
-
Provides-Extra: dev
|
|
27
|
-
Requires-Dist: pytest>=8; extra == "dev"
|
|
28
|
-
Dynamic: license-file
|
|
29
|
-
|
|
30
|
-
# ping-dataexport
|
|
31
|
-
|
|
32
|
-
## PING (ping-godhand)'s Universal Data Exporter
|
|
33
|
-
|
|
34
|
-
> **Provided as-is. No support, no warranty, no roadmap commitments, use at
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
pip install ping-dataexport
|
|
72
|
-
pip install ping-dataexport[
|
|
73
|
-
pip install ping-dataexport[
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
ping-dataexport -s SQLITE01 -q "SELECT * FROM sales" -o sales.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
| `-
|
|
170
|
-
| `-
|
|
171
|
-
| `-
|
|
172
|
-
| `-
|
|
173
|
-
| `-
|
|
174
|
-
| `-
|
|
175
|
-
| `-
|
|
176
|
-
| `-
|
|
177
|
-
| `-
|
|
178
|
-
| `-
|
|
179
|
-
| `-
|
|
180
|
-
| `-
|
|
181
|
-
| `-
|
|
182
|
-
| `-
|
|
183
|
-
| `-
|
|
184
|
-
| `-
|
|
185
|
-
| `-
|
|
186
|
-
| `-
|
|
187
|
-
| `-
|
|
188
|
-
| `-
|
|
189
|
-
| `-
|
|
190
|
-
| `-
|
|
191
|
-
| `-
|
|
192
|
-
| `-
|
|
193
|
-
| `-
|
|
194
|
-
| `-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
+ `-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
```
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
├──
|
|
225
|
-
├──
|
|
226
|
-
├──
|
|
227
|
-
├──
|
|
228
|
-
└──
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
**`
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
├── sales_2026-01-
|
|
241
|
-
├── sales_2026-01-
|
|
242
|
-
├── sales_2026-01-
|
|
243
|
-
└── sales_2026-01-
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
**`
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
ping-dataexport -s DB01 -q "SELECT * FROM sales" -m
|
|
251
|
-
-df 2026-
|
|
252
|
-
```
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
├──
|
|
256
|
-
├──
|
|
257
|
-
├──
|
|
258
|
-
└──
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
**`
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
|
347
|
-
|
|
348
|
-
| `
|
|
349
|
-
| `
|
|
350
|
-
| `
|
|
351
|
-
| `
|
|
352
|
-
| `
|
|
353
|
-
| `
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ping-dataexport
|
|
3
|
+
Version: 0.3.5
|
|
4
|
+
Summary: PING's Data Export Tool - To Parquet/CSV/TEXT as Accelerator for Data & AI Project
|
|
5
|
+
Author: vorapol
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Keywords: export,database,csv,parquet,etl,data-platform,airflow
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Database
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: pyarrow>=18
|
|
19
|
+
Provides-Extra: odbc
|
|
20
|
+
Requires-Dist: pyodbc>=5; extra == "odbc"
|
|
21
|
+
Provides-Extra: oracle
|
|
22
|
+
Requires-Dist: oracledb>=2; extra == "oracle"
|
|
23
|
+
Provides-Extra: drivers
|
|
24
|
+
Requires-Dist: pyodbc>=5; extra == "drivers"
|
|
25
|
+
Requires-Dist: oracledb>=2; extra == "drivers"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# ping-dataexport
|
|
31
|
+
|
|
32
|
+
## PING (ping-godhand)'s Universal Data Exporter
|
|
33
|
+
|
|
34
|
+
> **Provided as-is. No support, no warranty, no roadmap commitments, use at your own risk.**
|
|
35
|
+
|
|
36
|
+
## คำอธิบาย (ภาษาไทย)
|
|
37
|
+
|
|
38
|
+
โปรแกรมช่วย Export ข้อมูลอย่างง่าย: เชื่อมต่อฐานข้อมูล รันคำสั่ง SQL
|
|
39
|
+
แล้ว Export ผลลัพธ์ออกเป็นไฟล์ **CSV / TXT / Parquet** สร้างมาเพื่องาน Data Platform
|
|
40
|
+
โดยเฉพาะ — ส่งออกข้อมูลจากฐานข้อมูล ให้เป็นไฟล์ที่ Data Lake
|
|
41
|
+
หรือ Data Warehouse นำไปใช้ต่อได้ทันที
|
|
42
|
+
|
|
43
|
+
- **ความถูกต้องของชนิดข้อมูลมาก่อนเสมอ** — ข้อมูลจาก cursor → writer
|
|
44
|
+
ทีละ Row ด้วย Data Type ดั้งเดิมเสมอเช่น: `DECIMAL(p,s)` ลงใน Parquet เป็น
|
|
45
|
+
`decimal128(p,s)`, คอลัมน์ int ที่มี NULL ยังคงเป็น int, วันที่ยังคงเป็นวันที่
|
|
46
|
+
และไม่ใช้ pandas เด็ดขาด
|
|
47
|
+
- **ฐานข้อมูลที่รองรับ** — SQLite (Library มาตรฐาน ไม่ต้องติดตั้งอะไรเพิ่ม),
|
|
48
|
+
SQL Server (MS ODBC / FreeTDS), Oracle (โหมด thin แบบ Native หรือ ODBC)
|
|
49
|
+
และแหล่งข้อมูล ODBC ใดก็ได้
|
|
50
|
+
- **ใช้งานได้สองทาง** — ผ่าน CLI `ping-dataexport` หรือผ่าน Library API (`ping_dataexport.api`)
|
|
51
|
+
ที่ออกแบบมาสำหรับ Airflow
|
|
52
|
+
|
|
53
|
+
## Description (English)
|
|
54
|
+
|
|
55
|
+
Universal database data export: connect to a database, run a SQL query, stream
|
|
56
|
+
the result to **CSV / TXT / Parquet**. Built for data-platform work — exporting
|
|
57
|
+
legacy databases into files a data lake or warehouse can ingest.
|
|
58
|
+
|
|
59
|
+
- **Type fidelity first** — rows stream cursor → writer with native driver
|
|
60
|
+
types. `DECIMAL(p,s)` lands in Parquet as `decimal128(p,s)`, ints with NULLs
|
|
61
|
+
stay ints, dates stay dates. No pandas, ever.
|
|
62
|
+
- **Databases** — SQLite (stdlib, zero setup), SQL Server (MS ODBC / FreeTDS),
|
|
63
|
+
Oracle (native thin mode or ODBC), any ODBC source.
|
|
64
|
+
- **Two front doors** — a CLI and a library API (`ping_dataexport.api`) built
|
|
65
|
+
for Airflow: exceptions instead of exit codes, results as objects.
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install ping-dataexport # base (pyarrow) — sqlite works out of the box
|
|
71
|
+
pip install ping-dataexport[odbc] # + pyodbc (SQL Server, FreeTDS, ODBC)
|
|
72
|
+
pip install ping-dataexport[oracle] # + oracledb (Oracle native)
|
|
73
|
+
pip install ping-dataexport[drivers] # + both
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 1. Create a config.ini
|
|
77
|
+
|
|
78
|
+
One `[Main]` section for defaults, one section per database source:
|
|
79
|
+
|
|
80
|
+
```ini
|
|
81
|
+
[Main]
|
|
82
|
+
ExportFolder=./output ; relative output/log paths resolve under this
|
|
83
|
+
FetchSize=10000
|
|
84
|
+
ForceMakeDir=Yes
|
|
85
|
+
RowIDColumn=row_id ; optional audit column names
|
|
86
|
+
DataDateColumn=data_date ; optional audit data extract date
|
|
87
|
+
|
|
88
|
+
[SQLITE01]
|
|
89
|
+
Type=sqlite
|
|
90
|
+
Database=./demo.sqlite
|
|
91
|
+
|
|
92
|
+
[DB01]
|
|
93
|
+
Type=mssql-odbc
|
|
94
|
+
Host=host
|
|
95
|
+
Port=1433
|
|
96
|
+
Database=DB
|
|
97
|
+
Username=user
|
|
98
|
+
Password=secret
|
|
99
|
+
|
|
100
|
+
[ORA01]
|
|
101
|
+
Type=oracle
|
|
102
|
+
Host=orahost
|
|
103
|
+
Port=1521
|
|
104
|
+
Service=ORCLPDB
|
|
105
|
+
Username=scott
|
|
106
|
+
Password=tiger
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
A full sample ships as `config.sample.ini` in the source distribution.
|
|
110
|
+
|
|
111
|
+
## 2. Config keys — type / possible values
|
|
112
|
+
|
|
113
|
+
`[Main]` (all optional; a missing `[Main]` yields the defaults):
|
|
114
|
+
|
|
115
|
+
| Key | Type | Possible values / default |
|
|
116
|
+
|-----|------|---------------------------|
|
|
117
|
+
| `ExportFolder` | path | base folder for relative output/log paths; default: current directory |
|
|
118
|
+
| `TempFolder` | path | reserved for a future release |
|
|
119
|
+
| `FetchSize` | int > 0 | rows per batch; default `50000` |
|
|
120
|
+
| `PreviewRow` | int > 0 | rows shown in console/test preview; default `10` |
|
|
121
|
+
| `ForceMakeDir` | bool | `Yes`/`No`, `True`/`False`, `1`/`0`, `on`/`off`; default `Yes` |
|
|
122
|
+
| `RowIDColumn` | string | audit column name (running row number); absent = no column |
|
|
123
|
+
| `DataDateColumn` | string | audit column name (data extract date); absent = no column |
|
|
124
|
+
| `LogFile` | path | default `{ExportFolder}/logs/log_{jobname}.txt` |
|
|
125
|
+
|
|
126
|
+
DB section (one per source; section name is what `-s` refers to):
|
|
127
|
+
|
|
128
|
+
| Key | Type | Possible values / notes |
|
|
129
|
+
|-----|------|--------------------------|
|
|
130
|
+
| `Type` | enum | `sqlite` \| `mssql-odbc` \| `freetds` \| `oracle` \| `oracle-odbc` \| `odbc`; omitted with `ConnectionString`/`DSN` present ⇒ `odbc` |
|
|
131
|
+
| `Name` | string | display name in logs; default: the section name |
|
|
132
|
+
| `ConnectionString` | string | raw ODBC connection string (alternative to Host/Port/Database) |
|
|
133
|
+
| `DSN` | string | ODBC data source name |
|
|
134
|
+
| `Host` / `Port` | string / int | server address |
|
|
135
|
+
| `Database` | string | database name; for `sqlite`: the file path |
|
|
136
|
+
| `Service` / `SID` | string | Oracle service name or SID |
|
|
137
|
+
| `Username` | string | login user |
|
|
138
|
+
| `Password` | string | plain-text password |
|
|
139
|
+
| `PasswordBase64` | string | base64-encoded password (obfuscation, not encryption); when both are set, `Password` wins |
|
|
140
|
+
| `Encoding` | string | e.g. `utf-8`, `tis-620` |
|
|
141
|
+
| `Driver` | string | ODBC driver name override, e.g. `ODBC Driver 18 for SQL Server` |
|
|
142
|
+
|
|
143
|
+
```ini
|
|
144
|
+
PasswordBase64=c2VjcmV0 ; base64("secret")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## 3. Run an export (CLI)
|
|
148
|
+
|
|
149
|
+
`ping-dataexport` and `python -m ping_dataexport` are equivalent. The config
|
|
150
|
+
file defaults to `./config.ini` (`-cf` to point elsewhere).
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# preview to the console (no -o/-dir): prints the first rows
|
|
154
|
+
ping-dataexport -s SQLITE01 -q "SELECT * FROM sales"
|
|
155
|
+
|
|
156
|
+
# single file — format inferred from the extension (.csv/.txt/.parquet)
|
|
157
|
+
ping-dataexport -s SQLITE01 -q "SELECT * FROM sales" -o sales.parquet
|
|
158
|
+
ping-dataexport -s SQLITE01 -q "SELECT * FROM sales" -o sales.csv -gz # gzip
|
|
159
|
+
|
|
160
|
+
# validate + preview only, write nothing
|
|
161
|
+
ping-dataexport -s SQLITE01 -q "SELECT * FROM sales" -o sales.csv -t
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
All options:
|
|
165
|
+
|
|
166
|
+
| Option | Full name | Meaning |
|
|
167
|
+
|--------|-----------|---------|
|
|
168
|
+
| `-s` | `--source` | DB config section name (e.g. `DB01`) |
|
|
169
|
+
| `-q` | `--query` | SQL query |
|
|
170
|
+
| `-o` | `--output` | single output file (format from extension, or `-f`) |
|
|
171
|
+
| `-dir` | `--outputdir` | output directory (required for `-m` modes); `-od` still works as a legacy alias |
|
|
172
|
+
| `-pf` | `--prefix` | output filename prefix for mode exports; `-bn`/`--basename` still work as legacy aliases |
|
|
173
|
+
| `-f` | `--format` | `csv`, `txt`, `parquet` |
|
|
174
|
+
| `-gz` | `--gzip` | gzip the output (CSV/TXT only) |
|
|
175
|
+
| `-sep` | `--sep` | field separator (default `,`; `\t` maps to tab) |
|
|
176
|
+
| `-quo` | `--quote` | quote character (default `"`) |
|
|
177
|
+
| `-stnl` | `--stripnewline` | newline handling in text columns: `space` \| `blank` \| `escape` \| `doubleescape` |
|
|
178
|
+
| `-stmc` | `--stripmetachar` | strip invisible/control characters from text columns |
|
|
179
|
+
| `-fs` | `--fetchsize` | fetch size (rows per batch) |
|
|
180
|
+
| `-m` | `--mode` | export mode: `groupby` \| `date` \| `relativedate` \| `monthbydate` \| `splitrow` \| `watermark` — see section 4 |
|
|
181
|
+
| `-col` | `--column` | column the mode partitions on |
|
|
182
|
+
| `-row` | `--row` | rows per file (`-m splitrow`) |
|
|
183
|
+
| `-df` | `--datefrom` | start / anchor date `yyyy-MM-dd`; omitted with `-dr`/`-mr` ⇒ today (system date) |
|
|
184
|
+
| `-dt` | `--dateto` | end date `yyyy-MM-dd` (`-m date`) |
|
|
185
|
+
| `-dr` | `--daysrelative` | days back from `-df` (`-m relativedate`) |
|
|
186
|
+
| `-mr` | `--monthsrelative` | months back from `-df` (`-m monthbydate`) |
|
|
187
|
+
| `-rm` | `--removeexisting` | `Yes`: clear the output directory before exporting |
|
|
188
|
+
| `-j` | `--job` | run one job.ini section — see section 5 |
|
|
189
|
+
| `-jf` | `--jobfile` | job file path (default `./job.ini`) |
|
|
190
|
+
| `-p` | `--parallel` | run ALL job sections in parallel with N workers (default: CPU count); `-pl` still works as a legacy alias |
|
|
191
|
+
| `-n` | `--name` | job name shown in logs and the default log filename |
|
|
192
|
+
| `-cf` | `--configfile` | config file path (default `./config.ini`) |
|
|
193
|
+
| `-lf` | `--logfile` | log file path |
|
|
194
|
+
| `-t` | `--test` | test mode: validate + preview, no files |
|
|
195
|
+
|
|
196
|
+
### Which options must go together
|
|
197
|
+
|
|
198
|
+
| If you use… | You must also have… | Notes |
|
|
199
|
+
|-------------|--------------------|-------|
|
|
200
|
+
| (anything) | `-s` + `-q` | the two basics — always required, unless `-j` supplies them from a job file |
|
|
201
|
+
| `-o` | nothing else | single file; **cannot** be combined with `-dir`/`-pf` |
|
|
202
|
+
| `-m` (any mode) | `-dir` + `-pf` | every mode writes multiple files, so it needs a directory + filename prefix |
|
|
203
|
+
| `-dir` or `-pf` | `-m` | they only make sense for mode exports |
|
|
204
|
+
| `-m groupby` / `watermark` | `-col` | the column to group / track |
|
|
205
|
+
| `-m date` | `-col` + `-df` + `-dt` | the date column and an explicit range |
|
|
206
|
+
| `-m relativedate` | `-col` + `-dr` | `-df` optional (defaults to today) |
|
|
207
|
+
| `-m monthbydate` | `-col` + `-mr` | `-df` optional (defaults to today) |
|
|
208
|
+
| `-m splitrow` | `-row` | no `-col` needed |
|
|
209
|
+
| `-gz` | CSV/TXT output | rejected with parquet (it compresses itself) |
|
|
210
|
+
| no `-o` and no `-dir` | — | console preview only (first `PreviewRow` rows, nothing written) |
|
|
211
|
+
|
|
212
|
+
## 4. Export modes (partitioned outputs)
|
|
213
|
+
|
|
214
|
+
Modes split one query into multiple files. All need `-dir` (output directory)
|
|
215
|
+
+ `-pf` (filename prefix); partition values are always bound SQL parameters.
|
|
216
|
+
|
|
217
|
+
**`groupby` — one file per distinct value of a column:**
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
ping-dataexport -s DB01 -q "SELECT * FROM sales" -m groupby -col region -dir by_region -pf sales
|
|
221
|
+
```
|
|
222
|
+
```
|
|
223
|
+
by_region/
|
|
224
|
+
├── sales_@@NULL@@.csv <- rows where region IS NULL
|
|
225
|
+
├── sales_MY.csv
|
|
226
|
+
├── sales_SG.csv
|
|
227
|
+
├── sales_TH.csv
|
|
228
|
+
└── sales_VN.csv
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**`date` — one file per day in a date range (inclusive):**
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
ping-dataexport -s DB01 -q "SELECT * FROM sales" -m date -col txn_date \
|
|
235
|
+
-df 2026-01-01 -dt 2026-01-05 -dir daily -pf sales
|
|
236
|
+
```
|
|
237
|
+
```
|
|
238
|
+
daily/
|
|
239
|
+
├── sales_2026-01-01.csv
|
|
240
|
+
├── sales_2026-01-02.csv
|
|
241
|
+
├── sales_2026-01-03.csv
|
|
242
|
+
├── sales_2026-01-04.csv
|
|
243
|
+
└── sales_2026-01-05.csv
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**`relativedate` — N days back from an anchor date, anchor included
|
|
247
|
+
(omit `-df` to anchor on today):**
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
ping-dataexport -s DB01 -q "SELECT * FROM sales" -m relativedate -col txn_date \
|
|
251
|
+
-df 2026-01-10 -dr 3 -dir rel -pf sales
|
|
252
|
+
```
|
|
253
|
+
```
|
|
254
|
+
rel/
|
|
255
|
+
├── sales_2026-01-07.csv
|
|
256
|
+
├── sales_2026-01-08.csv
|
|
257
|
+
├── sales_2026-01-09.csv
|
|
258
|
+
└── sales_2026-01-10.csv
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**`monthbydate` — N months back plus the anchor month (the anchor month stops
|
|
262
|
+
at the anchor date; omit `-df` to anchor on today):**
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
ping-dataexport -s DB01 -q "SELECT * FROM sales" -m monthbydate -col txn_date \
|
|
266
|
+
-df 2026-03-15 -mr 3 -dir monthly -pf sales
|
|
267
|
+
```
|
|
268
|
+
```
|
|
269
|
+
monthly/
|
|
270
|
+
├── sales_202512.csv
|
|
271
|
+
├── sales_202601.csv
|
|
272
|
+
├── sales_202602.csv
|
|
273
|
+
└── sales_202603.csv <- 2026-03-01 .. 2026-03-15 only
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**`splitrow` — rotate to a new file every N rows:**
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
ping-dataexport -s DB01 -q "SELECT * FROM sales ORDER BY id" -m splitrow -row 100000 -dir split -pf part
|
|
280
|
+
```
|
|
281
|
+
```
|
|
282
|
+
split/
|
|
283
|
+
├── part_1.csv <- 100,000 rows each
|
|
284
|
+
├── part_2.csv
|
|
285
|
+
└── part_3.csv <- remainder
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**`watermark` — incremental: only rows newer than the last run:**
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
ping-dataexport -s DB01 -q "SELECT * FROM sales" -m watermark -col txn_date -dir incr -pf sales
|
|
292
|
+
```
|
|
293
|
+
```
|
|
294
|
+
incr/
|
|
295
|
+
├── sales_20260831_120000.csv <- filename = run timestamp
|
|
296
|
+
└── __watermark__.ini <- state file: highest txn_date exported so far
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Run the watermark job again: no new rows → no new file; new rows → one more
|
|
300
|
+
timestamped file. Delete `__watermark__.ini` to start over from a full export.
|
|
301
|
+
|
|
302
|
+
Every run also prints structured log lines to the console (and to the log
|
|
303
|
+
file — see *Output and logs* below):
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
2026-08-31 11:24:41 | JOB_20260831_112441 | INFO | CONFIG | JOB | source=DB01 mode=groupby
|
|
307
|
+
2026-08-31 11:24:41 | JOB_20260831_112441 | INFO | EXPORT | PARTITION | region = 'TH'
|
|
308
|
+
2026-08-31 11:24:41 | JOB_20260831_112441 | INFO | EXPORT | FILE | ./output/by_region/sales_TH.csv
|
|
309
|
+
2026-08-31 11:24:41 | JOB_20260831_112441 | INFO | EXPORT | PROGRESS | 200 rows (finished)
|
|
310
|
+
2026-08-31 11:24:41 | JOB_20260831_112441 | INFO | RESULT | COMPLETED | 0.2 seconds
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## 5. Job files (job.ini)
|
|
314
|
+
|
|
315
|
+
Put recurring exports in a job file so you never retype long commands. Each
|
|
316
|
+
`[Section]` is **one job**: run one with `-j SectionName`, or all of them at
|
|
317
|
+
once with `-p`. Every key is simply a CLI option written as a config key:
|
|
318
|
+
|
|
319
|
+
```ini
|
|
320
|
+
[DailySales]
|
|
321
|
+
Source=DB01
|
|
322
|
+
Query=SELECT id, txn_date, amount FROM sales
|
|
323
|
+
WHERE region = 'TH'
|
|
324
|
+
Mode=date
|
|
325
|
+
Column=txn_date
|
|
326
|
+
DateFrom=2026-01-01
|
|
327
|
+
DateTo=2026-01-31
|
|
328
|
+
OutputDir=out/daily_sales
|
|
329
|
+
BaseName=sales
|
|
330
|
+
Format=csv
|
|
331
|
+
|
|
332
|
+
[FullDump]
|
|
333
|
+
Source=DB01
|
|
334
|
+
Query=SELECT * FROM sales
|
|
335
|
+
Output=out/full_dump.parquet
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
ping-dataexport -j DailySales # one section (job.ini by default, -jf elsewhere)
|
|
340
|
+
ping-dataexport -j DailySales -dir other_dir # CLI options override the section
|
|
341
|
+
ping-dataexport -p 4 # ALL sections in parallel, 4 workers
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Every job.ini key and the CLI option it mirrors (keys are case-insensitive):
|
|
345
|
+
|
|
346
|
+
| Job key | CLI option | Meaning |
|
|
347
|
+
|---------|------------|---------|
|
|
348
|
+
| `Source` | `-s` / `--source` | which config.ini DB section to export from |
|
|
349
|
+
| `Query` | `-q` / `--query` | the SQL to run; indent continuation lines for multi-line queries |
|
|
350
|
+
| `Output` | `-o` / `--output` | single output file path |
|
|
351
|
+
| `OutputDir` | `-dir` / `--outputdir` | output directory (needed when `Mode=` is set) |
|
|
352
|
+
| `BaseName` | `-pf` / `--prefix` | output filename prefix for mode exports |
|
|
353
|
+
| `Format` | `-f` / `--format` | `csv` / `txt` / `parquet` (else inferred from `Output` extension) |
|
|
354
|
+
| `Gzip` | `-gz` / `--gzip` | `Yes`/`No` — gzip the output (CSV/TXT only) |
|
|
355
|
+
| `Sep` | `-sep` / `--sep` | field separator (default `,`; `\t` = tab) |
|
|
356
|
+
| `Quote` | `-quo` / `--quote` | quote character (default `"`) |
|
|
357
|
+
| `StripNewLine` | `-stnl` / `--stripnewline` | `space` \| `blank` \| `escape` \| `doubleescape` |
|
|
358
|
+
| `StripMetaChar` | `-stmc` / `--stripmetachar` | `Yes`/`No` — strip invisible/control characters |
|
|
359
|
+
| `FetchSize` | `-fs` / `--fetchsize` | rows per batch |
|
|
360
|
+
| `Mode` | `-m` / `--mode` | `groupby` / `date` / `relativedate` / `monthbydate` / `splitrow` / `watermark` |
|
|
361
|
+
| `Column` | `-col` / `--column` | the column the mode partitions on |
|
|
362
|
+
| `Row` | `-row` / `--row` | rows per file (`Mode=splitrow`) |
|
|
363
|
+
| `DateFrom` | `-df` / `--datefrom` | start / anchor date, `yyyy-MM-dd` |
|
|
364
|
+
| `DateTo` | `-dt` / `--dateto` | end date, `yyyy-MM-dd` (`Mode=date`) |
|
|
365
|
+
| `DaysRelative` | `-dr` / `--daysrelative` | days back from `DateFrom` (`Mode=relativedate`) |
|
|
366
|
+
| `MonthsRelative` | `-mr` / `--monthsrelative` | months back from `DateFrom` (`Mode=monthbydate`) |
|
|
367
|
+
| `RemoveExisting` | `-rm` / `--removeexisting` | `Yes`: clear `OutputDir` before exporting |
|
|
368
|
+
| `Name` | `-n` / `--name` | job name shown in logs; default: the section name |
|
|
369
|
+
|
|
370
|
+
A full sample ships as `job.sample.ini` in the source distribution.
|
|
371
|
+
|
|
372
|
+
## 6. Use as a library (Python / Airflow)
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
from ping_dataexport.api import run_export, run_job, run_jobfile
|
|
376
|
+
|
|
377
|
+
# ad-hoc — any CLI option works as a keyword argument
|
|
378
|
+
result = run_export("DB01", "SELECT * FROM sales",
|
|
379
|
+
configfile="config.ini", output="sales.parquet")
|
|
380
|
+
print(result.status, result.rows, result.files)
|
|
381
|
+
|
|
382
|
+
# one job.ini section / every section in parallel
|
|
383
|
+
run_job("DailySales", jobfile="job.ini", configfile="config.ini")
|
|
384
|
+
results = run_jobfile(jobfile="job.ini", configfile="config.ini", workers=4)
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Failures raise exceptions (`ConfigError`, `JobValidationError`, `ExportError`)
|
|
388
|
+
— never `sys.exit` — so an Airflow task fails cleanly. `run_jobfile` is the
|
|
389
|
+
exception: one job's failure never stops the others; check each
|
|
390
|
+
`ExportResult.status`.
|
|
391
|
+
|
|
392
|
+
The package also works with **no pip install at all**: copy the
|
|
393
|
+
`ping_dataexport/` folder next to your code and import it — relative imports
|
|
394
|
+
only, no metadata lookups.
|
|
395
|
+
|
|
396
|
+
## Output and logs
|
|
397
|
+
|
|
398
|
+
- Relative output paths resolve under `ExportFolder`; `ForceMakeDir=Yes`
|
|
399
|
+
creates missing directories.
|
|
400
|
+
- Every run logs structured lines to stdout and to a log file:
|
|
401
|
+
`-lf path` > `LogFile=` in config.ini > default
|
|
402
|
+
`{ExportFolder}/logs/log_{jobname}.txt`.
|
|
403
|
+
- CLI exit codes: `0` success, `1` failure (in `-p` parallel mode: `1` if any
|
|
404
|
+
job failed).
|
|
405
|
+
|
|
406
|
+
## ODBC prerequisites (ODBC sources only)
|
|
407
|
+
|
|
408
|
+
The Python drivers install via pip, but ODBC drivers are OS-level installs:
|
|
409
|
+
Microsoft ODBC Driver 17/18 for SQL Server, FreeTDS (`apt install tdsodbc`), or
|
|
410
|
+
your Oracle ODBC driver. `Type=sqlite` and `Type=oracle` (thin mode) need none.
|
|
411
|
+
|
|
412
|
+
## License
|
|
413
|
+
|
|
414
|
+
Apache-2.0.
|