agrobr 0.1.2__tar.gz → 0.5.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 (141) hide show
  1. agrobr-0.5.0/.github/workflows/docs.yml +42 -0
  2. agrobr-0.5.0/.gitignore +27 -0
  3. agrobr-0.5.0/CHANGELOG.md +180 -0
  4. {agrobr-0.1.2 → agrobr-0.5.0}/PKG-INFO +12 -12
  5. {agrobr-0.1.2 → agrobr-0.5.0}/README.md +11 -11
  6. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/__init__.py +3 -2
  7. agrobr-0.5.0/agrobr/benchmark/__init__.py +343 -0
  8. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cache/policies.py +3 -8
  9. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/api.py +87 -30
  10. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/client.py +0 -7
  11. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cli.py +141 -5
  12. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/conab/api.py +72 -6
  13. agrobr-0.5.0/agrobr/config.py +137 -0
  14. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/constants.py +1 -2
  15. agrobr-0.5.0/agrobr/contracts/__init__.py +186 -0
  16. agrobr-0.5.0/agrobr/contracts/cepea.py +80 -0
  17. agrobr-0.5.0/agrobr/contracts/conab.py +181 -0
  18. agrobr-0.5.0/agrobr/contracts/ibge.py +146 -0
  19. agrobr-0.5.0/agrobr/export.py +251 -0
  20. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/health/__init__.py +10 -0
  21. agrobr-0.5.0/agrobr/health/doctor.py +321 -0
  22. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/http/browser.py +0 -9
  23. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/ibge/api.py +104 -25
  24. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/ibge/client.py +5 -20
  25. agrobr-0.5.0/agrobr/models.py +184 -0
  26. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/noticias_agricolas/client.py +0 -7
  27. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/noticias_agricolas/parser.py +0 -17
  28. agrobr-0.5.0/agrobr/plugins/__init__.py +205 -0
  29. agrobr-0.5.0/agrobr/quality.py +319 -0
  30. agrobr-0.5.0/agrobr/sla.py +249 -0
  31. agrobr-0.5.0/agrobr/snapshots.py +321 -0
  32. agrobr-0.5.0/agrobr/stability.py +148 -0
  33. agrobr-0.5.0/agrobr/validators/semantic.py +447 -0
  34. {agrobr-0.1.2 → agrobr-0.5.0}/docs/advanced/resilience.md +57 -0
  35. agrobr-0.5.0/docs/sources/cepea.md +120 -0
  36. agrobr-0.5.0/docs/sources/conab.md +147 -0
  37. agrobr-0.5.0/docs/sources/ibge.md +156 -0
  38. agrobr-0.5.0/docs/sources/index.md +83 -0
  39. {agrobr-0.1.2 → agrobr-0.5.0}/pyproject.toml +1 -1
  40. agrobr-0.5.0/tests/test_config/__init__.py +1 -0
  41. agrobr-0.5.0/tests/test_config/test_config.py +152 -0
  42. agrobr-0.5.0/tests/test_config/test_export.py +208 -0
  43. agrobr-0.5.0/tests/test_config/test_snapshots.py +189 -0
  44. agrobr-0.5.0/tests/test_contracts/__init__.py +1 -0
  45. agrobr-0.5.0/tests/test_contracts/test_contracts.py +208 -0
  46. agrobr-0.5.0/tests/test_health/__init__.py +1 -0
  47. agrobr-0.5.0/tests/test_health/test_doctor.py +172 -0
  48. agrobr-0.5.0/tests/test_models.py +175 -0
  49. agrobr-0.5.0/tests/test_plugins/__init__.py +1 -0
  50. agrobr-0.5.0/tests/test_plugins/test_plugins.py +207 -0
  51. agrobr-0.5.0/tests/test_plugins/test_quality.py +213 -0
  52. agrobr-0.5.0/tests/test_plugins/test_sla.py +149 -0
  53. agrobr-0.5.0/tests/test_plugins/test_stability.py +172 -0
  54. agrobr-0.5.0/tests/test_validators/test_semantic.py +170 -0
  55. agrobr-0.1.2/.gitignore +0 -2
  56. agrobr-0.1.2/CHANGELOG.md +0 -60
  57. agrobr-0.1.2/agrobr/models.py +0 -85
  58. {agrobr-0.1.2 → agrobr-0.5.0}/.github/workflows/health_check.yml +0 -0
  59. {agrobr-0.1.2 → agrobr-0.5.0}/.github/workflows/publish.yml +0 -0
  60. {agrobr-0.1.2 → agrobr-0.5.0}/.github/workflows/structure_monitor.yml +0 -0
  61. {agrobr-0.1.2 → agrobr-0.5.0}/.github/workflows/tests.yml +0 -0
  62. {agrobr-0.1.2 → agrobr-0.5.0}/.pre-commit-config.yaml +0 -0
  63. {agrobr-0.1.2 → agrobr-0.5.0}/.structures/baseline.json +0 -0
  64. {agrobr-0.1.2 → agrobr-0.5.0}/CODE_OF_CONDUCT.md +0 -0
  65. {agrobr-0.1.2 → agrobr-0.5.0}/CONTRIBUTING.md +0 -0
  66. {agrobr-0.1.2 → agrobr-0.5.0}/LICENSE +0 -0
  67. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/alerts/__init__.py +0 -0
  68. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/alerts/notifier.py +0 -0
  69. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cache/__init__.py +0 -0
  70. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cache/duckdb_store.py +0 -0
  71. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cache/history.py +0 -0
  72. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cache/migrations.py +0 -0
  73. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/__init__.py +0 -0
  74. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/parsers/__init__.py +0 -0
  75. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/parsers/base.py +0 -0
  76. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/parsers/consensus.py +0 -0
  77. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/parsers/detector.py +0 -0
  78. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/parsers/fingerprint.py +0 -0
  79. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/cepea/parsers/v1.py +0 -0
  80. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/conab/__init__.py +0 -0
  81. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/conab/client.py +0 -0
  82. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/conab/parsers/__init__.py +0 -0
  83. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/conab/parsers/v1.py +0 -0
  84. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/exceptions.py +0 -0
  85. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/health/checker.py +0 -0
  86. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/health/reporter.py +0 -0
  87. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/http/__init__.py +0 -0
  88. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/http/rate_limiter.py +0 -0
  89. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/http/retry.py +0 -0
  90. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/http/user_agents.py +0 -0
  91. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/ibge/__init__.py +0 -0
  92. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/normalize/__init__.py +0 -0
  93. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/normalize/dates.py +0 -0
  94. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/normalize/encoding.py +0 -0
  95. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/normalize/regions.py +0 -0
  96. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/normalize/units.py +0 -0
  97. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/noticias_agricolas/__init__.py +0 -0
  98. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/sync.py +0 -0
  99. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/telemetry/__init__.py +0 -0
  100. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/telemetry/collector.py +0 -0
  101. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/utils/__init__.py +0 -0
  102. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/utils/logging.py +0 -0
  103. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/validators/__init__.py +0 -0
  104. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/validators/sanity.py +0 -0
  105. {agrobr-0.1.2 → agrobr-0.5.0}/agrobr/validators/structural.py +0 -0
  106. {agrobr-0.1.2 → agrobr-0.5.0}/docs/advanced/troubleshooting.md +0 -0
  107. {agrobr-0.1.2 → agrobr-0.5.0}/docs/api/cepea.md +0 -0
  108. {agrobr-0.1.2 → agrobr-0.5.0}/docs/api/conab.md +0 -0
  109. {agrobr-0.1.2 → agrobr-0.5.0}/docs/api/ibge.md +0 -0
  110. {agrobr-0.1.2 → agrobr-0.5.0}/docs/index.md +0 -0
  111. {agrobr-0.1.2 → agrobr-0.5.0}/docs/quickstart.md +0 -0
  112. {agrobr-0.1.2 → agrobr-0.5.0}/examples/analise_soja.py +0 -0
  113. {agrobr-0.1.2 → agrobr-0.5.0}/examples/pipeline_async.py +0 -0
  114. {agrobr-0.1.2 → agrobr-0.5.0}/mkdocs.yml +0 -0
  115. {agrobr-0.1.2 → agrobr-0.5.0}/scripts/alert_structure_change.py +0 -0
  116. {agrobr-0.1.2 → agrobr-0.5.0}/scripts/compare_structures.py +0 -0
  117. {agrobr-0.1.2 → agrobr-0.5.0}/scripts/create_workflows.py +0 -0
  118. {agrobr-0.1.2 → agrobr-0.5.0}/scripts/fetch_structures.py +0 -0
  119. {agrobr-0.1.2 → agrobr-0.5.0}/scripts/update_golden.py +0 -0
  120. {agrobr-0.1.2 → agrobr-0.5.0}/tests/__init__.py +0 -0
  121. {agrobr-0.1.2 → agrobr-0.5.0}/tests/conftest.py +0 -0
  122. {agrobr-0.1.2 → agrobr-0.5.0}/tests/golden_data/cepea/soja_sample/expected.json +0 -0
  123. {agrobr-0.1.2 → agrobr-0.5.0}/tests/golden_data/cepea/soja_sample/metadata.json +0 -0
  124. {agrobr-0.1.2 → agrobr-0.5.0}/tests/golden_data/cepea/soja_sample/response.html +0 -0
  125. {agrobr-0.1.2 → agrobr-0.5.0}/tests/golden_data/conab/safra_sample/expected.json +0 -0
  126. {agrobr-0.1.2 → agrobr-0.5.0}/tests/golden_data/conab/safra_sample/metadata.json +0 -0
  127. {agrobr-0.1.2 → agrobr-0.5.0}/tests/golden_data/conab/safra_sample/response.xlsx +0 -0
  128. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_cepea/__init__.py +0 -0
  129. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_cepea/test_api.py +0 -0
  130. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_cepea/test_fingerprint.py +0 -0
  131. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_cepea/test_parser.py +0 -0
  132. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_conab/__init__.py +0 -0
  133. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_conab/test_parser.py +0 -0
  134. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_golden.py +0 -0
  135. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_ibge/__init__.py +0 -0
  136. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_ibge/test_api.py +0 -0
  137. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_ibge/test_client.py +0 -0
  138. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_noticias_agricolas/__init__.py +0 -0
  139. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_noticias_agricolas/test_parser.py +0 -0
  140. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_validators/__init__.py +0 -0
  141. {agrobr-0.1.2 → agrobr-0.5.0}/tests/test_validators/test_sanity.py +0 -0
