etlplus 0.4.7__py3-none-any.whl → 0.8.3__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.
@@ -0,0 +1,128 @@
1
+ {# ---------- helpers ---------- #}
2
+ {% macro qid(name) -%}[{{ name | replace(']', ']]') }}]{%- endmacro %}
3
+ {% macro qname(schema, name) -%}{{ qid(schema) }}.{{ qid(name) }}{%- endmacro %}
4
+ {% macro df_name(table, col) -%}DF_{{ table }}_{{ col }}{%- endmacro %}
5
+ {% macro ck_name(table, col) -%}CK_{{ table }}_{{ col }}{%- endmacro %}
6
+
7
+ {# ---------- create schema (optional) ---------- #}
8
+ {% if spec.create_schema | default(true) %}
9
+ IF
10
+ NOT EXISTS (
11
+ SELECT 1
12
+ FROM sys.schemas
13
+ WHERE name = N'{{ spec.schema }}'
14
+ )
15
+ EXEC ('CREATE SCHEMA {{ qid(spec.schema) }}');
16
+ GO
17
+ {% endif %}
18
+
19
+ {# ---------- create table guarded ---------- #}
20
+ {% set fqtn = qname(spec.schema, spec.table) %}
21
+ IF OBJECT_ID(N'{{ spec.schema }}.{{ spec.table }}', N'U') IS NULL
22
+ BEGIN
23
+ CREATE TABLE {{ fqtn }} (
24
+ {% set ns = namespace(maxlen=0) %}
25
+ {% for c in spec.columns %}
26
+ {% set nlen = (c.name|string)|length %}
27
+ {% if nlen > ns.maxlen %}{% set ns.maxlen = nlen %}{% endif %}
28
+ {% endfor %}
29
+ {% set indent_cols = ' ' %}
30
+ {% for c in spec.columns %}
31
+ {# Map-safe lookups so StrictUndefined never bites on missing keys #}
32
+ {%- set ctype = c.get('type') -%}
33
+ {%- set ident = c.get('identity') -%}
34
+ {%- set computed = c.get('computed') -%}
35
+ {%- set nullable = c.get('nullable', true) -%}
36
+ {%- set defv = c.get('default') -%}
37
+ {%- set checkv = c.get('check') -%}
38
+ {{- indent_cols -}}{{ qid(c.name) }} {{ ctype }}
39
+ {%- if computed %} AS ({{ computed }}) PERSISTED{%- endif -%}
40
+ {%- if nullable %} NULL{%- else %} NOT NULL{%- endif -%}
41
+ {%- if ident %} IDENTITY({{ ident.get('seed', 1) }}, {{ ident.get('increment', 1) }}){%- endif -%}
42
+ {%- if defv %} CONSTRAINT {{ qid(df_name(spec.table, c.name)) }} DEFAULT ({{ defv }}){%- endif -%}
43
+ {%- if checkv %} CONSTRAINT {{ qid(ck_name(spec.table, c.name)) }} CHECK ({{ checkv }}){%- endif -%}
44
+ {{ "," if not loop.last }}
45
+ {% endfor %}
46
+
47
+ {%- if spec.primary_key is defined and spec.primary_key %}
48
+ , CONSTRAINT {{ qid(spec.primary_key.name
49
+ | default('PK_' ~ spec.table)) }}
50
+ PRIMARY KEY CLUSTERED (
51
+ {%- for col in spec.primary_key.columns -%}
52
+ {{ qid(col) }}{{ ", " if not loop.last }}
53
+ {%- endfor -%}
54
+ )
55
+ {%- endif %}
56
+
57
+ {%- for uq in spec.unique_constraints | default([]) %}
58
+ , CONSTRAINT {{ qid(uq.name) }} UNIQUE (
59
+ {%- for col in uq.columns -%}
60
+ {{ qid(col) }}{{ ", " if not loop.last }}
61
+ {%- endfor -%}
62
+ )
63
+ {%- endfor %}
64
+ );
65
+ END
66
+ GO
67
+
68
+ {# ---------- indexes ---------- #}
69
+ {% for ix in spec.indexes | default([]) %}
70
+ {%- set ix_unique = ix.get('unique', false) -%}
71
+ {%- set ix_include = ix.get('include') -%}
72
+ {%- set ix_where = ix.get('where') -%}
73
+ IF
74
+ NOT EXISTS (
75
+ SELECT 1
76
+ FROM sys.indexes
77
+ WHERE name = N'{{ ix.name }}'
78
+ AND object_id = OBJECT_ID(N'{{ spec.schema }}.{{ spec.table }}')
79
+ )
80
+ BEGIN
81
+ CREATE{% if ix_unique %} UNIQUE{% endif %} INDEX {{ qid(ix.name) }}
82
+ ON {{ fqtn }} (
83
+ {%- for col in ix.columns -%}
84
+ {{ qid(col) }}{{ ", " if not loop.last }}
85
+ {%- endfor -%}
86
+ )
87
+ {%- if ix_include %}
88
+ INCLUDE (
89
+ {%- for col in ix_include -%}
90
+ {{ qid(col) }}{{ ", " if not loop.last }}
91
+ {%- endfor -%}
92
+ )
93
+ {%- endif -%}
94
+ {%- if ix_where %} WHERE {{ ix_where }}{% endif %};
95
+ END
96
+ GO
97
+ {% endfor %}
98
+
99
+ {# ---------- foreign keys ---------- #}
100
+ {% for fk in spec.foreign_keys | default([]) %}
101
+ IF
102
+ NOT EXISTS (
103
+ SELECT 1
104
+ FROM sys.foreign_keys
105
+ WHERE
106
+ name = N'{{ fk.name }}'
107
+ AND parent_object_id = OBJECT_ID(N'{{ spec.schema }}.{{ spec.table }}'
108
+ )
109
+ )
110
+ BEGIN
111
+ ALTER TABLE {{ fqtn }} WITH CHECK
112
+ ADD CONSTRAINT {{ qid(fk.name) }} FOREIGN KEY (
113
+ {%- for col in fk.columns -%}
114
+ {{ qid(col) }}{{ ", " if not loop.last }}
115
+ {%- endfor -%}
116
+ ) REFERENCES
117
+ {{ qname(fk.ref_schema | default('dbo'), fk.ref_table) }} (
118
+ {%- for col in fk.ref_columns -%}
119
+ {{ qid(col) }}{{ ", " if not loop.last }}
120
+ {%- endfor -%}
121
+ )
122
+ {%- set on_upd = fk.get('on_update') -%}
123
+ {%- set on_del = fk.get('on_delete') -%}
124
+ {%- if on_upd %} ON UPDATE {{ on_upd }}{% endif -%}
125
+ {%- if on_del %} ON DELETE {{ on_del }}{% endif %};
126
+ END
127
+ GO
128
+ {% endfor %}
@@ -0,0 +1,69 @@
1
+ {# -- Helpers -- #}
2
+ {% macro qid(name) -%}
3
+ [{{ name | replace(']', ']]') }}]
4
+ {%- endmacro %}
5
+
6
+ {% macro qname(schema, name) -%}
7
+ {{ qid(schema) }}.{{ qid(name) }}
8
+ {%- endmacro %}
9
+
10
+ {# Convert any expression to nvarchar(MAX) with N'' for NULLs #}
11
+ {% macro to_str(expr) -%}
12
+ ISNULL(CONVERT(NVARCHAR(MAX), {{ expr }}), N'')
13
+ {%- endmacro %}
14
+
15
+ {# Normalize truthy values to 'TRUE' / 'FALSE' #}
16
+ {% macro truthy(expr) -%}
17
+ IIF(CONVERT(VARCHAR(5), {{ expr }}) IN ('TRUE', '1', 'YES'), 'TRUE', 'FALSE')
18
+ {%- endmacro %}
19
+
20
+ {# -- Create schema (optional) -- #}
21
+ {% if spec.create_schema | default(true) %}
22
+ IF
23
+ NOT EXISTS (
24
+ SELECT 1
25
+ FROM sys.schemas
26
+ WHERE name = N'{{ spec.schema }}'
27
+ )
28
+ EXEC ('CREATE SCHEMA {{ qid(spec.schema) }}');
29
+ GO
30
+ {% endif %}
31
+
32
+ {# -- SET options (match your source view) -- #}
33
+ SET QUOTED_IDENTIFIER ON
34
+ SET ANSI_NULLS ON
35
+ GO
36
+
37
+ {# -- Derive names with fallbacks so table-style specs also work -- #}
38
+ {% set view_name = spec.view if spec.view is defined else ('vw_' ~ spec.table) %}
39
+ {% set src_schema = (spec.from_schema if spec.from_schema is defined else (spec.schema if spec.schema is defined else 'dbo')) %}
40
+ {% set src_table = (
41
+ spec.from_table if spec.from_table is defined else
42
+ (spec.source_table if spec.source_table is defined else spec.table)
43
+ ) %}
44
+ {% set indent_cols = ' ' %}
45
+
46
+ {# -- Create view -- #}
47
+ CREATE VIEW {{ qname(spec.schema | default('dbo'), view_name) }} AS
48
+ SELECT
49
+ {% if spec.projections is defined and spec.projections %}
50
+ {% for p in spec.projections %}
51
+ {%- if p.type is defined and (p.type | lower)[:8] == 'nvarchar' -%}
52
+ {{- indent_cols -}}{{ to_str(p.expr) }} AS {{ qid(p.alias) }}
53
+ {%- else -%}
54
+ {{- indent_cols -}}{{ p.expr }}
55
+ {%- endif -%}
56
+ {{ "," if not loop.last }}
57
+ {% endfor %}
58
+ {% else %}
59
+ {% for c in spec.columns | default([]) %}
60
+ {%- if c.type is defined and (c.type | lower)[:8] == 'nvarchar' -%}
61
+ {{- indent_cols -}}{{ to_str(qid(c.name)) }} AS {{ qid(c.name) }}
62
+ {%- else -%}
63
+ {{- indent_cols -}}{{ qid(c.name) }}
64
+ {%- endif -%}
65
+ {{ "," if not loop.last }}
66
+ {% endfor %}
67
+ {%- endif %}
68
+ FROM {{ qname(src_schema, src_table) }};
69
+ GO
etlplus/types.py CHANGED
@@ -225,3 +225,8 @@ type Sleeper = Callable[[float], None]
225
225
 
226
226
  # Numeric timeout in seconds or ``None`` for no timeout.
227
227
  type Timeout = float | None
228
+
229
+ # -- Templates -- #
230
+
231
+ # Allowed template keys for bundled DDL rendering.
232
+ type TemplateKey = Literal['ddl', 'view']
etlplus/utils.py CHANGED
@@ -6,7 +6,6 @@ Small shared helpers used across modules.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- import argparse
10
9
  import json
11
10
  from collections.abc import Callable
12
11
  from collections.abc import Mapping
@@ -22,7 +21,6 @@ from .types import StrAnyMap
22
21
  __all__ = [
23
22
  # Data utilities
24
23
  'count_records',
25
- 'json_type',
26
24
  'print_json',
27
25
  # Mapping utilities
28
26
  'cast_str_dict',
@@ -119,35 +117,6 @@ def count_records(
119
117
  return len(data) if isinstance(data, list) else 1
120
118
 
121
119
 
122
- def json_type(
123
- option: str,
124
- ) -> Any:
125
- """
126
- Argparse ``type=`` hook that parses a JSON string.
127
-
128
- Parameters
129
- ----------
130
- option : str
131
- Raw CLI string to parse as JSON.
132
-
133
- Returns
134
- -------
135
- Any
136
- Parsed JSON value.
137
-
138
- Raises
139
- ------
140
- argparse.ArgumentTypeError
141
- If the input cannot be parsed as JSON.
142
- """
143
- try:
144
- return json.loads(option)
145
- except json.JSONDecodeError as e: # pragma: no cover - argparse path
146
- raise argparse.ArgumentTypeError(
147
- f'Invalid JSON: {e.msg} (pos {e.pos})',
148
- ) from e
149
-
150
-
151
120
  def maybe_mapping(
152
121
  value: Any,
153
122
  ) -> StrAnyMap | None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: etlplus
3
- Version: 0.4.7
3
+ Version: 0.8.3
4
4
  Summary: A Swiss Army knife for simple ETL operations
5
5
  Home-page: https://github.com/Dagitali/ETLPlus
6
6
  Author: ETLPlus Team
@@ -21,7 +21,10 @@ Requires-Dist: jinja2>=3.1.6
21
21
  Requires-Dist: pyodbc>=5.3.0
22
22
  Requires-Dist: python-dotenv>=1.2.1
23
23
  Requires-Dist: pandas>=2.3.3
24
+ Requires-Dist: pydantic>=2.12.5
25
+ Requires-Dist: PyYAML>=6.0.3
24
26
  Requires-Dist: requests>=2.32.5
27
+ Requires-Dist: SQLAlchemy>=2.0.45
25
28
  Requires-Dist: typer>=0.21.0
26
29
  Provides-Extra: dev
27
30
  Requires-Dist: black>=25.9.0; extra == "dev"
@@ -61,6 +64,8 @@ package and command-line interface for data extraction, validation, transformati
61
64
  - [Quickstart](#quickstart)
62
65
  - [Usage](#usage)
63
66
  - [Command Line Interface](#command-line-interface)
67
+ - [Check Pipelines](#check-pipelines)
68
+ - [Render SQL DDL](#render-sql-ddl)
64
69
  - [Extract Data](#extract-data)
65
70
  - [Validate Data](#validate-data)
66
71
  - [Transform Data](#transform-data)
@@ -88,6 +93,14 @@ package and command-line interface for data extraction, validation, transformati
88
93
 
89
94
  ## Features
90
95
 
96
+ - **Check** data pipeline definitions before running them:
97
+ - Summarize jobs, sources, targets, and transforms
98
+ - Confirm configuration changes by printing focused sections on demand
99
+
100
+ - **Render** SQL DDL from shared table specs:
101
+ - Generate CREATE TABLE or view statements
102
+ - Swap templates or direct output to files for database migrations
103
+
91
104
  - **Extract** data from multiple sources:
92
105
  - Files (CSV, JSON, XML, YAML)
93
106
  - Databases (connection string support)
@@ -169,6 +182,47 @@ etlplus --help
169
182
  etlplus --version
170
183
  ```
171
184
 
185
+ The CLI is implemented with Typer (Click-based). There is no argparse compatibility layer, so rely
186
+ on the documented commands/flags and run `etlplus <command> --help` for current options.
187
+
188
+ #### Check Pipelines
189
+
190
+ Use `etlplus check` to explore pipeline YAML definitions without running them. The command can print
191
+ job names, summarize configured sources and targets, or drill into specific sections.
192
+
193
+ List jobs and show a pipeline summary:
194
+ ```bash
195
+ etlplus check --config examples/configs/pipeline.yml --jobs
196
+ etlplus check --config examples/configs/pipeline.yml --summary
197
+ ```
198
+
199
+ Show sources or transforms for troubleshooting:
200
+ ```bash
201
+ etlplus check --config examples/configs/pipeline.yml --sources
202
+ etlplus check --config examples/configs/pipeline.yml --transforms
203
+ ```
204
+
205
+ #### Render SQL DDL
206
+
207
+ Use `etlplus render` to turn table schema specs into ready-to-run SQL. Render from a pipeline config
208
+ or from a standalone schema file, and choose the built-in `ddl` or `view` templates (or provide your
209
+ own).
210
+
211
+ Render all tables defined in a pipeline:
212
+ ```bash
213
+ etlplus render --config examples/configs/pipeline.yml --template ddl
214
+ ```
215
+
216
+ Render a single table in that pipeline:
217
+ ```bash
218
+ etlplus render --config examples/configs/pipeline.yml --table customers --template view
219
+ ```
220
+
221
+ Render from a standalone table spec to a file:
222
+ ```bash
223
+ etlplus render --spec schemas/customer.yml --template view -o temp/customer_view.sql
224
+ ```
225
+
172
226
  #### Extract Data
173
227
 
174
228
  Note: For file sources, the format is normally inferred from the filename extension. Use
@@ -306,6 +360,17 @@ For YAML-driven pipelines executed end-to-end (extract → validate → transfor
306
360
  - Authoring: [`docs/pipeline-guide.md`](docs/pipeline-guide.md)
307
361
  - Runner API and internals: [`docs/run-module.md`](docs/run-module.md)
308
362
 
363
+ CLI quick reference for pipelines:
364
+
365
+ ```bash
366
+ # List jobs or show a pipeline summary
367
+ etlplus check --config examples/configs/pipeline.yml --jobs
368
+ etlplus check --config examples/configs/pipeline.yml --summary
369
+
370
+ # Run a job
371
+ etlplus run --config examples/configs/pipeline.yml --job file_to_file_customers
372
+ ```
373
+
309
374
  ### Complete ETL Pipeline Example
310
375
 
311
376
  ```bash
@@ -7,13 +7,13 @@ etlplus/file.py,sha256=RxIAsGDN4f_vNA2B5-ct88JNd_ISAyYbooIRE5DstS8,17972
7
7
  etlplus/load.py,sha256=BwF3gT4gIr-5CvNMz_aLTCl-w2ihWSTxNVd4X92XFwI,8737
8
8
  etlplus/mixins.py,sha256=ifGpHwWv7U00yqGf-kN93vJax2IiK4jaGtTsPsO3Oak,1350
9
9
  etlplus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- etlplus/run.py,sha256=zl_Yx35spcgaa9Xx7-kcJEb1CAYyMIiqtPlsSrYxRfs,12448
10
+ etlplus/run.py,sha256=X4kp5FQlIWVf1_d9oSrchKau7BFDCE1Zkscvu7WPaWw,12340
11
11
  etlplus/run_helpers.py,sha256=bj6MkaeFxjl3CeKG1HoXKx5DwAlXNERVW-GX-z1P_qQ,24373
12
12
  etlplus/transform.py,sha256=uAUVDDHYCgx7GpVez9IK3OAZM-CnCuMa9iox3vwGGJA,25296
13
- etlplus/types.py,sha256=SJiZ7wJiSnV4CEvF-9E5nSFLBo4DT9OqHQqj1GSHkv8,6042
14
- etlplus/utils.py,sha256=_fn8b-SAdxiw28VX-Ugr8sZUPZI9mEkWKAGExlgxhJA,13993
13
+ etlplus/types.py,sha256=1hsDlnF6r76zAwaUYay-i6pCM-Y0IU5nP7Crj8PLCQ4,6157
14
+ etlplus/utils.py,sha256=GrRH6N9u_U-4gs6PYpO0_Y2RwsyD3Ju9cup0b0byUbk,13367
15
15
  etlplus/validate.py,sha256=7rJoEI_SIILdPpoBqqh2UJqg9oeReDz34mYSlc3t7Qg,12989
16
- etlplus/api/README.md,sha256=UkK5PiZWXbbnMNP0MaPa56S88PjSqOwhMNCyswOhvKc,7329
16
+ etlplus/api/README.md,sha256=ZiyjxLz0LfFCzeYKXwtH8yY1OJ4hXCju7t2ICroFoU8,7215
17
17
  etlplus/api/__init__.py,sha256=P2JUYFy6Ep4t6xnsBiCBfQCkQLHYYhA-yXPXCobS8Y0,4295
18
18
  etlplus/api/auth.py,sha256=GOO5on-LoMS1GXTAhtK9rFcfpjbBcNeA6NE5UZwIq0g,12158
19
19
  etlplus/api/config.py,sha256=wRpOaZ31sPReVzEMme0jKl_37nqgraESwuYSNxP_xDo,17397
@@ -31,21 +31,35 @@ etlplus/api/rate_limiting/__init__.py,sha256=ZySB1dZettEDnWvI1EHf_TZ9L08M_kKsNR-
31
31
  etlplus/api/rate_limiting/config.py,sha256=2b4wIynblN-1EyMqI4aXa71SljzSjXYh5N1Nngr3jOg,9406
32
32
  etlplus/api/rate_limiting/rate_limiter.py,sha256=Uxozqd_Ej5Lsj-M-mLT2WexChgWh7x35_YP10yqYPQA,7159
33
33
  etlplus/cli/__init__.py,sha256=J97-Rv931IL1_b4AXnB7Fbbd7HKnHBpx18NQfC_kE6c,299
34
- etlplus/cli/app.py,sha256=pc9VDUb3Qc8u5-XyDrHJkrSR9D3bq4e9zLbaD8KzyfY,32618
35
- etlplus/cli/handlers.py,sha256=aI_ZlnJCGGkVnVJJPhmPRCXc31MxtLaOeqqJoo3ci48,15816
36
- etlplus/cli/main.py,sha256=LaE3q4jbQ8KTTvs4D_khpOdlTKrdaPmxB28WML6hnLg,15059
34
+ etlplus/cli/commands.py,sha256=-lO8XX2IeTvdfZLeI_UpT2kH4qZuNpfujArw3IKS2vY,22585
35
+ etlplus/cli/constants.py,sha256=NJ6IvNyYEI8IdB7eMcc-vteQiiIwqid5YvmUk-5DRHY,1839
36
+ etlplus/cli/handlers.py,sha256=WBzJZz7ESU4Hljog_ON4g9PLIW6U6io_F9op7daksKY,17781
37
+ etlplus/cli/io.py,sha256=hkmbDh0HFrZhDjfZbma0Lp4tc0wrTwdD9Snqa-wNAd4,7308
38
+ etlplus/cli/main.py,sha256=_ipwLgIzkf8ks8cbu2KkVBbY3gcdaqDR98WUBdwviiw,5191
39
+ etlplus/cli/options.py,sha256=dswqAdnQ4fRJGI3xEFUyMifp0ry3rm2cCaPyEqI_99c,1199
40
+ etlplus/cli/state.py,sha256=20uKdYddYvAlR-HUQJAV-ThyXMZaPiSk9cN-tevRIpw,7991
41
+ etlplus/cli/types.py,sha256=tclhKVJXDqHzlTQBYKARfqMgDOcuBJ-Zej2pvFy96WM,652
37
42
  etlplus/config/__init__.py,sha256=VZWzOg7d2YR9NT6UwKTv44yf2FRUMjTHynkm1Dl5Qzo,1486
38
43
  etlplus/config/connector.py,sha256=0-TIwevHbKRHVmucvyGpPd-3tB1dKHB-dj0yJ6kq5eY,9809
39
44
  etlplus/config/jobs.py,sha256=hmzRCqt0OvCEZZR4ONKrd3lvSv0OmayjLc4yOBk3ug8,7399
40
- etlplus/config/pipeline.py,sha256=7w0WWe21vvPJvTlJoRTZh2j92I1_GOcdwan6xyKfQII,8973
45
+ etlplus/config/pipeline.py,sha256=Va4MQY6KEyKqHGMKPmh09ZcGpx95br-iNUjpkqtzVbw,9500
41
46
  etlplus/config/profile.py,sha256=Ss2zedQGjkaGSpvBLTD4SZaWViMJ7TJPLB8Q2_BTpPg,1898
42
47
  etlplus/config/types.py,sha256=a0epJ3z16HQ5bY3Ctf8s_cQPa3f0HHcwdOcjCP2xoG4,4954
43
48
  etlplus/config/utils.py,sha256=4SUHMkt5bKBhMhiJm-DrnmE2Q4TfOgdNCKz8PJDS27o,3443
49
+ etlplus/database/__init__.py,sha256=AKJsDl2RHuRGPS-eXgNJeh4aSncJP5Y0yLApBF6i7i8,1052
50
+ etlplus/database/ddl.py,sha256=z9KvHi1MPhPBLHxMDdqJgLTp3A2-lcz0gqhZ7HIE6kU,7916
51
+ etlplus/database/engine.py,sha256=7rr7YndA8LwyWJL8k1YhQbqxxmW4gWEUQjp0NwQcYtc,4061
52
+ etlplus/database/orm.py,sha256=gCSqH-CjQz6tV9133-VqgiwokK5ylun0BwXaIWfImAo,10008
53
+ etlplus/database/schema.py,sha256=HNTgglI8qvQLInr7gq--2lLmLKHzAZTL2MJUOIw9DlY,7025
54
+ etlplus/database/types.py,sha256=_pkQyC14TzAlgyeIqZG4F5LWYknZbHw3TW68Auk7Ya0,795
55
+ etlplus/templates/__init__.py,sha256=tsniN7XJYs3NwYxJ6c2HD5upHP3CDkLx-bQCMt97UOM,106
56
+ etlplus/templates/ddl.sql.j2,sha256=s8fMWvcb4eaJVXkifuib1aQPljtZ8buuyB_uA-ZdU3Q,4734
57
+ etlplus/templates/view.sql.j2,sha256=Iy8DHfhq5yyvrUKDxqp_aHIEXY4Tm6j4wT7YDEFWAhk,2180
44
58
  etlplus/validation/__init__.py,sha256=Pe5Xg1_EA4uiNZGYu5WTF3j7odjmyxnAJ8rcioaplSQ,1254
45
59
  etlplus/validation/utils.py,sha256=Mtqg449VIke0ziy_wd2r6yrwJzQkA1iulZC87FzXMjo,10201
46
- etlplus-0.4.7.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
47
- etlplus-0.4.7.dist-info/METADATA,sha256=zD88Gnu6IAFvbIR_L--yZE5EN2VpEK-Q3B1CZypmF9I,17278
48
- etlplus-0.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
- etlplus-0.4.7.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
50
- etlplus-0.4.7.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
51
- etlplus-0.4.7.dist-info/RECORD,,
60
+ etlplus-0.8.3.dist-info/licenses/LICENSE,sha256=MuNO63i6kWmgnV2pbP2SLqP54mk1BGmu7CmbtxMmT-U,1069
61
+ etlplus-0.8.3.dist-info/METADATA,sha256=fCmfB5aa9jgUEb2s_l-5j5b3vooe-l3M0pNYrrjhYAg,19513
62
+ etlplus-0.8.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
+ etlplus-0.8.3.dist-info/entry_points.txt,sha256=6w-2-jzuPa55spzK34h-UKh2JTEShh38adFRONNP9QE,45
64
+ etlplus-0.8.3.dist-info/top_level.txt,sha256=aWWF-udn_sLGuHTM6W6MLh99ArS9ROkUWO8Mi8y1_2U,8
65
+ etlplus-0.8.3.dist-info/RECORD,,