File size: 5,613 Bytes
0c14bd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56dfa20
0c14bd5
1cbc909
0c14bd5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1cbc909
0c14bd5
 
a2e90aa
0c14bd5
 
 
 
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
import time

import gradio as gr
import requests

# Default backend URL for Modal deployment
DEFAULT_BACKEND_URL = "PASTE_YOUR_DEPLOYED_URL_HERE"


def extract_text(receipt, api_uri, progress=gr.Progress(track_tqdm=True)):
    if receipt is None:
        return gr.update(value="Please upload a receipt image.", interactive=True)

    # Use user-provided API URI or default
    backend_url = (
        api_uri.strip() if api_uri and api_uri.strip() else DEFAULT_BACKEND_URL
    )
    parse_url = f"{backend_url}/parse"
    result_url = f"{backend_url}/result"

    try:
        mime_type = getattr(receipt, "type", "application/octet-stream")
        with open(receipt.name, "rb") as f:
            files = {"receipt": (receipt.name, f, mime_type)}
            resp = requests.post(parse_url, files=files)  # Send image to backend
        if not resp.ok:
            return f"Error from backend: {resp.status_code} {resp.text}"
        data = resp.json()
        call_id = data.get("call_id")
        if not call_id:
            return "No call_id returned from backend."
        # Poll /result/{call_id}
        for i in range(60):  # Poll for up to 60 seconds
            poll_resp = requests.get(f"{result_url}/{call_id}")
            if poll_resp.status_code == 202:
                progress((i + 1) / 60, desc="Processing...")
                time.sleep(1)
                continue
            if poll_resp.ok:
                try:
                    result = poll_resp.json()
                    if isinstance(result, dict):
                        return result.get("result", str(result))
                    return str(result)
                except Exception:
                    return poll_resp.text
            else:
                return f"Error polling result: {poll_resp.status_code} {poll_resp.text}"
        return "Timed out waiting for OCR result."
    except Exception as e:
        return f"Exception: {e}"


# JavaScript to force dark theme on Gradio
js_func = """
function refresh() {
    const url = new URL(window.location);
    if (url.searchParams.get('__theme') !== 'dark') {
        url.searchParams.set('__theme', 'dark');
        window.location.href = url.href;
    }
}
"""


def main():
    with gr.Blocks(
        js=js_func, theme=gr.themes.Soft(primary_hue="orange", secondary_hue="gray")
    ) as demo:
        # Title and subtitle
        gr.HTML("""
        <style>
        @media (prefers-color-scheme: dark) {
            .elegant-ocr-title, .elegant-ocr-subtitle { color: #fff !important; }
        }
        @media (prefers-color-scheme: light), (prefers-color-scheme: no-preference) {
            .elegant-ocr-title { color: #222 !important; }
            .elegant-ocr-subtitle { color: #555 !important; }
        }
        </style>
        <div style='text-align: center; margin-bottom: 1.5rem;'>
            <h1 class='elegant-ocr-title' style='font-size: 2.5rem; font-weight: 700;'>Receipt OCR based on NuExtract-2.0</h1>
            <p class='elegant-ocr-subtitle' style='font-size: 1.1rem;'>Upload a receipt image and extract text using NuExtract-2.0 hosted on Modal.</p>
        </div>
        """)

        with gr.Group():
            api_uri = gr.Textbox(
                label="Backend API URI",
                placeholder="https://your-backend-url.modal.run",
                info="Please enter your backend api url",
                lines=1,
            )

        with gr.Row():
            with gr.Column(scale=1, min_width=350):
                with gr.Group():
                    receipt = gr.File(
                        label="Upload Receipt Image",
                        file_types=["image"],
                        type="filepath",
                    )
                    image_preview = gr.Image(
                        label="Preview",
                        visible=False,
                        show_label=True,
                        height=250,
                        width=250,
                        elem_id="preview-img",
                    )
            with gr.Column(scale=2, min_width=400):
                with gr.Group():
                    result_box = gr.Textbox(
                        label="OCR Result",
                        lines=14,
                        interactive=True,
                        show_copy_button=True,
                        elem_id="result-box",
                        container=True,
                        max_lines=20,
                        placeholder="The extracted text will appear here...",
                    )
        extract_btn = gr.Button("Extract Text", variant="primary", size="lg")
        status = gr.Markdown("", visible=False)

        # Add examples section
        gr.Examples(
            examples=[["receipt.png"]],
            inputs=receipt,
            label="📄 Example Receipt",
        )

        # Show preview of uploaded image
        def show_preview(file):
            if file is not None:
                return gr.update(value=file.name, visible=True)
            return gr.update(visible=False)

        receipt.change(fn=show_preview, inputs=receipt, outputs=image_preview)
        extract_btn.click(
            fn=extract_text, inputs=[receipt, api_uri], outputs=result_box
        )

        gr.HTML("""
        <footer style='text-align: right; margin-top: 2rem; color: #888;'>
            Powered by <a href='https://modal.com' target='_blank' style='color: #007FFF; text-decoration: none; font-weight: 600;'>Mahimai AI Labs ❤️</a>
        </footer>
        """)
    demo.launch(share=True)


if __name__ == "__main__":
    main()