Spaces:
Sleeping
Sleeping
File size: 6,705 Bytes
1cc9417 c4e99ed 8314931 1cc9417 0fa5e33 f01b8f5 e014ca7 f5af3c4 6ed1496 f01b8f5 6ed1496 cec9f06 e014ca7 f01b8f5 cec9f06 f01b8f5 cec9f06 6ed1496 f01b8f5 e014ca7 f5af3c4 6ed1496 8314931 07c578c 0fa5e33 f5af3c4 0fa5e33 f5af3c4 8817595 6ed1496 07c578c f5af3c4 8314931 c4e99ed 8314931 e33b0b5 8314931 cec9f06 6ed1496 f5af3c4 8314931 6ed1496 8314931 cec9f06 8314931 1d4021b 8314931 07c578c e014ca7 cec9f06 f5af3c4 07c578c 8314931 0fa5e33 1e2fa73 0fa5e33 07c578c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | 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()
|