File size: 9,423 Bytes
2546160
3c118cd
 
 
 
9b40395
702e044
 
815a086
6930d8f
2546160
6930d8f
 
 
2546160
 
9b40395
6930d8f
 
9b40395
2546160
6930d8f
58bde6b
815a086
2546160
9b40395
6930d8f
 
 
 
 
815a086
 
 
 
 
6930d8f
 
 
 
 
815a086
 
6930d8f
 
 
815a086
58bde6b
9b40395
 
 
 
 
 
6930d8f
815a086
 
2546160
9b40395
6930d8f
 
 
 
2546160
58bde6b
6930d8f
 
 
9b40395
6930d8f
 
 
9f89845
6930d8f
 
9b40395
 
 
6930d8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b40395
 
6930d8f
 
 
 
9b40395
 
 
 
 
6930d8f
9b40395
 
6930d8f
 
 
 
 
58bde6b
6930d8f
 
 
815a086
6930d8f
815a086
6930d8f
 
815a086
6930d8f
815a086
6930d8f
 
 
 
 
 
815a086
6930d8f
 
 
 
 
815a086
6930d8f
815a086
 
6930d8f
 
 
 
 
 
 
 
815a086
 
 
9b40395
815a086
 
 
6930d8f
815a086
702e044
 
 
 
6930d8f
 
2546160
 
 
6930d8f
2546160
 
aba2e4d
2546160
 
9b40395
6930d8f
 
 
 
 
2546160
6930d8f
2546160
6930d8f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b40395
 
 
 
 
2546160
6930d8f
2546160
6930d8f
815a086
 
 
 
6930d8f
 
 
 
9b40395
 
 
 
 
 
 
 
6930d8f
 
815a086
3c118cd
2546160
 
6930d8f
 
 
 
 
 
 
 
 
 
 
2546160
 
 
3c118cd
6930d8f
 
 
 
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
from flask import Flask, render_template, request, send_file, redirect, url_for
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import io
import os

app = Flask(__name__)

# --- Cache updated to hold two test files ---
data_cache = {
    "df1": None,  # Golden Data
    "df2_temp": None,  # Test 1 Data
    "df3_temp": None,  # Test 2 Data
    "limits": {},
    "cols": [],
    "golden_loaded": False,
    "test1_loaded": False,
    "test2_loaded": False,
    "comparison_file": None
}
# ----------------------------------------------


def process_golden_file(golden_file):
    """Load Golden data and extract limits."""
    # Use pandas ExcelFile to read multiple times from the file-like object
    xls = pd.ExcelFile(golden_file)
    limits_df1 = pd.read_excel(xls, nrows=4)
    df1 = pd.read_excel(xls) # Read the entire sheet again for data

    df1 = df1.drop([0, 1, 2, 3])
    df1 = df1.apply(pd.to_numeric, errors="coerce")

    limits_df1 = limits_df1.drop([0])
    ignore_cols = ["SITE_NUM", "PART_ID", "PASSFG", "SOFT_BIN", "T_TIME", "TEST_NUM"]
    # Identify columns to plot/analyze: must contain '_' and not be in ignore_cols
    cols_to_plot = [col for col in limits_df1.columns if "_" in str(col) and col not in ignore_cols]
    
    # Drop ignore columns from limits df to only get limits for relevant parameters
    limits_df1_filtered = limits_df1.drop(columns=ignore_cols, errors='ignore')

    limits = {
        col: {"LL": limits_df1_filtered.iloc[0][col], "UL": limits_df1_filtered.iloc[1][col]}
        for col in limits_df1_filtered.columns
        if pd.notna(limits_df1_filtered.iloc[0][col]) or pd.notna(limits_df1_filtered.iloc[1][col])
    }

    data_cache.update({
        "df1": df1,
        "limits": limits,
        "cols": cols_to_plot,
        "golden_loaded": True
    })
    return "Golden data loaded successfully!"


def process_test_file(test_file):
    """Load Test data."""
    df_test = pd.read_excel(test_file)
    df_test = df_test.drop([0, 1, 2, 3])
    df_test = df_test.apply(pd.to_numeric, errors="coerce")
    return df_test


# --- Comparison function updated for two test files ---
def generate_comparison_excel():
    """Generate comparison Excel (mean, std, min, max for Golden, Test 1, and Test 2)."""
    df1 = data_cache["df1"]
    df2 = data_cache["df2_temp"]
    df3 = data_cache["df3_temp"]
    
    ignore_cols = ["SITE_NUM", "PART_ID", "PASSFG", "SOFT_BIN", "T_TIME", "TEST_NUM"]
    # Use columns identified during Golden file processing
    common_cols = data_cache["cols"] 

    summary = []
    for col in common_cols:
        g_mean, t1_mean, t2_mean = df1[col].mean(), df2.get(col, pd.Series()).mean(), df3.get(col, pd.Series()).mean()
        g_std, t1_std, t2_std = df1[col].std(), df2.get(col, pd.Series()).std(), df3.get(col, pd.Series()).std()
        g_min, t1_min, t2_min = df1[col].min(), df2.get(col, pd.Series()).min(), df3.get(col, pd.Series()).min()
        g_max, t1_max, t2_max = df1[col].max(), df2.get(col, pd.Series()).max(), df3.get(col, pd.Series()).max()

        # Calculate differences relative to Golden mean
        diff1 = t1_mean - g_mean if pd.notna(t1_mean) and pd.notna(g_mean) else np.nan
        diff2 = t2_mean - g_mean if pd.notna(t2_mean) and pd.notna(g_mean) else np.nan
        
        summary.append([
            col, g_mean, t1_mean, t2_mean, diff1, diff2, 
            g_std, t1_std, t2_std, 
            g_min, t1_min, t2_min, 
            g_max, t1_max, t2_max
        ])

    comp_df = pd.DataFrame(summary, columns=[
        "Parameter", "Golden_Mean", "Test1_Mean", "Test2_Mean", "Test1_Mean_Diff", "Test2_Mean_Diff",
        "Golden_Std", "Test1_Std", "Test2_Std", 
        "Golden_Min", "Test1_Min", "Test2_Min", 
        "Golden_Max", "Test1_Max", "Test2_Max"
    ])

    path = "comparison_result.xlsx"
    comp_df.to_excel(path, index=False)
    data_cache["comparison_file"] = path
