import gradio as gr import pandas as pd import os import logging from weasyprint import HTML from pdf2image import convert_from_path logging.basicConfig(level=logging.INFO) # Name of the default CSV file. Ensure this file exists in your repository. DEFAULT_CSV = "default.csv" def load_csv(file): """ Load a CSV file from the given file path. If the file is missing, empty, or is a directory, load the default CSV. """ cwd = os.getcwd() logging.info(f"load_csv received: {file}") # If file is empty, None, or a directory, use the default CSV. if not file or file.strip() == "" or os.path.isdir(file) or os.path.abspath(file) == cwd: logging.info("No valid file provided; using default CSV.") if os.path.isfile(DEFAULT_CSV): return pd.read_csv(DEFAULT_CSV) else: raise FileNotFoundError("Default CSV not found.") # If the file exists and is not a directory, load it. if os.path.isfile(file): logging.info(f"Loading uploaded CSV: {file}") return pd.read_csv(file) logging.warning(f"Provided file path '{file}' is not valid. Using default CSV.") return pd.read_csv(DEFAULT_CSV) def build_tree(df): employees = {} children = {} all_emps = set() all_managers = set() for _, row in df.iterrows(): name = row["Name"].strip() role = row["Role"].strip() label = f"{name}
({role})" employees[name] = label all_emps.add(name) children.setdefault(name, []) for _, row in df.iterrows(): subordinate = row["Name"].strip() manager = str(row["Reporting To"]).strip() if manager and manager.lower() != "nan": children.setdefault(manager, []).append(subordinate) all_managers.add(manager) roots = [emp for emp in all_emps if emp not in all_managers] if not roots: roots = [df.iloc[0]["Name"].strip()] return employees, children, roots def generate_node_html(node, employees, children, visited=None): if visited is None: visited = set() if node in visited: return f"
  • {employees.get(node, node)} (cycle)
  • " visited.add(node) label = employees.get(node, node) html = f"
  • {label}
    " if node in children and children[node]: html += "" html += "
  • " visited.remove(node) return html def generate_org_chart_html(df, title): employees, children, roots = build_tree(df) tree_html = "" for root in roots: tree_html += generate_node_html(root, employees, children) html_content = f""" {title}

    {title}

    """ return html_content def generate_chart(file, title): try: df = load_csv(file) except Exception as e: logging.error(f"Error loading CSV: {e}") return None, f"Error loading CSV: {e}" # Clean header names. df.columns = df.columns.str.strip() logging.info("CSV columns: " + ", ".join(df.columns)) logging.info(f"CSV read successfully with {df.shape[0]} rows.") expected_columns = {"Name", "Role", "Reporting To"} if not expected_columns.issubset(set(df.columns)): return None, "CSV must contain Name, Role, and Reporting To columns." html_content = generate_org_chart_html(df, title) pdf_path = "/tmp/chart.pdf" try: HTML(string=html_content).write_pdf(pdf_path) logging.info("PDF generated successfully.") except Exception as e: logging.error(f"Error generating PDF: {e}") return None, f"Error generating PDF: {e}" try: images = convert_from_path(pdf_path, dpi=150) if images: image_path = "/tmp/chart.png" images[0].save(image_path, 'PNG') else: image_path = "" except Exception as e: logging.error(f"Error converting PDF to image: {e}") image_path = "" return image_path, pdf_path with gr.Blocks() as demo: gr.Markdown("## Organization Chart Generator") gr.Markdown("Upload a CSV file (optional). If no file is uploaded or an invalid file is provided, the default CSV (default.csv) will be used.") file_input = gr.File(label="Upload CSV File (optional)", type="filepath") title_input = gr.Textbox(label="Enter PDF Title", placeholder="Company Org Chart") submit_button = gr.Button("Generate Chart") image_output = gr.Image(label="Generated Chart (PNG)") pdf_output = gr.File(label="Download PDF") submit_button.click(generate_chart, inputs=[file_input, title_input], outputs=[image_output, pdf_output]) demo.launch()