@@ -0,0 +1,42 @@
1
+ name: Deploy Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ jobs:
14
+ deploy:
15
+ runs-on: ubuntu-latest
16
+ environment:
17
+ name: github-pages
18
+ url: ${{ steps.deployment.outputs.page_url }}
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: '3.11'
25
+
26
+ - name: Install dependencies
27
+ run: pip install mkdocs-material mkdocstrings mkdocstrings-python
28
+
29
+ - name: Build docs
30
+ run: mkdocs build
31
+
32
+ - name: Setup Pages
33
+ uses: actions/configure-pages@v4
34
+
35
+ - name: Upload artifact
36
+ uses: actions/upload-pages-artifact@v3
37
+ with:
38
+ path: site
39
+
40
+ - name: Deploy to GitHub Pages
41
+ id: deployment
42
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,27 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Environments
7
+ .env
8
+ .venv
9
+ venv/
10
+
11
+ # IDE
12
+ .vscode/
13
+ .idea/
14
+
15
+ # Build
16
+ dist/
17
+ *.egg-info/
18
+
19
+ # Cache
20
+ .pytest_cache/
21
+ .mypy_cache/
22
+ .ruff_cache/
23
+
24
+ # Project
25
+ .claude/
26
+ agrobr_v3.txt
27
+ agrobr_roadmap_v2.txt
@@ -0,0 +1,180 @@
1
+ # Changelog
2
+
3
+ Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
4
+
5
+ O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.0.0/),
6
+ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.5.0] - 2026-02-04
11
+
12
+ ### Added
13
+ - **Plugin System** - Arquitetura extensível para fontes e validadores
14
+ - `SourcePlugin` - Interface para novas fontes de dados
15
+ - `ParserPlugin` - Interface para parsers customizados
16
+ - `ExporterPlugin` - Interface para exportadores customizados
17
+ - `ValidatorPlugin` - Interface para validadores customizados
18
+ - `register()`, `get_plugin()`, `list_plugins()` - Gerenciamento de plugins
19
+ - **API Stability Decorators** - Marcadores de estabilidade de API
20
+ - `@stable(since="x.y.z")` - Marca API como estável
21
+ - `@experimental(since="x.y.z")` - Marca API como experimental
22
+ - `@deprecated(since, removed_in, replacement)` - Marca API como deprecated
23
+ - `@internal` - Marca API como interna (não pública)
24
+ - `list_stable_apis()`, `list_experimental_apis()`, `list_deprecated_apis()`
25
+ - **SLA Documentado** - Contratos de nível de serviço por fonte
26
+ - `SourceSLA` - Definição de SLA com tier, freshness, latency, availability
27
+ - `CEPEA_SLA` - Tier CRITICAL, atualização diária 18h, 99% uptime
28
+ - `CONAB_SLA` - Tier STANDARD, atualização mensal, 98% uptime
29
+ - `IBGE_SLA` - Tier STANDARD, varia por pesquisa
30
+ - `get_sla()`, `list_slas()`, `get_sla_summary()`
31
+ - **Certificação de Qualidade** - Sistema de certificação de dados
32
+ - `QualityLevel` - GOLD, SILVER, BRONZE, UNCERTIFIED
33
+ - `QualityCheck` - Check individual com status e detalhes
34
+ - `QualityCertificate` - Certificado completo com score e validade
35
+ - `certify(df)` - Executa checks (completeness, duplicates, schema, freshness, range)
36
+ - `quick_check(df)` - Retorna (level, score) rapidamente
37
+
38
+ ## [0.4.0] - 2026-02-04
39
+
40
+ ### Added
41
+ - **Modo Determinístico** - Reprodutibilidade absoluta para backtests
42
+ - `agrobr.set_mode("deterministic", snapshot="2025-01-01")`
43
+ - `agrobr.configure()` para opções globais
44
+ - `agrobr.get_config()` para consultar configuração atual
45
+ - `agrobr.reset_config()` para resetar ao padrão
46
+ - **Sistema de Snapshots** - Gerenciamento de versões de dados
47
+ - `create_snapshot()` - Cria snapshot dos dados atuais
48
+ - `load_from_snapshot()` - Carrega dados de um snapshot
49
+ - `list_snapshots()` / `delete_snapshot()` - Gerenciamento
50
+ - CLI: `agrobr snapshot create/list/delete/use`
51
+ - **Export Auditável** - Formatos com metadados de proveniência
52
+ - `export_parquet()` - Parquet com metadata embutido
53
+ - `export_csv()` - CSV com arquivo sidecar .meta.json
54
+ - `export_json()` - JSON com metadados opcionais
55
+ - `verify_export()` - Verificação de integridade
56
+
57
+ ## [0.3.0] - 2026-02-04
58
+
59
+ ### Added
60
+ - **Stability Contracts** - Garantias formais de schema para todas as fontes
61
+ - `CEPEA_INDICADOR_V1` - Contrato para indicadores de preço CEPEA
62
+ - `CONAB_SAFRA_V1` - Contrato para dados de safra CONAB
63
+ - `CONAB_BALANCO_V1` - Contrato para balanço oferta/demanda CONAB
64
+ - `IBGE_PAM_V1` - Contrato para dados PAM do IBGE
65
+ - `IBGE_LSPA_V1` - Contrato para dados LSPA do IBGE
66
+ - `contract.validate(df)` - Validação automática contra contrato
67
+ - `contract.to_markdown()` - Documentação automática
68
+ - **Validação Semântica** - Verificações avançadas de qualidade
69
+ - Validação de preços positivos
70
+ - Validação de faixas de produtividade por cultura
71
+ - Detecção de anomalias em variação diária (>20%)
72
+ - Consistência de sequência de datas
73
+ - Consistência de áreas (colhida <= plantada)
74
+ - Validação de formato de safra
75
+ - `validate_semantic(df)` - Executa todas as regras
76
+ - `get_validation_summary(df)` - Resumo das violações
77
+ - **Benchmark Suite** - Ferramentas para medição de performance
78
+ - `benchmark_async()` / `benchmark_sync()` - Benchmark de funções
79
+ - `run_api_benchmarks()` - Benchmark das APIs
80
+ - `run_contract_benchmarks()` - Benchmark de validação de contratos
81
+ - `run_semantic_benchmarks()` - Benchmark de validação semântica
82
+
83
+ ### Changed
84
+ - Changelog reestruturado seguindo Keep a Changelog
85
+
86
+ ## [0.2.0] - 2026-02-04
87
+
88
+ ### Added
89
+ - **`agrobr doctor`** - Comando CLI para diagnóstico do sistema
90
+ - Verificação de conectividade das fontes
91
+ - Estatísticas do cache (tamanho, registros, por fonte)
92
+ - Status de configuração
93
+ - Output JSON (`--json`) e formatado Rich
94
+ - **Parâmetro `return_meta`** - Suporte a data lineage em todas as APIs
95
+ - `cepea.indicador(return_meta=True)` retorna `(DataFrame, MetaInfo)`
96
+ - `conab.safras(return_meta=True)` retorna `(DataFrame, MetaInfo)`
97
+ - `ibge.pam(return_meta=True)` retorna `(DataFrame, MetaInfo)`
98
+ - `ibge.lspa(return_meta=True)` retorna `(DataFrame, MetaInfo)`
99
+ - **Classe `MetaInfo`** - Metadados de proveniência e rastreabilidade
100
+ - Informações da fonte (nome, URL, método)
101
+ - Timing (duração fetch, duração parse)
102
+ - Status do cache (from_cache, cache_key, expires_at)
103
+ - Integridade do conteúdo (hash SHA256, tamanho)
104
+ - Versões (agrobr, parser, schema, python)
105
+ - `to_dict()` / `to_json()` para serialização
106
+ - `verify_hash(df)` para verificação de integridade
107
+ - **Documentação** - Guias de proveniência e resiliência
108
+ - `docs/sources/cepea.md` - Documentação da fonte CEPEA
109
+ - `docs/sources/conab.md` - Documentação da fonte CONAB
110
+ - `docs/sources/ibge.md` - Documentação da fonte IBGE
111
+ - `docs/advanced/resilience.md` - Documentação de resiliência
112
+
113
+ ### Changed
114
+ - `MetaInfo` exportado do pacote principal
115
+
116
+ ## [0.1.2] - 2026-02-04
117
+
118
+ ### Changed
119
+ - **Smart TTL** para cache CEPEA - expira às 18:00 (horário de atualização CEPEA)
120
+ - Reduz requests desnecessários em ~90%
121
+
122
+ ## [0.1.1] - 2026-02-04
123
+
124
+ ### Fixed
125
+ - Browser fallback desabilitado para CEPEA (Cloudflare bloqueia)
126
+ - CEPEA agora vai direto para Notícias Agrícolas, evitando timeout
127
+
128
+ ## [0.1.0] - 2026-02-04
129
+
130
+ ### Added
131
+ - **CEPEA**: Indicadores de preços agrícolas (soja, milho, boi, café, algodão, trigo)
132
+ - Fallback automático para Notícias Agrícolas quando CEPEA bloqueado
133
+ - Acumulação progressiva de histórico no DuckDB
134
+ - **CONAB**: Dados de safras e balanço oferta/demanda
135
+ - Parser para planilhas XLSX do boletim de safras
136
+ - Suporte a todos os produtos principais (soja, milho, arroz, feijão, etc.)
137
+ - **IBGE**: Integração com API SIDRA
138
+ - PAM (Produção Agrícola Municipal) - dados anuais
139
+ - LSPA (Levantamento Sistemático) - estimativas mensais
140
+ - **Cache**: Sistema de cache com DuckDB
141
+ - Separação entre cache volátil e histórico permanente
142
+ - TTL configurável por fonte
143
+ - Acumulação progressiva de dados
144
+ - **HTTP**: Cliente robusto com resiliência
145
+ - Retry com exponential backoff
146
+ - Rate limiting por fonte
147
+ - User-agent rotativo
148
+ - Fallback para Playwright quando necessário
149
+ - **CLI**: Interface de linha de comando completa
150
+ - Comandos para CEPEA, CONAB e IBGE
151
+ - Exportação em CSV, JSON e Parquet
152
+ - **Validação**: Sistema de validação multinível
153
+ - Pydantic v2 para validação de tipos
154
+ - Validação estatística (sanity checks)
155
+ - Fingerprinting de layout para detecção de mudanças
156
+ - **Monitoramento**: Health checks e alertas
157
+ - Health check por fonte
158
+ - Alertas multi-canal (Slack, Discord, Email)
159
+ - Monitoramento de estrutura
160
+ - **Suporte Polars**: Todas as APIs suportam `as_polars=True`
161
+ - **Testes**: 96 testes passando (~80% cobertura)
162
+ - **CI/CD**: GitHub Actions configurados
163
+ - Testes automatizados
164
+ - Health check diário
165
+ - Monitoramento de estrutura
166
+
167
+ ### Technical Details
168
+ - Python 3.11+ required
169
+ - Async-first design com sync wrapper
170
+ - Type hints completos
171
+ - Logging estruturado com structlog
172
+
173
+ [Unreleased]: https://github.com/bruno-portfolio/agrobr/compare/v0.5.0...HEAD
174
+ [0.5.0]: https://github.com/bruno-portfolio/agrobr/compare/v0.4.0...v0.5.0
175
+ [0.4.0]: https://github.com/bruno-portfolio/agrobr/compare/v0.3.0...v0.4.0
176
+ [0.3.0]: https://github.com/bruno-portfolio/agrobr/compare/v0.2.0...v0.3.0
177
+ [0.2.0]: https://github.com/bruno-portfolio/agrobr/compare/v0.1.2...v0.2.0
178
+ [0.1.2]: https://github.com/bruno-portfolio/agrobr/compare/v0.1.1...v0.1.2
179
+ [0.1.1]: https://github.com/bruno-portfolio/agrobr/compare/v0.1.0...v0.1.1
180
+ [0.1.0]: https://github.com/bruno-portfolio/agrobr/releases/tag/v0.1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agrobr
3
- Version: 0.1.2
3
+ Version: 0.5.0
4
4
  Summary: Dados agrícolas brasileiros em uma linha de código
