al-data-core 0.6.2__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AL Data Labs
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.
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: al-data-core
3
+ Version: 0.6.2
4
+ Summary: Generic data pipeline engine for Airflow (config, processor, DAG factory).
5
+ Author-email: AL Data Labs <pypi@aldatalabs.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://aldatalabs.com
8
+ Keywords: airflow,data-engineering,etl,dag
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Topic :: Software Development :: Libraries
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: al-data-utils>=0.3.1
16
+ Requires-Dist: pandas
17
+ Dynamic: license-file
18
+
19
+ # al-data-core
20
+
21
+ Generic Airflow data pipeline engine. Install once, configure per project.
22
+
23
+ ## Python and Setup
24
+
25
+ - Python: 3.10+
26
+ - Packaging: setuptools (src layout)
27
+
28
+ Install locally in editable mode:
29
+
30
+ ```bash
31
+ pip install -e .
32
+ ```
33
+
34
+ Requires `al-data-utils` to be installed first.
35
+
36
+ ## Project Structure
37
+
38
+ - src/al_data_core
39
+
40
+ Main modules under `src/al_data_core`:
41
+
42
+ - `config_processor` — `DataCoreConfig`
43
+ - `tasks_processor` — `TaskProcessor`
44
+ - `azure_as_processor` — `run_azure_as_refresh`
45
+ - `external_handler_base` — `ExternalHandlerBase`, `discover_handlers` (used for XLS and API handlers)
46
+ - `dags_installer` — CLI for copying DAG templates
47
+
48
+ ## DAG Templates
49
+
50
+ After installing, copy the ready-to-use DAG templates to your Airflow dags folder:
51
+
52
+ ```bash
53
+ al-data-core copy-dags # latest → /opt/airflow/dags
54
+ al-data-core copy-dags /my/dags # custom path
55
+ al-data-core copy-dags --version v2.0.0 # specific Airflow version
56
+ al-data-core list-versions # show available versions
57
+ ```
58
+
59
+ Available versions:
60
+
61
+ - `v2.0.0` — Airflow 2.x
62
+ - `v3.0.0` — Airflow 3.x
63
+
64
+ Two files are copied:
65
+
66
+ - `data_core_dag.py` — main DAG file, reads `data_core_tasks_config.yaml` and registers all project DAGs
67
+ - `data_core_config_engine_dag.py` — runs every 15 min, queries config DB and YAML, writes `data_core_tasks_config.yaml`
68
+
69
+ ## Project Layout (per-project repo)
70
+
71
+ ```text
72
+ dags/
73
+ ├── data_core_dag.py <- copied from template
74
+ ├── data_core_config_engine_dag.py <- copied from template
75
+ └── data_core/
76
+ ├── data_core_config.yaml <- project config
77
+ ├── .configs/data_core/data_core_tasks_config.yaml <- generated by config engine dag
78
+ ├── data_core_handler_sql/
79
+ │ └── sql_queries/
80
+ │ └── <scope>/
81
+ │ └── <param_code>.sql
82
+ ├── data_core_handler_xls/ <- optional, project-specific
83
+ └── data_core_handler_api/ <- optional, project-specific
84
+ ```
85
+
86
+ ## data_core_config.yaml
87
+
88
+ ```yaml
89
+ CONFIG:
90
+ config_connection_variable: my_mssql_secret # Airflow variable name
91
+ mappings:
92
+ config_get_tables_list: integrations_db.config.get_tables_list
93
+ config_parameters: integrations_db.config.parameters
94
+ config_fn_get_param_values: integrations_db.config.fn_get_param_values
95
+ config_calc_date_ranges: integrations_db.config.calc_date_ranges
96
+ config_update_param_values: integrations_db.config.update_param_values
97
+
98
+ DAGS:
99
+ my_project_ods_dag:
100
+ schedule_interval: '5 */1 * * *'
101
+ scopes: ['ODS_SOURCE']
102
+ tags: ['my_project']
103
+ my_project_dw_dag:
104
+ schedule_interval: '30 */1 * * *'
105
+ scopes: ['DW_TARGET']
106
+ tags: ['my_project']
107
+ azure_as: # optional: refresh semantic model after ETL
108
+ connection_variable: my_azure_as_secret
109
+ models:
110
+ - name: MyModel
111
+ refresh_request: {Type: Full, CommitMode: transactional, MaxParallelism: 2, RetryCount: 2, Objects: []}
112
+
113
+ ODS_SOURCE:
114
+ enabled: true
115
+ source_type: mssql
116
+ source_connection_variable: source_mssql_secret
117
+ destination_connection_variable: dest_mssql_secret
118
+
119
+ DW_TARGET:
120
+ enabled: true
121
+ destination_connection_variable: dest_mssql_secret
122
+
123
+ XLS:
124
+ enabled: true
125
+ handler: data_core_handler_xls # opts this scope into handler-based dispatch instead of seq_code-based copy/merge
126
+
127
+ ods_britix_api:
128
+ enabled: true
129
+ handler: data_core_handler_api/britix24
130
+ ```
131
+
132
+ ## Handlers
133
+
134
+ A scope opts into handler-based dispatch by setting `handler` to its handlers folder (relative to `data_core_config.yaml`, or an absolute path) — the value *is* the path, so `TaskProcessor` doesn't hardcode scope_code names and adding a new handler scope needs only config, never a template change. Two layouts are supported:
135
+
136
+ * **One file per param_code** — file name matches the task's `param_code` exactly.
137
+ * **One generic handler per scope** — when the folder holds a single handler file, it serves every param_code in the scope and distinguishes tasks via `self.context.param_code`.
138
+
139
+ ```python
140
+ # dags/data_core/data_core_handler_xls/fact_sales_plan.py
141
+ from al_data_core.external_handler_base import ExternalHandlerBase
142
+
143
+ class MySalesHandler(ExternalHandlerBase):
144
+ def proceed(self) -> None:
145
+ ...
146
+ ```
147
+
148
+ ```python
149
+ # dags/data_core/data_core_handler_api/britix24/bitrix_ingest.py
150
+ from al_data_core.external_handler_base import ExternalHandlerBase
151
+
152
+ class BitrixIngestHandler(ExternalHandlerBase):
153
+ def proceed(self) -> None:
154
+ scope = self.context.scope_config
155
+ entity = scope['entities'][self.context.param_code]
156
+ ...
157
+ ```
158
+
159
+ `TaskProcessor` constructs the handler with no arguments, sets `handler.context` to a `HandlerContext` (scope_code, param_code, scope_config, config_file_path, params_details, debug), then calls `proceed()` — handlers read run-time settings from `self.context` instead of hardcoding them. Discovery happens at task-run time, not DAG-parse time, so a broken handler file can't fail the whole DAG file's parse and handler modules aren't re-imported every scheduler cycle. Each file must define exactly one `ExternalHandlerBase` subclass.
160
+
161
+ ## Development Guides
162
+
163
+ - `RELEASING.md` — release flow and Azure Blob publish steps
164
+
165
+ ## Changelog
166
+
167
+ See `CHANGELOG.md` for release notes.
@@ -0,0 +1,149 @@
1
+ # al-data-core
2
+
3
+ Generic Airflow data pipeline engine. Install once, configure per project.
4
+
5
+ ## Python and Setup
6
+
7
+ - Python: 3.10+
8
+ - Packaging: setuptools (src layout)
9
+
10
+ Install locally in editable mode:
11
+
12
+ ```bash
13
+ pip install -e .
14
+ ```
15
+
16
+ Requires `al-data-utils` to be installed first.
17
+
18
+ ## Project Structure
19
+
20
+ - src/al_data_core
21
+
22
+ Main modules under `src/al_data_core`:
23
+
24
+ - `config_processor` — `DataCoreConfig`
25
+ - `tasks_processor` — `TaskProcessor`
26
+ - `azure_as_processor` — `run_azure_as_refresh`
27
+ - `external_handler_base` — `ExternalHandlerBase`, `discover_handlers` (used for XLS and API handlers)
28
+ - `dags_installer` — CLI for copying DAG templates
29
+
30
+ ## DAG Templates
31
+
32
+ After installing, copy the ready-to-use DAG templates to your Airflow dags folder:
33
+
34
+ ```bash
35
+ al-data-core copy-dags # latest → /opt/airflow/dags
36
+ al-data-core copy-dags /my/dags # custom path
37
+ al-data-core copy-dags --version v2.0.0 # specific Airflow version
38
+ al-data-core list-versions # show available versions
39
+ ```
40
+
41
+ Available versions:
42
+
43
+ - `v2.0.0` — Airflow 2.x
44
+ - `v3.0.0` — Airflow 3.x
45
+
46
+ Two files are copied:
47
+
48
+ - `data_core_dag.py` — main DAG file, reads `data_core_tasks_config.yaml` and registers all project DAGs
49
+ - `data_core_config_engine_dag.py` — runs every 15 min, queries config DB and YAML, writes `data_core_tasks_config.yaml`
50
+
51
+ ## Project Layout (per-project repo)
52
+
53
+ ```text
54
+ dags/
55
+ ├── data_core_dag.py <- copied from template
56
+ ├── data_core_config_engine_dag.py <- copied from template
57
+ └── data_core/
58
+ ├── data_core_config.yaml <- project config
59
+ ├── .configs/data_core/data_core_tasks_config.yaml <- generated by config engine dag
60
+ ├── data_core_handler_sql/
61
+ │ └── sql_queries/
62
+ │ └── <scope>/
63
+ │ └── <param_code>.sql
64
+ ├── data_core_handler_xls/ <- optional, project-specific
65
+ └── data_core_handler_api/ <- optional, project-specific
66
+ ```
67
+
68
+ ## data_core_config.yaml
69
+
70
+ ```yaml
71
+ CONFIG:
72
+ config_connection_variable: my_mssql_secret # Airflow variable name
73
+ mappings:
74
+ config_get_tables_list: integrations_db.config.get_tables_list
75
+ config_parameters: integrations_db.config.parameters
76
+ config_fn_get_param_values: integrations_db.config.fn_get_param_values
77
+ config_calc_date_ranges: integrations_db.config.calc_date_ranges
78
+ config_update_param_values: integrations_db.config.update_param_values
79
+
80
+ DAGS:
81
+ my_project_ods_dag:
82
+ schedule_interval: '5 */1 * * *'
83
+ scopes: ['ODS_SOURCE']
84
+ tags: ['my_project']
85
+ my_project_dw_dag:
86
+ schedule_interval: '30 */1 * * *'
87
+ scopes: ['DW_TARGET']
88
+ tags: ['my_project']
89
+ azure_as: # optional: refresh semantic model after ETL
90
+ connection_variable: my_azure_as_secret
91
+ models:
92
+ - name: MyModel
93
+ refresh_request: {Type: Full, CommitMode: transactional, MaxParallelism: 2, RetryCount: 2, Objects: []}
94
+
95
+ ODS_SOURCE:
96
+ enabled: true
97
+ source_type: mssql
98
+ source_connection_variable: source_mssql_secret
99
+ destination_connection_variable: dest_mssql_secret
100
+
101
+ DW_TARGET:
102
+ enabled: true
103
+ destination_connection_variable: dest_mssql_secret
104
+
105
+ XLS:
106
+ enabled: true
107
+ handler: data_core_handler_xls # opts this scope into handler-based dispatch instead of seq_code-based copy/merge
108
+
109
+ ods_britix_api:
110
+ enabled: true
111
+ handler: data_core_handler_api/britix24
112
+ ```
113
+
114
+ ## Handlers
115
+
116
+ A scope opts into handler-based dispatch by setting `handler` to its handlers folder (relative to `data_core_config.yaml`, or an absolute path) — the value *is* the path, so `TaskProcessor` doesn't hardcode scope_code names and adding a new handler scope needs only config, never a template change. Two layouts are supported:
117
+
118
+ * **One file per param_code** — file name matches the task's `param_code` exactly.
119
+ * **One generic handler per scope** — when the folder holds a single handler file, it serves every param_code in the scope and distinguishes tasks via `self.context.param_code`.
120
+
121
+ ```python
122
+ # dags/data_core/data_core_handler_xls/fact_sales_plan.py
123
+ from al_data_core.external_handler_base import ExternalHandlerBase
124
+
125
+ class MySalesHandler(ExternalHandlerBase):
126
+ def proceed(self) -> None:
127
+ ...
128
+ ```
129
+
130
+ ```python
131
+ # dags/data_core/data_core_handler_api/britix24/bitrix_ingest.py
132
+ from al_data_core.external_handler_base import ExternalHandlerBase
133
+
134
+ class BitrixIngestHandler(ExternalHandlerBase):
135
+ def proceed(self) -> None:
136
+ scope = self.context.scope_config
137
+ entity = scope['entities'][self.context.param_code]
138
+ ...
139
+ ```
140
+
141
+ `TaskProcessor` constructs the handler with no arguments, sets `handler.context` to a `HandlerContext` (scope_code, param_code, scope_config, config_file_path, params_details, debug), then calls `proceed()` — handlers read run-time settings from `self.context` instead of hardcoding them. Discovery happens at task-run time, not DAG-parse time, so a broken handler file can't fail the whole DAG file's parse and handler modules aren't re-imported every scheduler cycle. Each file must define exactly one `ExternalHandlerBase` subclass.
142
+
143
+ ## Development Guides
144
+
145
+ - `RELEASING.md` — release flow and Azure Blob publish steps
146
+
147
+ ## Changelog
148
+
149
+ See `CHANGELOG.md` for release notes.
@@ -0,0 +1,58 @@
1
+ [project]
2
+ name = "al-data-core"
3
+ version = "0.6.2"
4
+ description = "Generic data pipeline engine for Airflow (config, processor, DAG factory)."
5
+ authors = [{ name = "AL Data Labs", email = "pypi@aldatalabs.com" }]
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ keywords = ["airflow", "data-engineering", "etl", "dag"]
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Operating System :: OS Independent",
12
+ "Topic :: Software Development :: Libraries",
13
+ ]
14
+ readme = "README.md"
15
+ requires-python = ">=3.10"
16
+ dependencies = [
17
+ "al-data-utils>=0.3.1",
18
+ "pandas",
19
+ ]
20
+
21
+ [project.urls]
22
+ Homepage = "https://aldatalabs.com"
23
+
24
+
25
+ [build-system]
26
+ requires = ["setuptools>=77"]
27
+ build-backend = "setuptools.build_meta"
28
+
29
+ [project.scripts]
30
+ al-data-core = "al_data_core.dags_installer:main"
31
+
32
+ [tool.setuptools]
33
+ package-dir = {"" = "src"}
34
+
35
+ [tool.setuptools.packages.find]
36
+ where = ["src"]
37
+
38
+ [tool.setuptools.package-data]
39
+ al_data_core = ["templates/**/*.py"]
40
+
41
+ # Ruff configuration
42
+ [tool.ruff]
43
+ exclude = [
44
+ ".git",
45
+ ".venv",
46
+ ]
47
+
48
+ target-version = "py313"
49
+
50
+ line-length = 88
51
+ indent-width = 4
52
+
53
+
54
+ [tool.ruff.format]
55
+ quote-style = "single"
56
+ indent-style = "space"
57
+ skip-magic-trailing-comma = true
58
+ line-ending = "auto"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ from al_data_core.config_processor import DataCoreConfig
2
+ from al_data_core.tasks_processor import TaskProcessor
3
+ from al_data_core.external_handler_base import ExternalHandlerBase, discover_handlers
4
+
5
+ __all__ = ['DataCoreConfig', 'TaskProcessor', 'ExternalHandlerBase', 'discover_handlers']
@@ -0,0 +1,4 @@
1
+ from al_data_core.dags_installer import main
2
+
3
+ if __name__ == '__main__':
4
+ main()
@@ -0,0 +1,34 @@
1
+ from al_data_utils import airflow_utils
2
+ from al_data_utils.logging_utils import info
3
+
4
+
5
+ def run_azure_as_refresh(azure_as_cfg: dict, model_cfg: dict) -> None:
6
+ """
7
+ Refresh a single Azure Analysis Services semantic model.
8
+
9
+ Parameters
10
+ ----------
11
+ azure_as_cfg : dict
12
+ Must contain ``connection_variable`` — the Airflow variable name that
13
+ holds a dict with keys: tenant_id, client_id, client_secret, rollout,
14
+ server_name.
15
+ model_cfg : dict
16
+ Must contain ``name`` (model name) and ``refresh_request`` (the XMLA
17
+ refresh payload dict passed to SemanticModels.refresh_models).
18
+ """
19
+ from al_data_utils.azure_as_utils import SemanticModels
20
+
21
+ conn = airflow_utils.get_variable_value_dict(azure_as_cfg['connection_variable'])
22
+
23
+ sm = SemanticModels(
24
+ tenant_id=conn['tenant_id'],
25
+ client_id=conn['client_id'],
26
+ client_secret=conn['client_secret'],
27
+ rollout=conn['rollout'],
28
+ server_name=conn['server_name'],
29
+ )
30
+
31
+ model_name = model_cfg['name']
32
+ info(f'Starting Azure AS refresh for model: {model_name}')
33
+ sm.refresh_models([(model_name, model_cfg['refresh_request'])])
34
+ info(f'Azure AS refresh completed for model: {model_name}')