Claude commited on
Commit
bb76929
·
unverified ·
1 Parent(s): 9e437ab

Rebrand UI copy and visual style to enterprise document-intelligence positioning

Browse files

- Hero headline/subheadline reframed around "structured business data"
rather than raw OCR, matching the company's brand positioning.
- Added a small pipeline strip (Upload -> OCR -> AI-Assisted Extraction
-> Structured Data -> CSV Export) as a lightweight nod to the product's
data lifecycle.
- Swapped the indigo/emoji-heavy styling for a more muted blue/slate
palette, subtle card shadows, and eyebrow-style step labels; dropped
emoji from headings and quick-start buttons in favor of plain
professional labels.
- Renamed quick-start buttons and results tabs to match enterprise tone
(Structured fields / Notifications / Audit trail / Raw OCR output).
- Updated README title/intro and step references to match the new
single-flow layout and copy.

Scope stayed limited to rebranding this existing tool (per user
decision) rather than building a separate marketing website - the tool
still only covers construction/compliance/finance, no new extraction
capabilities were added.

Files changed (2) hide show
  1. README.md +27 -21
  2. app.py +72 -25
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: OCR Notification & Data Extractor
3
- emoji: 🧾
4
  colorFrom: blue
5
- colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 5.9.1
8
  app_file: app.py
@@ -10,11 +10,13 @@ pinned: false
10
  license: mit
11
  ---
12
 
13
- # OCR Notification & Data Extractor
14
 
15
- Upload scanned PDFs or images and turn them into structured, exportable
16
- data. Built for teams that need to pull actionable "notification" events
17
- and key fields out of paper-heavy workflows:
 
 
18
 
19
  - **Construction / SAP data support** — change orders, delay notices,
20
  punch list items, RFIs, PO numbers, SAP document numbers, quantities.
@@ -46,14 +48,18 @@ one blanket extraction pass over every document:
46
 
47
  ## How it works
48
 