5
5
  Project-URL: Homepage, https://github.com/bruno-portfolio/agrobr
6
6
  Project-URL: Documentation, https://agrobr.dev
@@ -201,16 +201,16 @@ agrobr health --all
201
201
 
202
202
  ## Diferenciais
203
203
 
204
- - 🚀 **Async-first** para pipelines de alta performance
205
- - 💾 **Cache inteligente** com DuckDB (analytics nativo)
206
- - 📊 **Histórico permanente** - acumula dados automaticamente
207
- - 🐼 **Suporte pandas + polars**
208
- -**Validação com Pydantic v2**
209
- - 📈 **Validação estatística** de sanidade (detecta anomalias)
210
- - 🔍 **Fingerprinting de layout** para detecção proativa de mudanças
211
- - 🔔 **Alertas multi-canal** (Slack, Discord, Email)
212
- - 🖥️ **CLI completo** para debug e automação
213
- - 🔄 **Fallback automático** entre fontes
204
+ - **Async-first** para pipelines de alta performance
205
+ - **Cache inteligente** com DuckDB (analytics nativo)
206
+ - **Histórico permanente** - acumula dados automaticamente
207
+ - **Suporte pandas + polars**
208
+ - **Validação com Pydantic v2**
209
+ - **Validação estatística** de sanidade (detecta anomalias)
210
+ - **Fingerprinting de layout** para detecção proativa de mudanças
211
+ - **Alertas multi-canal** (Slack, Discord, Email)
212
+ - **CLI completo** para debug e automação
213
+ - **Fallback automático** entre fontes
214
214
 
