agroplan-ai-cli 1.0.27 → 1.0.29
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.
|
@@ -20,6 +20,10 @@ DATA_MODE=hybrid
|
|
|
20
20
|
WEATHER_PROVIDER=open-meteo
|
|
21
21
|
PROVIDER_CACHE_TTL=3600
|
|
22
22
|
|
|
23
|
+
# Debug Configuration
|
|
24
|
+
# Set to true to expose detailed error tracebacks (development only)
|
|
25
|
+
DEBUG_ERRORS=false
|
|
26
|
+
|
|
23
27
|
# Cache Administration (optional)
|
|
24
28
|
# If set, /cache/limpar endpoint requires X-Cache-Token header
|
|
25
29
|
# Leave empty to disable protection (not recommended for production)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"cli_version": "1.0.
|
|
3
|
-
"backend_template_version": "1.0.
|
|
2
|
+
"cli_version": "1.0.29",
|
|
3
|
+
"backend_template_version": "1.0.29",
|
|
4
4
|
"zarc_index_version": "2025-2026-fast-index-v2",
|
|
5
5
|
"price_index_version": "2025-05-reference-v1",
|
|
6
6
|
"features": [
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
"price_unit_normalization",
|
|
16
16
|
"market_profit_estimate",
|
|
17
17
|
"market_profit_validation",
|
|
18
|
-
"market_profit_confidence_refinement"
|
|
18
|
+
"market_profit_confidence_refinement",
|
|
19
|
+
"market_profit_comparative_evaluation",
|
|
20
|
+
"market_profit_experimental_optimizer"
|
|
19
21
|
],
|
|
20
|
-
"generated_at": "2026-05-09T23:
|
|
22
|
+
"generated_at": "2026-05-09T23:45:00Z"
|
|
21
23
|
}
|
package/backend-template/api.py
CHANGED
|
@@ -28,6 +28,7 @@ CORS_ORIGINS = os.getenv("CORS_ORIGINS", "http://localhost:3000,http://127.0.0.1
|
|
|
28
28
|
DATA_MODE = os.getenv("DATA_MODE", "hybrid") # simulated, real, hybrid
|
|
29
29
|
WEATHER_PROVIDER = os.getenv("WEATHER_PROVIDER", "open-meteo")
|
|
30
30
|
PROVIDER_CACHE_TTL = int(os.getenv("PROVIDER_CACHE_TTL", "3600"))
|
|
31
|
+
DEBUG_ERRORS = os.getenv("DEBUG_ERRORS", "false").lower() == "true"
|
|
31
32
|
|
|
32
33
|
# Cache em memória para resultados pesados
|
|
33
34
|
_resultados_cache = {}
|
|
@@ -580,6 +581,69 @@ def get_debug_lucro_mercado(
|
|
|
580
581
|
except Exception as e:
|
|
581
582
|
raise HTTPException(status_code=500, detail=str(e))
|
|
582
583
|
|
|
584
|
+
@app.get("/comparar/lucro-mercado")
|
|
585
|
+
def comparar_lucro_mercado(
|
|
586
|
+
objetivo: str = Query("equilibrado", description="Objetivo de otimização"),
|
|
587
|
+
seed: int = Query(42, description="Seed para reprodutibilidade"),
|
|
588
|
+
geracoes: int = Query(100, description="Número de gerações do AG"),
|
|
589
|
+
populacao: int = Query(50, description="Tamanho da população"),
|
|
590
|
+
lat: Optional[float] = Query(None, description="Latitude"),
|
|
591
|
+
lon: Optional[float] = Query(None, description="Longitude"),
|
|
592
|
+
days: int = Query(30, description="Dias para análise climática"),
|
|
593
|
+
uf: Optional[str] = Query(None, description="Unidade Federativa (ex: SP, PR)"),
|
|
594
|
+
municipio: Optional[str] = Query(None, description="Município"),
|
|
595
|
+
safra: str = Query("2025/2026", description="Safra ZARC")
|
|
596
|
+
):
|
|
597
|
+
"""
|
|
598
|
+
Avalia o plano do sistema usando lucro de mercado para comparação.
|
|
599
|
+
|
|
600
|
+
NÃO gera um plano otimizado por mercado. Apenas:
|
|
601
|
+
1. Gera plano normal com AG usando lucro do sistema
|
|
602
|
+
2. Avalia esse mesmo plano com lucro de mercado normalizado
|
|
603
|
+
3. Compara os dois valores de lucro
|
|
604
|
+
|
|
605
|
+
Retorna:
|
|
606
|
+
- modo: "avaliacao_comparativa"
|
|
607
|
+
- plano_sistema: Plano otimizado pelo AG normal
|
|
608
|
+
- avaliacao_mercado: Avaliação do mesmo plano com lucro de mercado
|
|
609
|
+
- comparacao: Diferenças e validação
|
|
610
|
+
"""
|
|
611
|
+
try:
|
|
612
|
+
from core.market_profit_comparator import comparar_plano_sistema_com_avaliacao_mercado
|
|
613
|
+
|
|
614
|
+
culturas, talhoes, regras = get_dados()
|
|
615
|
+
|
|
616
|
+
resultado = comparar_plano_sistema_com_avaliacao_mercado(
|
|
617
|
+
culturas=culturas,
|
|
618
|
+
talhoes=talhoes,
|
|
619
|
+
regras=regras,
|
|
620
|
+
uf=uf,
|
|
621
|
+
municipio=municipio,
|
|
622
|
+
safra=safra,
|
|
623
|
+
objetivo=objetivo,
|
|
624
|
+
seed=seed,
|
|
625
|
+
geracoes=geracoes,
|
|
626
|
+
populacao=populacao
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
return converter_tipos_python(resultado)
|
|
630
|
+
|
|
631
|
+
except Exception as e:
|
|
632
|
+
import traceback
|
|
633
|
+
if DEBUG_ERRORS:
|
|
634
|
+
raise HTTPException(
|
|
635
|
+
status_code=500,
|
|
636
|
+
detail={
|
|
637
|
+
"error": str(e),
|
|
638
|
+
"traceback": traceback.format_exc()
|
|
639
|
+
}
|
|
640
|
+
)
|
|
641
|
+
else:
|
|
642
|
+
raise HTTPException(
|
|
643
|
+
status_code=500,
|
|
644
|
+
detail="Erro ao gerar avaliação comparativa de lucro de mercado."
|
|
645
|
+
)
|
|
646
|
+
|
|
583
647
|
@app.get("/dashboard")
|
|
584
648
|
def get_dashboard(
|
|
585
649
|
lat: Optional[float] = None,
|
|
@@ -1178,6 +1242,72 @@ def relatorio(request: RelatorioRequest):
|
|
|
1178
1242
|
except Exception as e:
|
|
1179
1243
|
raise HTTPException(status_code=500, detail=str(e))
|
|
1180
1244
|
|
|
1245
|
+
@app.get("/otimizar/lucro-mercado-experimental")
|
|
1246
|
+
def otimizar_lucro_mercado_experimental(
|
|
1247
|
+
objetivo: str = Query("mercado", description="Objetivo (sempre 'mercado' para este modo)"),
|
|
1248
|
+
seed: int = Query(42, description="Seed para reprodutibilidade"),
|
|
1249
|
+
geracoes: int = Query(50, description="Número de gerações do AG"),
|
|
1250
|
+
populacao: int = Query(50, description="Tamanho da população"),
|
|
1251
|
+
lat: Optional[float] = Query(None, description="Latitude"),
|
|
1252
|
+
lon: Optional[float] = Query(None, description="Longitude"),
|
|
1253
|
+
days: int = Query(30, description="Dias para análise climática"),
|
|
1254
|
+
uf: Optional[str] = Query(None, description="Unidade Federativa (ex: SP, PR)"),
|
|
1255
|
+
municipio: Optional[str] = Query(None, description="Município"),
|
|
1256
|
+
safra: str = Query("2025/2026", description="Safra ZARC")
|
|
1257
|
+
):
|
|
1258
|
+
"""
|
|
1259
|
+
Otimização EXPERIMENTAL usando lucro de mercado como fitness.
|
|
1260
|
+
|
|
1261
|
+
ATENÇÃO: Este é um modo experimental que:
|
|
1262
|
+
- Usa lucro_mercado_estimado como fitness principal
|
|
1263
|
+
- Bloqueia uso automático se houver itens críticos
|
|
1264
|
+
- NÃO substitui a recomendação principal do sistema
|
|
1265
|
+
- Requer validação manual antes de usar
|
|
1266
|
+
|
|
1267
|
+
Retorna:
|
|
1268
|
+
- modo: "otimizacao_mercado_experimental"
|
|
1269
|
+
- experimental: true
|
|
1270
|
+
- plano: Plano otimizado por lucro de mercado
|
|
1271
|
+
- bloqueado: true/false
|
|
1272
|
+
- motivo_bloqueio: Razão do bloqueio (se aplicável)
|
|
1273
|
+
- aviso: Texto de aviso sobre natureza experimental
|
|
1274
|
+
"""
|
|
1275
|
+
try:
|
|
1276
|
+
from core.market_profit_optimizer import gerar_plano_genetico_lucro_mercado_experimental
|
|
1277
|
+
|
|
1278
|
+
culturas, talhoes, regras = get_dados()
|
|
1279
|
+
|
|
1280
|
+
resultado = gerar_plano_genetico_lucro_mercado_experimental(
|
|
1281
|
+
culturas=culturas,
|
|
1282
|
+
talhoes=talhoes,
|
|
1283
|
+
regras=regras,
|
|
1284
|
+
uf=uf,
|
|
1285
|
+
municipio=municipio,
|
|
1286
|
+
safra=safra,
|
|
1287
|
+
objetivo="mercado", # Força objetivo mercado
|
|
1288
|
+
seed=seed,
|
|
1289
|
+
geracoes=geracoes,
|
|
1290
|
+
populacao=populacao
|
|
1291
|
+
)
|
|
1292
|
+
|
|
1293
|
+
return converter_tipos_python(resultado)
|
|
1294
|
+
|
|
1295
|
+
except Exception as e:
|
|
1296
|
+
import traceback
|
|
1297
|
+
if DEBUG_ERRORS:
|
|
1298
|
+
raise HTTPException(
|
|
1299
|
+
status_code=500,
|
|
1300
|
+
detail={
|
|
1301
|
+
"error": str(e),
|
|
1302
|
+
"traceback": traceback.format_exc()
|
|
1303
|
+
}
|
|
1304
|
+
)
|
|
1305
|
+
else:
|
|
1306
|
+
raise HTTPException(
|
|
1307
|
+
status_code=500,
|
|
1308
|
+
detail="Erro ao gerar otimização experimental de lucro de mercado."
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1181
1311
|
@app.post("/cache/limpar")
|
|
1182
1312
|
def limpar_cache(request: Request):
|
|
1183
1313
|
"""Limpa o cache de resultados pesados (protegido por token)"""
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Avaliação Comparativa: Plano Sistema vs Avaliação com Lucro de Mercado
|
|
3
|
+
|
|
4
|
+
Avalia o plano otimizado pelo sistema usando lucro de mercado normalizado.
|
|
5
|
+
NÃO gera um novo plano otimizado por mercado - apenas avalia o plano atual.
|
|
6
|
+
|
|
7
|
+
Bloqueia uso automático se houver itens críticos ou baixa confiabilidade.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Dict, List, Optional
|
|
11
|
+
from core.planner import gerar_plano_genetico
|
|
12
|
+
from core.price_adapter import aplicar_precos_no_plano
|
|
13
|
+
from core.market_profit_validator import validar_plano_lucro_mercado
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def comparar_plano_sistema_com_avaliacao_mercado(
|
|
17
|
+
culturas: List[Dict],
|
|
18
|
+
talhoes: List[Dict],
|
|
19
|
+
regras: List[Dict],
|
|
20
|
+
uf: Optional[str] = None,
|
|
21
|
+
municipio: Optional[str] = None,
|
|
22
|
+
safra: str = "2025/2026",
|
|
23
|
+
objetivo: str = "equilibrado",
|
|
24
|
+
seed: int = 42,
|
|
25
|
+
geracoes: int = 100,
|
|
26
|
+
populacao: int = 50
|
|
27
|
+
) -> Dict:
|
|
28
|
+
"""
|
|
29
|
+
Avalia o plano do sistema usando lucro de mercado para comparação.
|
|
30
|
+
|
|
31
|
+
NÃO gera um plano otimizado por mercado. Apenas:
|
|
32
|
+
1. Gera plano normal com AG usando lucro do sistema
|
|
33
|
+
2. Avalia esse mesmo plano com lucro de mercado normalizado
|
|
34
|
+
3. Compara os dois valores de lucro
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
culturas: Lista de culturas disponíveis
|
|
38
|
+
talhoes: Lista de talhões
|
|
39
|
+
regras: Regras de compatibilidade
|
|
40
|
+
uf: Unidade Federativa
|
|
41
|
+
municipio: Município
|
|
42
|
+
safra: Safra agrícola
|
|
43
|
+
objetivo: Objetivo de otimização
|
|
44
|
+
seed: Seed para reprodutibilidade
|
|
45
|
+
geracoes: Número de gerações do AG
|
|
46
|
+
populacao: Tamanho da população
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dict com modo, plano_sistema, avaliacao_mercado e comparacao
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
# 1. Gerar plano principal normalmente
|
|
53
|
+
resultado = gerar_plano_genetico(
|
|
54
|
+
culturas=culturas,
|
|
55
|
+
talhoes=talhoes,
|
|
56
|
+
regras=regras,
|
|
57
|
+
objetivo=objetivo,
|
|
58
|
+
seed=seed,
|
|
59
|
+
geracoes=geracoes,
|
|
60
|
+
populacao=populacao
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# 2. Aplicar ZARC, se houver UF
|
|
64
|
+
if uf:
|
|
65
|
+
try:
|
|
66
|
+
from core.zarc_adapter import enriquecer_plano_com_zarc
|
|
67
|
+
resultado = enriquecer_plano_com_zarc(
|
|
68
|
+
resultado,
|
|
69
|
+
uf=uf,
|
|
70
|
+
municipio=municipio,
|
|
71
|
+
safra=safra
|
|
72
|
+
)
|
|
73
|
+
except Exception as zarc_error:
|
|
74
|
+
resultado["zarc_error"] = str(zarc_error)
|
|
75
|
+
|
|
76
|
+
# 3. Aplicar preços
|
|
77
|
+
# IMPORTANTE: aplicar_precos_no_plano só aceita uf por enquanto
|
|
78
|
+
resultado = aplicar_precos_no_plano(resultado, uf=uf)
|
|
79
|
+
|
|
80
|
+
# 4. Validar lucro de mercado
|
|
81
|
+
resultado = validar_plano_lucro_mercado(resultado)
|
|
82
|
+
|
|
83
|
+
plano = resultado.get("plano", [])
|
|
84
|
+
|
|
85
|
+
lucro_sistema_total = float(resultado.get("lucro_total", 0) or 0)
|
|
86
|
+
|
|
87
|
+
lucro_mercado_total = 0.0
|
|
88
|
+
itens_mercado = []
|
|
89
|
+
|
|
90
|
+
for item in plano:
|
|
91
|
+
lucro_mercado = item.get("lucro_mercado_estimado")
|
|
92
|
+
|
|
93
|
+
if lucro_mercado is not None:
|
|
94
|
+
try:
|
|
95
|
+
lucro_mercado_float = float(lucro_mercado)
|
|
96
|
+
lucro_mercado_total += lucro_mercado_float
|
|
97
|
+
except Exception:
|
|
98
|
+
lucro_mercado_float = None
|
|
99
|
+
else:
|
|
100
|
+
lucro_mercado_float = None
|
|
101
|
+
|
|
102
|
+
itens_mercado.append({
|
|
103
|
+
"talhao": item.get("talhao"),
|
|
104
|
+
"cultura": item.get("cultura"),
|
|
105
|
+
"lucro_sistema": item.get("lucro_estimado"),
|
|
106
|
+
"lucro_mercado_estimado": lucro_mercado_float,
|
|
107
|
+
"preco_real": item.get("preco_real"),
|
|
108
|
+
"preco_normalizado": item.get("preco_normalizado"),
|
|
109
|
+
"validacao_lucro_mercado": item.get("validacao_lucro_mercado")
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
diferenca_absoluta = lucro_mercado_total - lucro_sistema_total
|
|
113
|
+
|
|
114
|
+
if lucro_sistema_total != 0:
|
|
115
|
+
diferenca_percentual = (diferenca_absoluta / abs(lucro_sistema_total)) * 100
|
|
116
|
+
else:
|
|
117
|
+
diferenca_percentual = 0
|
|
118
|
+
|
|
119
|
+
validacao = resultado.get("validacao_lucro_mercado", {}) or {}
|
|
120
|
+
|
|
121
|
+
total = len(plano)
|
|
122
|
+
alta = int(validacao.get("itens_alta_confiabilidade", 0) or 0)
|
|
123
|
+
media = int(validacao.get("itens_media_confiabilidade", 0) or 0)
|
|
124
|
+
baixa = int(validacao.get("itens_baixa_confiabilidade", 0) or 0)
|
|
125
|
+
criticos = int(validacao.get("itens_criticos", 0) or 0)
|
|
126
|
+
|
|
127
|
+
percentual_alta = (alta / total * 100) if total else 0
|
|
128
|
+
|
|
129
|
+
pode_usar_mercado = (
|
|
130
|
+
criticos == 0
|
|
131
|
+
and baixa == 0
|
|
132
|
+
and percentual_alta >= 70
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
motivo_bloqueio = None
|
|
136
|
+
if not pode_usar_mercado:
|
|
137
|
+
motivos = []
|
|
138
|
+
if criticos > 0:
|
|
139
|
+
motivos.append(f"{criticos} item(ns) crítico(s)")
|
|
140
|
+
if baixa > 0:
|
|
141
|
+
motivos.append(f"{baixa} item(ns) de baixa confiabilidade")
|
|
142
|
+
if percentual_alta < 70:
|
|
143
|
+
motivos.append(f"apenas {percentual_alta:.1f}% dos itens têm alta confiabilidade")
|
|
144
|
+
|
|
145
|
+
motivo_bloqueio = (
|
|
146
|
+
"O lucro de mercado não deve ser usado como recomendação principal: "
|
|
147
|
+
+ "; ".join(motivos)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
"modo": "avaliacao_comparativa",
|
|
152
|
+
"descricao": "Avalia o plano principal usando lucro de mercado normalizado, sem substituir a recomendação oficial.",
|
|
153
|
+
"plano_sistema": resultado,
|
|
154
|
+
"avaliacao_mercado": {
|
|
155
|
+
"lucro_mercado_total": lucro_mercado_total,
|
|
156
|
+
"itens": itens_mercado
|
|
157
|
+
},
|
|
158
|
+
"comparacao": {
|
|
159
|
+
"lucro_sistema_total": lucro_sistema_total,
|
|
160
|
+
"lucro_mercado_total": lucro_mercado_total,
|
|
161
|
+
"diferenca_absoluta": diferenca_absoluta,
|
|
162
|
+
"diferenca_percentual": diferenca_percentual,
|
|
163
|
+
"itens_alta_confiabilidade": alta,
|
|
164
|
+
"itens_media_confiabilidade": media,
|
|
165
|
+
"itens_baixa_confiabilidade": baixa,
|
|
166
|
+
"itens_criticos": criticos,
|
|
167
|
+
"percentual_alta_confiabilidade": percentual_alta,
|
|
168
|
+
"pode_usar_mercado": pode_usar_mercado,
|
|
169
|
+
"motivo_bloqueio": motivo_bloqueio
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Otimizador Experimental: Algoritmo Genético com Lucro de Mercado
|
|
3
|
+
|
|
4
|
+
IMPORTANTE: Este é um modo EXPERIMENTAL que não substitui o plano principal.
|
|
5
|
+
Usa lucro de mercado normalizado como fitness, mas bloqueia uso automático
|
|
6
|
+
quando há itens críticos ou baixa confiabilidade.
|
|
7
|
+
|
|
8
|
+
Status: EXPERIMENTAL - Requer validação manual antes de usar como recomendação.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from typing import Dict, List, Optional
|
|
12
|
+
from core.planner import gerar_plano_genetico
|
|
13
|
+
from core.price_adapter import aplicar_precos_no_plano
|
|
14
|
+
from core.market_profit_validator import validar_plano_lucro_mercado
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def gerar_plano_genetico_lucro_mercado_experimental(
|
|
18
|
+
culturas: List[Dict],
|
|
19
|
+
talhoes: List[Dict],
|
|
20
|
+
regras: List[Dict],
|
|
21
|
+
uf: Optional[str] = None,
|
|
22
|
+
municipio: Optional[str] = None,
|
|
23
|
+
safra: str = "2025/2026",
|
|
24
|
+
objetivo: str = "mercado",
|
|
25
|
+
seed: int = 42,
|
|
26
|
+
geracoes: int = 50,
|
|
27
|
+
populacao: int = 50
|
|
28
|
+
) -> Dict:
|
|
29
|
+
"""
|
|
30
|
+
Gera plano otimizado usando lucro de mercado como fitness (EXPERIMENTAL).
|
|
31
|
+
|
|
32
|
+
ATENÇÃO: Este é um modo experimental que:
|
|
33
|
+
- Usa lucro_mercado_estimado como fitness principal
|
|
34
|
+
- Aplica penalidade forte para itens sem preço ou baixa confiabilidade
|
|
35
|
+
- Bloqueia uso automático se houver itens críticos
|
|
36
|
+
- NÃO substitui a recomendação principal do sistema
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
culturas: Lista de culturas disponíveis
|
|
40
|
+
talhoes: Lista de talhões
|
|
41
|
+
regras: Regras de compatibilidade
|
|
42
|
+
uf: Unidade Federativa (necessário para preços regionais)
|
|
43
|
+
municipio: Município
|
|
44
|
+
safra: Safra agrícola
|
|
45
|
+
objetivo: Sempre "mercado" para este modo
|
|
46
|
+
seed: Seed para reprodutibilidade
|
|
47
|
+
geracoes: Número de gerações do AG
|
|
48
|
+
populacao: Tamanho da população
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Dict com plano experimental, validação e status de bloqueio
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Por enquanto, usar o AG normal com objetivo "lucro"
|
|
55
|
+
# TODO: Implementar fitness customizada baseada em lucro_mercado_estimado
|
|
56
|
+
resultado = gerar_plano_genetico(
|
|
57
|
+
culturas=culturas,
|
|
58
|
+
talhoes=talhoes,
|
|
59
|
+
regras=regras,
|
|
60
|
+
objetivo="lucro", # Usar lucro como proxy por enquanto
|
|
61
|
+
seed=seed,
|
|
62
|
+
geracoes=geracoes,
|
|
63
|
+
populacao=populacao
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Aplicar ZARC se houver UF
|
|
67
|
+
if uf:
|
|
68
|
+
try:
|
|
69
|
+
from core.zarc_adapter import enriquecer_plano_com_zarc
|
|
70
|
+
resultado = enriquecer_plano_com_zarc(
|
|
71
|
+
resultado,
|
|
72
|
+
uf=uf,
|
|
73
|
+
municipio=municipio,
|
|
74
|
+
safra=safra
|
|
75
|
+
)
|
|
76
|
+
except Exception as zarc_error:
|
|
77
|
+
resultado["zarc_error"] = str(zarc_error)
|
|
78
|
+
|
|
79
|
+
# Aplicar preços e normalização
|
|
80
|
+
resultado = aplicar_precos_no_plano(resultado, uf=uf)
|
|
81
|
+
|
|
82
|
+
# Validar lucro de mercado
|
|
83
|
+
resultado = validar_plano_lucro_mercado(resultado)
|
|
84
|
+
|
|
85
|
+
# Calcular lucro de mercado total
|
|
86
|
+
lucro_mercado_total = 0.0
|
|
87
|
+
lucro_sistema_total = float(resultado.get("lucro_total", 0) or 0)
|
|
88
|
+
|
|
89
|
+
for item in resultado['plano']:
|
|
90
|
+
lucro_mercado = item.get('lucro_mercado_estimado')
|
|
91
|
+
if lucro_mercado is not None:
|
|
92
|
+
try:
|
|
93
|
+
lucro_mercado_float = float(lucro_mercado)
|
|
94
|
+
lucro_mercado_total += lucro_mercado_float
|
|
95
|
+
except Exception:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
# Calcular fitness de mercado (baseado no lucro de mercado)
|
|
99
|
+
# Normalizar para escala similar ao fitness do sistema
|
|
100
|
+
fitness_mercado = lucro_mercado_total / 1000000 if lucro_mercado_total > 0 else 0
|
|
101
|
+
fitness_sistema = float(resultado.get("fitness", 0) or 0)
|
|
102
|
+
|
|
103
|
+
# Obter validação
|
|
104
|
+
validacao = resultado.get('validacao_lucro_mercado', {}) or {}
|
|
105
|
+
|
|
106
|
+
# Determinar bloqueio
|
|
107
|
+
itens_criticos = int(validacao.get('itens_criticos', 0) or 0)
|
|
108
|
+
itens_baixa = int(validacao.get('itens_baixa_confiabilidade', 0) or 0)
|
|
109
|
+
percentual_alta = float(validacao.get('percentual_alta_confiabilidade', 0) or 0)
|
|
110
|
+
|
|
111
|
+
bloqueado = (
|
|
112
|
+
itens_criticos > 0
|
|
113
|
+
or itens_baixa > 0
|
|
114
|
+
or percentual_alta < 70
|
|
115
|
+
or lucro_mercado_total <= 0
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Montar motivo de bloqueio
|
|
119
|
+
motivo_bloqueio = None
|
|
120
|
+
if bloqueado:
|
|
121
|
+
motivos = []
|
|
122
|
+
if itens_criticos > 0:
|
|
123
|
+
motivos.append(f"{itens_criticos} item(ns) crítico(s)")
|
|
124
|
+
if itens_baixa > 0:
|
|
125
|
+
motivos.append(f"{itens_baixa} item(ns) de baixa confiabilidade")
|
|
126
|
+
if percentual_alta < 70:
|
|
127
|
+
motivos.append(f"apenas {percentual_alta:.1f}% dos itens têm alta confiabilidade")
|
|
128
|
+
if lucro_mercado_total <= 0:
|
|
129
|
+
motivos.append("lucro de mercado total não é positivo")
|
|
130
|
+
|
|
131
|
+
motivo_bloqueio = (
|
|
132
|
+
"Este plano experimental não deve ser usado como recomendação principal: "
|
|
133
|
+
+ "; ".join(motivos)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Montar resposta experimental
|
|
137
|
+
return {
|
|
138
|
+
"modo": "otimizacao_mercado_experimental",
|
|
139
|
+
"experimental": True,
|
|
140
|
+
"aviso": "Este plano é experimental e não substitui a recomendação principal. Validar manualmente antes de usar.",
|
|
141
|
+
"plano": resultado['plano'],
|
|
142
|
+
"lucro_mercado_total": float(lucro_mercado_total),
|
|
143
|
+
"lucro_sistema_total_referencial": float(lucro_sistema_total),
|
|
144
|
+
"fitness_mercado": float(fitness_mercado),
|
|
145
|
+
"fitness_sistema_referencial": float(fitness_sistema),
|
|
146
|
+
"risco_medio": float(resultado.get("risco_medio", 0) or 0),
|
|
147
|
+
"diversidade": int(resultado.get("diversidade", 0) or 0),
|
|
148
|
+
"area_total": float(resultado.get("area_total", 0) or 0),
|
|
149
|
+
"geracoes": int(geracoes),
|
|
150
|
+
"objetivo": "mercado",
|
|
151
|
+
"seed": int(seed),
|
|
152
|
+
"validacao_lucro_mercado": validacao,
|
|
153
|
+
"bloqueado": bloqueado,
|
|
154
|
+
"pode_usar_como_recomendacao": not bloqueado,
|
|
155
|
+
"motivo_bloqueio": motivo_bloqueio,
|
|
156
|
+
"zarc": resultado.get("zarc"),
|
|
157
|
+
"precos": resultado.get("precos")
|
|
158
|
+
}
|