File size: 7,702 Bytes
881a2c6
 
 
 
 
 
 
b6c9a39
 
881a2c6
 
b6c9a39
 
881a2c6
 
 
 
 
b6c9a39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
881a2c6
 
 
 
b6c9a39
881a2c6
 
b6c9a39
881a2c6
 
 
 
 
 
 
 
b6c9a39
 
 
881a2c6
b6c9a39
 
 
 
 
 
 
 
 
881a2c6
b6c9a39
 
 
 
881a2c6
b6c9a39
881a2c6
 
 
 
 
 
 
b6c9a39
 
881a2c6
 
 
 
 
 
 
 
 
 
 
 
 
b6c9a39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
881a2c6
 
 
b6c9a39
881a2c6
b6c9a39
881a2c6
b6c9a39
881a2c6
b6c9a39
 
881a2c6
b6c9a39
 
881a2c6
 
 
 
 
 
 
 
b6c9a39
 
881a2c6
 
 
 
b6c9a39
 
881a2c6
b6c9a39
 
 
 
 
 
 
 
 
 
881a2c6
b6c9a39
 
881a2c6
 
 
 
 
b6c9a39
 
 
 
 
881a2c6
 
 
 
 
 
b6c9a39
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
from fastapi import FastAPI, File, UploadFile, HTTPException, Query
from fastapi.responses import JSONResponse
import gradio as gr
from docling.document_converter import DocumentConverter
from docling.datamodel.base_models import InputFormat
import tempfile
import os
import json
import re

app = FastAPI(
    title="Docling Document Conversion API",
    description="Convert documents to Markdown and extract basic information",
    version="1.0.0"
)

# Initialize Docling
converter = DocumentConverter()

def extract_fields_from_markdown(markdown_text: str, template_type: str) -> dict:
    """Basic field extraction from markdown using regex patterns"""
    
    if template_type == "payment_advice":
        return {
            "document_type": "payment_advice",
            "payment_document_no": re.search(r'(?:Payment Document|Doc(?:ument)? No\.?)[:\s]+(\w+)', markdown_text, re.I),
            "payment_date": re.search(r'(?:Payment Date)[:\s]+([\d\-\/\.]+)', markdown_text, re.I),
            "total_amount": re.search(r'(?:Total|Amount)[:\s]+([\d,\.]+)', markdown_text, re.I),
            "vendor_code": re.search(r'(?:Vendor Code)[:\s]+(\w+)', markdown_text, re.I),
        }
    elif template_type == "invoice":
        return {
            "document_type": "invoice",
            "bill_no": re.search(r'(?:Bill|Invoice) No\.?[:\s]+(\w+)', markdown_text, re.I),
            "bill_date": re.search(r'(?:Bill|Invoice) Date[:\s]+([\d\-\/\.]+)', markdown_text, re.I),
            "total_amount": re.search(r'(?:Total|Grand Total)[:\s]+([\d,\.]+)', markdown_text, re.I),
            "gst_no": re.search(r'GST No\.?[:\s]+(\w+)', markdown_text, re.I),
        }
    
    return {"document_type": "unknown"}

def clean_extracted_fields(fields: dict) -> dict:
    """Convert regex match objects to strings"""
    cleaned = {}
    for key, value in fields.items():
        if hasattr(value, 'group'):
            cleaned[key] = value.group(1).strip()
        elif value:
            cleaned[key] = value
        else:
            cleaned[key] = None
    return cleaned

