arif670 commited on
Commit
e1fe5b3
·
verified ·
1 Parent(s): e43b094

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +24 -26
app.py CHANGED
@@ -13,11 +13,9 @@ from networkx.drawing.nx_pydot import graphviz_layout
13
  logging.basicConfig(level=logging.INFO)
14
 
15
  def get_wkhtmltopdf_path():
16
- # Try using shutil.which first
17
  path = shutil.which("wkhtmltopdf")
18
  if path:
19
  return path
20
- # Then check common locations
21
  for candidate in ["/usr/local/bin/wkhtmltopdf", "/usr/bin/wkhtmltopdf"]:
22
  if os.path.exists(candidate):
23
  return candidate
@@ -26,17 +24,15 @@ def get_wkhtmltopdf_path():
26
  def process_uploaded_file(file):
27
  """
28
  Process the file input from Gradio.
29
- If file is a dict:
30
- - If "data" exists and is non-empty, write it to a temporary file.
31
- - Otherwise, if "name" exists and points to a file, use it.
32
- If file is a string, check that it's not a directory.
33
  """
 
34
  if isinstance(file, dict):
35
- # Prefer file content from "data" if available
36
  if "data" in file and file["data"]:
37
  temp_path = "/tmp/uploaded.csv"
38
  try:
39
- # Write as binary if data is bytes; otherwise, write as text.
40
  mode = "wb" if isinstance(file["data"], bytes) else "w"
41
  with open(temp_path, mode) as f:
42
  f.write(file["data"])
@@ -45,11 +41,14 @@ def process_uploaded_file(file):
45
  except Exception as e:
46
  logging.error(f"Error writing uploaded file data: {e}")
47
  return None
48
- # Otherwise, try using the "name" if it points to a real file.
49
- elif "name" in file and file["name"] and os.path.isfile(file["name"]):
50
- return file["name"]
 
 
 
51
  else:
52
- logging.error("File dictionary does not contain valid 'data' or file path.")
53
  return None
54
  else:
55
  # If file is a string, ensure it is not a directory.
@@ -59,16 +58,23 @@ def process_uploaded_file(file):
59
  return file
60
 
61
  def generate_chart(file, title):
 
62
  if file is None:
63
  return None, "Please upload a CSV file."
64
 
65
  file_path = process_uploaded_file(file)
66
  if file_path is None:
67
- return None, "Error processing uploaded file. Please try again."
 
 
 
 
 
68
 
69
  try:
70
  df = pd.read_csv(file_path)
71
  except Exception as e:
 
72
  return None, f"Error reading CSV: {e}"
73
 
74
  # Validate required columns
@@ -76,7 +82,7 @@ def generate_chart(file, title):
76
  if not expected_columns.issubset(set(df.columns)):
77
  return None, "CSV must contain Name, Role, and Reporting To columns."
78
 
79
- # Build a directed graph from CSV data
80
  G = nx.DiGraph()
81
  for _, row in df.iterrows():
82
  node_label = f"{row['Name']}\n({row['Role']})"
@@ -94,34 +100,30 @@ def generate_chart(file, title):
94
  if len(G.nodes) == 0:
95
  return None, "No nodes to display in the chart."
96
 
97
- # Determine layout: use Graphviz "dot" if available; otherwise, use spring_layout.
98
  if shutil.which("dot") is not None:
99
  try:
100
  logging.info("Using dot for layout.")
101
  pos = graphviz_layout(G, prog="dot")
102
  except Exception as e:
103
  logging.error(f"Error using dot for layout: {e}")
104
- logging.info("Falling back to spring_layout.")
105
  pos = nx.spring_layout(G)
106
  else:
107
- logging.info("dot not found. Using spring_layout as fallback.")
108
  pos = nx.spring_layout(G)
109
 
110
- # Create the plot with a 3D shadow effect
111
  plt.figure(figsize=(10, 6))
112
  ax = plt.gca()
113
  for node, (x, y) in pos.items():
114
- # Draw shadow rectangle for a 3D effect
115
  shadow = plt.Rectangle((x - 40 + 5, y - 20 - 5), 80, 40,
116
  color='gray', alpha=0.3, zorder=1, ec="none")
117
  ax.add_patch(shadow)
118
- # Draw node rectangle with rounded edges
119
  rect = plt.Rectangle((x - 40, y - 20), 80, 40, color='lightblue',
120
  ec='black', lw=1.5, zorder=2, joinstyle="round")
121
  ax.add_patch(rect)
122
  plt.text(x, y, node, horizontalalignment='center', verticalalignment='center',
123
  fontsize=10, zorder=3)
124
- # Draw edges between nodes
125
  for (u, v) in G.edges():
126
  if u in pos and v in pos:
127
  x1, y1 = pos[u]
@@ -129,18 +131,14 @@ def generate_chart(file, title):
129
  plt.plot([x1, x2], [y1, y2], color="gray", zorder=1)
130
 
131
  plt.axis('off')
132
-
133
- # Save the chart as an image
134
  chart_path = "/tmp/chart.png"
135
  plt.savefig(chart_path, format="png", dpi=300, bbox_inches="tight")
136
  plt.close()
137
 
138
- # Convert the image to base64 for embedding in HTML (for PDF conversion)
139
  with open(chart_path, "rb") as img_file:
140
  encoded_string = base64.b64encode(img_file.read()).decode()
141
  img_html = f"<img src='data:image/png;base64,{encoded_string}' style='width:100%;'/>"
142
 
