arif670 commited on
Commit
0fa5e33
·
verified ·
1 Parent(s): 186f304

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -47
app.py CHANGED
@@ -4,31 +4,37 @@ import networkx as nx
4
  import matplotlib.pyplot as plt
5
  import pdfkit
6
  import base64
 
 
7
  from networkx.drawing.nx_pydot import graphviz_layout
8
 
9
- # Function to generate the organization chart and PDF
 
 
10
  def generate_chart(file, title):
11
  if file is None:
12
  return None, "Please upload a CSV file."
13
-
14
- # Read CSV file
15
- df = pd.read_csv(file)
16
-
17
- # Validate CSV columns
 
 
 
 
 
18
  expected_columns = {"Name", "Role", "Reporting To"}
19
  if not expected_columns.issubset(set(df.columns)):
20
  return None, "CSV must contain Name, Role, and Reporting To columns."
21
-
22
- # Build graph from CSV (structure based on CSV data)
23
  G = nx.DiGraph()
24
  for _, row in df.iterrows():
25
- # Create node label
26
  node_label = f"{row['Name']}\n({row['Role']})"
27
  G.add_node(node_label)
28
- # Add edge if "Reporting To" is specified
29
  if pd.notna(row['Reporting To']) and row['Reporting To'] != "":
30
  rep_name = row['Reporting To']
31
- # Try to find the reporting person's role from the CSV
32
  rep_row = df[df['Name'] == rep_name]
33
  if not rep_row.empty:
34
  rep_role = rep_row.iloc[0]['Role']
@@ -36,47 +42,58 @@ def generate_chart(file, title):
36
  else:
37
  rep_label = rep_name
38
  G.add_edge(rep_label, node_label)
39
-
40
- # Create layout using graphviz (via pydot)
41
- pos = graphviz_layout(G, prog="dot")
42
-
43
- # Create the plot
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  plt.figure(figsize=(10, 6))
45
  ax = plt.gca()
46
-
47
- # Draw nodes with a shadow for a 3D effect
48
  for node, (x, y) in pos.items():
49
- # Draw shadow (offset rectangle)
50
- shadow = plt.Rectangle((x - 40 + 5, y - 20 - 5), 80, 40,
51
  color='gray', alpha=0.3, zorder=1, ec="none")
52
  ax.add_patch(shadow)
53
- # Draw node rectangle
54
- rect = plt.Rectangle((x - 40, y - 20), 80, 40, color='lightblue',
55
  ec='black', lw=1.5, zorder=2, joinstyle="round")
56
  ax.add_patch(rect)
57
- # Draw node text
58
- plt.text(x, y, node, horizontalalignment='center', verticalalignment='center',
59
  fontsize=10, zorder=3)
60
-
61
- # Draw edges manually
62
  for (u, v) in G.edges():
63
- x1, y1 = pos[u]
64
- x2, y2 = pos[v]
65
- plt.plot([x1, x2], [y1, y2], color="gray", zorder=1)
66
-
 
67
  plt.axis('off')
68
-
69
- # Save the chart as an image in /tmp/
70
  chart_path = "/tmp/chart.png"
71
  plt.savefig(chart_path, format="png", dpi=300, bbox_inches="tight")
72
  plt.close()
73
-
74
- # Convert image to base64 for embedding in HTML
75
  with open(chart_path, "rb") as img_file:
76
  encoded_string = base64.b64encode(img_file.read()).decode()
77
  img_html = f"<img src='data:image/png;base64,{encoded_string}' style='width:100%;'/>"
78
-
79
- # Build HTML content for PDF (includes user-defined title and the chart image)
80
  html_content = f"""
81
  <html>
82
  <head>
@@ -89,14 +106,22 @@ def generate_chart(file, title):
89
  </body>
90
  </html>
91
  """
92
-
93
- # Generate PDF from HTML content and save to /tmp/
94
  pdf_path = "/tmp/chart.pdf"
