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.
Files changed (70) hide show
  1. wolfzbr-0.4.0/.gitignore +69 -0
  2. wolfzbr-0.4.0/LICENSE +21 -0
  3. wolfzbr-0.4.0/PKG-INFO +169 -0
  4. wolfzbr-0.4.0/README.md +114 -0
  5. wolfzbr-0.4.0/hatch_build.py +29 -0
  6. wolfzbr-0.4.0/pyproject.toml +129 -0
  7. wolfzbr-0.4.0/src/wolfz/__init__.py +22 -0
  8. wolfzbr-0.4.0/src/wolfz/api/__init__.py +1 -0
  9. wolfzbr-0.4.0/src/wolfz/api/app.py +58 -0
  10. wolfzbr-0.4.0/src/wolfz/api/modelos.py +148 -0
  11. wolfzbr-0.4.0/src/wolfz/api/problems.py +102 -0
  12. wolfzbr-0.4.0/src/wolfz/api/projetos.py +97 -0
  13. wolfzbr-0.4.0/src/wolfz/api/rotas.py +398 -0
  14. wolfzbr-0.4.0/src/wolfz/api/security.py +101 -0
  15. wolfzbr-0.4.0/src/wolfz/api/server.py +72 -0
  16. wolfzbr-0.4.0/src/wolfz/br/__init__.py +6 -0
  17. wolfzbr-0.4.0/src/wolfz/br/booleanos.py +25 -0
  18. wolfzbr-0.4.0/src/wolfz/br/constants.py +45 -0
  19. wolfzbr-0.4.0/src/wolfz/br/datas.py +137 -0
  20. wolfzbr-0.4.0/src/wolfz/br/documentos.py +215 -0
  21. wolfzbr-0.4.0/src/wolfz/br/geo.py +48 -0
  22. wolfzbr-0.4.0/src/wolfz/br/numeros.py +229 -0
  23. wolfzbr-0.4.0/src/wolfz/cli.py +336 -0
  24. wolfzbr-0.4.0/src/wolfz/core/__init__.py +1 -0
  25. wolfzbr-0.4.0/src/wolfz/core/detect/__init__.py +6 -0
  26. wolfzbr-0.4.0/src/wolfz/core/detect/metrics.py +144 -0
  27. wolfzbr-0.4.0/src/wolfz/core/detect/structure.py +297 -0
  28. wolfzbr-0.4.0/src/wolfz/core/engine/__init__.py +132 -0
  29. wolfzbr-0.4.0/src/wolfz/core/engine/cache.py +195 -0
  30. wolfzbr-0.4.0/src/wolfz/core/engine/errors.py +58 -0
  31. wolfzbr-0.4.0/src/wolfz/core/engine/preview.py +160 -0
  32. wolfzbr-0.4.0/src/wolfz/core/ingest/__init__.py +176 -0
  33. wolfzbr-0.4.0/src/wolfz/core/ingest/cells.py +153 -0
  34. wolfzbr-0.4.0/src/wolfz/core/ingest/csv.py +196 -0
  35. wolfzbr-0.4.0/src/wolfz/core/ingest/errors.py +93 -0
  36. wolfzbr-0.4.0/src/wolfz/core/ingest/excel.py +256 -0
  37. wolfzbr-0.4.0/src/wolfz/core/ingest/formats.py +145 -0
  38. wolfzbr-0.4.0/src/wolfz/core/ingest/limits.py +98 -0
  39. wolfzbr-0.4.0/src/wolfz/core/ingest/structure.py +208 -0
  40. wolfzbr-0.4.0/src/wolfz/core/naming.py +45 -0
  41. wolfzbr-0.4.0/src/wolfz/core/profile/__init__.py +20 -0
  42. wolfzbr-0.4.0/src/wolfz/core/profile/column.py +137 -0
  43. wolfzbr-0.4.0/src/wolfz/core/profile/infer.py +51 -0
  44. wolfzbr-0.4.0/src/wolfz/core/profile/inspect.py +68 -0
  45. wolfzbr-0.4.0/src/wolfz/core/profile/quick.py +45 -0
  46. wolfzbr-0.4.0/src/wolfz/core/recipe/__init__.py +32 -0
  47. wolfzbr-0.4.0/src/wolfz/core/recipe/io.py +173 -0
  48. wolfzbr-0.4.0/src/wolfz/core/recipe/migrate.py +62 -0
  49. wolfzbr-0.4.0/src/wolfz/core/recipe/models.py +205 -0
  50. wolfzbr-0.4.0/src/wolfz/core/suggest/__init__.py +1 -0
  51. wolfzbr-0.4.0/src/wolfz/core/text.py +115 -0
  52. wolfzbr-0.4.0/src/wolfz/core/workspace.py +134 -0
  53. wolfzbr-0.4.0/src/wolfz/export/__init__.py +1 -0
  54. wolfzbr-0.4.0/src/wolfz/export/dados.py +285 -0
  55. wolfzbr-0.4.0/src/wolfz/export/m.py +67 -0
  56. wolfzbr-0.4.0/src/wolfz/export/python.py +144 -0
  57. wolfzbr-0.4.0/src/wolfz/ops/__init__.py +55 -0
  58. wolfzbr-0.4.0/src/wolfz/ops/br.py +620 -0
  59. wolfzbr-0.4.0/src/wolfz/ops/colunas.py +354 -0
  60. wolfzbr-0.4.0/src/wolfz/ops/estrutura.py +517 -0
  61. wolfzbr-0.4.0/src/wolfz/ops/linhas.py +410 -0
  62. wolfzbr-0.4.0/src/wolfz/ops/m_br.py +369 -0
  63. wolfzbr-0.4.0/src/wolfz/ops/tabela.py +189 -0
  64. wolfzbr-0.4.0/src/wolfz/ops/texto.py +251 -0
  65. wolfzbr-0.4.0/src/wolfz/static/.gitkeep +0 -0
  66. wolfzbr-0.4.0/src/wolfz/static/assets/data-grid-overlay-editor-DbA0JPlf.js +1 -0
  67. wolfzbr-0.4.0/src/wolfz/static/assets/index-DWorz2kP.css +1 -0
  68. wolfzbr-0.4.0/src/wolfz/static/assets/index-Dx3RP7p-.js +182 -0
  69. wolfzbr-0.4.0/src/wolfz/static/assets/number-overlay-editor-DDUrQ7zj.js +9 -0
  70. wolfzbr-0.4.0/src/wolfz/static/index.html +15 -0
@@ -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
+ [![CI](https://github.com/RafaelGomide/WolfzBR/actions/workflows/ci.yml/badge.svg)](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
+ ![Importar, ajustar a receita e exportar](https://raw.githubusercontent.com/RafaelGomide/WolfzBR/main/docs/demo.gif)
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)
@@ -0,0 +1,114 @@
1
+ # WolfzBR
2
+
3
+ [![CI](https://github.com/RafaelGomide/WolfzBR/actions/workflows/ci.yml/badge.svg)](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
+ ![Importar, ajustar a receita e exportar](https://raw.githubusercontent.com/RafaelGomide/WolfzBR/main/docs/demo.gif)
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