215
215
  ## Como Funciona
216
216
 
@@ -226,7 +226,7 @@ Consultas a períodos antigos são instantâneas (cache). Apenas dados recentes
226
226
 
227
227
  ## Documentação
228
228
 
229
- 📚 [Documentação completa](https://bruno-portfolio.github.io/agrobr/)
229
+ [Documentação completa](https://bruno-portfolio.github.io/agrobr/)
230
230
 
231
231
  - [Guia Rápido](https://bruno-portfolio.github.io/agrobr/quickstart/)
232
232
  - [API CEPEA](https://bruno-portfolio.github.io/agrobr/api/cepea/)
@@ -132,16 +132,16 @@ agrobr health --all
132
132
 
133
133
  ## Diferenciais
134
134
 
135
- - 🚀 **Async-first** para pipelines de alta performance
136
- - 💾 **Cache inteligente** com DuckDB (analytics nativo)
137
- - 📊 **Histórico permanente** - acumula dados automaticamente
138
- - 🐼 **Suporte pandas + polars**
139
- -**Validação com Pydantic v2**
140
- - 📈 **Validação estatística** de sanidade (detecta anomalias)
141
- - 🔍 **Fingerprinting de layout** para detecção proativa de mudanças
142
- - 🔔 **Alertas multi-canal** (Slack, Discord, Email)
143
- - 🖥️ **CLI completo** para debug e automação
144
- - 🔄 **Fallback automático** entre fontes
135
+ - **Async-first** para pipelines de alta performance
136
+ - **Cache inteligente** com DuckDB (analytics nativo)
137
+ - **Histórico permanente** - acumula dados automaticamente
138
+ - **Suporte pandas + polars**
139
+ - **Validação com Pydantic v2**
140
+ - **Validação estatística** de sanidade (detecta anomalias)
141
+ - **Fingerprinting de layout** para detecção proativa de mudanças
142
+ - **Alertas multi-canal** (Slack, Discord, Email)
143
+ - **CLI completo** para debug e automação
144
+ - **Fallback automático** entre fontes
145
145
 
146
146
  ## Como Funciona
147
147
 
@@ -157,7 +157,7 @@ Consultas a períodos antigos são instantâneas (cache). Apenas dados recentes
157
157
 
158
158
  ## Documentação
159
159
 
160
- 📚 [Documentação completa](https://bruno-portfolio.github.io/agrobr/)
160
+ [Documentação completa](https://bruno-portfolio.github.io/agrobr/)
161
161
 
162
162
  - [Guia Rápido](https://bruno-portfolio.github.io/agrobr/quickstart/)
163
163
  - [API CEPEA](https://bruno-portfolio.github.io/agrobr/api/cepea/)
@@ -2,9 +2,10 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "0.1.0"
5
+ __version__ = "0.2.0"
6
6
  __author__ = "Bruno"
7
7
 
8
8
  from agrobr import cepea, conab, ibge
9
+ from agrobr.models import MetaInfo
9
10
 
10
- __all__ = ["cepea", "conab", "ibge", "__version__"]
11
+ __all__ = ["cepea", "conab", "ibge", "MetaInfo", "__version__"]
@@ -0,0 +1,343 @@
1
+ """Benchmark suite para testes de performance do agrobr."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import statistics
6
+ import time
7
+ from collections.abc import Callable, Coroutine
8
+ from dataclasses import dataclass, field
9
+ from datetime import datetime
10
+ from typing import Any
11
+
12
+ import structlog
13
+
14
+ logger = structlog.get_logger()
15
+
16
+
17
+ @dataclass
18
+ class BenchmarkResult:
19
+ """Resultado de um benchmark."""
20
+
21
+ name: str
22
+ iterations: int
23
+ total_time_ms: float
24
+ mean_time_ms: float
25
+ median_time_ms: float
26
+ min_time_ms: float
27
+ max_time_ms: float
28
+ std_dev_ms: float
29
+ times_ms: list[float] = field(default_factory=list)
30
+ timestamp: datetime = field(default_factory=datetime.now)
31
+ metadata: dict[str, Any] = field(default_factory=dict)
32
+
33
+ def to_dict(self) -> dict[str, Any]:
34
+ """Converte para dicionario."""
35
+ return {
36
+ "name": self.name,
37
+ "iterations": self.iterations,
38
+ "total_time_ms": round(self.total_time_ms, 2),
39
+ "mean_time_ms": round(self.mean_time_ms, 2),
40
+ "median_time_ms": round(self.median_time_ms, 2),
41
+ "min_time_ms": round(self.min_time_ms, 2),
42
+ "max_time_ms": round(self.max_time_ms, 2),
43
+ "std_dev_ms": round(self.std_dev_ms, 2),
44
+ "timestamp": self.timestamp.isoformat(),
45
+ "metadata": self.metadata,
46
+ }
47
+
48
+ def summary(self) -> str:
49
+ """Retorna resumo formatado."""
50
+ return (
51
+ f"{self.name}: "
52
+ f"mean={self.mean_time_ms:.2f}ms, "
53
+ f"median={self.median_time_ms:.2f}ms, "
54
+ f"min={self.min_time_ms:.2f}ms, "
55
+ f"max={self.max_time_ms:.2f}ms "
56
+ f"({self.iterations} iterations)"
57
+ )
58
+
59
+
60
+ @dataclass
61
+ class BenchmarkSuite:
62
+ """Suite de benchmarks."""
63
+
64
+ name: str
65
+ results: list[BenchmarkResult] = field(default_factory=list)
66
+ timestamp: datetime = field(default_factory=datetime.now)
67
+
68
+ def add_result(self, result: BenchmarkResult) -> None:
69
+ """Adiciona resultado."""
70
+ self.results.append(result)
71
+
72
+ def to_dict(self) -> dict[str, Any]:
73
+ """Converte para dicionario."""
74
+ return {
75
+ "name": self.name,
76
+ "timestamp": self.timestamp.isoformat(),
77
+ "results": [r.to_dict() for r in self.results],
78
+ }
79
+
80
+ def summary(self) -> str:
81
+ """Retorna resumo formatado."""
82
+ lines = [f"Benchmark Suite: {self.name}", "=" * 50]
83
+ for result in self.results:
84
+ lines.append(result.summary())
85
+ return "\n".join(lines)
86
+
87
+
88
+ async def benchmark_async(
89
+ name: str,
90
+ func: Callable[..., Coroutine[Any, Any, Any]],
91
+ iterations: int = 10,
92
+ warmup: int = 1,
93
+ **kwargs: Any,
94
+ ) -> BenchmarkResult:
95
+ """
96
+ Executa benchmark de funcao async.
97
+
98
+ Args:
99
+ name: Nome do benchmark
100
+ func: Funcao async a testar
101
+ iterations: Numero de iteracoes
102
+ warmup: Iteracoes de aquecimento
103
+ **kwargs: Argumentos para a funcao
104
+
105
+ Returns:
106
+ BenchmarkResult com estatisticas
107
+ """
108
+ for _ in range(warmup):
109
+ await func(**kwargs)
110
+
111
+ times: list[float] = []
112
+ for _ in range(iterations):
113
+ start = time.perf_counter()
114
+ await func(**kwargs)
115
+ elapsed = (time.perf_counter() - start) * 1000
116
+ times.append(elapsed)
117
+
118
+ return BenchmarkResult(
119
+ name=name,
120
+ iterations=iterations,
121
+ total_time_ms=sum(times),
122
+ mean_time_ms=statistics.mean(times),
123
+ median_time_ms=statistics.median(times),
124
+ min_time_ms=min(times),
125
+ max_time_ms=max(times),
126
+ std_dev_ms=statistics.stdev(times) if len(times) > 1 else 0,
127
+ times_ms=times,
128
+ metadata={"warmup": warmup, "kwargs": str(kwargs)},
129
+ )
130
+
131
+
132
+ def benchmark_sync(
133
+ name: str,
134
+ func: Callable[..., Any],
135
+ iterations: int = 10,
136
+ warmup: int = 1,
137
+ **kwargs: Any,
138
+ ) -> BenchmarkResult:
139
+ """
140
+ Executa benchmark de funcao sincrona.
141
+
142
+ Args:
143
+ name: Nome do benchmark
144
+ func: Funcao a testar
145
+ iterations: Numero de iteracoes
146
+ warmup: Iteracoes de aquecimento
147
+ **kwargs: Argumentos para a funcao
148
+
149
+ Returns:
150
+ BenchmarkResult com estatisticas
151
+ """
152
+ for _ in range(warmup):
153
+ func(**kwargs)
154
+
155
+ times: list[float] = []
156
+ for _ in range(iterations):
157
+ start = time.perf_counter()
158
+ func(**kwargs)
159
+ elapsed = (time.perf_counter() - start) * 1000
160
+ times.append(elapsed)
161
+
162
+ return BenchmarkResult(
163
+ name=name,
164
+ iterations=iterations,
165
+ total_time_ms=sum(times),
166
+ mean_time_ms=statistics.mean(times),
167
+ median_time_ms=statistics.median(times),
168
+ min_time_ms=min(times),
169
+ max_time_ms=max(times),
170
+ std_dev_ms=statistics.stdev(times) if len(times) > 1 else 0,
171
+ times_ms=times,
172
+ metadata={"warmup": warmup, "kwargs": str(kwargs)},
173
+ )
174
+
175
+
176
+ async def run_api_benchmarks(iterations: int = 5) -> BenchmarkSuite:
177
+ """
178
+ Executa benchmarks das APIs principais.
179
+
180
+ Args:
181
+ iterations: Numero de iteracoes por benchmark
182
+
183
+ Returns:
184
+ BenchmarkSuite com resultados
185
+ """
186
+ from agrobr import cepea, conab, ibge
187
+
188
+ suite = BenchmarkSuite(name="agrobr_api_benchmarks")
189
+
190
+ try:
191
+ result = await benchmark_async(
192
+ "cepea.indicador(soja, offline=True)",
193
+ cepea.indicador,
194
+ iterations=iterations,
195
+ produto="soja",
196
+ offline=True,
197
+ )
198
+ suite.add_result(result)
199
+ except Exception as e:
200
+ logger.warning("benchmark_failed", name="cepea.indicador", error=str(e))
201
+
202
+ try:
203
+ result = await benchmark_async(
204
+ "cepea.produtos()",
205
+ cepea.produtos,
206
+ iterations=iterations,
207
+ )
208
+ suite.add_result(result)
209
+ except Exception as e:
210
+ logger.warning("benchmark_failed", name="cepea.produtos", error=str(e))
211
+
212
+ try:
213
+ result = await benchmark_async(
214
+ "conab.produtos()",
215
+ conab.produtos,
216
+ iterations=iterations,
217
+ )
218
+ suite.add_result(result)
219
+ except Exception as e:
220
+ logger.warning("benchmark_failed", name="conab.produtos", error=str(e))
221
+
222
+ try:
223
+ result = await benchmark_async(
224
+ "ibge.produtos_pam()",
225
+ ibge.produtos_pam,
226
+ iterations=iterations,
227
+ )
228
+ suite.add_result(result)
229
+ except Exception as e:
230
+ logger.warning("benchmark_failed", name="ibge.produtos_pam", error=str(e))
231
+
232
+ return suite
233
+
234
+
235
+ def run_contract_benchmarks(iterations: int = 100) -> BenchmarkSuite:
236
+ """
237
+ Executa benchmarks de validacao de contratos.
238
+
239
+ Args:
240
+ iterations: Numero de iteracoes por benchmark
241
+
242
+ Returns:
243
+ BenchmarkSuite com resultados
244
+ """
245
+ import pandas as pd
246
+
247
+ from agrobr.contracts.cepea import CEPEA_INDICADOR_V1
248
+
249
+ suite = BenchmarkSuite(name="contract_validation_benchmarks")
250
+
251
+ df_small = pd.DataFrame(
252
+ {
253
+ "data": pd.date_range("2024-01-01", periods=10),
254
+ "produto": ["soja"] * 10,
255
+ "praca": ["paranagua"] * 10,
256
+ "valor": [150.0] * 10,
257
+ "unidade": ["BRL/sc60kg"] * 10,
258
+ "fonte": ["cepea"] * 10,
259
+ "metodologia": [None] * 10,
260
+ "anomalies": [None] * 10,
261
+ }
262
+ )
263
+
264
+ result = benchmark_sync(
265
+ "contract.validate(10 rows)",
266
+ CEPEA_INDICADOR_V1.validate,
267
+ iterations=iterations,
268
+ df=df_small,
269
+ )
270
+ suite.add_result(result)
271
+
272
+ df_large = pd.DataFrame(
273
+ {
274
+ "data": pd.date_range("2020-01-01", periods=1000),
275
+ "produto": ["soja"] * 1000,
276
+ "praca": ["paranagua"] * 1000,
277
+ "valor": [150.0] * 1000,
278
+ "unidade": ["BRL/sc60kg"] * 1000,
279
+ "fonte": ["cepea"] * 1000,
280
+ "metodologia": [None] * 1000,
281
+ "anomalies": [None] * 1000,
282
+ }
283
+ )
284
+
285
+ result = benchmark_sync(
286
+ "contract.validate(1000 rows)",
287
+ CEPEA_INDICADOR_V1.validate,
288
+ iterations=iterations,
289
+ df=df_large,
290
+ )
291
+ suite.add_result(result)
292
+
293
+ return suite
294
+
295
+
296
+ def run_semantic_benchmarks(iterations: int = 50) -> BenchmarkSuite:
297
+ """
298
+ Executa benchmarks de validacao semantica.
299
+
300
+ Args:
301
+ iterations: Numero de iteracoes por benchmark
302
+
303
+ Returns:
304
+ BenchmarkSuite com resultados
305
+ """
306
+ import pandas as pd
307
+
308
+ from agrobr.validators.semantic import validate_semantic
309
+
310
+ suite = BenchmarkSuite(name="semantic_validation_benchmarks")
311
+
312
+ df = pd.DataFrame(
313
+ {
314
+ "data": pd.date_range("2024-01-01", periods=100),
315
+ "valor": [150.0 + i * 0.5 for i in range(100)],
316
+ "produto": ["soja"] * 100,
317
+ "produtividade": [3500.0] * 100,
318
+ "area_plantada": [1000.0] * 100,
319
+ "area_colhida": [950.0] * 100,
320
+ "safra": ["2024/25"] * 100,
321
+ }
322
+ )
323
+
324
+ result = benchmark_sync(
325
+ "validate_semantic(100 rows)",
326
+ validate_semantic,
327
+ iterations=iterations,
328
+ df=df,
329
+ )
330
+ suite.add_result(result)
331
+
332
+ return suite
333
+
334
+
335
+ __all__ = [
336
+ "BenchmarkResult",
337
+ "BenchmarkSuite",
338
+ "benchmark_async",
339
+ "benchmark_sync",
340
+ "run_api_benchmarks",
341
+ "run_contract_benchmarks",
342
+ "run_semantic_benchmarks",
343
+ ]