manualforge 0.1.1__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.
- manualforge-0.1.1/PKG-INFO +236 -0
- manualforge-0.1.1/README.md +205 -0
- manualforge-0.1.1/pyproject.toml +92 -0
- manualforge-0.1.1/setup.cfg +4 -0
- manualforge-0.1.1/src/manualforge/__init__.py +3 -0
- manualforge-0.1.1/src/manualforge/__main__.py +25 -0
- manualforge-0.1.1/src/manualforge/config.py +73 -0
- manualforge-0.1.1/src/manualforge/hooks.py +425 -0
- manualforge-0.1.1/src/manualforge/io/__init__.py +7 -0
- manualforge-0.1.1/src/manualforge/io/polars_excel_dataset.py +144 -0
- manualforge-0.1.1/src/manualforge/pipeline_registry.py +17 -0
- manualforge-0.1.1/src/manualforge/pipelines/__init__.py +0 -0
- manualforge-0.1.1/src/manualforge/pipelines/data_processong_pl/__init__.py +10 -0
- manualforge-0.1.1/src/manualforge/pipelines/data_processong_pl/nodes.py +1282 -0
- manualforge-0.1.1/src/manualforge/pipelines/data_processong_pl/pipeline.py +120 -0
- manualforge-0.1.1/src/manualforge/pipelines/data_processong_pl/rulecsv2typ.py +333 -0
- manualforge-0.1.1/src/manualforge/pipelines/data_processong_pl/standardize_fields.py +709 -0
- manualforge-0.1.1/src/manualforge/settings.py +53 -0
- manualforge-0.1.1/src/manualforge.egg-info/PKG-INFO +236 -0
- manualforge-0.1.1/src/manualforge.egg-info/SOURCES.txt +22 -0
- manualforge-0.1.1/src/manualforge.egg-info/dependency_links.txt +1 -0
- manualforge-0.1.1/src/manualforge.egg-info/entry_points.txt +2 -0
- manualforge-0.1.1/src/manualforge.egg-info/requires.txt +27 -0
- manualforge-0.1.1/src/manualforge.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: manualforge
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Requires-Python: >=3.10
|
|
5
|
+
Description-Content-Type: text/markdown
|
|
6
|
+
Requires-Dist: kedro~=1.2.0
|
|
7
|
+
Requires-Dist: kedro-datasets~=9.2
|
|
8
|
+
Requires-Dist: polars>=1.0
|
|
9
|
+
Requires-Dist: polars-runtime-compat>=1.38.1
|
|
10
|
+
Requires-Dist: fastexcel~=0.19
|
|
11
|
+
Requires-Dist: Jinja2>=3.0
|
|
12
|
+
Requires-Dist: duckdb>=1.0
|
|
13
|
+
Provides-Extra: docs
|
|
14
|
+
Requires-Dist: docutils<0.21; extra == "docs"
|
|
15
|
+
Requires-Dist: sphinx<7.3,>=5.3; extra == "docs"
|
|
16
|
+
Requires-Dist: sphinx_rtd_theme==2.0.0; extra == "docs"
|
|
17
|
+
Requires-Dist: nbsphinx==0.8.1; extra == "docs"
|
|
18
|
+
Requires-Dist: sphinx-autodoc-typehints==1.20.2; extra == "docs"
|
|
19
|
+
Requires-Dist: sphinx_copybutton==0.5.2; extra == "docs"
|
|
20
|
+
Requires-Dist: ipykernel<7.0,>=5.3; extra == "docs"
|
|
21
|
+
Requires-Dist: Jinja2<3.2.0; extra == "docs"
|
|
22
|
+
Requires-Dist: myst-parser<2.1,>=1.0; extra == "docs"
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: ipython>=8.10; extra == "dev"
|
|
25
|
+
Requires-Dist: jupyterlab>=3.0; extra == "dev"
|
|
26
|
+
Requires-Dist: notebook; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov<7,>=3; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-mock<2.0,>=1.7.1; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest~=9.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff~=0.15.0; extra == "dev"
|
|
31
|
+
|
|
32
|
+
# ManualForge
|
|
33
|
+
|
|
34
|
+
> **Configuration-driven management manual generation framework.**
|
|
35
|
+
> Define your data sources, fields, and templates in YAML — get a formatted report.
|
|
36
|
+
|
|
37
|
+
Built on [Kedro](https://kedro.org) pipelines with [Polars](https://pola.rs) for data processing and [Typst](https://typst.app) for document rendering.
|
|
38
|
+
|
|
39
|
+
## Philosophy
|
|
40
|
+
|
|
41
|
+
ManualForge separates **what** you want to produce from **how** it's produced.
|
|
42
|
+
|
|
43
|
+
- **What**: Defined in `conf/base/parameters_manualforge.yml` — your data sources, expected columns, standardization rules, sort orders, summary dimensions, and report templates.
|
|
44
|
+
- **How**: Implemented by the pipeline nodes — reusable data processing functions that read from your config.
|
|
45
|
+
|
|
46
|
+
To create a new manual for a different domain, you only need to edit the config file (and optionally provide new templates). No Python code changes required.
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
| Capability | Description |
|
|
51
|
+
|---|---|
|
|
52
|
+
| **Multi-sheet Excel ingestion** | Auto-detect headers, filter cover sheets, merge into structured DataFrames |
|
|
53
|
+
| **Field standardization** | Mapping files + exact matching + fuzzy matching (difflib / duckdb) |
|
|
54
|
+
| **Config-driven summaries** | Define group-by dimensions, sort orders, ability categories, and output paths in YAML |
|
|
55
|
+
| **Typst report generation** | Jinja2 templates → Typst source → PDF compilation |
|
|
56
|
+
| **Pipeline hooks** | Shell command hooks at pipeline/node granularity for pre/post processing |
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 1. Install dependencies
|
|
62
|
+
pip install -r requirements.txt
|
|
63
|
+
|
|
64
|
+
# 2. Copy and customize configuration
|
|
65
|
+
cp conf/examples/parameters_manualforge.yml.example conf/base/parameters_manualforge.yml
|
|
66
|
+
cp conf/examples/catalog.yml.example conf/base/catalog.yml
|
|
67
|
+
cp conf/examples/hooks.yml.example conf/base/hooks.yml
|
|
68
|
+
cp conf/examples/parameters.yml.example conf/base/parameters.yml
|
|
69
|
+
cp conf/examples/credentials.yml.example conf/local/credentials.yml
|
|
70
|
+
|
|
71
|
+
# 3. Edit the config files to point to your data sources
|
|
72
|
+
# (conf/base/ is gitignored — your real configs stay local)
|
|
73
|
+
|
|
74
|
+
# 4. Run the pipeline
|
|
75
|
+
kedro run
|
|
76
|
+
|
|
77
|
+
# Run specific node groups
|
|
78
|
+
kedro run --tags conversion # Excel → Parquet only
|
|
79
|
+
kedro run --tags standardization # Standardization only
|
|
80
|
+
kedro run --tags csv # Summary tables only
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Project Structure
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
├── conf/
|
|
87
|
+
│ ├── base/ # ★ Gitignored — copy from examples/
|
|
88
|
+
│ │ ├── parameters_manualforge.yml # Central project configuration
|
|
89
|
+
│ │ ├── catalog.yml # Kedro data catalog
|
|
90
|
+
│ │ ├── hooks.yml # Pipeline hooks (shell commands)
|
|
91
|
+
│ │ └── parameters.yml # Pipeline parameters
|
|
92
|
+
│ ├── examples/ # ★ Tracked example templates
|
|
93
|
+
│ │ ├── parameters_manualforge.yml.example
|
|
94
|
+
│ │ ├── catalog.yml.example
|
|
95
|
+
│ │ ├── hooks.yml.example
|
|
96
|
+
│ │ ├── parameters.yml.example
|
|
97
|
+
│ │ └── credentials.yml.example
|
|
98
|
+
│ ├── local/ # Local-only (gitignored)
|
|
99
|
+
│ │ └── credentials.yml
|
|
100
|
+
│ └── logging.yml
|
|
101
|
+
├── data/ # Gitignored except .gitkeep
|
|
102
|
+
│ ├── 01_raw/ # Raw Excel/CSV + mapping files
|
|
103
|
+
│ ├── 02_intermediate/ # Parquet, reconcile reports
|
|
104
|
+
│ ├── 03_primary/ # Standardized data
|
|
105
|
+
│ ├── 04_feature/ # Summary tables (CSV + Markdown)
|
|
106
|
+
│ └── 08_reporting/ # Typst sources & compiled PDFs
|
|
107
|
+
├── scripts/ # Auxiliary scripts
|
|
108
|
+
│ ├── convert_csv_to_md.py # CSV → Markdown conversion
|
|
109
|
+
│ ├── extract_rule_field_mapping.py # Rule field extraction
|
|
110
|
+
│ ├── extract_rule_overview.py # Rule overview extraction
|
|
111
|
+
│ └── render_with_forge.py # Markdown → DOCX/PDF rendering
|
|
112
|
+
├── src/manualforge/ # Framework source code
|
|
113
|
+
│ ├── config.py # Configuration helper utilities
|
|
114
|
+
│ ├── hooks.py # Kedro pipeline hooks
|
|
115
|
+
│ ├── io/ # Custom Kedro datasets (PolarsExcelDataset)
|
|
116
|
+
│ ├── pipelines/ # Pipeline definitions & node functions
|
|
117
|
+
│ └── settings.py # Kedro project settings
|
|
118
|
+
├── templates/ # Jinja2 Typst templates
|
|
119
|
+
│ └── report.typ.j2
|
|
120
|
+
├── pyproject.toml # Project metadata & dependencies
|
|
121
|
+
└── requirements.txt
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Configuration Guide
|
|
125
|
+
|
|
126
|
+
The central configuration file is `conf/base/parameters_manualforge.yml`. Copy from `conf/examples/` and customize:
|
|
127
|
+
|
|
128
|
+
### 1. Data Sources
|
|
129
|
+
|
|
130
|
+
Define your Excel files, expected headers, and sheet filtering rules:
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
datasources:
|
|
134
|
+
primary_data:
|
|
135
|
+
filepath: "data/01_raw/your_data.xlsx"
|
|
136
|
+
sheet:
|
|
137
|
+
exclude_names: ["封面", "封皮"]
|
|
138
|
+
name_becomes_column: "sheet_name"
|
|
139
|
+
header_detection:
|
|
140
|
+
mode: keyword_match
|
|
141
|
+
expected_headers:
|
|
142
|
+
- "column_a"
|
|
143
|
+
- "column_b"
|
|
144
|
+
cleaning:
|
|
145
|
+
drop_rows_where:
|
|
146
|
+
column_a: ["column_a"] # drop residual header rows
|
|
147
|
+
fill_null: forward
|
|
148
|
+
deduplicate: true
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### 2. Field Standardization
|
|
152
|
+
|
|
153
|
+
Define which fields to standardize, their mapping files, and special corrections:
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
156
|
+
standardization:
|
|
157
|
+
fields:
|
|
158
|
+
- name: "dept_name"
|
|
159
|
+
mapping_file: "data/01_raw/dept_list"
|
|
160
|
+
case_corrections:
|
|
161
|
+
wrong_name: "correct_name"
|
|
162
|
+
special_mappings:
|
|
163
|
+
alias: "canonical_name"
|
|
164
|
+
fuzzy:
|
|
165
|
+
enabled: true
|
|
166
|
+
threshold: 0.8
|
|
167
|
+
method: difflib # difflib | duckdb
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 3. Sort Orders
|
|
171
|
+
|
|
172
|
+
Define reusable sort order lists referenced by summaries:
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
sort_orders:
|
|
176
|
+
model_names:
|
|
177
|
+
- "Model A"
|
|
178
|
+
- "Model B"
|
|
179
|
+
dep_names:
|
|
180
|
+
- "HR"
|
|
181
|
+
- "Finance"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 4. Summaries
|
|
185
|
+
|
|
186
|
+
Define what summary tables to generate:
|
|
187
|
+
|
|
188
|
+
```yaml
|
|
189
|
+
summaries:
|
|
190
|
+
my_summary:
|
|
191
|
+
description: "Fields grouped by model and department"
|
|
192
|
+
group_by: ["model", "department"]
|
|
193
|
+
struct_columns: ["module", "system", "field_name"]
|
|
194
|
+
sort_by:
|
|
195
|
+
department: dep_names
|
|
196
|
+
output:
|
|
197
|
+
csv: "data/04_feature/my_summary.csv"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 5. Reports
|
|
201
|
+
|
|
202
|
+
Define report templates and output:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
reports:
|
|
206
|
+
my_report:
|
|
207
|
+
description: "Rules cookbook"
|
|
208
|
+
template_source: inline
|
|
209
|
+
data_source: rules_data
|
|
210
|
+
output_typ: "data/08_reporting/output.typ"
|
|
211
|
+
typst_compile:
|
|
212
|
+
enabled: true
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Data Layers
|
|
216
|
+
|
|
217
|
+
| Layer | Directory | Description |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| Raw | `data/01_raw/` | Source Excel/CSV files, mapping files |
|
|
220
|
+
| Intermediate | `data/02_intermediate/` | Parquet, reconcile reports |
|
|
221
|
+
| Primary | `data/03_primary/` | Standardized data |
|
|
222
|
+
| Feature | `data/04_feature/` | Summary tables (CSV + Markdown) |
|
|
223
|
+
| Reporting | `data/08_reporting/` | Typst sources & PDF output |
|
|
224
|
+
|
|
225
|
+
## Requirements
|
|
226
|
+
|
|
227
|
+
- Python >= 3.10
|
|
228
|
+
- [Typst](https://github.com/typst/typst) CLI (for PDF compilation)
|
|
229
|
+
|
|
230
|
+
## Development
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
pip install -e ".[dev]"
|
|
234
|
+
ruff check src/
|
|
235
|
+
pytest
|
|
236
|
+
```
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# ManualForge
|
|
2
|
+
|
|
3
|
+
> **Configuration-driven management manual generation framework.**
|
|
4
|
+
> Define your data sources, fields, and templates in YAML — get a formatted report.
|
|
5
|
+
|
|
6
|
+
Built on [Kedro](https://kedro.org) pipelines with [Polars](https://pola.rs) for data processing and [Typst](https://typst.app) for document rendering.
|
|
7
|
+
|
|
8
|
+
## Philosophy
|
|
9
|
+
|
|
10
|
+
ManualForge separates **what** you want to produce from **how** it's produced.
|
|
11
|
+
|
|
12
|
+
- **What**: Defined in `conf/base/parameters_manualforge.yml` — your data sources, expected columns, standardization rules, sort orders, summary dimensions, and report templates.
|
|
13
|
+
- **How**: Implemented by the pipeline nodes — reusable data processing functions that read from your config.
|
|
14
|
+
|
|
15
|
+
To create a new manual for a different domain, you only need to edit the config file (and optionally provide new templates). No Python code changes required.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
| Capability | Description |
|
|
20
|
+
|---|---|
|
|
21
|
+
| **Multi-sheet Excel ingestion** | Auto-detect headers, filter cover sheets, merge into structured DataFrames |
|
|
22
|
+
| **Field standardization** | Mapping files + exact matching + fuzzy matching (difflib / duckdb) |
|
|
23
|
+
| **Config-driven summaries** | Define group-by dimensions, sort orders, ability categories, and output paths in YAML |
|
|
24
|
+
| **Typst report generation** | Jinja2 templates → Typst source → PDF compilation |
|
|
25
|
+
| **Pipeline hooks** | Shell command hooks at pipeline/node granularity for pre/post processing |
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# 1. Install dependencies
|
|
31
|
+
pip install -r requirements.txt
|
|
32
|
+
|
|
33
|
+
# 2. Copy and customize configuration
|
|
34
|
+
cp conf/examples/parameters_manualforge.yml.example conf/base/parameters_manualforge.yml
|
|
35
|
+
cp conf/examples/catalog.yml.example conf/base/catalog.yml
|
|
36
|
+
cp conf/examples/hooks.yml.example conf/base/hooks.yml
|
|
37
|
+
cp conf/examples/parameters.yml.example conf/base/parameters.yml
|
|
38
|
+
cp conf/examples/credentials.yml.example conf/local/credentials.yml
|
|
39
|
+
|
|
40
|
+
# 3. Edit the config files to point to your data sources
|
|
41
|
+
# (conf/base/ is gitignored — your real configs stay local)
|
|
42
|
+
|
|
43
|
+
# 4. Run the pipeline
|
|
44
|
+
kedro run
|
|
45
|
+
|
|
46
|
+
# Run specific node groups
|
|
47
|
+
kedro run --tags conversion # Excel → Parquet only
|
|
48
|
+
kedro run --tags standardization # Standardization only
|
|
49
|
+
kedro run --tags csv # Summary tables only
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Project Structure
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
├── conf/
|
|
56
|
+
│ ├── base/ # ★ Gitignored — copy from examples/
|
|
57
|
+
│ │ ├── parameters_manualforge.yml # Central project configuration
|
|
58
|
+
│ │ ├── catalog.yml # Kedro data catalog
|
|
59
|
+
│ │ ├── hooks.yml # Pipeline hooks (shell commands)
|
|
60
|
+
│ │ └── parameters.yml # Pipeline parameters
|
|
61
|
+
│ ├── examples/ # ★ Tracked example templates
|
|
62
|
+
│ │ ├── parameters_manualforge.yml.example
|
|
63
|
+
│ │ ├── catalog.yml.example
|
|
64
|
+
│ │ ├── hooks.yml.example
|
|
65
|
+
│ │ ├── parameters.yml.example
|
|
66
|
+
│ │ └── credentials.yml.example
|
|
67
|
+
│ ├── local/ # Local-only (gitignored)
|
|
68
|
+
│ │ └── credentials.yml
|
|
69
|
+
│ └── logging.yml
|
|
70
|
+
├── data/ # Gitignored except .gitkeep
|
|
71
|
+
│ ├── 01_raw/ # Raw Excel/CSV + mapping files
|
|
72
|
+
│ ├── 02_intermediate/ # Parquet, reconcile reports
|
|
73
|
+
│ ├── 03_primary/ # Standardized data
|
|
74
|
+
│ ├── 04_feature/ # Summary tables (CSV + Markdown)
|
|
75
|
+
│ └── 08_reporting/ # Typst sources & compiled PDFs
|
|
76
|
+
├── scripts/ # Auxiliary scripts
|
|
77
|
+
│ ├── convert_csv_to_md.py # CSV → Markdown conversion
|
|
78
|
+
│ ├── extract_rule_field_mapping.py # Rule field extraction
|
|
79
|
+
│ ├── extract_rule_overview.py # Rule overview extraction
|
|
80
|
+
│ └── render_with_forge.py # Markdown → DOCX/PDF rendering
|
|
81
|
+
├── src/manualforge/ # Framework source code
|
|
82
|
+
│ ├── config.py # Configuration helper utilities
|
|
83
|
+
│ ├── hooks.py # Kedro pipeline hooks
|
|
84
|
+
│ ├── io/ # Custom Kedro datasets (PolarsExcelDataset)
|
|
85
|
+
│ ├── pipelines/ # Pipeline definitions & node functions
|
|
86
|
+
│ └── settings.py # Kedro project settings
|
|
87
|
+
├── templates/ # Jinja2 Typst templates
|
|
88
|
+
│ └── report.typ.j2
|
|
89
|
+
├── pyproject.toml # Project metadata & dependencies
|
|
90
|
+
└── requirements.txt
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Configuration Guide
|
|
94
|
+
|
|
95
|
+
The central configuration file is `conf/base/parameters_manualforge.yml`. Copy from `conf/examples/` and customize:
|
|
96
|
+
|
|
97
|
+
### 1. Data Sources
|
|
98
|
+
|
|
99
|
+
Define your Excel files, expected headers, and sheet filtering rules:
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
datasources:
|
|
103
|
+
primary_data:
|
|
104
|
+
filepath: "data/01_raw/your_data.xlsx"
|
|
105
|
+
sheet:
|
|
106
|
+
exclude_names: ["封面", "封皮"]
|
|
107
|
+
name_becomes_column: "sheet_name"
|
|
108
|
+
header_detection:
|
|
109
|
+
mode: keyword_match
|
|
110
|
+
expected_headers:
|
|
111
|
+
- "column_a"
|
|
112
|
+
- "column_b"
|
|
113
|
+
cleaning:
|
|
114
|
+
drop_rows_where:
|
|
115
|
+
column_a: ["column_a"] # drop residual header rows
|
|
116
|
+
fill_null: forward
|
|
117
|
+
deduplicate: true
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. Field Standardization
|
|
121
|
+
|
|
122
|
+
Define which fields to standardize, their mapping files, and special corrections:
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
standardization:
|
|
126
|
+
fields:
|
|
127
|
+
- name: "dept_name"
|
|
128
|
+
mapping_file: "data/01_raw/dept_list"
|
|
129
|
+
case_corrections:
|
|
130
|
+
wrong_name: "correct_name"
|
|
131
|
+
special_mappings:
|
|
132
|
+
alias: "canonical_name"
|
|
133
|
+
fuzzy:
|
|
134
|
+
enabled: true
|
|
135
|
+
threshold: 0.8
|
|
136
|
+
method: difflib # difflib | duckdb
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 3. Sort Orders
|
|
140
|
+
|
|
141
|
+
Define reusable sort order lists referenced by summaries:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
sort_orders:
|
|
145
|
+
model_names:
|
|
146
|
+
- "Model A"
|
|
147
|
+
- "Model B"
|
|
148
|
+
dep_names:
|
|
149
|
+
- "HR"
|
|
150
|
+
- "Finance"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### 4. Summaries
|
|
154
|
+
|
|
155
|
+
Define what summary tables to generate:
|
|
156
|
+
|
|
157
|
+
```yaml
|
|
158
|
+
summaries:
|
|
159
|
+
my_summary:
|
|
160
|
+
description: "Fields grouped by model and department"
|
|
161
|
+
group_by: ["model", "department"]
|
|
162
|
+
struct_columns: ["module", "system", "field_name"]
|
|
163
|
+
sort_by:
|
|
164
|
+
department: dep_names
|
|
165
|
+
output:
|
|
166
|
+
csv: "data/04_feature/my_summary.csv"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 5. Reports
|
|
170
|
+
|
|
171
|
+
Define report templates and output:
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
reports:
|
|
175
|
+
my_report:
|
|
176
|
+
description: "Rules cookbook"
|
|
177
|
+
template_source: inline
|
|
178
|
+
data_source: rules_data
|
|
179
|
+
output_typ: "data/08_reporting/output.typ"
|
|
180
|
+
typst_compile:
|
|
181
|
+
enabled: true
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Data Layers
|
|
185
|
+
|
|
186
|
+
| Layer | Directory | Description |
|
|
187
|
+
|---|---|---|
|
|
188
|
+
| Raw | `data/01_raw/` | Source Excel/CSV files, mapping files |
|
|
189
|
+
| Intermediate | `data/02_intermediate/` | Parquet, reconcile reports |
|
|
190
|
+
| Primary | `data/03_primary/` | Standardized data |
|
|
191
|
+
| Feature | `data/04_feature/` | Summary tables (CSV + Markdown) |
|
|
192
|
+
| Reporting | `data/08_reporting/` | Typst sources & PDF output |
|
|
193
|
+
|
|
194
|
+
## Requirements
|
|
195
|
+
|
|
196
|
+
- Python >= 3.10
|
|
197
|
+
- [Typst](https://github.com/typst/typst) CLI (for PDF compilation)
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
pip install -e ".[dev]"
|
|
203
|
+
ruff check src/
|
|
204
|
+
pytest
|
|
205
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
name = "manualforge"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
dynamic = ["version"]
|
|
10
|
+
dependencies = [
|
|
11
|
+
"kedro~=1.2.0",
|
|
12
|
+
"kedro-datasets~=9.2",
|
|
13
|
+
"polars>=1.0",
|
|
14
|
+
"polars-runtime-compat>=1.38.1",
|
|
15
|
+
"fastexcel~=0.19",
|
|
16
|
+
"Jinja2>=3.0",
|
|
17
|
+
"duckdb>=1.0",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
"manualforge" = "manualforge.__main__:main"
|
|
22
|
+
|
|
23
|
+
[project.entry-points."kedro.hooks"]
|
|
24
|
+
|
|
25
|
+
[project.optional-dependencies]
|
|
26
|
+
docs = [
|
|
27
|
+
"docutils<0.21",
|
|
28
|
+
"sphinx>=5.3,<7.3",
|
|
29
|
+
"sphinx_rtd_theme==2.0.0",
|
|
30
|
+
"nbsphinx==0.8.1",
|
|
31
|
+
"sphinx-autodoc-typehints==1.20.2",
|
|
32
|
+
"sphinx_copybutton==0.5.2",
|
|
33
|
+
"ipykernel>=5.3, <7.0",
|
|
34
|
+
"Jinja2<3.2.0",
|
|
35
|
+
"myst-parser>=1.0,<2.1"
|
|
36
|
+
]
|
|
37
|
+
dev = [
|
|
38
|
+
"ipython>=8.10",
|
|
39
|
+
"jupyterlab>=3.0",
|
|
40
|
+
"notebook",
|
|
41
|
+
"pytest-cov>=3,<7",
|
|
42
|
+
"pytest-mock>=1.7.1, <2.0",
|
|
43
|
+
"pytest~=9.0",
|
|
44
|
+
"ruff~=0.15.0"
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.dynamic]
|
|
48
|
+
version = {attr = "manualforge.__version__"}
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.packages.find]
|
|
51
|
+
where = ["src"]
|
|
52
|
+
namespaces = false
|
|
53
|
+
|
|
54
|
+
[tool.kedro]
|
|
55
|
+
package_name = "manualforge"
|
|
56
|
+
project_name = "manualforge"
|
|
57
|
+
kedro_init_version = "1.1.1"
|
|
58
|
+
tools = "['Linting', 'Testing', 'Custom Logging', 'Documentation', 'Data Structure']"
|
|
59
|
+
example_pipeline = "False"
|
|
60
|
+
source_dir = "src"
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
addopts = """
|
|
64
|
+
--cov-report term-missing \
|
|
65
|
+
--cov src/manualforge -ra"""
|
|
66
|
+
|
|
67
|
+
[tool.coverage.report]
|
|
68
|
+
fail_under = 0
|
|
69
|
+
show_missing = true
|
|
70
|
+
exclude_lines = ["pragma: no cover", "raise NotImplementedError"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.format]
|
|
73
|
+
docstring-code-format = true
|
|
74
|
+
|
|
75
|
+
[tool.ruff]
|
|
76
|
+
line-length = 88
|
|
77
|
+
show-fixes = true
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint]
|
|
80
|
+
select = [
|
|
81
|
+
"F", # Pyflakes
|
|
82
|
+
"W", # pycodestyle
|
|
83
|
+
"E", # pycodestyle
|
|
84
|
+
"I", # isort
|
|
85
|
+
"UP", # pyupgrade
|
|
86
|
+
"PL", # Pylint
|
|
87
|
+
"T201", # Print Statement
|
|
88
|
+
]
|
|
89
|
+
ignore = ["E501"] # Ruff format takes care of line-too-long
|
|
90
|
+
|
|
91
|
+
[tool.kedro_telemetry]
|
|
92
|
+
project_id = "d5d6a6859fac4a9c899980b536809946"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""manualforge file for ensuring the package is executable
|
|
2
|
+
as `manualforge` and `python -m manualforge`
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from kedro.framework.cli.utils import find_run_command
|
|
10
|
+
from kedro.framework.project import configure_project
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(*args, **kwargs) -> Any:
|
|
14
|
+
package_name = Path(__file__).parent.name
|
|
15
|
+
configure_project(package_name)
|
|
16
|
+
|
|
17
|
+
interactive = hasattr(sys, "ps1")
|
|
18
|
+
kwargs["standalone_mode"] = not interactive
|
|
19
|
+
|
|
20
|
+
run = find_run_command(package_name)
|
|
21
|
+
return run(*args, **kwargs)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
main()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""ManualForge configuration helpers.
|
|
2
|
+
|
|
3
|
+
Utilities for safely reading project configuration and providing sensible
|
|
4
|
+
defaults, so node functions stay clean when operating in config-driven mode.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
# Sentinel for "not set" to distinguish from explicit None.
|
|
15
|
+
_UNSET = object()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_datasource(config: dict, source_id: str) -> dict:
|
|
19
|
+
"""Return the datasource sub-config for *source_id*."""
|
|
20
|
+
sources = config.get("datasources", {})
|
|
21
|
+
if source_id not in sources:
|
|
22
|
+
raise KeyError(f"Datasource '{source_id}' not found in config.datasources")
|
|
23
|
+
return sources[source_id]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_standardization(config: dict) -> dict:
|
|
27
|
+
"""Return the standardization config section."""
|
|
28
|
+
return config.get("standardization", {})
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_standardization_fields(config: dict) -> list[dict]:
|
|
32
|
+
"""Return the list of field-standardization definitions."""
|
|
33
|
+
return get_standardization(config).get("fields", [])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_summary(config: dict, summary_id: str) -> dict:
|
|
37
|
+
"""Return the summary sub-config for *summary_id*."""
|
|
38
|
+
summaries = config.get("summaries", {})
|
|
39
|
+
if summary_id not in summaries:
|
|
40
|
+
raise KeyError(f"Summary '{summary_id}' not found in config.summaries")
|
|
41
|
+
return summaries[summary_id]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_sort_order(config: dict, order_name: str) -> list[str]:
|
|
45
|
+
"""Return a named sort-order list."""
|
|
46
|
+
return config.get("sort_orders", {}).get(order_name, [])
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _resolve_sort_ref(config: dict, ref: str | list) -> list[str]:
|
|
50
|
+
"""Resolve a sort_by value which is either a sort-order name or an inline list."""
|
|
51
|
+
if isinstance(ref, list):
|
|
52
|
+
return ref
|
|
53
|
+
if isinstance(ref, str):
|
|
54
|
+
return get_sort_order(config, ref)
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_sort_list(config: dict, sort_by: dict) -> dict[str, list[str]]:
|
|
59
|
+
"""Resolve a sort_by dict {column: order_name_or_list} → {column: [values]}."""
|
|
60
|
+
return {col: _resolve_sort_ref(config, ref) for col, ref in sort_by.items()}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_report(config: dict, report_id: str) -> dict:
|
|
64
|
+
"""Return the report sub-config for *report_id*."""
|
|
65
|
+
reports = config.get("reports", {})
|
|
66
|
+
if report_id not in reports:
|
|
67
|
+
raise KeyError(f"Report '{report_id}' not found in config.reports")
|
|
68
|
+
return reports[report_id]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_project(config: dict) -> dict:
|
|
72
|
+
"""Return the project metadata section."""
|
|
73
|
+
return config.get("project", {})
|