49
- 1. **Tailor extraction (chat tab)** describe in plain language what you
50
- want flagged vs. pulled as data (e.g. *"flag overdue invoices over
51
- $10k and non-compliance notices, and pull the reference number and
52
- gross weight"*). A Hugging Face-hosted chat model turns that into
53
- extraction settings (categories, notification keywords, custom fields,
54
- export filter), which populate the Upload & Run tab automatically. You
55
- can also skip the chat and set everything by hand there.
56
- 2. Upload one or more PDFs or images.
 
 
 
 
57
  3. Each page's text is pulled directly from the PDF when it has a text
58
  layer; scanned/image-only pages are rendered and run through
59
  Tesseract OCR.
@@ -79,20 +85,20 @@ one blanket extraction pass over every document:
79
 
80
  ## Chat assistant setup
81
 
82
- The chat tab calls the Hugging Face Inference API, so it needs a token:
83
 
84
  - **On a Space:** add a repo secret named `HF_TOKEN` (Settings ->
85
  Variables and secrets) with a token that has Inference API access.
86
- - **Locally / as a fallback:** paste a token into the "Hugging Face
87
- token" field in the chat tab; it's only used for that session.
88
 
89
  The model defaults to `HuggingFaceH4/zephyr-7b-beta` (ungated). Override
90
  it with the `HF_CHAT_MODEL` environment variable/secret if you'd rather
91
  use a different instruct model your token can access.
92
 
93
- If the chat call fails or no token is available, the Upload & Run tab
94
- still works fully via its manual checkboxes/textboxes the chat is a
95
- convenience layer on top, not a dependency.
96
 
97
  ## Local development
98
 
 
1
  ---
2
+ title: Document Intelligence - OCR Data Extractor
3
+ emoji: 📄
4
  colorFrom: blue
5
+ colorTo: gray
6
  sdk: gradio
7
  sdk_version: 5.9.1
8
  app_file: app.py
 
10
  license: mit
11
  ---
12
 
13
+ # Turn Documents Into Structured Business Data
14
 
15
+ The real value isn't converting images into text it's turning
16
+ unstructured documents into reliable, actionable data. This tool takes
17
+ scanned PDFs or images, runs them through OCR, and uses an AI-assisted
18
+ extraction step (configured by describing what you need in plain
19
+ language) to produce clean, audit-ready structured data. Built for:
20
 
21
  - **Construction / SAP data support** — change orders, delay notices,
22
  punch list items, RFIs, PO numbers, SAP document numbers, quantities.
 
48
 
49
  ## How it works
50
 
51
+ The app is a single guided flow: describe what you need, upload your
52
+ documents, run and review.
53
+
54
+ 1. **Step 1 - describe what you need** — in plain language (e.g. *"flag
55
+ overdue invoices over $10k and non-compliance notices, and pull the
56
+ reference number and gross weight"*), or tap one of the
57
+ construction/compliance/finance starting-point buttons. A Hugging
58
+ Face-hosted chat model turns that into extraction settings
59
+ (categories, notification keywords, custom fields, export filter),
60
+ shown live in the "Extraction plan" panel. You can also skip the chat
61
+ and set everything by hand in the "Advanced" accordion in Step 2.
62
+ 2. **Step 2 - upload your documents** — one or more PDFs or images.
63
  3. Each page's text is pulled directly from the PDF when it has a text
64
  layer; scanned/image-only pages are rendered and run through
65
  Tesseract OCR.
 
85
 
86
  ## Chat assistant setup
87
 
88
+ The Step 1 assistant calls the Hugging Face Inference API, so it needs a token:
89
 
90
  - **On a Space:** add a repo secret named `HF_TOKEN` (Settings ->
91
  Variables and secrets) with a token that has Inference API access.
92
+ - **Locally / as a fallback:** paste a token into the "Use your own
93
+ Hugging Face token" accordion in Step 1; it's only used for that session.
94
 
95
  The model defaults to `HuggingFaceH4/zephyr-7b-beta` (ungated). Override
96
  it with the `HF_CHAT_MODEL` environment variable/secret if you'd rather
97
  use a different instruct model your token can access.
98
 
99
+ If the chat call fails or no token is available, the rest of the app
100
+ still works fully via the "Advanced: edit settings manually" accordion
101
+ in Step 2 — the chat is a convenience layer on top, not a dependency.
102
 
103
  ## Local development
104
 
app.py CHANGED
@@ -42,31 +42,77 @@ METADATA_COLUMNS = [
42
  ]
43
 
44
  QUICK_PROMPTS = {
45
- "🏗️ Construction / SAP": (
46
  "Flag change orders, delay notices, and safety violations. "
47
  "Pull PO numbers, SAP document numbers, and reference numbers."
48
  ),
49
- " Compliance": (
50
  "Flag non-compliance, violations, and expired certifications. "
51
  "Pull certification codes and expiration dates."
52
  ),
53
- "💵 Finance / AR": (
54
  "Flag overdue and past-due invoices, credit holds, and disputes. "
55
  "Pull invoice numbers, amounts, and due dates."
56
  ),
57
  }
58
 
59
  CUSTOM_CSS = """
60
- #hero { text-align: center; padding: 8px 8px 4px 8px; }
61
- #hero h1 { font-size: 1.9rem; margin-bottom: 2px; }
62
- #hero p { color: var(--body-text-color-subdued); max-width: 720px; margin: 0 auto; }
63
- .step-card { border-radius: 16px !important; padding: 22px !important; margin-bottom: 18px; }
64
- .step-kicker { font-weight: 600; font-size: 1.05rem; margin-bottom: 2px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  .step-subtitle { color: var(--body-text-color-subdued); margin-bottom: 14px; font-size: 0.92rem; }
66
- #run-button button { font-size: 1.05rem !important; padding: 14px !important; }
67
  #quick-prompts button { font-size: 0.85rem !important; }
68
  """
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  def _file_path(f):
72
  return f if isinstance(f, str) else f.name
@@ -212,28 +258,29 @@ def handle_chat(history, message, settings, hf_token):
212
 
213
 
214
  theme = gr.themes.Soft(
215
- primary_hue="indigo",
216
  secondary_hue="slate",
217
  neutral_hue="slate",
218
  font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"],
219
  )
220
 
221
- with gr.Blocks(title="OCR Notification & Data Extractor", theme=theme, css=CUSTOM_CSS) as demo:
222
  gr.Markdown(
223
- "# 🧾 OCR Notification & Data Extractor\n"
224
- "Tell it what you need, upload your documents, and get a clean, "
225
- "audit-ready CSV back built for construction/SAP, compliance, "
226
- "and finance teams.",
227
  elem_id="hero",
228
  )
 
229
 
230
  settings_state = gr.State(dict(DEFAULT_SETTINGS))
231
 
232
  with gr.Group(elem_classes=["step-card"]):
233
- gr.Markdown('<div class="step-kicker">Step 1 · Tell us what you need</div>')
234
  gr.Markdown(
235
  '<div class="step-subtitle">Describe what to flag and what to pull as data, '
236
- "in your own words — or tap a starting point below.</div>"
237
  )
238
  with gr.Row(elem_id="quick-prompts"):
239
  quick_buttons = {label: gr.Button(label, size="sm") for label in QUICK_PROMPTS}
@@ -249,10 +296,10 @@ with gr.Blocks(title="OCR Notification & Data Extractor", theme=theme, css=CUSTO
249
  placeholder="e.g. Flag overdue invoices; pull reference number, gross weight, delivery date",
250
  )
251
  chat_send = gr.Button("Send", scale=1, variant="primary")
252
- settings_display = gr.JSON(label="What we'll extract", value=DEFAULT_SETTINGS)
253
 
254
  with gr.Group(elem_classes=["step-card"]):
255
- gr.Markdown('<div class="step-kicker">Step 2 · Upload your documents</div>')
256
  gr.Markdown('<div class="step-subtitle">Upload as many PDFs or images as you need reviewed at once.</div>')
257
  files_input = gr.Files(
258
  label="PDF or image files",
@@ -282,18 +329,18 @@ with gr.Blocks(title="OCR Notification & Data Extractor", theme=theme, css=CUSTO
282
  )
283
 
284
  with gr.Group(elem_classes=["step-card"]):
285
- gr.Markdown('<div class="step-kicker">Step 3 · Run &amp; review</div>')
286
  run_button = gr.Button("Run OCR & Extract", variant="primary", elem_id="run-button")
287
  status_output = gr.Textbox(label="Status", lines=2, interactive=False)
288
- csv_output = gr.Files(label="Download CSVs (fields, notifications, run metadata)")
289
  with gr.Tabs():
290
- with gr.Tab("Extracted fields"):
291
  fields_table_output = gr.Dataframe(headers=FIELDS_COLUMNS, wrap=True)
292
- with gr.Tab("Notification hits"):
293
  notifications_table_output = gr.Dataframe(headers=NOTIFICATIONS_COLUMNS, wrap=True)
294
- with gr.Tab("Run audit"):
295
  run_metadata_output = gr.JSON(label="Exactly what settings produced this run")
296
- with gr.Tab("Raw OCR text (debug)"):
297
  text_output = gr.Textbox(label="Raw extracted text", lines=20, show_label=False)
298
 
299
  chat_outputs = [
 
42
  ]
43
 
44
  QUICK_PROMPTS = {
45
+ "Construction & SAP data": (
46
  "Flag change orders, delay notices, and safety violations. "
47
  "Pull PO numbers, SAP document numbers, and reference numbers."
48
  ),
49
+ "Regulatory compliance": (
50
  "Flag non-compliance, violations, and expired certifications. "
51
  "Pull certification codes and expiration dates."
52
  ),
53
+ "Finance & accounts receivable": (
54
  "Flag overdue and past-due invoices, credit holds, and disputes. "
55
  "Pull invoice numbers, amounts, and due dates."
56
  ),
57
  }
58
 
59
  CUSTOM_CSS = """
60
+ #hero { text-align: center; padding: 28px 16px 8px 16px; }
61
+ #hero h1 {
62
+ font-size: 2.1rem; font-weight: 700; letter-spacing: -0.02em;
63
+ margin-bottom: 8px; color: var(--body-text-color);
64
+ }
65
+ #hero p {
66
+ color: var(--body-text-color-subdued); max-width: 640px; margin: 0 auto;
67
+ font-size: 1.02rem; line-height: 1.5;
68
+ }
69
+ .pipeline-strip {
70
+ display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
71
+ gap: 6px; margin: 4px auto 28px auto; max-width: 900px; padding: 0 16px;
72
+ }
73
+ .pipeline-step {
74
+ display: flex; align-items: center; gap: 7px;
75
+ font-size: 0.78rem; font-weight: 600; letter-spacing: 0.01em;
76
+ color: var(--body-text-color-subdued);
77
+ background: var(--background-fill-secondary);
78
+ border: 1px solid var(--border-color-primary);
79
+ border-radius: 999px; padding: 5px 13px; white-space: nowrap;
80
+ }
81
+ .pipeline-step .num {
82
+ display: inline-flex; align-items: center; justify-content: center;
83
+ width: 16px; height: 16px; border-radius: 50%;
84
+ background: var(--primary-500); color: white; font-size: 0.65rem;
85
+ }
86
+ .pipeline-arrow { color: var(--body-text-color-subdued); font-size: 0.85rem; opacity: 0.6; }
87
+
88
+ .step-card {
89
+ border-radius: 14px !important; padding: 24px !important; margin-bottom: 18px;
90
+ box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 1px 8px rgba(0,0,0,0.03) !important;
91
+ }
92
+ .step-eyebrow {
93
+ font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
94
+ letter-spacing: 0.08em; color: var(--primary-500); margin-bottom: 4px;
95
+ }
96
+ .step-kicker { font-weight: 600; font-size: 1.15rem; margin-bottom: 2px; }
97
  .step-subtitle { color: var(--body-text-color-subdued); margin-bottom: 14px; font-size: 0.92rem; }
98
+ #run-button button { font-size: 1.02rem !important; padding: 14px !important; font-weight: 600 !important; }
99
  #quick-prompts button { font-size: 0.85rem !important; }
100
  """
101
 
102
+ PIPELINE_HTML = """
103
+ <div class="pipeline-strip">
104
+ <div class="pipeline-step"><span class="num">1</span>Upload</div>
105
+ <div class="pipeline-arrow">&rarr;</div>
106
+ <div class="pipeline-step"><span class="num">2</span>OCR Recognition</div>
107
+ <div class="pipeline-arrow">&rarr;</div>
108
+ <div class="pipeline-step"><span class="num">3</span>AI-Assisted Extraction</div>
109
+ <div class="pipeline-arrow">&rarr;</div>
110
+ <div class="pipeline-step"><span class="num">4</span>Structured Data</div>
111
+ <div class="pipeline-arrow">&rarr;</div>
112
+ <div class="pipeline-step"><span class="num">5</span>CSV Export</div>
113
+ </div>
114
+ """
115
+
116
 
117
  def _file_path(f):
118
  return f if isinstance(f, str) else f.name
 
258
 
259
 
260
  theme = gr.themes.Soft(
261
+ primary_hue="blue",
262
  secondary_hue="slate",
263
  neutral_hue="slate",
264
  font=[gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif"],
265
  )
266
 
267
+ with gr.Blocks(title="Document Intelligence — OCR & Structured Data Extraction", theme=theme, css=CUSTOM_CSS) as demo:
268
  gr.Markdown(
269
+ "# Turn Documents Into Structured Business Data\n"
270
+ "AI-assisted OCR built for construction, compliance, and finance "
271
+ "teams. Describe what you need, upload your documents, and get "
272
+ "clean, audit-ready data back — not just extracted text.",
273
  elem_id="hero",
274
  )
275
+ gr.HTML(PIPELINE_HTML)
276
 
277
  settings_state = gr.State(dict(DEFAULT_SETTINGS))
278
 
279
  with gr.Group(elem_classes=["step-card"]):
280
+ gr.Markdown('<div class="step-eyebrow">Step 1</div><div class="step-kicker">Tell us what you need</div>')
281
  gr.Markdown(
282
  '<div class="step-subtitle">Describe what to flag and what to pull as data, '
283
+ "in your own words — or start from a template below.</div>"
284
  )
285
  with gr.Row(elem_id="quick-prompts"):
286
  quick_buttons = {label: gr.Button(label, size="sm") for label in QUICK_PROMPTS}
 
296
  placeholder="e.g. Flag overdue invoices; pull reference number, gross weight, delivery date",
297
  )
298
  chat_send = gr.Button("Send", scale=1, variant="primary")
299
+ settings_display = gr.JSON(label="Extraction plan", value=DEFAULT_SETTINGS)
300
 
301
  with gr.Group(elem_classes=["step-card"]):
302
+ gr.Markdown('<div class="step-eyebrow">Step 2</div><div class="step-kicker">Upload your documents</div>')
303
  gr.Markdown('<div class="step-subtitle">Upload as many PDFs or images as you need reviewed at once.</div>')
304
  files_input = gr.Files(
305
  label="PDF or image files",
 
329
  )
330
 
331
  with gr.Group(elem_classes=["step-card"]):
332
+ gr.Markdown('<div class="step-eyebrow">Step 3</div><div class="step-kicker">Run &amp; review</div>')
333
  run_button = gr.Button("Run OCR & Extract", variant="primary", elem_id="run-button")
334
  status_output = gr.Textbox(label="Status", lines=2, interactive=False)
335
+ csv_output = gr.Files(label="Download CSVs (structured fields, notifications, audit trail)")
336
  with gr.Tabs():
337
+ with gr.Tab("Structured fields"):
338
  fields_table_output = gr.Dataframe(headers=FIELDS_COLUMNS, wrap=True)
339
+ with gr.Tab("Notifications"):
340
  notifications_table_output = gr.Dataframe(headers=NOTIFICATIONS_COLUMNS, wrap=True)
341
+ with gr.Tab("Audit trail"):
342
  run_metadata_output = gr.JSON(label="Exactly what settings produced this run")
343
+ with gr.Tab("Raw OCR output (debug)"):
344
  text_output = gr.Textbox(label="Raw extracted text", lines=20, show_label=False)
345
 
346
  chat_outputs = [