4444
4545def make_conn ():
4646 conn = duckdb .connect ()
47- conn . execute ( "INSTALL spatial; LOAD spatial;" )
47+ # No spatial extension — dashboard only reads scalar columns from parquet
4848 conn .execute (f"""
4949 CREATE SECRET IF NOT EXISTS s3_secret (
5050 TYPE s3,
@@ -83,7 +83,9 @@ def read_ts(country, aoi_name):
8383 try :
8484 df = CONN .execute (f"""
8585 SELECT time, ndvi, bsi, ndmi, nbr
86- FROM read_parquet('{ glob } ')
86+ FROM read_parquet('{ glob } ',
87+ union_by_name=true,
88+ hive_partitioning=false)
8789 WHERE aoi_name = '{ aoi_name } '
8890 AND time > '2018-01-01'
8991 ORDER BY time
@@ -94,7 +96,28 @@ def read_ts(country, aoi_name):
9496 except Exception as e :
9597 print (f"read_ts error: { e } " )
9698 traceback .print_exc ()
97- return pd .DataFrame ()
99+ # Fallback: use boto3 + pyarrow to read parquet directly
100+ try :
101+ import boto3 , io , pyarrow .parquet as pq
102+ s3 = boto3 .client ("s3" )
103+ prefix = f"{ country } /{ aoi_name } /ts/"
104+ resp = s3 .list_objects_v2 (Bucket = BUCKET , Prefix = prefix )
105+ dfs = []
106+ for obj in resp .get ("Contents" , []):
107+ if obj ["Key" ].endswith (".parquet" ):
108+ buf = io .BytesIO (s3 .get_object (Bucket = BUCKET , Key = obj ["Key" ])["Body" ].read ())
109+ tbl = pq .read_table (buf , columns = ["time" ,"ndvi" ,"bsi" ,"ndmi" ,"nbr" ,"aoi_name" ])
110+ dfs .append (tbl .to_pandas ())
111+ if not dfs :
112+ return pd .DataFrame ()
113+ df = pd .concat (dfs )
114+ df = df [df ["aoi_name" ] == aoi_name ].query ("time > '2018-01-01'" ).sort_values ("time" )
115+ df ["time" ] = pd .to_datetime (df ["time" ])
116+ print (f"read_ts fallback OK: { df .shape } " )
117+ return df [["time" ,"ndvi" ,"bsi" ,"ndmi" ,"nbr" ]]
118+ except Exception as e2 :
119+ print (f"read_ts fallback error: { e2 } " )
120+ return pd .DataFrame ()
98121
99122def read_forecasts (country , aoi_name ):
100123 glob = f"s3://{ BUCKET } /{ country } /{ aoi_name } /ml/forecast_{ aoi_name } _*.parquet"
0 commit comments