Skip to content

Commit 3d73b00

Browse files
update the benchmark FIM look up and more
2 parents fa0f5ce + 1a995fa commit 3d73b00

File tree

5 files changed

+6117
-46
lines changed

5 files changed

+6117
-46
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ dependencies = [
3434
"notebook>=6.5.7",
3535
"geocube<=0.7.1",
3636
"geopandas>=0.14.3",
37-
"fimeval>=0.1.56"
37+
"fimeval>=0.1.59"
3838
]
3939

4040
[project.optional-dependencies]

src/fimserve/fimevaluation/fims_setup.py

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from collections import defaultdict
77

88
# Internal utilities
9-
from .utilis import (
9+
from .utils import (
1010
load_catalog_core,
1111
download_fim_assets,
1212
_to_date,
@@ -15,6 +15,7 @@
1515
_record_hour_or_none,
1616
format_records_for_print,
1717
find_fims,
18+
_record_huc8_list,
1819
)
1920

2021
from ..datadownload import DownloadHUC8, setup_directories
@@ -42,7 +43,7 @@ def _ensure_roots(self):
4243
self._roots_initialized = True
4344

4445
def availability(self, HUCID: str) -> str:
45-
from .utilis import availability as _avail
46+
from .utils import availability as _avail
4647

4748
return _avail(HUCID)
4849

@@ -171,7 +172,7 @@ def process(
171172
r
172173
for r in records
173174
if str(r.get("file_name", "")).strip() == fname
174-
and str(r.get("huc8", "")).strip() == str(huc8).strip()
175+
and str(huc8).strip() in set(_record_huc8_list(r))
175176
]
176177
cand_any_huc = [
177178
r for r in records if str(r.get("file_name", "")).strip() == fname
@@ -348,7 +349,7 @@ def process(
348349

349350
# Ensure/generate OWP HAND FIM for that event time and copy to all matching site folders
350351
owp_src_copied_any = False
351-
if user_dt:
352+
if ensure_owp and user_dt:
352353
for site in dl_by_site.keys():
353354
folder = inputs_root / f"HUC{huc8}_{site}"
354355
owp_path = self._ensure_owp_to(
@@ -370,7 +371,7 @@ def process(
370371
"downloads": dl_records,
371372
"owp_path": (
372373
str(folder / f"NWM_{label}_{huc8}_inundation.tif")
373-
if owp_src_copied_any
374+
if (ensure_owp and owp_src_copied_any)
374375
else None
375376
),
376377
}
@@ -379,9 +380,10 @@ def process(
379380
msg_bits = [
380381
f"Downloaded {total_downloaded} benchmark item(s) into '{inputs_root}'."
381382
]
382-
msg_bits.append(
383-
"OWP HAND FIMs ensured per event (based on benchmark timestamps)."
384-
)
383+
if ensure_owp:
384+
msg_bits.append(
385+
"OWP HAND FIMs ensured per event (based on benchmark timestamps)."
386+
)
385387

386388
return {
387389
"status": "ok",
@@ -517,25 +519,41 @@ def _generate_owp(self, huc8: str, user_dt: str) -> Optional[Path]:
517519
def fim_lookup(
518520
HUCID: str,
519521
date_input: Optional[str] = None,
520-
file_name: Optional[str] = None,
521-
run_handfim: bool = False,
522-
out_dir: Optional[str] = None, # Directory to place downloaded/generated files
523522
start_date: Optional[str] = None,
524523
end_date: Optional[str] = None,
524+
file_name: Optional[str] = None,
525+
run_handfim: bool = False,
526+
out_dir: Optional[str] = None,
525527
) -> str:
526528
"""
527-
- run_handfim=False (default): show a formatted benchmark list.
528-
- run_handfim=True: run the OWP HAND process (copy/generate), DO NOT print the benchmark summary;
529-
just return the operational message from the process step.
529+
Behavior:
530+
- If file_name is provided: ALWAYS download the benchmark assets (tif + gpkg) into out_dir (or CWD),
531+
regardless of run_handfim.
532+
- If run_handfim=True: additionally ensure/generate OWP HAND FIM (copied into the same folder(s)).
533+
- If file_name is not provided:
534+
* run_handfim=False -> listing mode (query/pretty print)
535+
* run_handfim=True -> process mode (download strict matches + ensure OWP)
530536
"""
531537
svc = FIMService()
532538

533-
# List-only mode
539+
# If filename is provided, always download benchmark assets.
540+
if file_name:
541+
rep = svc.process(
542+
huc8=HUCID,
543+
date_input=date_input,
544+
ensure_owp=run_handfim,
545+
generate_owp_if_missing=run_handfim,
546+
out_dir=out_dir,
547+
file_name=file_name,
548+
)
549+
return rep.get("message", "")
550+
551+
# If No filename provided: preserve original behavior
534552
if not run_handfim:
535553
q = svc.query(
536554
HUCID=HUCID,
537555
date_input=date_input,
538-
file_name=file_name,
556+
file_name=None,
539557
start_date=start_date,
540558
end_date=end_date,
541559
)
@@ -545,7 +563,6 @@ def fim_lookup(
545563
"No benchmark FIMs were matched with the information you provided.\n"
546564
f"(HUC={HUCID}"
547565
f"{', date='+date_input if date_input else ''}"
548-
f"{', file_name='+file_name if file_name else ''}"
549566
f"{', range=['+str(start_date)+' , '+str(end_date)+']' if (start_date or end_date) else ''})"
550567
)
551568

@@ -557,18 +574,16 @@ def fim_lookup(
557574
filt.append(f"date '{date_input}'")
558575
if start_date or end_date:
559576
filt.append(f"range [{start_date or '-∞'} , {end_date or '∞'}]")
560-
if file_name:
561-
filt.append(f"file '{file_name}'")
562577
prefix = header + (" for " + ", ".join(filt) + ":\n" if filt else ":\n")
563578
return prefix + txt
564579

565-
# Run/ensure OWP mode
580+
# If run_handfim=True, no filename: process strict matches with date and ensure OWP
566581
rep = svc.process(
567582
huc8=HUCID,
568583
date_input=date_input,
569584
ensure_owp=True,
570585
generate_owp_if_missing=True,
571586
out_dir=out_dir,
572-
file_name=file_name,
587+
file_name=None,
573588
)
574-
return rep.get("message", "")
589+
return rep.get("message", "")
Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,7 @@ def _context_str(
143143

144144
return ", ".join(parts) if parts else "your filters"
145145

146-
147-
def format_records_for_print(
148-
records: List[Dict[str, Any]], context: Optional[str] = None
149-
) -> str:
146+
def format_records_for_print(records: List[Dict[str, Any]], context: Optional[str] = None) -> str:
150147
if not records:
151148
ctx = context or "your filters"
152149
return f"Benchmark FIMs were not matched for {ctx}."
@@ -159,7 +156,7 @@ def format_records_for_print(
159156

160157
blocks: List[str] = []
161158
for r in records:
162-
tier = r.get("tier") or r.get("quality") or "Unknown"
159+
tier = _tier_label(r)
163160
date_str = _pretty_date_for_print(r)
164161
res = r.get("resolution_m")
165162
res_txt = f"{res}m" if res is not None else "NA"
@@ -194,8 +191,89 @@ def _download(bucket: str, key: str, dest_path: str) -> str:
194191
_S3.download_file(bucket, key, dest_path)
195192
return dest_path
196193

194+
def _record_huc8_list(rec: Dict[str, Any]) -> List[str]:
195+
"""
196+
Return HUC8s from a record as a normalized list of strings.
197+
198+
Catalog store:
199+
- "huc8": "['03020201','03020202',...]"
200+
"""
201+
v = rec.get("huc8")
202+
203+
if v is None:
204+
return []
205+
if isinstance(v, (list, tuple, set)):
206+
out: List[str] = []
207+
for x in v:
208+
if x is None:
209+
continue
210+
s = str(x).strip().strip("'").strip('"')
211+
if s:
212+
out.append(s)
213+
return out
214+
215+
if isinstance(v, str):
216+
s = v.strip()
217+
if not s:
218+
return []
219+
220+
# stringified list like "['03020201', '03020202']"
221+
if s.startswith("[") and s.endswith("]"):
222+
try:
223+
import ast
224+
parsed = ast.literal_eval(s)
225+
if isinstance(parsed, (list, tuple, set)):
226+
out: List[str] = []
227+
for x in parsed:
228+
if x is None:
229+
continue
230+
t = str(x).strip().strip("'").strip('"')
231+
if t:
232+
out.append(t)
233+
return out
234+
except Exception:
235+
pass
236+
237+
inner = s[1:-1].strip()
238+
if not inner:
239+
return []
240+
parts = [p.strip() for p in inner.split(",") if p.strip()]
241+
out2: List[str] = []
242+
for p in parts:
243+
t = p.strip().strip("'").strip('"')
244+
if t:
245+
out2.append(t)
246+
return out2
247+
return [s.strip().strip("'").strip('"')]
248+
return [str(v).strip()]
249+
250+
251+
def _tier_label(rec: Dict[str, Any]) -> str:
252+
"""
253+
Normalize tier/quality/HWM style labels into a consistent printable string.
254+
"""
255+
raw = rec.get("tier")
256+
if raw is None or str(raw).strip() == "":
257+
raw = rec.get("quality")
258+
if raw is None or str(raw).strip() == "":
259+
raw = rec.get("HWM") #For High Water Marks (HWM)
260+
261+
s = str(raw).strip() if raw is not None else ""
262+
if not s:
263+
return "Unknown"
264+
265+
s_low = s.lower().replace(" ", "").replace("-", "_")
266+
#forms: Tier_2, tier2, 2, Tier 2
267+
if "tier" in s_low:
268+
m = re.search(r"tier[_ ]*(\d+)", s_low)
269+
if m:
270+
return f"Tier {m.group(1)}"
271+
return s.replace("_", " ").strip()
272+
if s.isdigit():
273+
return f"Tier {s}"
274+
return s
197275

198-
# Search FIMs record in database
276+
#finding the benchmark FIMs
199277
def find_fims(
200278
records: List[Dict[str, Any]],
201279
huc8: str,
@@ -222,7 +300,7 @@ def find_fims(
222300
- Else (date with hour, or no date): fall back to strict behavior
223301
"""
224302
huc8 = str(huc8).strip()
225-
recs = [r for r in records if str(r.get("huc8", "")).strip() == huc8]
303+
recs = [r for r in records if huc8 in set(_record_huc8_list(r))]
226304

227305
if file_name:
228306
fname = file_name.strip()
@@ -268,7 +346,7 @@ def find_fims(
268346

269347
if date_input and _to_hour_or_none(date_input) is None:
270348
target_day = _to_date(date_input)
271-
out = []
349+
out: List[Dict[str, Any]] = []
272350
for r in recs:
273351
r_day = _record_day(r)
274352
if r_day == target_day:
@@ -291,7 +369,7 @@ def find_fims(
291369

292370
def summarize_huc_availability(records: List[Dict[str, Any]], huc8: str) -> str:
293371
huc8 = str(huc8).strip()
294-
recs = [r for r in records if str(r.get("huc8", "")).strip() == huc8]
372+
recs = [r for r in records if huc8 in set(_record_huc8_list(r))]
295373
if not recs:
296374
return f"No benchmark FIMs on HUC {huc8}."
297375

@@ -302,11 +380,12 @@ def summarize_huc_availability(records: List[Dict[str, Any]], huc8: str) -> str:
302380
with_raw.append(r)
303381

304382
if not with_raw:
305-
rps = sorted(
306-
{str(r.get("return_period")) for r in recs if r.get("return_period")}
307-
)
383+
rps = sorted({str(r.get("return_period")) for r in recs if r.get("return_period")})
308384
if rps:
309-
return f"No real flood-based benchmarks on HUC {huc8}. Only synthetic return periods available: {', '.join(rps)}."
385+
return (
386+
f"No real flood-based benchmarks on HUC {huc8}. "
387+
f"Only synthetic return periods available: {', '.join(rps)}."
388+
)
310389
return f"No real flood-based benchmarks on HUC {huc8}."
311390

312391
day_set, hour_set = set(), set()
@@ -375,13 +454,16 @@ def download_fim_assets(record: Dict[str, Any], dest_dir: str) -> Dict[str, Any]
375454
def build_huc_event_dict(records: List[Dict[str, Any]]) -> Dict[str, List[str]]:
376455
d: Dict[str, List[str]] = {}
377456
for r in records:
378-
huc = str(r.get("huc8"))
457+
hucs = _record_huc8_list(r)
458+
if not hucs:
459+
continue
379460
day = _record_day(r)
380461
if not day:
381462
continue
382463
hour = _record_hour_or_none(r)
383464
ts = day.isoformat() if hour is None else f"{day:%Y-%m-%d} {hour:02d}:00:00"
384-
d.setdefault(huc, []).append(ts)
465+
for huc in hucs:
466+
d.setdefault(str(huc), []).append(ts)
385467
for k in list(d.keys()):
386468
d[k] = sorted(set(d[k]))
387469
return d
@@ -412,7 +494,7 @@ def bmFIMFindandDownload(
412494
catalog = load_catalog_core()
413495
records = catalog.get("records", [])
414496

415-
# STRICT set (used for status/logic/downloads)
497+
# STRICT set
416498
strict_matches = find_fims(
417499
records,
418500
huc8=HUC8,
@@ -423,7 +505,7 @@ def bmFIMFindandDownload(
423505
relaxed_for_print=False,
424506
)
425507

426-
# RELAXED set (printing only)
508+
# RELAXED set
427509
relaxed_records_for_print = find_fims(
428510
records,
429511
huc8=HUC8,

tests/test_evalutionhandfim.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
# Look for the benchmark FIM data for the HUC8 and event date
66
def test_bm_fimlookup():
77
out = fm.fim_lookup(
8-
HUCID="07070005",
9-
date_input="2019-05-30 23:00:00", # If user is more specific then they can pass date (with hour if known) along with HUC8
10-
run_handfim=True, # It will look for the OWP HAND FIM for the mentioned HUC8 and date; if not found it will download and generate the OWP HAND FIM
11-
file_name="S1A_9_6m_20190530T23573_910244W430506N_BM.tif", # If user passes a specific filename, it will download that and assume it is the right benchmark
12-
out_dir="./FIMserv/test", # If user wants to save the benchmark FIM in a specific directory
13-
# start_date="2024-06-20", # If user is not sure of the exact date then they can pass a range of dates
8+
HUCID="10240011",
9+
# date_input="2019-06-15", # If user is more specific then they can pass date (with hour if known) along with HUC8
10+
# start_date="2017-06-20", #If user is not sure of the exact date then they can pass a range of dates
1411
# end_date="2024-06-25",
12+
run_handfim=True, #It will look for the owp hand fim for the mentioned HUC8 and date, if not found it will download and generate the owp hand fim; default is False
13+
file_name= "S1A_9_2m_20190615T00210_945738W393944N_BM.tif", #If user pass the specific filename, it will download that and assume that this is the right benchmark, else based on exact match of date it will look for the benchmark
14+
out_dir= '../test_FIMeval', #Required if user wants to download the benchmark FIM data
1515
)
1616
print(out)
1717

0 commit comments

Comments
 (0)