CoolFace
Apppublic

lrkkumar/portfolio-analysis

sourceHugging Faceapache-2.0updated 9mo agoView on Hugging Face
0likes
app.py181 linesDownload Raw Back to root
1import gradio as gr2import requests3import os4import json5 6import time, re, math7 8def call_n8n(input_type, uploaded_file, json_data):9    N8N_WEBHOOK_URL = "https://tsademo.app.n8n.cloud/webhook-test/68f9ae31-c226-4a14-b398-671819b8433b"10    if input_type in ("pdf", "csv"):11        file_path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)12        with open(file_path, "rb") as f:13            files = {14                "file": (os.path.basename(file_path), f, "application/pdf" if input_type == "pdf" else "text/csv")15            }16            data = {"type": input_type}17            resp = requests.post(N8N_WEBHOOK_URL, data=data, files=files, timeout=120)18    else:19        payload = {"type": "json", "data":json.loads(json_data)}20        resp = requests.post(N8N_WEBHOOK_URL, json=payload, timeout=120)21    ct = resp.headers.get("content-type", "").lower()22    if "application/json" in ct:23        data = resp.json()24        # Expecting {"text": "..."}25        if isinstance(data, dict) and "text" in data:26            return data["text"]27        # Fallback if structure is unexpected28        return json.dumps(data, indent=2)29    else:30        return resp.text31 32def text_to_pdf(text: str) -> str:33    text = text or ""34    ts = time.strftime("%Y%m%d-%H%M%S")35    out_path = os.path.join("/tmp", f"portfolio_analysis_{ts}.pdf")36 37    # --- Sanitize text to avoid PDF string/encoding issues ---38    # Keep it stable: replace non-Latin-1 chars so PDF always renders.39    # (If you need Telugu/Unicode in the PDF, you must embed a Unicode font—requires a library.)40    safe = text.encode("latin-1", errors="replace").decode("latin-1")41 42    # Normalize line endings and remove problematic control chars (except \n)43    safe = safe.replace("\r\n", "\n").replace("\r", "\n")44    safe = re.sub(r"[\x00-\x08\x0B\x0C\x0E-\x1F]", " ", safe)45 46    def esc_pdf_string(s: str) -> str:47        return s.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)")48 49    # --- Page geometry (A4 points) ---50    page_w, page_h = 595, 84251    left, right, top, bottom = 50, 50, 60, 6052    font_size = 1153    leading = 1454    max_chars = 90  # rough wrap; works well enough without font metrics55 56    # Wrap lines57    wrapped_lines = []58    for raw in safe.split("\n"):59        raw = raw.rstrip()60        if not raw:61            wrapped_lines.append("")62            continue63        # simple wrap by character count64        while len(raw) > max_chars:65            wrapped_lines.append(raw[:max_chars])66            raw = raw[max_chars:]67        wrapped_lines.append(raw)68 69    # Paginate70    lines_per_page = max(1, int((page_h - top - bottom) / leading))71    pages = [72        wrapped_lines[i:i + lines_per_page]73        for i in range(0, len(wrapped_lines), lines_per_page)74    ] or [[""]]75 76    # --- Build PDF objects dynamically with correct xref offsets ---77    objects = []78 79    def add_obj(obj_str: str) -> int:80        objects.append(obj_str)81        return len(objects)  # object number (1-based, 0 reserved)82 83    # 1) Catalog (added later after Pages obj number known)84    # 2) Pages85    # 3..n) Page objects86    # Contents objects per page87    # Font object88 89    # Font object90    font_obj = add_obj("<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>")91 92    # Placeholder for Pages and Catalog; we’ll fill after we know Kids93    pages_placeholder_index = len(objects)  # 0-based index94    pages_obj_num = add_obj("<<>>")  # placeholder95    catalog_obj_num = add_obj(f"<< /Type /Catalog /Pages {pages_obj_num} 0 R >>")96 97    page_obj_nums = []98    contents_obj_nums = []99 100    # Create each page + its content stream101    for page_lines in pages:102        # Build content stream103        y = page_h - top104        stream_cmds = [f"BT /F1 {font_size} Tf"]105        for line in page_lines:106            stream_cmds.append(f"1 0 0 1 {left} {y} Tm ({esc_pdf_string(line)}) Tj")107            y -= leading108        stream_cmds.append("ET")109        stream_data = "\n".join(stream_cmds).encode("latin-1", errors="replace")110        contents_obj_num = add_obj(111            f"<< /Length {len(stream_data)} >>\nstream\n{stream_data.decode('latin-1')}\nendstream"112        )113        contents_obj_nums.append(contents_obj_num)114 115        # Page object (refers to Pages parent, Font, and its Contents)116        page_obj_num = add_obj(117            f"<< /Type /Page /Parent {pages_obj_num} 0 R "118            f"/MediaBox [0 0 {page_w} {page_h}] "119            f"/Resources << /Font << /F1 {font_obj} 0 R >> >> "120            f"/Contents {contents_obj_num} 0 R >>"121        )122        page_obj_nums.append(page_obj_num)123 124    # Now replace Pages placeholder with correct Kids list125    kids = " ".join([f"{n} 0 R" for n in page_obj_nums])126    pages_obj = f"<< /Type /Pages /Kids [ {kids} ] /Count {len(page_obj_nums)} >>"127    objects[pages_placeholder_index] = pages_obj  # overwrite placeholder string128 129    # --- Assemble PDF with xref table ---130    pdf_parts = [b"%PDF-1.4\n"]131    xref_offsets = [0]  # object 0 offset is 0 by spec132 133    # write each obj and record offset134    for i, obj in enumerate(objects, start=1):135        xref_offsets.append(sum(len(p) for p in pdf_parts))136        pdf_parts.append(f"{i} 0 obj\n{obj}\nendobj\n".encode("latin-1", errors="replace"))137 138    xref_start = sum(len(p) for p in pdf_parts)139 140    # xref table141    xref = [b"xref\n"]142    xref.append(f"0 {len(objects)+1}\n".encode("latin-1"))143    xref.append(b"0000000000 65535 f \n")144    for off in xref_offsets[1:]:145        xref.append(f"{off:010d} 00000 n \n".encode("latin-1"))146 147    trailer = (148        f"trailer\n<< /Size {len(objects)+1} /Root {catalog_obj_num} 0 R >>\n"149        f"startxref\n{xref_start}\n%%EOF\n"150    ).encode("latin-1")151 152    with open(out_path, "wb") as f:153        for part in pdf_parts:154            f.write(part)155        for part in xref:156            f.write(part)157        f.write(trailer)158 159    return out_path160with gr.Blocks() as demo:161    type = gr.Radio(choices=['json', 'pdf', 'csv'], label="Data type")162    file = gr.File()163    json_text = gr.Textbox(lines=5, label="Paste JSON portfolio")164    process_btn = gr.Button(value="Process")165    analysis_text = gr.Textbox(label="LLM Analysis", lines=20, interactive=True)166    process_btn.click(167        fn=call_n8n,168        inputs=[type, file, json_text],169        outputs=analysis_text170    )171    gr.Markdown("### Download")172    pdf_btn = gr.Button(value="Download as PDF")173    pdf_file = gr.File(label="Generated PDF")174 175    pdf_btn.click(176        fn=text_to_pdf,177        inputs=analysis_text,178        outputs=pdf_file179    )180 181demo.launch()