Add EXTRACT_IGNORE list for labels that aren't food or events

Grade ranges like K-8 are always dropped; other labels (such as BHS) are now listed in EXTRACT_IGNORE instead of being built in. Matches whole entries in any case, in menu items and event titles.
This commit is contained in:
iamdoubz committed 2026-11-05 14:59:24 -06:00
1 parent d781d3d0ea
commit 3b0fb016ed
1 file changed
+23 -5
+23 -5
View File
@@ -9,6 +9,7 @@ Settings (environment variables):
EXTRACT_THINK 1 to let an Ollama model reason before answering (default 0)
EXTRACT_TEMPERATURE Ollama sampling temperature (default 0)
EXTRACT_DOUBLE_READ 1 to read every page twice and flag where the reads disagree (default 0)
EXTRACT_IGNORE comma-separated labels to drop from menu items and event titles, e.g. "BHS,Milk" (default none)
ANTHROPIC_API_KEY only for claude-* models
"""
import base64
@@ -38,6 +39,7 @@ OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://localhost:11434").rstrip("/")
THINK = os.environ.get("EXTRACT_THINK") == "1"
TEMPERATURE = float(os.environ.get("EXTRACT_TEMPERATURE", "0"))
DOUBLE_READ = os.environ.get("EXTRACT_DOUBLE_READ") == "1"
IGNORE = {w.strip().lower() for w in os.environ.get("EXTRACT_IGNORE", "").split(",") if w.strip()}
EFFORT = "high" # Claude only
LONG_EDGE = 2000 # ponytail: rotate + downscale only; add deskew/crop when angled photos score worse than flat ones
LOW_CONFIDENCE = 0.7
@@ -153,7 +155,13 @@ Menus
`confidence` is 0 to 1. Go below 0.7 whenever you had to guess."""
GRID_LAYOUTS = ("month_grid", "menu")
LABEL = re.compile(r"(k|\d{1,2})\s*-\s*\d{1,2}|bhs", re.I) # ponytail: school-specific labels belong in the source's prompt notes
GRADE_RANGE = re.compile(r"(k|\d{1,2})\s*-\s*\d{1,2}", re.I) # "K-8", "6-12": never food, never an event
def ignored(text, words):
"""A grade range, or one of the labels the user listed (whole text, any case)."""
text = (text or "").strip()
return bool(GRADE_RANGE.fullmatch(text)) or text.lower() in words
def load_image(path):
@@ -184,15 +192,18 @@ def iso(s):
return s
def cleanup(result):
"""Deterministic tidy-up before validation: pad dates, drop events with no real title."""
def cleanup(result, ignore=None):
"""Deterministic tidy-up before validation: pad dates, drop events with no real title,
and drop labels (grade ranges and the ignore list) from event titles and menu items."""
ignore = IGNORE if ignore is None else ignore
for item in [result["date_range"]] + result["events"] + result["menu_items"]:
for key in ("date", "end_date", "start", "end"):
if item.get(key):
item[key] = iso(item[key])
result["events"] = [e for e in result["events"] if re.sub(r"[^a-z]", "", e["title"].lower()) not in ("", "event")]
result["events"] = [e for e in result["events"] if not ignored(e["title"], ignore)]
for m in result["menu_items"]:
m["items"] = [i for i in m["items"] if not LABEL.fullmatch(i.strip())]
m["items"] = [i for i in m["items"] if not ignored(i, ignore)]
return result
@@ -490,7 +501,14 @@ def selftest():
# Year boundary: December row ending in January
row = [col(wd, d) for wd, d in zip(sun_first, [27, 28, 29, 30, 31, 1, 2])]
assert first_column_date(row, 2026, 12) == (date(2026, 12, 27), 0)
assert [i for i in ["K-8", "Grapes", "BHS", "6-12", "Corn"] if not LABEL.fullmatch(i)] == ["Grapes", "Corn"]
# Labels: grade ranges always go; other words only when listed, matched whole and in any case
items = ["K-8", "Grapes", "BHS", "6-12", "Corn", "bhs ", "BHS Pizza"]
assert [i for i in items if not ignored(i, set())] == ["Grapes", "BHS", "Corn", "bhs ", "BHS Pizza"]
assert [i for i in items if not ignored(i, {"bhs"})] == ["Grapes", "Corn", "BHS Pizza"]
r = cleanup({"date_range": {}, "menu_items": [{"date": "2026-11-04", "items": ["K-8", "Grapes", "BHS"]}],
"events": [{"date": "2026-11-04", "title": "BHS", "kind": "event"},
{"date": "2026-11-04", "title": "No School", "kind": "no_school"}]}, {"bhs"})
assert r["menu_items"][0]["items"] == ["Grapes"] and [e["title"] for e in r["events"]] == ["No School"], r
# Double read: agreement adds nothing, a moved date and a one-sided item are flagged
ev = lambda d, t, **kw: {"date": d, "end_date": None, "all_day": True, "start_time": None,
"end_time": None, "kind": "event", "title": t, **kw}