143
- # Build HTML content for PDF generation
144
  html_content = f"""
145
  <html>
146
  <head>
@@ -158,7 +156,7 @@ def generate_chart(file, title):
158
  wkhtmltopdf_path = get_wkhtmltopdf_path()
159
  if wkhtmltopdf_path is None:
160
  logging.error("wkhtmltopdf not found, skipping PDF generation.")
161
- pdf_path = "" # Return empty PDF output if wkhtmltopdf is not available.
162
  else:
163
  logging.info(f"wkhtmltopdf found at: {wkhtmltopdf_path}")
164
  config = pdfkit.configuration(wkhtmltopdf=wkhtmltopdf_path)
 
13
  logging.basicConfig(level=logging.INFO)
14
 
15
  def get_wkhtmltopdf_path():
 
16
  path = shutil.which("wkhtmltopdf")
17
  if path:
18
  return path
 
19
  for candidate in ["/usr/local/bin/wkhtmltopdf", "/usr/bin/wkhtmltopdf"]:
20
  if os.path.exists(candidate):
21
  return candidate
 
24
  def process_uploaded_file(file):
25
  """
26
  Process the file input from Gradio.
27
+ If 'file' is a dict, check if it contains valid file data.
28
+ Otherwise, if it's a string, ensure it's a file (not a directory).
 
 
29
  """
30
+ logging.info(f"Processing uploaded file: {file}")
31
  if isinstance(file, dict):
32
+ # If "data" exists and is non-empty, write it to a temporary file.
33
  if "data" in file and file["data"]:
34
  temp_path = "/tmp/uploaded.csv"
35
  try:
 
36
  mode = "wb" if isinstance(file["data"], bytes) else "w"
37
  with open(temp_path, mode) as f:
38
  f.write(file["data"])
 
41
  except Exception as e:
42
  logging.error(f"Error writing uploaded file data: {e}")
43
  return None
44
+ elif "name" in file and file["name"]:
45
+ if os.path.isfile(file["name"]):
46
+ return file["name"]
47
+ else:
48
+ logging.error("Uploaded file 'name' is not a file.")
49
+ return None
50
  else:
51
+ logging.error("Uploaded file dict does not contain valid keys.")
52
  return None
53
  else:
54
  # If file is a string, ensure it is not a directory.
 
58
  return file
59
 
60
  def generate_chart(file, title):
61
+ logging.info(f"generate_chart called with file: {file} and title: {title}")
62
  if file is None:
63
  return None, "Please upload a CSV file."
64
 
65
  file_path = process_uploaded_file(file)
66
  if file_path is None:
67
+ return None, "Error processing uploaded file. Please ensure you upload a valid CSV file."
68
+
69
+ # Check if the resulting file path is a directory (should never happen)
70
+ if os.path.isdir(file_path):
71
+ logging.error(f"Processed file path '{file_path}' is a directory.")
72
+ return None, "Uploaded file is a directory. Please upload a valid CSV file."
73
 
74
  try:
75
  df = pd.read_csv(file_path)
76
  except Exception as e:
77
+ logging.error(f"Error reading CSV: {e}")
78
  return None, f"Error reading CSV: {e}"
79
 
80
  # Validate required columns
 
82
  if not expected_columns.issubset(set(df.columns)):
83
  return None, "CSV must contain Name, Role, and Reporting To columns."
84
 
85
+ # Build the directed graph
86
  G = nx.DiGraph()
87
  for _, row in df.iterrows():
88
  node_label = f"{row['Name']}\n({row['Role']})"
 
100
  if len(G.nodes) == 0:
101
  return None, "No nodes to display in the chart."
102
 
103
+ # Use Graphviz 'dot' layout if available; otherwise, fall back to spring_layout.
104
  if shutil.which("dot") is not None:
105
  try:
106
  logging.info("Using dot for layout.")
107
  pos = graphviz_layout(G, prog="dot")
108
  except Exception as e:
109
  logging.error(f"Error using dot for layout: {e}")
 
110
  pos = nx.spring_layout(G)
111
  else:
112
+ logging.info("dot not found; using spring_layout.")
113
  pos = nx.spring_layout(G)
114
 
115
+ # Create the chart plot
116
  plt.figure(figsize=(10, 6))
117
  ax = plt.gca()
118
  for node, (x, y) in pos.items():
 
119
  shadow = plt.Rectangle((x - 40 + 5, y - 20 - 5), 80, 40,
120
  color='gray', alpha=0.3, zorder=1, ec="none")
121
  ax.add_patch(shadow)
 
122
  rect = plt.Rectangle((x - 40, y - 20), 80, 40, color='lightblue',
123
  ec='black', lw=1.5, zorder=2, joinstyle="round")
124
  ax.add_patch(rect)
125
  plt.text(x, y, node, horizontalalignment='center', verticalalignment='center',
126
  fontsize=10, zorder=3)
 
127
  for (u, v) in G.edges():
128
  if u in pos and v in pos:
129
  x1, y1 = pos[u]
 
131
  plt.plot([x1, x2], [y1, y2], color="gray", zorder=1)
132
 
133
  plt.axis('off')
 
 
134
  chart_path = "/tmp/chart.png"
135
  plt.savefig(chart_path, format="png", dpi=300, bbox_inches="tight")
136
  plt.close()
137
 
 
138
  with open(chart_path, "rb") as img_file:
139
  encoded_string = base64.b64encode(img_file.read()).decode()
140
  img_html = f"<img src='data:image/png;base64,{encoded_string}' style='width:100%;'/>"
141
 
 
142
  html_content = f"""
143
  <html>
144
  <head>
 
156
  wkhtmltopdf_path = get_wkhtmltopdf_path()
157
  if wkhtmltopdf_path is None:
158
  logging.error("wkhtmltopdf not found, skipping PDF generation.")
159
+ pdf_path = ""
160
  else:
161
  logging.info(f"wkhtmltopdf found at: {wkhtmltopdf_path}")
162
  config = pdfkit.configuration(wkhtmltopdf=wkhtmltopdf_path)