jeffrey1963 commited on
Commit
d8fdeae
·
verified ·
1 Parent(s): 2a59f54

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +17 -55
app.py CHANGED
@@ -39,15 +39,6 @@ def _coerce_numeric(x):
39
  try: return float(s)
40
  except: return pd.NA
41
 
42
- # ---- NEW: in‑memory file helpers (no disk writes) ----
43
- def _csv_bytes(df: pd.DataFrame | None) -> bytes:
44
- buf = io.StringIO()
45
- (df if df is not None else pd.DataFrame()).to_csv(buf, index=False)
46
- return buf.getvalue().encode("utf-8")
47
-
48
- def _text_bytes(txt: str | None) -> bytes:
49
- return (txt or "").encode("utf-8")
50
-
51
  PARAM_PATTERNS = {
52
  "cost": r"cost\s*[:=]\s*\$?\s*([\d,]+(?:\.\d+)?)",
53
  "salvage": r"salvage\s*[:=]\s*\$?\s*([\d,]+(?:\.\d+)?)",
@@ -250,42 +241,31 @@ def handle_docx(file):
250
  # )
251
 
252
  def handle_image(img):
253
- """OCR → table (raw + normalized) → keep types safe for Gradio."""
254
- from PIL import Image as PILImage
255
-
256
- # If nothing uploaded, return safe empties for every output
257
  if img is None:
258
- empty = pd.DataFrame()
259
- yr = pd.Timestamp.now().year
260
- return ("(no image)", {}, empty, empty, 0.0, 0.0, 10, yr, {}, empty)
261
 
262
- # Convert to PIL (handles ndarray vs PIL.Image)
263
  pil = img if isinstance(img, PILImage.Image) else PILImage.fromarray(img)
264
 
265
- # 1) OCR text
266
- ocr_text = _image_to_text(pil) or ""
 
 
267
 
268
- # 2) Try to pick params out of the OCR text
269
- params = _extract_params(ocr_text)
270
  cost, salv, life, year = _params_tuple(params)
271
 
272
- # 3) Parse a table from OCR text
273
- raw = _table_from_ocr_text(ocr_text or "")
274
-
275
- # 🔒 IMPORTANT: never return None to gr.Dataframe
276
- raw_df = raw if isinstance(raw, pd.DataFrame) else pd.DataFrame()
277
- norm_df = _normalize_depr_columns(raw_df) if not raw_df.empty else pd.DataFrame()
278
-
279
  return (
280
- (ocr_text if ocr_text.strip() else "(empty OCR)"),
281
- params, # JSON box
282
- raw_df, # Raw table (always a DataFrame)
283
- norm_df, # Normalized table (always a DataFrame)
284
- cost, salv, life, year, # Autofill numbers
285
- params, # last_params state
286
- norm_df # last_table state (what the checker expects)
287
  )
288
 
 
 
289
  def fill_from_state(p):
290
  p = p or {}
291
  return (
@@ -311,7 +291,7 @@ def check_cb(cost, salv, life, year, table_state):
311
  if not isinstance(table_state, pd.DataFrame) or table_state.empty:
312
  return pd.DataFrame(), "No student table found to check."
313
 
314
- # re-normalize and coerce here every time
315
  actual = _normalize_depr_columns(table_state)
316
  for c in ["Year", "Begin BV", "Depreciation", "Accum Dep", "End BV"]:
317
  actual[c] = pd.to_numeric(actual[c], errors="coerce")
@@ -342,16 +322,7 @@ with gr.Blocks(title="Jerry • HW Intake (Echo)") as demo:
342
  ocr_txt = gr.Textbox(label="Raw OCR text", lines=12)
343
  params_json2 = gr.JSON(label="Detected parameters")
344
  raw_df = gr.Dataframe(label="Raw table guess", interactive=False)
345
- # make editable so you can fix numbers
346
- norm_df = gr.Dataframe(label="Detected table (normalized)", interactive=True)
347
-
348
- # ---- NEW: in‑memory downloads ----
349
- raw_dl = gr.DownloadButton(label="Download RAW CSV")
350
- norm_dl = gr.DownloadButton(label="Download NORMALIZED CSV")
351
- txt_dl = gr.DownloadButton(label="Download OCR TEXT")
352
-
353
- # re‑export after manual edits
354
- export_btn = gr.DownloadButton(label="Download EDITED NORMALIZED CSV")
355
 
356
  # --- Tab 3: Solve & Check ---
357
  with gr.Tab("Straight-Line • Solve & Check"):
@@ -391,9 +362,6 @@ with gr.Blocks(title="Jerry • HW Intake (Echo)") as demo:
391
  params_json2, # json
392
  raw_df, # raw table
393
  norm_df, # normalized table (tab 2)
394
- raw_dl, # NEW: bytes -> RAW CSV download
395
- norm_dl, # NEW: bytes -> NORMALIZED CSV download
396
- txt_dl, # NEW: bytes -> OCR TEXT download
397
  in_cost, in_salv, in_life, in_year, # autofill inputs
398
  last_params, # state
399
  last_table, # state
@@ -410,12 +378,6 @@ with gr.Blocks(title="Jerry • HW Intake (Echo)") as demo:
410
  btn_build.click(build_cb, [in_cost, in_salv, in_life, in_year], [expected_df])
411
  btn_check.click(check_cb, [in_cost, in_salv, in_life, in_year, last_table], [deltas_df, coach_txt])
412
 
413
- # ---- NEW: export edited normalized CSV ----
414
- def export_current(norm_df_current: pd.DataFrame):
415
- return _csv_bytes(norm_df_current if isinstance(norm_df_current, pd.DataFrame) else pd.DataFrame())
416
-
417
- export_btn.click(export_current, inputs=norm_df, outputs=export_btn)
418
-
419
  gr.Markdown("— Echo mode finished. When this looks good, we’ll plug in the SL solver + coaching.")
420
 
421
  if __name__ == "__main__":
 
39
  try: return float(s)
40
  except: return pd.NA
41
 
 
 
 
 
 
 
 
 
 
42
  PARAM_PATTERNS = {
43
  "cost": r"cost\s*[:=]\s*\$?\s*([\d,]+(?:\.\d+)?)",
44
  "salvage": r"salvage\s*[:=]\s*\$?\s*([\d,]+(?:\.\d+)?)",
 
241
  # )
242
 
243
  def handle_image(img):
 
 
 
 
244
  if img is None:
245
+ return "(no image)", {}, pd.DataFrame(), pd.DataFrame(), 0.0, 0.0, 10, pd.Timestamp.now().year, {}, pd.DataFrame()
 
 
246
 
247
+ from PIL import Image as PILImage
248
  pil = img if isinstance(img, PILImage.Image) else PILImage.fromarray(img)
249
 
250
+ ocr_text = _image_to_text(pil)
251
+ params = _extract_params(ocr_text or "")
252
+ df_raw = _table_from_ocr_text(ocr_text or "")
253
+ df_norm = _normalize_depr_columns(df_raw) if df_raw is not None else pd.DataFrame()
254
 
 
 
255
  cost, salv, life, year = _params_tuple(params)
256
 
 
 
 
 
 
 
 
257
  return (
258
+ ocr_text or "(empty OCR)",
259
+ params,
260
+ df_raw, # raw table shown in OCR tab
261
+ df_norm, # normalized table shown in OCR tab
262
+ cost, salv, life, year, # auto-fill numbers
263
+ params, # save params state
264
+ df_norm # 🔹 save normalized table to last_table (same as docx)
265
  )
266
 
267
+
268
+
269
  def fill_from_state(p):
270
  p = p or {}
271
  return (
 
291
  if not isinstance(table_state, pd.DataFrame) or table_state.empty:
292
  return pd.DataFrame(), "No student table found to check."
293
 
294
+ # 👇 Gradio returns strings → re-normalize and coerce here every time
295
  actual = _normalize_depr_columns(table_state)
296
  for c in ["Year", "Begin BV", "Depreciation", "Accum Dep", "End BV"]:
297
  actual[c] = pd.to_numeric(actual[c], errors="coerce")
 
322
  ocr_txt = gr.Textbox(label="Raw OCR text", lines=12)
323
  params_json2 = gr.JSON(label="Detected parameters")
324
  raw_df = gr.Dataframe(label="Raw table guess", interactive=False)
325
+ norm_df = gr.Dataframe(label="Detected table (normalized)", interactive=False)
 
 
 
 
 
 
 
 
 
326
 
327
  # --- Tab 3: Solve & Check ---
328
  with gr.Tab("Straight-Line • Solve & Check"):
 
362
  params_json2, # json
363
  raw_df, # raw table
364
  norm_df, # normalized table (tab 2)
 
 
 
365
  in_cost, in_salv, in_life, in_year, # autofill inputs
366
  last_params, # state
367
  last_table, # state
 
378
  btn_build.click(build_cb, [in_cost, in_salv, in_life, in_year], [expected_df])
379
  btn_check.click(check_cb, [in_cost, in_salv, in_life, in_year, last_table], [deltas_df, coach_txt])
380
 
 
 
 
 
 
 
381
  gr.Markdown("— Echo mode finished. When this looks good, we’ll plug in the SL solver + coaching.")
382
 
383
  if __name__ == "__main__":