OCG / app.py
arif670's picture
Update app.py
f01b8f5 verified
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()