@app.post("/api/extract")
async def extract_document(
    file: UploadFile = File(...),
    template_type: str = Query(default="auto", description="Template type: auto, payment_advice, invoice")
):
    """
    Convert document to markdown and extract basic structured data
    """
    try:
        # Save uploaded file temporarily
        with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp_file:
            content = await file.read()
            tmp_file.write(content)
            tmp_file_path = tmp_file.name
        
        # Convert document to markdown
        result = converter.convert(tmp_file_path)
        markdown_content = result.document.export_to_markdown()
        
        # Auto-detect template if needed
        if template_type == "auto":
            text_lower = markdown_content.lower()
            if any(keyword in text_lower for keyword in ["payment advice", "payment document", "vendor code"]):
                template_type = "payment_advice"
            elif any(keyword in text_lower for keyword in ["invoice", "bill no", "gst no"]):
                template_type = "invoice"
            else:
                template_type = "general"
        
        # Extract basic fields
        if template_type in ["payment_advice", "invoice"]:
            fields = extract_fields_from_markdown(markdown_content, template_type)
            extracted_data = clean_extracted_fields(fields)
        else:
            extracted_data = {"document_type": template_type}
        
        # Clean up
        os.unlink(tmp_file_path)
        
        return JSONResponse(content={
            "success": True,
            "template_used": template_type,
            "data": extracted_data,
            "markdown": markdown_content,
            "filename": file.filename
        })
    
    except HTTPException:
        raise
    except Exception as e:
        if 'tmp_file_path' in locals():
            try:
                os.unlink(tmp_file_path)
            except:
                pass
        raise HTTPException(status_code=500, detail=f"Processing error: {str(e)}")

@app.post("/api/convert")
async def convert_to_markdown(file: UploadFile = File(...)):
    """Simple document to markdown conversion"""
    try:
        with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.filename)[1]) as tmp_file:
            content = await file.read()
            tmp_file.write(content)
            tmp_file_path = tmp_file.name
        
        result = converter.convert(tmp_file_path)
        markdown_content = result.document.export_to_markdown()
        os.unlink(tmp_file_path)
        
        return JSONResponse(content={
            "success": True,
            "markdown": markdown_content,
            "filename": file.filename
        })
    except Exception as e:
        if 'tmp_file_path' in locals():
            try:
                os.unlink(tmp_file_path)
            except:
                pass
        raise HTTPException(status_code=500, detail=f"Processing error: {str(e)}")

@app.get("/")
def read_root():
    """API information"""
    return {
        "message": "Docling Document Conversion API",
        "version": "1.0.0",
        "note": "Advanced extraction API is in beta - currently using markdown conversion with basic field extraction",
        "endpoints": {
            "extract": "/api/extract (POST) - Convert and extract basic fields",
            "convert": "/api/convert (POST) - Convert to markdown only",
            "docs": "/docs - Interactive API documentation",
            "web_ui": "/gradio - Web interface"
        }
    }

@app.get("/health")
def health_check():
    """Health check endpoint"""
    return {"status": "healthy", "service": "docling-api"}

# Gradio interface
def process_document(file):
    """Gradio function to process documents"""
    try:
        if file is None:
            return "Please upload a file"
        
        result = converter.convert(file.name)
        markdown_content = result.document.export_to_markdown()
        
        # Try basic extraction
        text_lower = markdown_content.lower()
        if "invoice" in text_lower or "bill" in text_lower:
            fields = extract_fields_from_markdown(markdown_content, "invoice")
            extracted = clean_extracted_fields(fields)
            return f"βœ… Detected: Invoice\n\nExtracted Fields:\n{json.dumps(extracted, indent=2)}\n\n--- Markdown ---\n{markdown_content[:2000]}"
        elif "payment" in text_lower:
            fields = extract_fields_from_markdown(markdown_content, "payment_advice")
            extracted = clean_extracted_fields(fields)
            return f"βœ… Detected: Payment Advice\n\nExtracted Fields:\n{json.dumps(extracted, indent=2)}\n\n--- Markdown ---\n{markdown_content[:2000]}"
        else:
            return f"βœ… Converted to Markdown\n\n{markdown_content[:3000]}"
            
    except Exception as e:
        return f"❌ Error: {str(e)}"

demo = gr.Interface(
    fn=process_document,
    inputs=gr.File(label="πŸ“„ Upload Document (PDF, Image, DOCX)"),
    outputs=gr.Textbox(label="πŸ“Š Output", lines=30),
    title="Docling Document Processor",
    description="Upload documents to convert to Markdown and extract basic information. Advanced extraction API coming soon!",
    article="Built with [Docling](https://github.com/docling-project/docling) | API available at `/api/extract` and `/api/convert`",
)

app = gr.mount_gradio_app(app, demo, path="/gradio")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=7860)