TameemY commited on
Commit
191e1df
·
verified ·
1 Parent(s): 5666517

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -202
app.py DELETED
@@ -1,202 +0,0 @@
1
- # app.py — Gradio frontend
2
- #
3
- # Run locally: pip install gradio requests then python app.py
4
- # Deploy to hosting: push this file + requirements.txt to a Gradio-compatible host
5
-
6
- import gradio as gr
7
- import requests
8
- import os
9
- import tempfile
10
- import time
11
- import uuid
12
- from concurrent.futures import ThreadPoolExecutor
13
-
14
- # ---------------------------------------------------------------------------
15
- # Production Modal endpoints should come from environment variables in your
16
- # deployment platform so they stay hidden from end users and out of public code.
17
- # ---------------------------------------------------------------------------
18
- MODAL_PREDICT_URL = os.environ.get("MODAL_PREDICT_URL", "").rstrip("/")
19
- MODAL_STATUS_URL = os.environ.get("MODAL_STATUS_URL", "").rstrip("/")
20
- if not MODAL_STATUS_URL and MODAL_PREDICT_URL:
21
- MODAL_STATUS_URL = MODAL_PREDICT_URL.replace("-predict.", "-status.")
22
- DEFAULT_DPI = 500
23
- DEFAULT_SCORE_THRESHOLD = 0.9
24
- DEFAULT_OVERLAP_PX = 200
25
- REQUEST_EXECUTOR = ThreadPoolExecutor(max_workers=4)
26
-
27
-
28
- def _submit_pdf_request(pdf_path, draw_mode, job_id):
29
- url = MODAL_PREDICT_URL
30
- if not url:
31
- raise RuntimeError("Backend is not configured.")
32
-
33
- with open(pdf_path, "rb") as file_handle:
34
- return requests.post(
35
- url,
36
- files={"file": ("plan.pdf", file_handle, "application/pdf")},
37
- data={
38
- "job_id": job_id,
39
- "dpi": str(DEFAULT_DPI),
40
- "score_thr": str(DEFAULT_SCORE_THRESHOLD),
41
- "draw_mode": draw_mode,
42
- "overlap_px": str(DEFAULT_OVERLAP_PX),
43
- },
44
- timeout=600,
45
- )
46
-
47
-
48
- def _fetch_status(job_id):
49
- if not MODAL_STATUS_URL:
50
- return None
51
-
52
- response = requests.get(MODAL_STATUS_URL, params={"job_id": job_id}, timeout=15)
53
- if response.status_code == 200:
54
- return response.json()
55
- return None
56
-
57
-
58
- def _format_progress(status_payload):
59
- if not status_payload:
60
- return "Processing PDF..."
61
-
62
- tiles_total = int(status_payload.get("tiles_total", 0) or 0)
63
- tiles_done = int(status_payload.get("tiles_done", 0) or 0)
64
- eta_text = status_payload.get("eta_text", "--:--")
65
- if tiles_total > 0:
66
- remaining_pct = max(0.0, 100.0 * (tiles_total - tiles_done) / tiles_total)
67
- remaining_text = f"{remaining_pct:.0f}%"
68
- else:
69
- remaining_text = "--%"
70
-
71
- return (
72
- f"Estimated remaining time: {eta_text}\n"
73
- f"Remaining: {remaining_text}"
74
- )
75
-
76
-
77
- def annotate_pdf(pdf_file, draw_mode):
78
- if pdf_file is None:
79
- yield None, "❌ Please upload a PDF."
80
- return
81
-
82
- if not MODAL_PREDICT_URL:
83
- yield None, "❌ Backend is not configured."
84
- return
85
-
86
- job_id = uuid.uuid4().hex
87
- future = REQUEST_EXECUTOR.submit(_submit_pdf_request, pdf_file.name, draw_mode, job_id)
88
- last_status = None
89
-
90
- yield None, "Processing PDF..."
91
-
92
- while not future.done():
93
- try:
94
- status_payload = _fetch_status(job_id)
95
- status_text = _format_progress(status_payload)
96
- except requests.exceptions.ConnectionError:
97
- status_text = "Connecting to backend..."
98
- except requests.exceptions.Timeout:
99
- status_text = "Waiting for progress update..."
100
- except Exception as exc:
101
- status_text = f"Waiting for progress update...\n{exc}"
102
-
103
- if status_text != last_status:
104
- yield None, status_text
105
- last_status = status_text
106
- time.sleep(1.5)
107
-
108
- try:
109
- resp = future.result()
110
- except requests.exceptions.ConnectionError:
111
- yield None, "❌ Could not reach the Modal server. Is the URL correct?"
112
- return
113
- except requests.exceptions.Timeout:
114
- yield None, "❌ Request timed out (>10 min). Try a lower DPI or smaller file."
115
- return
116
- except Exception as exc:
117
- yield None, f"❌ Request failed:\n{exc}"
118
- return
119
-
120
- if resp.status_code == 200:
121
- with tempfile.NamedTemporaryFile(
122
- mode="wb",
123
- suffix=".pdf",
124
- prefix="annotated_result_",
125
- delete=False,
126
- ) as temp_file:
127
- temp_file.write(resp.content)
128
- out_path = temp_file.name
129
-
130
- counts = resp.headers.get("X-Detection-Counts", "N/A")
131
- elapsed = resp.headers.get("X-Elapsed-Seconds", "?")
132
- status = f"Estimated remaining time: 00:00\nRemaining: 0%\n\n✅ Done in {elapsed}s\n{counts}"
133
- yield out_path, status
134
-
135
- else:
136
- try:
137
- detail = resp.json()
138
- except Exception:
139
- detail = resp.text[:500]
140
- yield None, f"❌ Server error {resp.status_code}:\n{detail}"
141
-
142
-
143
- # ---------------------------------------------------------------------------
144
- # UI
145
- # ---------------------------------------------------------------------------
146
-
147
- with gr.Blocks(title="PDF Plan Annotator") as demo:
148
-
149
- gr.Markdown(
150
- """
151
- # PDF Plan Annotator
152
- Upload a PDF plan and download an annotated result.
153
-
154
- > **How it works:** Processing runs on a remote GPU service.
155
- > Processing typically takes **1–4 minutes** depending on plan size.
156
- """
157
- )
158
-
159
- with gr.Row():
160
- with gr.Column(scale=1):
161
- pdf_input = gr.File(
162
- label="Upload PDF",
163
- file_types=[".pdf"],
164
- )
165
-
166
- with gr.Accordion("⚙️ Annotation settings", open=False):
167
- draw_radio = gr.Radio(
168
- choices=["line", "rectangle"],
169
- value="line",
170
- label="Annotation style",
171
- info="Line = measured edge only. Rectangle = full bounding box."
172
- )
173
-
174
- run_btn = gr.Button("Process PDF", variant="primary", size="lg")
175
-
176
- with gr.Column(scale=1):
177
- pdf_output = gr.File(label="Download annotated PDF")
178
- status_box = gr.Textbox(
179
- label="Status",
180
- lines=4,
181
- interactive=False,
182
- placeholder="Results will appear here after processing…"
183
- )
184
-
185
- run_btn.click(
186
- fn=annotate_pdf,
187
- inputs=[pdf_input, draw_radio],
188
- outputs=[pdf_output, status_box],
189
- )
190
-
191
- gr.Markdown(
192
- """
193
- ---
194
- **Tips**
195
- - First request after a period of inactivity takes ~15s extra (GPU cold start).
196
- - Large files may take longer to process.
197
- - The output PDF contains the generated annotations.
198
- """
199
- )
200
-
201
- if __name__ == "__main__":
202
- demo.launch()