Files changed (1) hide show
  1. app.py +261 -35
app.py CHANGED
@@ -1,59 +1,285 @@
 
1
  import gradio as gr
2
  import json
3
- from docling.document_converter import DocumentConverter, PdfFormatOption
4
- from docling.datamodel.pipeline_options import PdfPipelineOptions, TesseractCliOcrOptions
5
- from docling.datamodel.base_models import InputFormat
6
  import spaces
7
 
8
- # GPU decorator not really required for Docling OCR, but kept if you want
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  @spaces.GPU
10
  def convert_document(file, output_format):
11
- # Configure OCR pipeline
12
- pdf_opts = PdfPipelineOptions(
13
- do_ocr=True,
14
- ocr_options=TesseractCliOcrOptions(lang=["eng"]) # or ["eng","ara"] if needed
15
- )
16
 
17
- # Correct way: pass options via format_options
18
- converter = DocumentConverter(
19
- format_options={
20
- InputFormat.PDF: PdfFormatOption(pipeline_options=pdf_opts)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
- )
23
 
24
- # Convert document
25
- result = converter.convert(file.name)
26
 
27
- # Choose output format safely
28
- if output_format == "Markdown":
29
- converted_text = result.document.export_to_markdown()
30
- elif output_format == "JSON":
31
- converted_text = result.document.export_to_dict()
32
- else:
33
- converted_text = "⚠️ Unsupported format"
34
 
35
- # Metadata as JSON-friendly dict
36
- metadata = {"Available Attributes": dir(result.document)}
 
 
 
 
37
 
38
- return converted_text, metadata
39
 
 
 
 
40
 
41
  with gr.Blocks() as app:
42
- gr.Markdown("# 📄 Document Converter with Docling OCR")
43
- gr.Markdown("Upload a PDF, choose the output format, and get the converted text + metadata.")
 
 
 
 
 
 
44
 
45
  with gr.Row():
46
- file_input = gr.File(label="Upload PDF", file_types=[".pdf"])
47
- format_input = gr.Radio(["Markdown", "JSON"], label="Choose Output Format")
48
 
49
- output_text = gr.Textbox(label="Converted Document", lines=20)
50
- output_metadata = gr.JSON(label="Metadata")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- convert_button = gr.Button("Convert")
53
  convert_button.click(
54
  fn=convert_document,
55
- inputs=[file_input, format_input],
56
- outputs=[output_text, output_metadata]
 
 
 
 
 
 
 
57
  )
58
 
59
  app.launch(debug=True)
 
 
1
+ ```python
2
  import gradio as gr
3
  import json
4
+ import re
 
 
5
  import spaces
6
 
7
+ from docling.document_converter import (
8
+ DocumentConverter,
9
+ PdfFormatOption
10
+ )
11
+
12
+ from docling.datamodel.pipeline_options import (
13
+ PdfPipelineOptions,
14
+ TesseractCliOcrOptions
15
+ )
16
+
17
+ from docling.datamodel.base_models import InputFormat
18
+
19
+
20
+ # -----------------------------
21
+ # Helpers
22
+ # -----------------------------
23
+
24
+ def clean_amount(x):
25
+ if not x:
26
+ return ""
27
+ return x.replace(",", "").strip()
28
+
29
+
30
+ def convert_date(d):
31
+ m = re.match(r"(\d{2})-(\d{2})-(\d{2,4})", d)
32
+ if not m:
33
+ return d
34
+
35
+ dd, mm, yy = m.groups()
36
+
37
+ yyyy = "20" + yy if len(yy) == 2 else yy
38
+
39
+ return f"{yyyy}-{mm}-{dd}"
40
+
41
+
42
+ def detect_voucher_type(text):
43
+ text = text.upper()
44
+
45
+ receipt_words = [
46
+ "CR",
47
+ "DEPOSIT",
48
+ "SUBSIDY",
49
+ "INTEREST",
50
+ "CREDIT",
51
+ "NEFT",
52
+ "IMPS",
53
+ "RTGS"
54
+ ]
55
+
56
+ for word in receipt_words:
57
+ if word in text:
58
+ return "Receipt"
59
+
60
+ return "Payment"
61
+
62
+
63
+ # -----------------------------
64
+ # Transaction Extraction
65
+ # -----------------------------
66
+
67
+ def extract_transactions(doc_dict):
68
+
69
+ transactions = []
70
+
71
+ tables = doc_dict.get("tables", [])
72
+
73
+ for table in tables:
74
+
75
+ cells = table.get("data", {}).get("table_cells", [])
76
+
77
+ grouped_rows = {}
78
+
79
+ for cell in cells:
80
+ row_index = cell.get("start_row_offset_idx")
81
+
82
+ grouped_rows.setdefault(row_index, []).append(cell)
83
+
84
+ for _, row_cells in sorted(grouped_rows.items()):
85
+
86
+ row_text = " ".join(
87
+ c.get("text", "") for c in row_cells
88
+ )
89
+
90
+ date_match = re.search(
91
+ r"\d{2}-\d{2}-\d{2,4}",
92
+ row_text
93
+ )
94
+
95
+ if not date_match:
96
+ continue
97
+
98
+ amounts = re.findall(
99
+ r"\d{1,3}(?:,\d{3})*(?:\.\d{2})|\d+\.\d{2}",
100
+ row_text
101
+ )
102
+
103
+ if len(amounts) < 2:
104
+ continue
105
+
106
+ date = convert_date(date_match.group(0))
107
+
108
+ amount = clean_amount(amounts[-2])
109
+
110
+ closing_balance = clean_amount(amounts[-1])
111
+
112
+ narration = row_text
113
+
114
+ narration = narration.replace(
115
+ date_match.group(0),
116
+ ""
117
+ )
118
+
119
+ for amt in amounts[-2:]:
120
+ narration = narration.replace(amt, "")
121
+
122
+ narration = re.sub(
123
+ r"\s+",
124
+ " ",
125
+ narration
126
+ ).strip()
127
+
128
+ transactions.append({
129
+ "date": date,
130
+ "description": narration[:80],
131
+ "raw_narration": narration,
132
+ "cheque_reference": "",
133
+ "voucher_type": detect_voucher_type(narration),
134
+ "amount": amount,
135
+ "closing_balance": closing_balance
136
+ })
137
+
138
+ return {
139
+ "success": True,
140
+ "total_transactions": len(transactions),
141
+ "transactions": transactions
142
+ }
143
+
144
+
145
+ # -----------------------------
146
+ # Main Convert Function
147
+ # -----------------------------
148
+
149
  @spaces.GPU
150
  def convert_document(file, output_format):
 
 
 
 
 
151
 
152
+ if file is None:
153
+ return "No file uploaded", {}
154
+
155
+ try:
156
+
157
+ pdf_opts = PdfPipelineOptions(
158
+ do_ocr=True,
159
+ ocr_options=TesseractCliOcrOptions(
160
+ lang=["eng"]
161
+ )
162
+ )
163
+
164
+ converter = DocumentConverter(
165
+ format_options={
166
+ InputFormat.PDF:
167
+ PdfFormatOption(
168
+ pipeline_options=pdf_opts
169
+ )
170
+ }
171
+ )
172
+
173
+ result = converter.convert(file.name)
174
+
175
+ doc = result.document
176
+
177
+ # Markdown
178
+ if output_format == "Markdown":
179
+
180
+ converted_text = doc.export_to_markdown()
181
+
182
+ # Raw JSON
183
+ elif output_format == "JSON":
184
+
185
+ converted_text = json.dumps(
186
+ doc.export_to_dict(),
187
+ indent=2,
188
+ ensure_ascii=False
189
+ )
190
+
191
+ # Clean Transaction JSON
192
+ elif output_format == "Bank Transaction JSON":
193
+
194
+ tx_json = extract_transactions(
195
+ doc.export_to_dict()
196
+ )
197
+
198
+ converted_text = json.dumps(
199
+ tx_json,
200
+ indent=2,
201
+ ensure_ascii=False
202
+ )
203
+
204
+ else:
205
+ converted_text = "Unsupported format"
206
+
207
+ metadata = {
208
+ "status": "success",
209
+ "pages": len(
210
+ getattr(doc, "pages", [])
211
+ ),
212
+ "output_format": output_format
213
  }
 
214
 
215
+ return converted_text, metadata
 
216
 
217
+ except Exception as e:
 
 
 
 
 
 
218
 
219
+ return (
220
+ f"ERROR: {str(e)}",
221
+ {
222
+ "status": "error"
223
+ }
224
+ )
225
 
 
226
 
227
+ # -----------------------------
228
+ # UI
229
+ # -----------------------------
230
 
231
  with gr.Blocks() as app:
232
+
233
+ gr.Markdown(
234
+ "# 📄 Docling OCR + Bank Statement Extractor"
235
+ )
236
+
237
+ gr.Markdown(
238
+ "Upload PDF and extract Markdown, JSON, or clean bank transactions."
239
+ )
240
 
241
  with gr.Row():
 
 
242
 
243
+ file_input = gr.File(
244
+ label="Upload PDF",
245
+ file_types=[".pdf"]
246
+ )
247
+
248
+ format_input = gr.Radio(
249
+ [
250
+ "Markdown",
251
+ "JSON",
252
+ "Bank Transaction JSON"
253
+ ],
254
+ value="Bank Transaction JSON",
255
+ label="Choose Output Format"
256
+ )
257
+
258
+ output_text = gr.Textbox(
259
+ label="Converted Output",
260
+ lines=25
261
+ )
262
+
263
+ output_metadata = gr.JSON(
264
+ label="Metadata"
265
+ )
266
+
267
+ convert_button = gr.Button(
268
+ "Convert"
269
+ )
270
 
 
271
  convert_button.click(
272
  fn=convert_document,
273
+ inputs=[
274
+ file_input,
275
+ format_input
276
+ ],
277
+ outputs=[
278
+ output_text,
279
+ output_metadata
280
+ ],
281
+ api_name="/convert_document"
282
  )
283
 
284
  app.launch(debug=True)
285
+ ```