agroplan-ai-cli 1.0.35 → 1.0.36

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "cli_version": "1.0.35",
3
- "backend_template_version": "1.0.35",
2
+ "cli_version": "1.0.36",
3
+ "backend_template_version": "1.0.36",
4
4
  "zarc_index_version": "2025-2026-fast-index-v2",
5
5
  "price_index_version": "2025-05-reference-v1",
6
6
  "features": [
@@ -24,7 +24,8 @@
24
24
  "expanded_crop_calendar_10_cultures",
25
25
  "calendar_date_safety",
26
26
  "calendar_weather_integration",
27
- "calendar_weather_dependency_fix"
27
+ "calendar_weather_dependency_fix",
28
+ "nasa_power_climatology"
28
29
  ],
29
- "generated_at": "2026-05-10T22:00:00Z"
30
+ "generated_at": "2026-05-10T23:00:00Z"
30
31
  }
@@ -372,6 +372,49 @@ def get_clima(
372
372
  except Exception as e:
373
373
  raise HTTPException(status_code=500, detail=str(e))
374
374
 
375
+ @app.get("/dados/clima/nasa-power")
376
+ def get_clima_nasa_power(
377
+ lat: Optional[float] = Query(None, description="Latitude"),
378
+ lon: Optional[float] = Query(None, description="Longitude"),
379
+ month: int = Query(None, description="Mês (1-12)")
380
+ ):
381
+ """Obtém climatologia NASA POWER para um mês específico"""
382
+ try:
383
+ from providers.nasa_power_provider import buscar_climatologia_nasa_power, get_nasa_power_status
384
+
385
+ # Se lat ou lon não foram fornecidos, retornar informações
386
+ if lat is None or lon is None or month is None:
387
+ return {
388
+ "message": "Informe latitude, longitude e mês para consultar climatologia NASA POWER.",
389
+ "exemplo": "/dados/clima/nasa-power?lat=-21.56&lon=-50.45&month=5",
390
+ "parametros": {
391
+ "lat": "Latitude da localização",
392
+ "lon": "Longitude da localização",
393
+ "month": "Mês (1-12)"
394
+ },
395
+ "provider_info": get_nasa_power_status()
396
+ }
397
+
398
+ if month < 1 or month > 12:
399
+ raise HTTPException(status_code=400, detail="Month deve estar entre 1 e 12")
400
+
401
+ # Buscar climatologia
402
+ climatology = buscar_climatologia_nasa_power(lat, lon, month)
403
+
404
+ if climatology:
405
+ return climatology
406
+ else:
407
+ return {
408
+ "message": "Não foi possível obter dados NASA POWER para esta localização.",
409
+ "lat": lat,
410
+ "lon": lon,
411
+ "month": month,
412
+ "note": "NASA POWER pode estar temporariamente indisponível ou a localização pode estar fora da cobertura."
413
+ }
414
+
415
+ except Exception as e:
416
+ raise HTTPException(status_code=500, detail=str(e))
417
+
375
418
  @app.get("/dados/zarc")
376
419
  def get_zarc(
377
420
  cultura: Optional[str] = Query(None, description="Nome da cultura"),
@@ -121,8 +121,12 @@ def enriquecer_calendario_com_clima(
121
121
 
122
122
  # Gerar resumo
123
123
  forecast_type = weather_data.get("forecast_type", "climatology")
124
+ source = weather_data.get("source", "unknown")
125
+
124
126
  if forecast_type == "forecast":
125
127
  summary = f"Previsão: {weather_data.get('precipitation_sum', 0):.1f}mm de chuva, {weather_data.get('temperature_min', 0):.0f}°C a {weather_data.get('temperature_max', 0):.0f}°C"
128
+ elif source == "nasa-power":
129
+ summary = f"Climatologia NASA POWER: temperatura média {weather_data.get('temperature_avg', 0):.0f}°C, {weather_data.get('precipitation_expected', 'condições típicas')}"
126
130
  else:
127
131
  summary = f"Climatologia: {weather_data.get('precipitation_expected', 'Condições típicas')}"
128
132
 
@@ -99,8 +99,7 @@ def buscar_climatologia_longo_prazo(
99
99
  """
100
100
  Busca climatologia/histórico para períodos longos (17+ dias).
101
101
 
102
- Inicialmente usa fallback local mensal.
103
- Futuramente pode integrar NASA POWER.
102
+ Tenta usar NASA POWER primeiro, fallback local se falhar.
104
103
 
105
104
  Args:
106
105
  lat: Latitude
@@ -112,28 +111,52 @@ def buscar_climatologia_longo_prazo(
112
111
  Lista de dicionários com climatologia por data/mês
113
112
  """
114
113
 
115
- # Por enquanto, usar fallback local baseado em médias mensais
116
- # Futuramente: integrar NASA POWER Climatology API
114
+ from .nasa_power_provider import buscar_climatologia_nasa_power
117
115
 
118
116
  result = []
119
117
  current = start_date
120
118
 
119
+ # Cache de dados NASA POWER por mês
120
+ nasa_cache = {}
121
+
121
122
  while current <= end_date:
122
123
  month = current.month
123
124
 
124
- # Climatologia simplificada por mês (Brasil)
125
- climatology = _get_monthly_climatology(month, lat, lon)
125
+ # Tentar NASA POWER primeiro
126
+ if month not in nasa_cache:
127
+ nasa_data = buscar_climatologia_nasa_power(lat, lon, month)
128
+ nasa_cache[month] = nasa_data
129
+
130
+ nasa_data = nasa_cache[month]
126
131
 
127
- result.append({
128
- "date": current.isoformat(),
129
- "source": "climate-fallback",
130
- "forecast_type": "climatology",
131
- "temperature_max": climatology["temp_max"],
132
- "temperature_min": climatology["temp_min"],
133
- "precipitation_expected": climatology["precip_desc"],
134
- "precipitation_mm_avg": climatology["precip_mm"],
135
- "confidence": "media"
136
- })
132
+ if nasa_data:
133
+ # Usar dados NASA POWER
134
+ result.append({
135
+ "date": current.isoformat(),
136
+ "source": "nasa-power",
137
+ "forecast_type": "climatology",
138
+ "temperature_max": nasa_data["temperature_max"],
139
+ "temperature_min": nasa_data["temperature_min"],
140
+ "temperature_avg": nasa_data["temperature_avg"],
141
+ "precipitation_expected": nasa_data["precipitation_expected"],
142
+ "precipitation_mm_avg": nasa_data["precipitation_mm_avg"],
143
+ "confidence": "media",
144
+ "note": "Climatologia NASA POWER, não previsão exata"
145
+ })
146
+ else:
147
+ # Fallback local se NASA POWER falhar
148
+ climatology = _get_monthly_climatology(month, lat, lon)
149
+ result.append({
150
+ "date": current.isoformat(),
151
+ "source": "climate-fallback",
152
+ "forecast_type": "climatology",
153
+ "temperature_max": climatology["temp_max"],
154
+ "temperature_min": climatology["temp_min"],
155
+ "precipitation_expected": climatology["precip_desc"],
156
+ "precipitation_mm_avg": climatology["precip_mm"],
157
+ "confidence": "baixa",
158
+ "note": "Climatologia simplificada, NASA POWER indisponível"
159
+ })
137
160
 
138
161
  current += timedelta(days=1)
139
162
 
@@ -0,0 +1,136 @@
1
+ """
2
+ Provider NASA POWER para Climatologia
3
+
4
+ Usa NASA POWER Climatology API para obter dados históricos/climatológicos
5
+ para períodos além da janela de previsão meteorológica (17+ dias).
6
+
7
+ NASA POWER não é previsão exata, é climatologia/histórico.
8
+ """
9
+
10
+ from typing import Dict, Optional
11
+ import requests
12
+ from .cache import get_cache, set_cache
13
+
14
+
15
+ def buscar_climatologia_nasa_power(
16
+ lat: float,
17
+ lon: float,
18
+ month: int
19
+ ) -> Optional[Dict]:
20
+ """
21
+ Busca climatologia NASA POWER para um mês específico.
22
+
23
+ Args:
24
+ lat: Latitude
25
+ lon: Longitude
26
+ month: Mês (1-12)
27
+
28
+ Returns:
29
+ Dicionário com dados climatológicos ou None se falhar
30
+ """
31
+
32
+ # Cache key
33
+ cache_key = f"nasa_power:{lat}:{lon}:{month}"
34
+ cached = get_cache(cache_key)
35
+ if cached:
36
+ return cached
37
+
38
+ try:
39
+ # NASA POWER Climatology API
40
+ # Documentação: https://power.larc.nasa.gov/docs/services/api/
41
+
42
+ # Parâmetros climatológicos
43
+ parameters = [
44
+ "T2M", # Temperatura média a 2m
45
+ "T2M_MAX", # Temperatura máxima a 2m
46
+ "T2M_MIN", # Temperatura mínima a 2m
47
+ "PRECTOTCORR", # Precipitação total corrigida
48
+ ]
49
+
50
+ # Endpoint NASA POWER Climatology
51
+ url = "https://power.larc.nasa.gov/api/temporal/climatology/point"
52
+
53
+ params = {
54
+ "parameters": ",".join(parameters),
55
+ "community": "AG", # Agricultural community
56
+ "longitude": lon,
57
+ "latitude": lat,
58
+ "format": "JSON"
59
+ }
60
+
61
+ response = requests.get(url, params=params, timeout=15)
62
+ response.raise_for_status()
63
+ data = response.json()
64
+
65
+ # Extrair dados do mês específico
66
+ properties = data.get("properties", {}).get("parameter", {})
67
+
68
+ # NASA POWER retorna dados mensais (1-12)
69
+ month_str = str(month)
70
+
71
+ temp_avg = properties.get("T2M", {}).get(month_str)
72
+ temp_max = properties.get("T2M_MAX", {}).get(month_str)
73
+ temp_min = properties.get("T2M_MIN", {}).get(month_str)
74
+ precip = properties.get("PRECTOTCORR", {}).get(month_str)
75
+
76
+ # Validar dados
77
+ if temp_avg is None or precip is None:
78
+ return None
79
+
80
+ # Classificar precipitação
81
+ if precip < 50:
82
+ precip_desc = "Período seco"
83
+ elif precip < 100:
84
+ precip_desc = "Chuvas ocasionais"
85
+ elif precip < 150:
86
+ precip_desc = "Chuvas moderadas"
87
+ elif precip < 200:
88
+ precip_desc = "Chuvas frequentes"
89
+ else:
90
+ precip_desc = "Estação chuvosa"
91
+
92
+ result = {
93
+ "source": "nasa-power",
94
+ "forecast_type": "climatology",
95
+ "month": month,
96
+ "temperature_avg": round(temp_avg, 1),
97
+ "temperature_max": round(temp_max, 1) if temp_max else None,
98
+ "temperature_min": round(temp_min, 1) if temp_min else None,
99
+ "precipitation_expected": precip_desc,
100
+ "precipitation_mm_avg": round(precip, 1),
101
+ "confidence": "media",
102
+ "note": "Dados climatológicos/históricos NASA POWER, não previsão exata."
103
+ }
104
+
105
+ # Cache por 7 dias (climatologia muda pouco)
106
+ set_cache(cache_key, result, ttl_seconds=604800)
107
+
108
+ return result
109
+
110
+ except requests.exceptions.Timeout:
111
+ print(f"Timeout ao buscar NASA POWER para lat={lat}, lon={lon}, month={month}")
112
+ return None
113
+ except requests.exceptions.RequestException as e:
114
+ print(f"Erro ao buscar NASA POWER: {e}")
115
+ return None
116
+ except Exception as e:
117
+ print(f"Erro inesperado ao processar NASA POWER: {e}")
118
+ return None
119
+
120
+
121
+ def get_nasa_power_status() -> Dict:
122
+ """
123
+ Retorna status do provider NASA POWER.
124
+
125
+ Returns:
126
+ Dicionário com informações de status
127
+ """
128
+ return {
129
+ "provider": "NASA POWER",
130
+ "type": "climatology",
131
+ "source": "https://power.larc.nasa.gov/",
132
+ "parameters": ["T2M", "T2M_MAX", "T2M_MIN", "PRECTOTCORR"],
133
+ "community": "AG (Agricultural)",
134
+ "cache_ttl": "7 days",
135
+ "note": "Climatologia/histórico, não previsão exata"
136
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agroplan-ai-cli",
3
- "version": "1.0.35",
3
+ "version": "1.0.36",
4
4
  "description": "CLI global para AgroPlan AI - modo local acelerado",
5
5
  "type": "module",
6
6
  "bin": {