Prince Vaviya commited on
Commit
7a83d16
·
1 Parent(s): 76c0be6

size_predictor

Browse files
Files changed (5) hide show
  1. app.py +139 -0
  2. feedback.csv +9 -0
  3. requirements.txt +1 -0
  4. size_chart.py +32 -0
  5. size_rules.py +87 -0
app.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import csv
3
+ import os
4
+ from datetime import datetime
5
+ from size_rules import evaluate_size, apply_fit_preference
6
+
7
+ FEEDBACK_FILE = "feedback.csv"
8
+
9
+ # Initialize CSV if it doesn't exist
10
+ if not os.path.exists(FEEDBACK_FILE):
11
+ with open(FEEDBACK_FILE, "w", newline="") as f:
12
+ writer = csv.writer(f)
13
+ writer.writerow(["Timestamp", "Chest", "Waist", "Bicep", "Shoulder", "Fit_Pref", "Base_Size", "Predicted_Size", "Feedback"])
14
+
15
+ def predict_size(chest, waist, bicep, shoulder, fit_pref):
16
+ data = {
17
+ "chest": chest,
18
+ "waist": waist,
19
+ "bicep": bicep,
20
+ "shoulder": shoulder
21
+ }
22
+
23
+ base_size, base_reason = evaluate_size(data)
24
+ final_size, fit_reason = apply_fit_preference(base_size, fit_pref, data)
25
+
26
+ explanation = {
27
+ "recommended_size": final_size,
28
+ "base_size": base_size,
29
+ "fit_preference": fit_pref,
30
+ "final_size": final_size,
31
+ "confidence": "high",
32
+ "reason": f"{base_reason}. {fit_reason}"
33
+ }
34
+
35
+ html_output = f"""
36
+ <div style="
37
+ background-color: #d1fae5;
38
+ color: #065f46;
39
+ font-size: 32px;
40
+ font-weight: bold;
41
+ text-align: center;
42
+ border: 2px solid #10b981;
43
+ padding: 20px;
44
+ border-radius: 8px;
45
+ ">
46
+ {final_size}
47
+ </div>
48
+ """
49
+
50
+ # Return data for state
51
+ state_data = {
52
+ "chest": chest,
53
+ "waist": waist,
54
+ "bicep": bicep,
55
+ "shoulder": shoulder,
56
+ "fit": fit_pref,
57
+ "base": base_size,
58
+ "prediction": final_size
59
+ }
60
+
61
+ return html_output, explanation, state_data
62
+
63
+ def save_feedback(vote, data):
64
+ if not data:
65
+ return "No prediction data found."
66
+
67
+ try:
68
+ with open(FEEDBACK_FILE, "a", newline="") as f:
69
+ writer = csv.writer(f)
70
+ writer.writerow([
71
+ datetime.now().isoformat(),
72
+ data["chest"],
73
+ data["waist"],
74
+ data["bicep"],
75
+ data["shoulder"],
76
+ data["fit"],
77
+ data["base"],
78
+ data["prediction"],
79
+ vote
80
+ ])
81
+ return f"Thanks! for the feedback"
82
+ except Exception as e:
83
+ return f"Error saving feedback: {str(e)}"
84
+
85
+ css = """
86
+ #result-box textarea {
87
+ font-size: 32px !important;
88
+ }
89
+ """
90
+
91
+ with gr.Blocks(title="AI Size Recommendation Engine") as demo:
92
+ gr.Markdown("# AI Size Recommendation Engine")
93
+ gr.Markdown("Enter your body measurements (in inches) to get a deterministic size recommendation.")
94
+
95
+ # State to store the last prediction data
96
+ prediction_state = gr.State()
97
+
98
+ with gr.Row():
99
+ with gr.Column():
100
+ chest = gr.Number(label="Chest (inches)", value=38.0, step=0.5)
101
+ waist = gr.Number(label="Waist (inches)", value=32.0, step=0.5)
102
+ bicep = gr.Number(label="Bicep (inches)", value=13.0, step=0.5)
103
+ shoulder = gr.Number(label="Shoulder (inches)", value=46.0, step=0.5)
104
+ fit = gr.Dropdown(choices=["tight", "regular", "loose"], label="Fit Preference", value="regular")
105
+ submit_btn = gr.Button("Predict Size", variant="primary")
106
+
107
+ with gr.Column():
108
+ gr.Markdown("### Recommended Size")
109
+ output_size = gr.HTML()
110
+ output_json = gr.JSON(label="Explainable AI Output")
111
+
112
+ with gr.Row():
113
+ like_btn = gr.Button("👍 Good Prediction")
114
+ dislike_btn = gr.Button("👎 Bad Prediction")
115
+
116
+ feedback_msg = gr.Label(label="Feedback Status", visible=False)
117
+
118
+ # Prediction event
119
+ submit_btn.click(
120
+ fn=predict_size,
121
+ inputs=[chest, waist, bicep, shoulder, fit],
122
+ outputs=[output_size, output_json, prediction_state]
123
+ )
124
+
125
+ # Feedback events
126
+ like_btn.click(
127
+ fn=lambda d: save_feedback("Like", d),
128
+ inputs=[prediction_state],
129
+ outputs=[feedback_msg]
130
+ ).then(lambda: gr.Label(visible=True), None, feedback_msg)
131
+
132
+ dislike_btn.click(
133
+ fn=lambda d: save_feedback("Dislike", d),
134
+ inputs=[prediction_state],
135
+ outputs=[feedback_msg]
136
+ ).then(lambda: gr.Label(visible=True), None, feedback_msg)
137
+
138
+ if __name__ == "__main__":
139
+ demo.launch()
feedback.csv ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ Timestamp,Chest,Waist,Bicep,Shoulder,Fit_Pref,Base_Size,Predicted_Size,Feedback
2
+ 2026-01-02T15:26:54.295743,38,32,13,46,regular,S,M,Like
3
+ 2026-01-02T15:26:58.463759,38,32,13,46,regular,S,M,Dislike
4
+ 2026-01-02T15:28:21.262074,38,32,13,46,regular,S,M,Like
5
+ 2026-01-02T15:28:42.327924,38,32,13,46,regular,S,M,Like
6
+ 2026-01-02T15:28:43.110742,38,32,13,46,regular,S,M,Dislike
7
+ 2026-01-02T15:28:44.095717,38,32,13,46,regular,S,M,Dislike
8
+ 2026-01-02T15:28:44.377499,38,32,13,46,regular,S,M,Dislike
9
+ 2026-01-02T15:28:45.495196,38,32,13,46,regular,S,M,Dislike
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ gradio
size_chart.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SIZE_CHART = {
2
+ "XS": {
3
+ "chest": (34, 36),
4
+ "waist": (28, 30),
5
+ "bicep": (11, 12),
6
+ "shoulder": (42, 44)
7
+ },
8
+ "S": {
9
+ "chest": (36, 38),
10
+ "waist": (30, 32),
11
+ "bicep": (12, 13),
12
+ "shoulder": (44, 46)
13
+ },
14
+ "M": {
15
+ "chest": (38, 40),
16
+ "waist": (32, 34),
17
+ "bicep": (13, 14),
18
+ "shoulder": (46, 48)
19
+ },
20
+ "L": {
21
+ "chest": (40, 42),
22
+ "waist": (34, 36),
23
+ "bicep": (14, 15),
24
+ "shoulder": (48, 50)
25
+ },
26
+ "XL": {
27
+ "chest": (42, 44),
28
+ "waist": (36, 38),
29
+ "bicep": (15, 16),
30
+ "shoulder": (50, 52)
31
+ }
32
+ }
size_rules.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from size_chart import SIZE_CHART
2
+
3
+ PRIORITY = ["shoulder", "chest", "waist", "bicep"]
4
+
5
+ def evaluate_size(measurements):
6
+ """
7
+ Evaluates the base size based on measurements.
8
+ Returns: (size, reason)
9
+ """
10
+ ordered_sizes = ["XS", "S", "M", "L", "XL"]
11
+
12
+ for i, size in enumerate(ordered_sizes):
13
+ limits = SIZE_CHART[size]
14
+ valid = True
15
+
16
+ # Check if measurements fit within this size's limits
17
+ for key in PRIORITY:
18
+ # We strictly check the upper bound.
19
+ # If measurement > max, this size is too small.
20
+ if measurements[key] > limits[key][1]:
21
+ valid = False
22
+ break
23
+
24
+ if valid:
25
+ # This is the smallest size that fits.
26
+ # Determine reasoning.
27
+ if i == 0:
28
+ return size, "Smallest available size fits all measurements."
29
+
30
+ # Check why the previous size failed logic
31
+ prev_size = ordered_sizes[i-1]
32
+ prev_limits = SIZE_CHART[prev_size]
33
+ reasons = []
34
+ for key in PRIORITY:
35
+ if measurements[key] > prev_limits[key][1]:
36
+ reasons.append(f"{key.capitalize()} ({measurements[key]}) > {prev_size} limit ({prev_limits[key][1]})")
37
+
38
+ reason_str = f"Requires {size} because: " + ", ".join(reasons)
39
+ return size, reason_str
40
+
41
+ # If even XL fails (XL max is exceeded)
42
+ # We return XL+
43
+ # Find what exceeded XL
44
+ xl_limits = SIZE_CHART["XL"]
45
+ reasons = []
46
+ for key in PRIORITY:
47
+ if measurements[key] > xl_limits[key][1]:
48
+ reasons.append(f"{key.capitalize()} ({measurements[key]}) > XL limit ({xl_limits[key][1]})")
49
+
50
+ return "XL+", "Measurements exceed XL limits: " + ", ".join(reasons)
51
+
52
+ def apply_fit_preference(base_size, preference, measurements=None):
53
+ """
54
+ Applies fit preference to the base size.
55
+ For 'regular', requires measurements to re-evaluate.
56
+ Returns: (final_size, reason)
57
+ """
58
+ sizes = ["XS", "S", "M", "L", "XL"]
59
+
60
+ if base_size not in sizes:
61
+ return base_size, "Fit preference not applicable for sizes outside chart"
62
+
63
+ idx = sizes.index(base_size)
64
+
65
+ if preference == "tight":
66
+ return base_size, "Fit: Tight (Kept base size)"
67
+
68
+ if preference == "loose":
69
+ if idx < len(sizes) - 1:
70
+ return sizes[idx + 1], "Fit: Loose (Moved up one size)"
71
+ else:
72
+ return base_size, "Fit: Loose (Already at max size)"
73
+
74
+ if preference == "regular":
75
+ if measurements is None:
76
+ return base_size, "Fit: Regular (No measurements provided for adjustment)"
77
+
78
+ # Add 0.5 inches to all measurements
79
+ modified_measurements = {k: v + 0.5 for k, v in measurements.items()}
80
+ new_size, _ = evaluate_size(modified_measurements)
81
+
82
+ if new_size == base_size:
83
+ return new_size, "Fit: Regular (Adjustment didn't change size)"
84
+ else:
85
+ return new_size, f"Fit: Regular (Adjustment pushed size to {new_size})"
86
+
87
+ return base_size, "Unknown preference"