Spaces:
Sleeping
Sleeping
| 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}<br>({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"<li><div class='node'>{employees.get(node, node)} (cycle)</div></li>" | |
| visited.add(node) | |
| label = employees.get(node, node) | |
| html = f"<li><div class='node'>{label}</div>" | |
| if node in children and children[node]: | |
| html += "<ul>" | |
| for child in children[node]: | |
| html += generate_node_html(child, employees, children, visited) | |
| html += "</ul>" | |
| html += "</li>" | |
| 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""" | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>{title}</title> | |
| <style> | |
| body {{ | |
| font-family: Arial, sans-serif; | |
| }} | |
| .org-chart {{ | |
| text-align: center; | |
| margin: 20px; | |
| }} | |
| .org-chart ul {{ | |
| padding-top: 20px; | |
| position: relative; | |
| display: inline-block; | |
| }} | |
| .org-chart li {{ | |
| list-style-type: none; | |
| position: relative; | |
| padding: 20px 5px 0 5px; | |
| text-align: center; | |
| }} | |
| .org-chart li::before, .org-chart li::after {{ | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| border-top: 2px solid #ccc; | |
| width: 50%; | |
| height: 20px; | |
| }} | |
| .org-chart li::before {{ | |
| right: 50%; | |
| border-right: 2px solid #ccc; | |
| }} | |
| .org-chart li::after {{ | |
| left: 50%; | |
| border-left: 2px solid #ccc; | |
| }} | |
| .org-chart li:only-child::after, .org-chart li:only-child::before {{ | |
| display: none; | |
| }} | |
| .org-chart li:only-child {{ | |
| padding-top: 0; | |
| }} | |
| .org-chart .node {{ | |
| display: inline-block; | |
| padding: 5px 10px; | |
| border: 1px solid #ccc; | |
| border-radius: 5px; | |
| background: #e5e5e5; | |
| white-space: nowrap; | |
| }} | |
| </style> | |
| </head> | |
| <body> | |
| <h1 style="text-align:center;">{title}</h1> | |
| <div class="org-chart"> | |
| <ul> | |
| {tree_html} | |
| </ul> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| 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() | |