Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,11 +4,32 @@ from invoice import (
|
|
| 4 |
do_parse, do_generate, load_pricing, compute_totals,
|
| 5 |
today_str, new_invoice_code, logo_data_uri
|
| 6 |
)
|
|
|
|
| 7 |
|
| 8 |
PREVIEW_LOGO = logo_data_uri() # empty string if no asset file is present
|
| 9 |
|
| 10 |
|
| 11 |
# ---------- helpers ----------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
def _unit(modules, unit_override):
|
| 13 |
tiers = load_pricing()
|
| 14 |
try:
|
|
@@ -221,9 +242,22 @@ def on_change(inv_type, inv_date, inv_number, modules, audit_type,
|
|
| 221 |
|
| 222 |
def on_generate(uploaded_file, inv_date, inv_num, inv_type, modules, audit_type, s_audit_date, s_name, s_addr, s_email, s_phone, unit_override):
|
| 223 |
# backend handles date, number, unit automatically
|
| 224 |
-
meta, xlsx, pdf = do_generate(
|
|
|
|
|
|
|
|
|
|
| 225 |
preview_html = _preview(inv_type, inv_date, inv_num, modules, audit_type, s_name, s_addr, s_email, s_phone, s_audit_date, unit_override)
|
| 226 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
def update_audit_dropdown(inv_type_value, current_audit):
|
| 229 |
choices = _choices_for(inv_type_value)
|
|
@@ -439,7 +473,7 @@ with gr.Blocks(title="Professional Invoice Generator", css=custom_css) as demo:
|
|
| 439 |
gr.HTML('<div class="controls-title">Invoice Settings</div>')
|
| 440 |
with gr.Group(elem_classes=["upload-section"]):
|
| 441 |
gr.HTML('<div style="text-align: center; margin-bottom: 8px; font-weight: 600; color: #374151; font-size: 11px;">Upload Report</div>')
|
| 442 |
-
up = gr.
|
| 443 |
gr.HTML('<div style="font-size: 10px; color: #6b7280; text-align: center; margin-top: 4px;">Auto-fill invoice details</div>')
|
| 444 |
|
| 445 |
with gr.Group(elem_classes=["form-controls"]):
|
|
@@ -464,8 +498,9 @@ with gr.Blocks(title="Professional Invoice Generator", css=custom_css) as demo:
|
|
| 464 |
1, "NHVR Maintenance Audit", "", "", "", "", "", "auto"
|
| 465 |
))
|
| 466 |
with gr.Group(elem_classes=["download-section"]):
|
| 467 |
-
dl_xlsx = gr.DownloadButton("Download Excel", elem_classes=["download-btn"], size="sm")
|
| 468 |
-
dl_pdf
|
|
|
|
| 469 |
|
| 470 |
# Hidden states
|
| 471 |
s_name = gr.State("")
|
|
|
|
| 4 |
do_parse, do_generate, load_pricing, compute_totals,
|
| 5 |
today_str, new_invoice_code, logo_data_uri
|
| 6 |
)
|
| 7 |
+
import os, re, shutil, uuid
|
| 8 |
|
| 9 |
PREVIEW_LOGO = logo_data_uri() # empty string if no asset file is present
|
| 10 |
|
| 11 |
|
| 12 |
# ---------- helpers ----------
|
| 13 |
+
def _stage_for_download(path):
|
| 14 |
+
"""
|
| 15 |
+
Copy `path` to /tmp with a safe filename so Hugging Face Spaces can serve it.
|
| 16 |
+
Returns the new path or None.
|
| 17 |
+
"""
|
| 18 |
+
if not path or not os.path.isfile(path):
|
| 19 |
+
return None
|
| 20 |
+
base = os.path.basename(path)
|
| 21 |
+
root, ext = os.path.splitext(base)
|
| 22 |
+
# sanitize: keep only safe chars
|
| 23 |
+
safe_root = re.sub(r'[^A-Za-z0-9_.-]+', '_', root).strip('_') or f"file_{uuid.uuid4().hex[:8]}"
|
| 24 |
+
safe_ext = ext if ext else ""
|
| 25 |
+
safe = f"/tmp/{safe_root}{safe_ext}"
|
| 26 |
+
try:
|
| 27 |
+
if os.path.abspath(path) != os.path.abspath(safe):
|
| 28 |
+
shutil.copyfile(path, safe)
|
| 29 |
+
return safe
|
| 30 |
+
except Exception:
|
| 31 |
+
return None
|
| 32 |
+
|
| 33 |
def _unit(modules, unit_override):
|
| 34 |
tiers = load_pricing()
|
| 35 |
try:
|
|
|
|
| 242 |
|
| 243 |
def on_generate(uploaded_file, inv_date, inv_num, inv_type, modules, audit_type, s_audit_date, s_name, s_addr, s_email, s_phone, unit_override):
|
| 244 |
# backend handles date, number, unit automatically
|
| 245 |
+
meta, xlsx, pdf = do_generate(
|
| 246 |
+
uploaded_file, inv_type, modules, audit_type, s_audit_date, s_name, s_addr, s_email, s_phone
|
| 247 |
+
)
|
| 248 |
+
# refresh preview
|
| 249 |
preview_html = _preview(inv_type, inv_date, inv_num, modules, audit_type, s_name, s_addr, s_email, s_phone, s_audit_date, unit_override)
|
| 250 |
+
|
| 251 |
+
# IMPORTANT: stage files into /tmp with safe names so Spaces can serve them
|
| 252 |
+
staged_xlsx = _stage_for_download(xlsx)
|
| 253 |
+
staged_pdf = _stage_for_download(pdf)
|
| 254 |
+
|
| 255 |
+
return (
|
| 256 |
+
preview_html,
|
| 257 |
+
gr.DownloadButton.update(value=staged_xlsx, visible=bool(staged_xlsx)),
|
| 258 |
+
gr.DownloadButton.update(value=staged_pdf, visible=bool(staged_pdf))
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
|
| 262 |
def update_audit_dropdown(inv_type_value, current_audit):
|
| 263 |
choices = _choices_for(inv_type_value)
|
|
|
|
| 473 |
gr.HTML('<div class="controls-title">Invoice Settings</div>')
|
| 474 |
with gr.Group(elem_classes=["upload-section"]):
|
| 475 |
gr.HTML('<div style="text-align: center; margin-bottom: 8px; font-weight: 600; color: #374151; font-size: 11px;">Upload Report</div>')
|
| 476 |
+
up = gr.File(label="Upload Report (.pdf or .docx)", file_types=[".pdf", ".docx"], file_count="single")
|
| 477 |
gr.HTML('<div style="font-size: 10px; color: #6b7280; text-align: center; margin-top: 4px;">Auto-fill invoice details</div>')
|
| 478 |
|
| 479 |
with gr.Group(elem_classes=["form-controls"]):
|
|
|
|
| 498 |
1, "NHVR Maintenance Audit", "", "", "", "", "", "auto"
|
| 499 |
))
|
| 500 |
with gr.Group(elem_classes=["download-section"]):
|
| 501 |
+
dl_xlsx = gr.DownloadButton("Download Excel", elem_classes=["download-btn"], size="sm", visible=False)
|
| 502 |
+
dl_pdf = gr.DownloadButton("Download PDF", elem_classes=["download-btn"], size="sm", visible=False, mime_type="application/pdf")
|
| 503 |
+
|
| 504 |
|
| 505 |
# Hidden states
|
| 506 |
s_name = gr.State("")
|