95
- pdfkit.from_string(html_content, pdf_path)
96
-
 
 
 
 
 
 
 
 
 
 
97
  return chart_path, pdf_path
98
 
99
- # Function to provide CSV template
100
  def download_template():
101
  template_path = "/tmp/template.csv"
102
  template_data = "Name,Role,Reporting To\nAlice,CEO,\nBob,Manager,Alice\nCharlie,Engineer,Bob"
@@ -107,18 +132,22 @@ def download_template():
107
  # Gradio interface
108
  with gr.Blocks() as demo:
109
  gr.Markdown("## Organization Chart Generator")
110
- gr.Markdown("Upload a CSV file (with columns: **Name, Role, Reporting To**) to generate an interactive Organization Chart. Download the template to see the required format.")
 
 
 
111
 
112
  with gr.Row():
113
  template_button = gr.Button("Download CSV Template")
114
  file_input = gr.File(label="Upload CSV File")
115
  title_input = gr.Textbox(label="Enter PDF Title", placeholder="Company Org Chart")
116
  submit_button = gr.Button("Generate Chart")
117
-
118
  image_output = gr.Image(label="Generated Chart")
119
- download_pdf = gr.File(label="Download PDF")
120
-
121
- template_button.click(download_template, outputs=download_pdf)
122
- submit_button.click(generate_chart, inputs=[file_input, title_input], outputs=[image_output, download_pdf])
 
123
 
124
  demo.launch()
 
4
  import matplotlib.pyplot as plt
5
  import pdfkit
6
  import base64
7
+ import shutil
8
+ import logging
9
  from networkx.drawing.nx_pydot import graphviz_layout
10
 
11
+ # Set up logging to see which layout is used
12
+ logging.basicConfig(level=logging.INFO)
13
+
14
  def generate_chart(file, title):
15
  if file is None:
16
  return None, "Please upload a CSV file."
17
+
18
+ # Handle the uploaded file: either a dict (Gradio v3.x) or a file path
19
+ file_path = file["name"] if isinstance(file, dict) and "name" in file else file
20
+
21
+ try:
22
+ df = pd.read_csv(file_path)
23
+ except Exception as e:
24
+ return None, f"Error reading CSV: {e}"
25
+
26
+ # Validate required columns
27
  expected_columns = {"Name", "Role", "Reporting To"}
28
  if not expected_columns.issubset(set(df.columns)):
29
  return None, "CSV must contain Name, Role, and Reporting To columns."
30
+
31
+ # Build a directed graph from CSV data
32
  G = nx.DiGraph()
33
  for _, row in df.iterrows():
 
34
  node_label = f"{row['Name']}\n({row['Role']})"
35
  G.add_node(node_label)
 
36
  if pd.notna(row['Reporting To']) and row['Reporting To'] != "":
37
  rep_name = row['Reporting To']
 
38
  rep_row = df[df['Name'] == rep_name]
39
  if not rep_row.empty:
40
  rep_role = rep_row.iloc[0]['Role']
 
42
  else:
43
  rep_label = rep_name
44
  G.add_edge(rep_label, node_label)
45
+
46
+ if len(G.nodes) == 0:
47
+ return None, "No nodes to display in the chart."
48
+
49
+ # Determine layout method: use 'dot' if available, else fall back to spring_layout
50
+ if shutil.which("dot") is not None:
51
+ try:
52
+ logging.info("Using dot for layout.")
53
+ pos = graphviz_layout(G, prog="dot")
54
+ except Exception as e:
55
+ logging.error(f"Error using dot for layout: {e}")
56
+ logging.info("Falling back to spring_layout.")
57
+ pos = nx.spring_layout(G)
58
+ else:
59
+ logging.info("dot not found. Using spring_layout as fallback.")
60
+ pos = nx.spring_layout(G)
61
+
62
+ # Create the plot with a shadow effect for a 3D look
63
  plt.figure(figsize=(10, 6))
