Spaces:
Sleeping
Sleeping
Commit
·
815a086
1
Parent(s):
702e044
Enhancement in the UI
Browse files- app.py +80 -59
- templates/index.html +9 -15
- templates/plot.html +56 -0
app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from flask import Flask, render_template, request, send_file
|
| 2 |
import pandas as pd
|
| 3 |
import matplotlib.pyplot as plt
|
| 4 |
import numpy as np
|
|
@@ -6,9 +6,71 @@ import io
|
|
| 6 |
import os
|
| 7 |
|
| 8 |
app = Flask(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
@app.route("/", methods=["GET", "POST"])
|
| 11 |
def index():
|
|
|
|
|
|
|
| 12 |
if request.method == "POST":
|
| 13 |
golden_file = request.files.get("golden_file")
|
| 14 |
test_file = request.files.get("test_file")
|
|
@@ -17,69 +79,28 @@ def index():
|
|
| 17 |
return render_template("index.html", error="Please upload both files.")
|
| 18 |
|
| 19 |
try:
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
# --- Build Limits ---
|
| 31 |
-
limits_df1 = limits_df1.drop([0])
|
| 32 |
-
ignore_cols = ["SITE_NUM", "PART_ID", "PASSFG", "SOFT_BIN", "T_TIME", "TEST_NUM"]
|
| 33 |
-
cols_to_plot = [col for col in limits_df1.columns if "_" in col and col not in ignore_cols]
|
| 34 |
-
limits_df1 = limits_df1.drop(columns=ignore_cols)
|
| 35 |
-
|
| 36 |
-
limits = {
|
| 37 |
-
col: {"LL": limits_df1.iloc[0][col], "UL": limits_df1.iloc[1][col]}
|
| 38 |
-
for col in limits_df1.columns
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
# --- Plot ---
|
| 42 |
-
n_cols = 3
|
| 43 |
-
n_rows = (len(cols_to_plot) + n_cols - 1) // n_cols
|
| 44 |
-
|
| 45 |
-
plt.figure(figsize=(n_cols * 5, n_rows * 3.5))
|
| 46 |
-
for i, col in enumerate(cols_to_plot, 1):
|
| 47 |
-
plt.subplot(n_rows, n_cols, i)
|
| 48 |
-
x1 = np.arange(1, len(df1[col]) + 1)
|
| 49 |
-
y1 = pd.to_numeric(df1[col], errors="coerce").values
|
| 50 |
-
plt.plot(x1, y1, marker="o", linestyle="-", color="blue", label="Golden")
|
| 51 |
-
|
| 52 |
-
if col in df2.columns:
|
| 53 |
-
x2 = np.arange(1, len(df2[col]) + 1)
|
| 54 |
-
y2 = pd.to_numeric(df2[col], errors="coerce").values
|
| 55 |
-
plt.plot(x2, y2, marker="s", linestyle="--", color="red", label="Test")
|
| 56 |
-
|
| 57 |
-
if col in limits:
|
| 58 |
-
ll, ul = limits[col]["LL"], limits[col]["UL"]
|
| 59 |
-
plt.axhline(ll, color="green", linestyle="--", linewidth=2, label="LL")
|
| 60 |
-
plt.axhline(ul, color="orange", linestyle="--", linewidth=2, label="UL")
|
| 61 |
-
|
| 62 |
-
plt.title(f"{col}")
|
| 63 |
-
plt.xlabel("Part # (sequence)")
|
| 64 |
-
plt.ylabel("Value")
|
| 65 |
-
plt.grid(True, linestyle="--", alpha=0.7)
|
| 66 |
-
plt.legend(fontsize="small")
|
| 67 |
-
|
| 68 |
-
plt.tight_layout()
|
| 69 |
-
|
| 70 |
-
# Save plot to in-memory buffer
|
| 71 |
-
buf = io.BytesIO()
|
| 72 |
-
plt.savefig(buf, format="png", bbox_inches="tight")
|
| 73 |
-
buf.seek(0)
|
| 74 |
-
plt.close()
|
| 75 |
-
|
| 76 |
-
return send_file(buf, mimetype="image/png")
|
| 77 |
|
| 78 |
except Exception as e:
|
| 79 |
return render_template("index.html", error=f"Error: {str(e)}")
|
| 80 |
|
| 81 |
return render_template("index.html")
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
|
| 84 |
if __name__ == "__main__":
|
| 85 |
app.run(host="0.0.0.0", port=7860, debug=True)
|
|
|
|
| 1 |
+
from flask import Flask, render_template, request, redirect, url_for, send_file
|
| 2 |
import pandas as pd
|
| 3 |
import matplotlib.pyplot as plt
|
| 4 |
import numpy as np
|
|
|
|
| 6 |
import os
|
| 7 |
|
| 8 |
app = Flask(__name__)
|
| 9 |
+
app.config["UPLOAD_FOLDER"] = "uploads"
|
| 10 |
+
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
|
| 11 |
+
|
| 12 |
+
# Global variables to hold data between requests
|
| 13 |
+
df1, df2, limits, cols_to_plot = None, None, {}, []
|
| 14 |
+
|
| 15 |
+
def process_files(golden_file, test_file):
|
| 16 |
+
global df1, df2, limits, cols_to_plot
|
| 17 |
+
limits_df1 = pd.read_excel(golden_file, nrows=4)
|
| 18 |
+
df1 = pd.read_excel(golden_file)
|
| 19 |
+
df1 = df1.drop([0, 1, 2, 3])
|
| 20 |
+
df2 = pd.read_excel(test_file)
|
| 21 |
+
df2 = df2.drop([0, 1, 2, 3])
|
| 22 |
+
|
| 23 |
+
df1 = df1.apply(pd.to_numeric, errors="coerce")
|
| 24 |
+
df2 = df2.apply(pd.to_numeric, errors="coerce")
|
| 25 |
+
|
| 26 |
+
limits_df1 = limits_df1.drop([0])
|
| 27 |
+
ignore_cols = ["SITE_NUM", "PART_ID", "PASSFG", "SOFT_BIN", "T_TIME", "TEST_NUM"]
|
| 28 |
+
cols_to_plot = [col for col in limits_df1.columns if "_" in col and col not in ignore_cols]
|
| 29 |
+
limits_df1 = limits_df1.drop(columns=ignore_cols)
|
| 30 |
+
|
| 31 |
+
limits = {
|
| 32 |
+
col: {"LL": limits_df1.iloc[0][col], "UL": limits_df1.iloc[1][col]}
|
| 33 |
+
for col in limits_df1.columns
|
| 34 |
+
}
|
| 35 |
+
return df1, df2, limits, cols_to_plot
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def generate_plot(col):
|
| 39 |
+
"""Generate a matplotlib plot for a single column."""
|
| 40 |
+
plt.figure(figsize=(6, 4))
|
| 41 |
+
|
| 42 |
+
x1 = np.arange(1, len(df1[col]) + 1)
|
| 43 |
+
y1 = pd.to_numeric(df1[col], errors="coerce").values
|
| 44 |
+
plt.plot(x1, y1, marker="o", linestyle="-", color="blue", label="Golden")
|
| 45 |
+
|
| 46 |
+
if col in df2.columns:
|
| 47 |
+
x2 = np.arange(1, len(df2[col]) + 1)
|
| 48 |
+
y2 = pd.to_numeric(df2[col], errors="coerce").values
|
| 49 |
+
plt.plot(x2, y2, marker="s", linestyle="--", color="red", label="Test")
|
| 50 |
+
|
| 51 |
+
if col in limits:
|
| 52 |
+
ll, ul = limits[col]["LL"], limits[col]["UL"]
|
| 53 |
+
plt.axhline(ll, color="green", linestyle="--", linewidth=2, label="LL")
|
| 54 |
+
plt.axhline(ul, color="orange", linestyle="--", linewidth=2, label="UL")
|
| 55 |
+
|
| 56 |
+
plt.title(f"{col}")
|
| 57 |
+
plt.xlabel("Part # (sequence)")
|
| 58 |
+
plt.ylabel("Value")
|
| 59 |
+
plt.grid(True, linestyle="--", alpha=0.7)
|
| 60 |
+
plt.legend(fontsize="small")
|
| 61 |
+
plt.tight_layout()
|
| 62 |
+
|
| 63 |
+
buf = io.BytesIO()
|
| 64 |
+
plt.savefig(buf, format="png", bbox_inches="tight")
|
| 65 |
+
buf.seek(0)
|
| 66 |
+
plt.close()
|
| 67 |
+
return buf
|
| 68 |
+
|
| 69 |
|
| 70 |
@app.route("/", methods=["GET", "POST"])
|
| 71 |
def index():
|
| 72 |
+
global df1, df2, limits, cols_to_plot
|
| 73 |
+
|
| 74 |
if request.method == "POST":
|
| 75 |
golden_file = request.files.get("golden_file")
|
| 76 |
test_file = request.files.get("test_file")
|
|
|
|
| 79 |
return render_template("index.html", error="Please upload both files.")
|
| 80 |
|
| 81 |
try:
|
| 82 |
+
df1, df2, limits, cols_to_plot = process_files(golden_file, test_file)
|
| 83 |
+
|
| 84 |
+
# Show the first 3 plots initially
|
| 85 |
+
preview_cols = cols_to_plot[:3]
|
| 86 |
+
return render_template(
|
| 87 |
+
"plot.html",
|
| 88 |
+
cols=cols_to_plot,
|
| 89 |
+
preview_cols=preview_cols,
|
| 90 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
|
| 92 |
except Exception as e:
|
| 93 |
return render_template("index.html", error=f"Error: {str(e)}")
|
| 94 |
|
| 95 |
return render_template("index.html")
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
@app.route("/plot_image/<col>")
|
| 99 |
+
def plot_image(col):
|
| 100 |
+
"""Return plot as PNG stream for the selected column."""
|
| 101 |
+
buf = generate_plot(col)
|
| 102 |
+
return send_file(buf, mimetype="image/png")
|
| 103 |
+
|
| 104 |
|
| 105 |
if __name__ == "__main__":
|
| 106 |
app.run(host="0.0.0.0", port=7860, debug=True)
|
templates/index.html
CHANGED
|
@@ -1,42 +1,36 @@
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html>
|
| 3 |
<head>
|
| 4 |
-
<title>IPM
|
| 5 |
<style>
|
| 6 |
body {
|
| 7 |
font-family: Arial, sans-serif;
|
| 8 |
-
background: #f2f2f2;
|
| 9 |
text-align: center;
|
|
|
|
| 10 |
padding: 40px;
|
| 11 |
}
|
| 12 |
form {
|
| 13 |
background: white;
|
| 14 |
-
padding:
|
| 15 |
-
border-radius:
|
| 16 |
display: inline-block;
|
| 17 |
}
|
| 18 |
-
input[type=file] {
|
| 19 |
margin: 10px;
|
| 20 |
}
|
| 21 |
button {
|
| 22 |
background-color: #0078d7;
|
| 23 |
color: white;
|
| 24 |
-
border: none;
|
| 25 |
padding: 10px 20px;
|
|
|
|
| 26 |
border-radius: 5px;
|
| 27 |
-
cursor: pointer;
|
| 28 |
-
}
|
| 29 |
-
button:hover {
|
| 30 |
-
background-color: #005fa3;
|
| 31 |
-
}
|
| 32 |
-
.error {
|
| 33 |
-
color: red;
|
| 34 |
}
|
|
|
|
| 35 |
</style>
|
| 36 |
</head>
|
| 37 |
<body>
|
| 38 |
<h2>📊 IPM Golden vs Test Data Visualizer</h2>
|
| 39 |
-
<p>Upload
|
| 40 |
|
| 41 |
{% if error %}
|
| 42 |
<p class="error">{{ error }}</p>
|
|
@@ -49,7 +43,7 @@
|
|
| 49 |
<label>Test Data (.xlsx):</label><br>
|
| 50 |
<input type="file" name="test_file" accept=".xlsx" required><br><br>
|
| 51 |
|
| 52 |
-
<button type="submit">Generate
|
| 53 |
</form>
|
| 54 |
</body>
|
| 55 |
</html>
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html>
|
| 3 |
<head>
|
| 4 |
+
<title>IPM Data Visualizer</title>
|
| 5 |
<style>
|
| 6 |
body {
|
| 7 |
font-family: Arial, sans-serif;
|
|
|
|
| 8 |
text-align: center;
|
| 9 |
+
background: #f8f9fa;
|
| 10 |
padding: 40px;
|
| 11 |
}
|
| 12 |
form {
|
| 13 |
background: white;
|
| 14 |
+
padding: 30px;
|
| 15 |
+
border-radius: 8px;
|
| 16 |
display: inline-block;
|
| 17 |
}
|
| 18 |
+
input[type=file], button {
|
| 19 |
margin: 10px;
|
| 20 |
}
|
| 21 |
button {
|
| 22 |
background-color: #0078d7;
|
| 23 |
color: white;
|
|
|
|
| 24 |
padding: 10px 20px;
|
| 25 |
+
border: none;
|
| 26 |
border-radius: 5px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
}
|
| 28 |
+
.error { color: red; }
|
| 29 |
</style>
|
| 30 |
</head>
|
| 31 |
<body>
|
| 32 |
<h2>📊 IPM Golden vs Test Data Visualizer</h2>
|
| 33 |
+
<p>Upload both Golden and Test Excel files to generate visual comparison.</p>
|
| 34 |
|
| 35 |
{% if error %}
|
| 36 |
<p class="error">{{ error }}</p>
|
|
|
|
| 43 |
<label>Test Data (.xlsx):</label><br>
|
| 44 |
<input type="file" name="test_file" accept=".xlsx" required><br><br>
|
| 45 |
|
| 46 |
+
<button type="submit">Generate Plots</button>
|
| 47 |
</form>
|
| 48 |
</body>
|
| 49 |
</html>
|
templates/plot.html
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<title>IPM Data Plots</title>
|
| 5 |
+
<style>
|
| 6 |
+
body {
|
| 7 |
+
font-family: Arial, sans-serif;
|
| 8 |
+
text-align: center;
|
| 9 |
+
background: #f4f4f4;
|
| 10 |
+
padding: 20px;
|
| 11 |
+
}
|
| 12 |
+
img {
|
| 13 |
+
max-width: 90%;
|
| 14 |
+
border: 1px solid #ccc;
|
| 15 |
+
border-radius: 8px;
|
| 16 |
+
margin: 15px;
|
| 17 |
+
}
|
| 18 |
+
select {
|
| 19 |
+
padding: 10px;
|
| 20 |
+
margin: 20px;
|
| 21 |
+
font-size: 16px;
|
| 22 |
+
}
|
| 23 |
+
.preview-title {
|
| 24 |
+
font-weight: bold;
|
| 25 |
+
margin-top: 20px;
|
| 26 |
+
}
|
| 27 |
+
</style>
|
| 28 |
+
<script>
|
| 29 |
+
function showPlot() {
|
| 30 |
+
const col = document.getElementById("col-select").value;
|
| 31 |
+
document.getElementById("dynamic-plot").src = "/plot_image/" + encodeURIComponent(col);
|
| 32 |
+
}
|
| 33 |
+
</script>
|
| 34 |
+
</head>
|
| 35 |
+
<body>
|
| 36 |
+
<h2>📈 IPM Data Comparison</h2>
|
| 37 |
+
|
| 38 |
+
<h3>Preview (First few plots):</h3>
|
| 39 |
+
{% for c in preview_cols %}
|
| 40 |
+
<div class="preview-title">{{ c }}</div>
|
| 41 |
+
<img src="/plot_image/{{ c }}" alt="{{ c }}">
|
| 42 |
+
{% endfor %}
|
| 43 |
+
|
| 44 |
+
<hr>
|
| 45 |
+
<h3>View Other Columns</h3>
|
| 46 |
+
<select id="col-select" onchange="showPlot()">
|
| 47 |
+
<option disabled selected>Select a column</option>
|
| 48 |
+
{% for c in cols %}
|
| 49 |
+
<option value="{{ c }}">{{ c }}</option>
|
| 50 |
+
{% endfor %}
|
| 51 |
+
</select>
|
| 52 |
+
|
| 53 |
+
<br>
|
| 54 |
+
<img id="dynamic-plot" style="display:block; margin:auto; max-width:90%;">
|
| 55 |
+
</body>
|
| 56 |
+
</html>
|