wolfzbr 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wolfzbr-0.4.0/.gitignore +69 -0
- wolfzbr-0.4.0/LICENSE +21 -0
- wolfzbr-0.4.0/PKG-INFO +169 -0
- wolfzbr-0.4.0/README.md +114 -0
- wolfzbr-0.4.0/hatch_build.py +29 -0
- wolfzbr-0.4.0/pyproject.toml +129 -0
- wolfzbr-0.4.0/src/wolfz/__init__.py +22 -0
- wolfzbr-0.4.0/src/wolfz/api/__init__.py +1 -0
- wolfzbr-0.4.0/src/wolfz/api/app.py +58 -0
- wolfzbr-0.4.0/src/wolfz/api/modelos.py +148 -0
- wolfzbr-0.4.0/src/wolfz/api/problems.py +102 -0
- wolfzbr-0.4.0/src/wolfz/api/projetos.py +97 -0
- wolfzbr-0.4.0/src/wolfz/api/rotas.py +398 -0
- wolfzbr-0.4.0/src/wolfz/api/security.py +101 -0
- wolfzbr-0.4.0/src/wolfz/api/server.py +72 -0
- wolfzbr-0.4.0/src/wolfz/br/__init__.py +6 -0
- wolfzbr-0.4.0/src/wolfz/br/booleanos.py +25 -0
- wolfzbr-0.4.0/src/wolfz/br/constants.py +45 -0
- wolfzbr-0.4.0/src/wolfz/br/datas.py +137 -0
- wolfzbr-0.4.0/src/wolfz/br/documentos.py +215 -0
- wolfzbr-0.4.0/src/wolfz/br/geo.py +48 -0
- wolfzbr-0.4.0/src/wolfz/br/numeros.py +229 -0
- wolfzbr-0.4.0/src/wolfz/cli.py +336 -0
- wolfzbr-0.4.0/src/wolfz/core/__init__.py +1 -0
- wolfzbr-0.4.0/src/wolfz/core/detect/__init__.py +6 -0
- wolfzbr-0.4.0/src/wolfz/core/detect/metrics.py +144 -0
- wolfzbr-0.4.0/src/wolfz/core/detect/structure.py +297 -0
- wolfzbr-0.4.0/src/wolfz/core/engine/__init__.py +132 -0
- wolfzbr-0.4.0/src/wolfz/core/engine/cache.py +195 -0
- wolfzbr-0.4.0/src/wolfz/core/engine/errors.py +58 -0
- wolfzbr-0.4.0/src/wolfz/core/engine/preview.py +160 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/__init__.py +176 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/cells.py +153 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/csv.py +196 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/errors.py +93 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/excel.py +256 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/formats.py +145 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/limits.py +98 -0
- wolfzbr-0.4.0/src/wolfz/core/ingest/structure.py +208 -0
- wolfzbr-0.4.0/src/wolfz/core/naming.py +45 -0
- wolfzbr-0.4.0/src/wolfz/core/profile/__init__.py +20 -0
- wolfzbr-0.4.0/src/wolfz/core/profile/column.py +137 -0
- wolfzbr-0.4.0/src/wolfz/core/profile/infer.py +51 -0
- wolfzbr-0.4.0/src/wolfz/core/profile/inspect.py +68 -0
- wolfzbr-0.4.0/src/wolfz/core/profile/quick.py +45 -0
- wolfzbr-0.4.0/src/wolfz/core/recipe/__init__.py +32 -0
- wolfzbr-0.4.0/src/wolfz/core/recipe/io.py +173 -0
- wolfzbr-0.4.0/src/wolfz/core/recipe/migrate.py +62 -0
- wolfzbr-0.4.0/src/wolfz/core/recipe/models.py +205 -0
- wolfzbr-0.4.0/src/wolfz/core/suggest/__init__.py +1 -0
- wolfzbr-0.4.0/src/wolfz/core/text.py +115 -0
- wolfzbr-0.4.0/src/wolfz/core/workspace.py +134 -0
- wolfzbr-0.4.0/src/wolfz/export/__init__.py +1 -0
- wolfzbr-0.4.0/src/wolfz/export/dados.py +285 -0
- wolfzbr-0.4.0/src/wolfz/export/m.py +67 -0
- wolfzbr-0.4.0/src/wolfz/export/python.py +144 -0
- wolfzbr-0.4.0/src/wolfz/ops/__init__.py +55 -0
- wolfzbr-0.4.0/src/wolfz/ops/br.py +620 -0
- wolfzbr-0.4.0/src/wolfz/ops/colunas.py +354 -0
- wolfzbr-0.4.0/src/wolfz/ops/estrutura.py +517 -0
- wolfzbr-0.4.0/src/wolfz/ops/linhas.py +410 -0
- wolfzbr-0.4.0/src/wolfz/ops/m_br.py +369 -0
- wolfzbr-0.4.0/src/wolfz/ops/tabela.py +189 -0
- wolfzbr-0.4.0/src/wolfz/ops/texto.py +251 -0
- wolfzbr-0.4.0/src/wolfz/static/.gitkeep +0 -0
- wolfzbr-0.4.0/src/wolfz/static/assets/data-grid-overlay-editor-DbA0JPlf.js +1 -0
- wolfzbr-0.4.0/src/wolfz/static/assets/index-DWorz2kP.css +1 -0
- wolfzbr-0.4.0/src/wolfz/static/assets/index-Dx3RP7p-.js +182 -0
- wolfzbr-0.4.0/src/wolfz/static/assets/number-overlay-editor-DDUrQ7zj.js +9 -0
- wolfzbr-0.4.0/src/wolfz/static/index.html +15 -0
wolfzbr-0.4.0/.gitignore
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.spec
|
|
9
|
+
|
|
10
|
+
# Ambientes virtuais
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Variáveis de ambiente
|
|
16
|
+
.env
|
|
17
|
+
.env.*
|
|
18
|
+
!.env.example
|
|
19
|
+
|
|
20
|
+
# Testes / lint
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.hypothesis/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
coverage.xml
|
|
27
|
+
htmlcov/
|
|
28
|
+
|
|
29
|
+
# Frontend
|
|
30
|
+
node_modules/
|
|
31
|
+
frontend/dist/
|
|
32
|
+
frontend/test-results/
|
|
33
|
+
frontend/playwright-report/
|
|
34
|
+
src/wolfz/static/*
|
|
35
|
+
!src/wolfz/static/.gitkeep
|
|
36
|
+
|
|
37
|
+
# Workspace local e saídas do WolfzBR
|
|
38
|
+
.wolfz/
|
|
39
|
+
saida/
|
|
40
|
+
*.hyper
|
|
41
|
+
*.log
|
|
42
|
+
|
|
43
|
+
# Dados de usuário (nunca versionar planilhas fora do corpus de testes)
|
|
44
|
+
*.xlsx
|
|
45
|
+
*.xls
|
|
46
|
+
*.ods
|
|
47
|
+
*.csv
|
|
48
|
+
*.parquet
|
|
49
|
+
!tests/corpus/**
|
|
50
|
+
!src/wolfz/br/data/*.parquet
|
|
51
|
+
# Download pela metade de scripts/baixar_corpus.py. O original baixado é
|
|
52
|
+
# ignorado pelo .gitignore da própria pasta do corpus (F1.B2).
|
|
53
|
+
tests/corpus/**/.original.*.tmp
|
|
54
|
+
|
|
55
|
+
# Excel: arquivos temporários de lock
|
|
56
|
+
~$*
|
|
57
|
+
|
|
58
|
+
# Notebooks
|
|
59
|
+
.ipynb_checkpoints/
|
|
60
|
+
|
|
61
|
+
# IDE / SO
|
|
62
|
+
.vscode/
|
|
63
|
+
.idea/
|
|
64
|
+
.DS_Store
|
|
65
|
+
Thumbs.db
|
|
66
|
+
desktop.ini
|
|
67
|
+
|
|
68
|
+
# Planejamento pessoal (não versionado)
|
|
69
|
+
dev/
|
wolfzbr-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rafael Gomide
|
|
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.
|
wolfzbr-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: wolfzbr
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Limpeza de planilhas brasileiras com receitas reproduzíveis e exportação para Power Query M, Python, Tableau e Power BI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/RafaelGomide/WolfzBR
|
|
6
|
+
Project-URL: Repository, https://github.com/RafaelGomide/WolfzBR
|
|
7
|
+
Project-URL: Issues, https://github.com/RafaelGomide/WolfzBR/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/RafaelGomide/WolfzBR/blob/main/docs/system_design.md
|
|
9
|
+
Author: Rafael Gomide
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: brasil,excel,limpeza de dados,planilhas,polars,power bi,power query
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Environment :: Web Environment
|
|
16
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
17
|
+
Classifier: Intended Audience :: Science/Research
|
|
18
|
+
Classifier: Natural Language :: Portuguese (Brazilian)
|
|
19
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
20
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Office/Business :: Financial :: Spreadsheet
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
27
|
+
Requires-Python: >=3.12
|
|
28
|
+
Requires-Dist: fastapi>=0.111
|
|
29
|
+
Requires-Dist: fastexcel>=0.11
|
|
30
|
+
Requires-Dist: filelock>=3.13
|
|
31
|
+
Requires-Dist: openpyxl>=3.1
|
|
32
|
+
Requires-Dist: polars>=1.0
|
|
33
|
+
Requires-Dist: pyarrow>=16.0
|
|
34
|
+
Requires-Dist: pydantic>=2.7
|
|
35
|
+
Requires-Dist: python-calamine>=0.2
|
|
36
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
37
|
+
Requires-Dist: python-ulid>=2.7
|
|
38
|
+
Requires-Dist: rapidfuzz>=3.9
|
|
39
|
+
Requires-Dist: typer>=0.12
|
|
40
|
+
Requires-Dist: uvicorn[standard]>=0.30
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
43
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
44
|
+
Requires-Dist: import-linter>=2.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
46
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
48
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
49
|
+
Requires-Dist: syrupy>=4.6; extra == 'dev'
|
|
50
|
+
Provides-Extra: sql
|
|
51
|
+
Requires-Dist: duckdb>=1.0; extra == 'sql'
|
|
52
|
+
Provides-Extra: tableau
|
|
53
|
+
Requires-Dist: tableauhyperapi>=0.0.19; extra == 'tableau'
|
|
54
|
+
Description-Content-Type: text/markdown
|
|
55
|
+
|
|
56
|
+
# WolfzBR
|
|
57
|
+
|
|
58
|
+
[](https://github.com/RafaelGomide/WolfzBR/actions/workflows/ci.yml)
|
|
59
|
+
|
|
60
|
+
Limpeza de planilhas brasileiras com receitas reproduzíveis. Você arrasta a planilha suja (título, cabeçalho no meio, totais, `1.234,56`, `R$`, datas `31/12/2024`), ajusta os passos na tela e exporta a tabela limpa junto com a **receita**: o mesmo passo a passo em Power Query M e em script Python, para refazer no mês seguinte.
|
|
61
|
+
|
|
62
|
+
Tudo roda na sua máquina. Nenhum dado sai do seu computador.
|
|
63
|
+
|
|
64
|
+
> **Status:** pré-alfa (0.4). O formato da receita ainda pode mudar até a 1.0.
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
## Instalação
|
|
69
|
+
|
|
70
|
+
Requer Python 3.12 ou mais novo. Recomendado com [pipx](https://pipx.pypa.io/), que isola o WolfzBR num ambiente próprio:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pipx install wolfzbr
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
ou com `pip`, num ambiente virtual:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install wolfzbr
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
O pacote já traz a interface compilada; não precisa de Node.
|
|
83
|
+
|
|
84
|
+
## Uso
|
|
85
|
+
|
|
86
|
+
### Interface
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
wolfz ui
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Abre o navegador com a interface. Para usar:
|
|
93
|
+
|
|
94
|
+
1. Arraste o arquivo (`.xlsx`, `.xls`, `.ods` ou `.csv`). Se tiver mais de uma aba, escolha qual.
|
|
95
|
+
2. Confira a estrutura detectada: título, cabeçalho, dados, totais e rodapé aparecem em cores. Se a área estiver errada, clique e arraste na grade para marcar a certa.
|
|
96
|
+
3. Ajuste a receita: adicione, edite, reordene ou desative passos, vendo a tabela antes e depois de cada um. Clique no cabeçalho de uma coluna para ver o perfil dela.
|
|
97
|
+
4. Exporte: CSV (pronto para o Excel em português), Excel, Parquet, Power Query M e script Python.
|
|
98
|
+
|
|
99
|
+
A receita é salva automaticamente a cada edição. O endereço só funciona com a chave de acesso que o terminal mostra, nova a cada execução. Deixe o terminal aberto enquanto usa; Ctrl+C encerra.
|
|
100
|
+
|
|
101
|
+
Opções: `--port` fixa a porta; `--no-browser` só mostra o endereço, sem abrir o navegador.
|
|
102
|
+
|
|
103
|
+
### Linha de comando
|
|
104
|
+
|
|
105
|
+
Aplicar uma receita salva ao arquivo do mês seguinte:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
wolfz run vendas.wolfz.json vendas_fevereiro.xlsx -o saida/ --format xlsx
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- Se algum passo não se aplicar ao arquivo novo (uma coluna mudou de nome, por exemplo), nada é gravado e a mensagem diz qual passo falhou e quais colunas existem.
|
|
112
|
+
- Se a aba muda de nome todo mês (`Vendas 2024` → `Vendas 2025`), use `source.match.sheet_name_regex` na receita.
|
|
113
|
+
|
|
114
|
+
Outros comandos:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
wolfz inspect planilha.xlsx # perfil das colunas no terminal
|
|
118
|
+
wolfz schema # JSON Schema da receita
|
|
119
|
+
wolfz --help
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Códigos de saída do `run`: 0 ok · 1 um passo falhou · 2 receita inválida ou uso incorreto · 3 arquivo ilegível.
|
|
123
|
+
|
|
124
|
+
### Em Python
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
import wolfz
|
|
128
|
+
|
|
129
|
+
df = wolfz.run("vendas.wolfz.json", "vendas_fevereiro.xlsx") # polars.DataFrame
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Exportação
|
|
133
|
+
|
|
134
|
+
| Formato | O que sai |
|
|
135
|
+
|---|---|
|
|
136
|
+
| CSV | UTF-8 com BOM; separador `,`, `;` ou tabulação; vírgula decimal opcional. Opção de neutralizar fórmulas (`=`, `+`, `-`, `@`). |
|
|
137
|
+
| Excel (`.xlsx`) | Data como data, número como número; texto nunca vira fórmula. |
|
|
138
|
+
| Parquet | Tipos preservados, compressão zstd. |
|
|
139
|
+
| Power Query M | A receita como função M, para colar no Power BI ou no Excel. |
|
|
140
|
+
| Script Python | A receita como script autônomo, que só depende do Polars. |
|
|
141
|
+
|
|
142
|
+
Tableau (`.hyper`) e projeto do Power BI (`.pbip`) estão previstos para versões futuras.
|
|
143
|
+
|
|
144
|
+
## Padrões brasileiros
|
|
145
|
+
|
|
146
|
+
O catálogo tem 31 operações: estrutura (área, cabeçalho, linhas e colunas vazias, preencher para baixo, despivotar), colunas, texto e padrões brasileiros. Os padrões cobrem:
|
|
147
|
+
|
|
148
|
+
- números (`1.234,56`, `(1,5)`, `10-` do SAP, `R$`);
|
|
149
|
+
- percentual, datas e mês/ano;
|
|
150
|
+
- sim/não;
|
|
151
|
+
- CPF, CNPJ (inclusive o alfanumérico), CEP e UF.
|
|
152
|
+
|
|
153
|
+
Cada operação tem a mesma regra no motor (Polars), no M e no script Python, com teste de equivalência.
|
|
154
|
+
|
|
155
|
+
## Limites
|
|
156
|
+
|
|
157
|
+
- Arquivo de até 100 MB.
|
|
158
|
+
- Uma tabela por aba. Duas tabelas na mesma aba: marque a área de cada uma em projetos separados.
|
|
159
|
+
- Cabeçalho em várias linhas é juntado num nome só, mas o cabeçalho mesclado na horizontal ainda não é preenchido. Isso chega na 0.6.
|
|
160
|
+
|
|
161
|
+
## Documentação
|
|
162
|
+
|
|
163
|
+
- Desenho técnico: [`docs/system_design.md`](docs/system_design.md)
|
|
164
|
+
- Mudanças: [`CHANGELOG.md`](CHANGELOG.md)
|
|
165
|
+
- Como contribuir: [`CONTRIBUTING.md`](CONTRIBUTING.md)
|
|
166
|
+
|
|
167
|
+
## Licença
|
|
168
|
+
|
|
169
|
+
[MIT](LICENSE)
|
wolfzbr-0.4.0/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# WolfzBR
|
|
2
|
+
|
|
3
|
+
[](https://github.com/RafaelGomide/WolfzBR/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
Limpeza de planilhas brasileiras com receitas reproduzíveis. Você arrasta a planilha suja (título, cabeçalho no meio, totais, `1.234,56`, `R$`, datas `31/12/2024`), ajusta os passos na tela e exporta a tabela limpa junto com a **receita**: o mesmo passo a passo em Power Query M e em script Python, para refazer no mês seguinte.
|
|
6
|
+
|
|
7
|
+
Tudo roda na sua máquina. Nenhum dado sai do seu computador.
|
|
8
|
+
|
|
9
|
+
> **Status:** pré-alfa (0.4). O formato da receita ainda pode mudar até a 1.0.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
## Instalação
|
|
14
|
+
|
|
15
|
+
Requer Python 3.12 ou mais novo. Recomendado com [pipx](https://pipx.pypa.io/), que isola o WolfzBR num ambiente próprio:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pipx install wolfzbr
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
ou com `pip`, num ambiente virtual:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install wolfzbr
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
O pacote já traz a interface compilada; não precisa de Node.
|
|
28
|
+
|
|
29
|
+
## Uso
|
|
30
|
+
|
|
31
|
+
### Interface
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
wolfz ui
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Abre o navegador com a interface. Para usar:
|
|
38
|
+
|
|
39
|
+
1. Arraste o arquivo (`.xlsx`, `.xls`, `.ods` ou `.csv`). Se tiver mais de uma aba, escolha qual.
|
|
40
|
+
2. Confira a estrutura detectada: título, cabeçalho, dados, totais e rodapé aparecem em cores. Se a área estiver errada, clique e arraste na grade para marcar a certa.
|
|
41
|
+
3. Ajuste a receita: adicione, edite, reordene ou desative passos, vendo a tabela antes e depois de cada um. Clique no cabeçalho de uma coluna para ver o perfil dela.
|
|
42
|
+
4. Exporte: CSV (pronto para o Excel em português), Excel, Parquet, Power Query M e script Python.
|
|
43
|
+
|
|
44
|
+
A receita é salva automaticamente a cada edição. O endereço só funciona com a chave de acesso que o terminal mostra, nova a cada execução. Deixe o terminal aberto enquanto usa; Ctrl+C encerra.
|
|
45
|
+
|
|
46
|
+
Opções: `--port` fixa a porta; `--no-browser` só mostra o endereço, sem abrir o navegador.
|
|
47
|
+
|
|
48
|
+
### Linha de comando
|
|
49
|
+
|
|
50
|
+
Aplicar uma receita salva ao arquivo do mês seguinte:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
wolfz run vendas.wolfz.json vendas_fevereiro.xlsx -o saida/ --format xlsx
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- Se algum passo não se aplicar ao arquivo novo (uma coluna mudou de nome, por exemplo), nada é gravado e a mensagem diz qual passo falhou e quais colunas existem.
|
|
57
|
+
- Se a aba muda de nome todo mês (`Vendas 2024` → `Vendas 2025`), use `source.match.sheet_name_regex` na receita.
|
|
58
|
+
|
|
59
|
+
Outros comandos:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
wolfz inspect planilha.xlsx # perfil das colunas no terminal
|
|
63
|
+
wolfz schema # JSON Schema da receita
|
|
64
|
+
wolfz --help
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Códigos de saída do `run`: 0 ok · 1 um passo falhou · 2 receita inválida ou uso incorreto · 3 arquivo ilegível.
|
|
68
|
+
|
|
69
|
+
### Em Python
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
import wolfz
|
|
73
|
+
|
|
74
|
+
df = wolfz.run("vendas.wolfz.json", "vendas_fevereiro.xlsx") # polars.DataFrame
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Exportação
|
|
78
|
+
|
|
79
|
+
| Formato | O que sai |
|
|
80
|
+
|---|---|
|
|
81
|
+
| CSV | UTF-8 com BOM; separador `,`, `;` ou tabulação; vírgula decimal opcional. Opção de neutralizar fórmulas (`=`, `+`, `-`, `@`). |
|
|
82
|
+
| Excel (`.xlsx`) | Data como data, número como número; texto nunca vira fórmula. |
|
|
83
|
+
| Parquet | Tipos preservados, compressão zstd. |
|
|
84
|
+
| Power Query M | A receita como função M, para colar no Power BI ou no Excel. |
|
|
85
|
+
| Script Python | A receita como script autônomo, que só depende do Polars. |
|
|
86
|
+
|
|
87
|
+
Tableau (`.hyper`) e projeto do Power BI (`.pbip`) estão previstos para versões futuras.
|
|
88
|
+
|
|
89
|
+
## Padrões brasileiros
|
|
90
|
+
|
|
91
|
+
O catálogo tem 31 operações: estrutura (área, cabeçalho, linhas e colunas vazias, preencher para baixo, despivotar), colunas, texto e padrões brasileiros. Os padrões cobrem:
|
|
92
|
+
|
|
93
|
+
- números (`1.234,56`, `(1,5)`, `10-` do SAP, `R$`);
|
|
94
|
+
- percentual, datas e mês/ano;
|
|
95
|
+
- sim/não;
|
|
96
|
+
- CPF, CNPJ (inclusive o alfanumérico), CEP e UF.
|
|
97
|
+
|
|
98
|
+
Cada operação tem a mesma regra no motor (Polars), no M e no script Python, com teste de equivalência.
|
|
99
|
+
|
|
100
|
+
## Limites
|
|
101
|
+
|
|
102
|
+
- Arquivo de até 100 MB.
|
|
103
|
+
- Uma tabela por aba. Duas tabelas na mesma aba: marque a área de cada uma em projetos separados.
|
|
104
|
+
- Cabeçalho em várias linhas é juntado num nome só, mas o cabeçalho mesclado na horizontal ainda não é preenchido. Isso chega na 0.6.
|
|
105
|
+
|
|
106
|
+
## Documentação
|
|
107
|
+
|
|
108
|
+
- Desenho técnico: [`docs/system_design.md`](docs/system_design.md)
|
|
109
|
+
- Mudanças: [`CHANGELOG.md`](CHANGELOG.md)
|
|
110
|
+
- Como contribuir: [`CONTRIBUTING.md`](CONTRIBUTING.md)
|
|
111
|
+
|
|
112
|
+
## Licença
|
|
113
|
+
|
|
114
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Hook de build do hatch: o wheel só sai com a interface compilada.
|
|
2
|
+
|
|
3
|
+
A interface (``frontend/``) é compilada para ``src/wolfz/static/`` com
|
|
4
|
+
``npm run build``. Esses arquivos ficam fora do Git, e esquecer o build
|
|
5
|
+
publicaria um pacote sem interface. A instalação editável (``uv sync``) não
|
|
6
|
+
passa por aqui. Para gerar um wheel sem interface de propósito:
|
|
7
|
+
``WOLFZ_SEM_INTERFACE=1``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ConfereInterface(BuildHookInterface):
|
|
20
|
+
PLUGIN_NAME = "custom"
|
|
21
|
+
|
|
22
|
+
def initialize(self, version: str, build_data: dict[str, Any]) -> None:
|
|
23
|
+
if version != "standard" or os.environ.get("WOLFZ_SEM_INTERFACE") == "1":
|
|
24
|
+
return
|
|
25
|
+
if not (Path(self.root) / "src" / "wolfz" / "static" / "index.html").is_file():
|
|
26
|
+
raise RuntimeError(
|
|
27
|
+
"A interface não foi compilada: rode `npm ci && npm run build` em frontend/ "
|
|
28
|
+
"antes de gerar o wheel (ou WOLFZ_SEM_INTERFACE=1 para gerar sem ela)."
|
|
29
|
+
)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wolfzbr"
|
|
7
|
+
version = "0.4.0"
|
|
8
|
+
description = "Limpeza de planilhas brasileiras com receitas reproduzíveis e exportação para Power Query M, Python, Tableau e Power BI."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Rafael Gomide" }]
|
|
14
|
+
keywords = ["limpeza de dados", "planilhas", "excel", "power bi", "power query", "polars", "brasil"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
17
|
+
"Environment :: Console",
|
|
18
|
+
"Environment :: Web Environment",
|
|
19
|
+
"Intended Audience :: End Users/Desktop",
|
|
20
|
+
"Intended Audience :: Science/Research",
|
|
21
|
+
"Natural Language :: Portuguese (Brazilian)",
|
|
22
|
+
"Operating System :: Microsoft :: Windows",
|
|
23
|
+
"Operating System :: POSIX :: Linux",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Topic :: Office/Business :: Financial :: Spreadsheet",
|
|
29
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
# Motor
|
|
33
|
+
"polars>=1.0",
|
|
34
|
+
"pyarrow>=16.0",
|
|
35
|
+
# Ingestão
|
|
36
|
+
"fastexcel>=0.11",
|
|
37
|
+
"python-calamine>=0.2",
|
|
38
|
+
"openpyxl>=3.1",
|
|
39
|
+
# Receita / validação
|
|
40
|
+
"pydantic>=2.7",
|
|
41
|
+
# Padrões BR
|
|
42
|
+
"rapidfuzz>=3.9",
|
|
43
|
+
# Workspace (ID de projeto e lock por projeto, §4.3 e §9 do design)
|
|
44
|
+
"python-ulid>=2.7",
|
|
45
|
+
"filelock>=3.13",
|
|
46
|
+
# API
|
|
47
|
+
"fastapi>=0.111",
|
|
48
|
+
"uvicorn[standard]>=0.30",
|
|
49
|
+
"python-multipart>=0.0.9",
|
|
50
|
+
# CLI
|
|
51
|
+
"typer>=0.12",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[project.optional-dependencies]
|
|
55
|
+
tableau = ["tableauhyperapi>=0.0.19"]
|
|
56
|
+
sql = ["duckdb>=1.0"]
|
|
57
|
+
dev = [
|
|
58
|
+
"pytest>=8.0",
|
|
59
|
+
"hypothesis>=6.100",
|
|
60
|
+
"syrupy>=4.6",
|
|
61
|
+
"ruff>=0.5",
|
|
62
|
+
"import-linter>=2.0",
|
|
63
|
+
"pytest-cov>=5.0",
|
|
64
|
+
# TestClient do FastAPI (testes de integração da API, F1.G8)
|
|
65
|
+
"httpx>=0.27",
|
|
66
|
+
"pre-commit>=3.7",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[project.urls]
|
|
70
|
+
Homepage = "https://github.com/RafaelGomide/WolfzBR"
|
|
71
|
+
Repository = "https://github.com/RafaelGomide/WolfzBR"
|
|
72
|
+
Issues = "https://github.com/RafaelGomide/WolfzBR/issues"
|
|
73
|
+
Documentation = "https://github.com/RafaelGomide/WolfzBR/blob/main/docs/system_design.md"
|
|
74
|
+
|
|
75
|
+
[project.scripts]
|
|
76
|
+
wolfz = "wolfz.cli:app"
|
|
77
|
+
|
|
78
|
+
[tool.hatch.build]
|
|
79
|
+
# A interface compilada (npm run build em frontend/) fica fora do Git, mas
|
|
80
|
+
# entra no wheel e no sdist: o usuário final não precisa de Node (F1.H14).
|
|
81
|
+
artifacts = ["src/wolfz/static/**"]
|
|
82
|
+
|
|
83
|
+
[tool.hatch.build.targets.wheel]
|
|
84
|
+
packages = ["src/wolfz"]
|
|
85
|
+
|
|
86
|
+
# hatch_build.py: recusa gerar o wheel sem a interface compilada.
|
|
87
|
+
[tool.hatch.build.targets.wheel.hooks.custom]
|
|
88
|
+
|
|
89
|
+
[tool.hatch.build.targets.sdist]
|
|
90
|
+
include = ["/src/wolfz", "/README.md", "/LICENSE", "/hatch_build.py"]
|
|
91
|
+
|
|
92
|
+
[tool.ruff]
|
|
93
|
+
line-length = 110
|
|
94
|
+
target-version = "py312"
|
|
95
|
+
src = ["src", "tests"]
|
|
96
|
+
# Não formatar blocos de código dentro da documentação.
|
|
97
|
+
extend-exclude = ["*.md"]
|
|
98
|
+
|
|
99
|
+
[tool.ruff.lint]
|
|
100
|
+
select = ["E", "F", "W", "I", "UP", "B", "SIM", "RUF"]
|
|
101
|
+
# Docstrings e comentários em português usam travessão e meia-risca de propósito.
|
|
102
|
+
ignore = ["RUF002", "RUF003"]
|
|
103
|
+
|
|
104
|
+
[tool.ruff.lint.isort]
|
|
105
|
+
known-first-party = ["wolfz"]
|
|
106
|
+
|
|
107
|
+
[tool.ruff.format]
|
|
108
|
+
quote-style = "double"
|
|
109
|
+
|
|
110
|
+
[tool.importlinter]
|
|
111
|
+
root_package = "wolfz"
|
|
112
|
+
|
|
113
|
+
[[tool.importlinter.contracts]]
|
|
114
|
+
name = "Núcleo não importa API nem CLI (§6.3 do design)"
|
|
115
|
+
type = "forbidden"
|
|
116
|
+
source_modules = ["wolfz.core", "wolfz.ops", "wolfz.br", "wolfz.export"]
|
|
117
|
+
forbidden_modules = ["wolfz.api", "wolfz.cli"]
|
|
118
|
+
|
|
119
|
+
[tool.pytest.ini_options]
|
|
120
|
+
testpaths = ["tests"]
|
|
121
|
+
addopts = ["--strict-markers", "--cov", "--cov-report=term-missing"]
|
|
122
|
+
|
|
123
|
+
[tool.coverage.run]
|
|
124
|
+
source = ["wolfz"]
|
|
125
|
+
branch = true
|
|
126
|
+
|
|
127
|
+
[tool.coverage.report]
|
|
128
|
+
skip_empty = true
|
|
129
|
+
show_missing = true
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""WolfzBR: limpeza de planilhas brasileiras com receitas reproduzíveis.
|
|
2
|
+
|
|
3
|
+
API pública mínima para uso em notebook e scripts::
|
|
4
|
+
|
|
5
|
+
import wolfz
|
|
6
|
+
|
|
7
|
+
receita = wolfz.load_recipe("vendas.wolfz.json")
|
|
8
|
+
df = wolfz.run(receita, "vendas_jan.csv")
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
12
|
+
|
|
13
|
+
from wolfz.core.engine import StepError
|
|
14
|
+
from wolfz.core.engine import run_file as run
|
|
15
|
+
from wolfz.core.recipe import RecipeError, load_recipe
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
__version__ = version("wolfzbr")
|
|
19
|
+
except PackageNotFoundError: # rodando do código-fonte sem instalar
|
|
20
|
+
__version__ = "0.0.0+desconhecida"
|
|
21
|
+
|
|
22
|
+
__all__ = ["RecipeError", "StepError", "__version__", "load_recipe", "run"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""API FastAPI local: transporte, token de sessão e servir a SPA. Sem regra de negócio."""
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Aplicação FastAPI: rotas em ``/api``, a SPA compilada na raiz."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import AsyncIterator
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
|
|
8
|
+
from fastapi import FastAPI
|
|
9
|
+
from fastapi.responses import HTMLResponse
|
|
10
|
+
from fastapi.staticfiles import StaticFiles
|
|
11
|
+
|
|
12
|
+
import wolfz.ops # noqa: F401 (registra o catálogo de operações)
|
|
13
|
+
from wolfz import __version__
|
|
14
|
+
from wolfz.api import problems, security
|
|
15
|
+
from wolfz.api.projetos import Projetos
|
|
16
|
+
from wolfz.api.rotas import router
|
|
17
|
+
from wolfz.api.security import Settings
|
|
18
|
+
|
|
19
|
+
_SEM_INTERFACE = """<!doctype html>
|
|
20
|
+
<html lang="pt-BR"><head><meta charset="utf-8"><title>WolfzBR</title></head>
|
|
21
|
+
<body style="font-family: system-ui, sans-serif; max-width: 40rem; margin: 4rem auto">
|
|
22
|
+
<h1>WolfzBR</h1>
|
|
23
|
+
<p>A API está no ar, mas a interface ainda não foi compilada neste pacote.</p>
|
|
24
|
+
</body></html>
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def create_app(settings: Settings) -> FastAPI:
|
|
29
|
+
projetos = Projetos(settings.workspace, settings.port)
|
|
30
|
+
|
|
31
|
+
@asynccontextmanager
|
|
32
|
+
async def ciclo(app: FastAPI) -> AsyncIterator[None]:
|
|
33
|
+
yield
|
|
34
|
+
projetos.fechar_todos() # libera os locks ao encerrar
|
|
35
|
+
|
|
36
|
+
app = FastAPI(
|
|
37
|
+
title="WolfzBR Local API",
|
|
38
|
+
version=__version__,
|
|
39
|
+
lifespan=ciclo,
|
|
40
|
+
docs_url="/api/docs" if settings.dev else None,
|
|
41
|
+
redoc_url=None,
|
|
42
|
+
openapi_url="/api/openapi.json" if settings.dev else None,
|
|
43
|
+
)
|
|
44
|
+
app.state.settings = settings
|
|
45
|
+
app.state.projetos = projetos
|
|
46
|
+
problems.install(app)
|
|
47
|
+
security.install(app, settings)
|
|
48
|
+
app.include_router(router(settings, projetos))
|
|
49
|
+
|
|
50
|
+
if (settings.static_dir / "index.html").is_file():
|
|
51
|
+
app.mount("/", StaticFiles(directory=settings.static_dir, html=True), name="spa")
|
|
52
|
+
else:
|
|
53
|
+
|
|
54
|
+
@app.get("/", include_in_schema=False)
|
|
55
|
+
def sem_interface() -> HTMLResponse:
|
|
56
|
+
return HTMLResponse(_SEM_INTERFACE)
|
|
57
|
+
|
|
58
|
+
return app
|