64
  ax = plt.gca()
 
 
65
  for node, (x, y) in pos.items():
66
+ # Draw shadow rectangle for a 3D effect
67
+ shadow = plt.Rectangle((x - 40 + 5, y - 20 - 5), 80, 40,
68
  color='gray', alpha=0.3, zorder=1, ec="none")
69
  ax.add_patch(shadow)
70
+ # Draw node rectangle with rounded edges
71
+ rect = plt.Rectangle((x - 40, y - 20), 80, 40, color='lightblue',
72
  ec='black', lw=1.5, zorder=2, joinstyle="round")
73
  ax.add_patch(rect)
74
+ plt.text(x, y, node, horizontalalignment='center', verticalalignment='center',
 
75
  fontsize=10, zorder=3)
76
+
77
+ # Draw edges between nodes
78
  for (u, v) in G.edges():
79
+ if u in pos and v in pos:
80
+ x1, y1 = pos[u]
81
+ x2, y2 = pos[v]
82
+ plt.plot([x1, x2], [y1, y2], color="gray", zorder=1)
83
+
84
  plt.axis('off')
85
+
86
+ # Save the chart image
87
  chart_path = "/tmp/chart.png"
88
  plt.savefig(chart_path, format="png", dpi=300, bbox_inches="tight")
89
  plt.close()
90
+
91
+ # Convert the image to base64 for embedding in the PDF
92
  with open(chart_path, "rb") as img_file:
93
  encoded_string = base64.b64encode(img_file.read()).decode()
94
  img_html = f"<img src='data:image/png;base64,{encoded_string}' style='width:100%;'/>"
95
+
96
+ # Build HTML for PDF generation including the user-defined title and the chart image
97
  html_content = f"""
98
  <html>
99
  <head>
 
106
  </body>
107
  </html>
108
  """
109
+
 
110
  pdf_path = "/tmp/chart.pdf"
111
+
112
+ # Locate wkhtmltopdf and configure pdfkit
113
+ wkhtmltopdf_path = shutil.which("wkhtmltopdf")
114
+ if wkhtmltopdf_path is None:
115
+ return chart_path, "Error generating PDF: wkhtmltopdf executable not found. Ensure it is installed."
116
+ config = pdfkit.configuration(wkhtmltopdf=wkhtmltopdf_path)
117
+
118
+ try:
119
+ pdfkit.from_string(html_content, pdf_path, configuration=config)
120
+ except Exception as e:
121
+ return chart_path, f"Error generating PDF: {e}"
122
+
123
  return chart_path, pdf_path
124
 
 
125
  def download_template():
126
  template_path = "/tmp/template.csv"
127
  template_data = "Name,Role,Reporting To\nAlice,CEO,\nBob,Manager,Alice\nCharlie,Engineer,Bob"
 
132
  # Gradio interface
133
  with gr.Blocks() as demo:
134
  gr.Markdown("## Organization Chart Generator")
135
+ gr.Markdown(
136
+ "Upload a CSV file (with columns: **Name, Role, Reporting To**) to generate an interactive Organization Chart. "
137
+ "Use the button below to download a CSV template."
138
+ )
139
 
140
  with gr.Row():
141
  template_button = gr.Button("Download CSV Template")
142
  file_input = gr.File(label="Upload CSV File")
143
  title_input = gr.Textbox(label="Enter PDF Title", placeholder="Company Org Chart")
144
  submit_button = gr.Button("Generate Chart")
145
+
146
  image_output = gr.Image(label="Generated Chart")
147
+ pdf_output = gr.File(label="Download PDF")
148
+ template_output = gr.File(label="CSV Template")
149
+
150
+ template_button.click(download_template, outputs=template_output)
151
+ submit_button.click(generate_chart, inputs=[file_input, title_input], outputs=[image_output, pdf_output])
152
 
153
  demo.launch()