# -------------------------------------------------------------


# --- Plot function updated for two test files ---
def generate_plot(col):
    """Generate and return a plot comparing Golden vs Test 1 vs Test 2."""
    df1, df2, df3 = data_cache["df1"], data_cache.get("df2_temp"), data_cache.get("df3_temp")
    limits = data_cache["limits"]

    plt.figure(figsize=(10, 6)) # Increased size for better visibility
    
    # Golden Plot
    x1 = np.arange(1, len(df1[col]) + 1)
    plt.plot(x1, df1[col], 'o-', label="Golden", color='blue', alpha=0.7)

    # Test 1 Plot
    if df2 is not None and col in df2.columns:
        x2 = np.arange(1, len(df2[col]) + 1)
        plt.plot(x2, df2[col], 's--', label="Test 1", color='red', alpha=0.7)

    # Test 2 Plot
    if df3 is not None and col in df3.columns:
        x3 = np.arange(1, len(df3[col]) + 1)
        plt.plot(x3, df3[col], 'x:', label="Test 2", color='purple', alpha=0.8)

    # Limits Plot
    if col in limits:
        ll, ul = limits[col].get("LL"), limits[col].get("UL")
        if pd.notna(ll):
            plt.axhline(ll, color='green', linestyle='--', label='LL', linewidth=1)
        if pd.notna(ul):
            plt.axhline(ul, color='orange', linestyle='--', label='UL', linewidth=1)

    plt.title(f"Parameter: {col}")
    plt.xlabel("Part # (sequence)")
    plt.ylabel("Value")
    plt.legend(fontsize='small', loc='best')
    plt.grid(True, linestyle='--', alpha=0.5)
    
    # Set x-ticks based on the largest dataset (assuming Golden is the reference)
    max_len = len(df1[col])
    if max_len > 1:
        plt.xticks(np.arange(1, max_len + 1, max(1, max_len // 10))) # Show max 10 ticks
    
    plt.tight_layout()

    buf = io.BytesIO()
    plt.savefig(buf, format='png', bbox_inches='tight')
    buf.seek(0)
    plt.close()
    return buf
# -------------------------------------------------------------


@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        
        # 1. Upload Golden file
        if not data_cache["golden_loaded"]:
            golden_file = request.files.get("golden_file")
            if not golden_file:
                return render_template("index.html", error="Please upload the Golden file.")
            try:
                process_golden_file(golden_file)
                return redirect(url_for("index"))
            except Exception as e:
                return render_template("index.html", error=f"Error loading Golden file: {e}")

        # 2. Upload Test 1 file
        elif not data_cache["test1_loaded"]:
            test1_file = request.files.get("test1_file")
            if not test1_file:
                return render_template("index.html", error="Please upload the first Test file (Test 1).", **data_cache)
            try:
                df2 = process_test_file(test1_file)
                data_cache["df2_temp"] = df2
                data_cache["test1_loaded"] = True
                return redirect(url_for("index"))
            except Exception as e:
                return render_template("index.html", error=f"Error processing Test 1 file: {e}", **data_cache)

        # 3. Upload Test 2 file
        elif not data_cache["test2_loaded"]:
            test2_file = request.files.get("test2_file")
            if not test2_file:
                return render_template("index.html", error="Please upload the second Test file (Test 2).", **data_cache)
            try:
                df3 = process_test_file(test2_file)
                data_cache["df3_temp"] = df3
                data_cache["test2_loaded"] = True
                
                # Generate comparison and move to plot view after all files are loaded
                generate_comparison_excel() 
                
                return render_template(
                    "plot.html",
                    cols=data_cache["cols"],
                    file_ready=True
                )
            except Exception as e:
                return render_template("index.html", error=f"Error processing Test 2 file: {e}", **data_cache)

    return render_template("index.html", **data_cache)


@app.route("/plot_image/<col>")
def plot_image(col):
    # df2 and df3 are checked inside generate_plot
    if data_cache.get("df1") is None:
        return "No Golden data loaded."
    buf = generate_plot(col)
    return send_file(buf, mimetype="image/png")


@app.route("/download_comparison")
def download_comparison():
    """Download comparison Excel file."""
    path = data_cache.get("comparison_file")
    if path and os.path.exists(path):
        return send_file(path, as_attachment=True, download_name="three_way_comparison_result.xlsx")
    return "No comparison file available. Please upload all data first."


@app.route("/reset_golden")
def reset_golden():
    """Reset all data."""
    global data_cache
    if data_cache.get("comparison_file") and os.path.exists(data_cache["comparison_file"]):
        os.remove(data_cache["comparison_file"])
    
    data_cache = {
        "df1": None, "df2_temp": None, "df3_temp": None, 
        "limits": {}, "cols": [], 
        "golden_loaded": False, "test1_loaded": False, "test2_loaded": False, 
        "comparison_file": None
    }
    return redirect(url_for("index"))


if __name__ == "__main__":
    # Ensure a local directory exists for comparison file (optional but good practice)
    # if not os.path.exists("temp"):
    #     os.makedirs("temp")
    app.run(host="0.0.0.0", port=7860, debug=True)