aimodelshare 0.3.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. aimodelshare/README.md +26 -0
  2. aimodelshare/__init__.py +100 -0
  3. aimodelshare/aimsonnx.py +2381 -0
  4. aimodelshare/api.py +836 -0
  5. aimodelshare/auth.py +163 -0
  6. aimodelshare/aws.py +511 -0
  7. aimodelshare/aws_client.py +173 -0
  8. aimodelshare/base_image.py +154 -0
  9. aimodelshare/bucketpolicy.py +106 -0
  10. aimodelshare/color_mappings/color_mapping_keras.csv +121 -0
  11. aimodelshare/color_mappings/color_mapping_pytorch.csv +117 -0
  12. aimodelshare/containerisation.py +244 -0
  13. aimodelshare/containerization.py +712 -0
  14. aimodelshare/containerization_templates/Dockerfile.txt +8 -0
  15. aimodelshare/containerization_templates/Dockerfile_PySpark.txt +23 -0
  16. aimodelshare/containerization_templates/buildspec.txt +14 -0
  17. aimodelshare/containerization_templates/lambda_function.txt +40 -0
  18. aimodelshare/custom_approach/__init__.py +1 -0
  19. aimodelshare/custom_approach/lambda_function.py +17 -0
  20. aimodelshare/custom_eval_metrics.py +103 -0
  21. aimodelshare/data_sharing/__init__.py +0 -0
  22. aimodelshare/data_sharing/data_sharing_templates/Dockerfile.txt +3 -0
  23. aimodelshare/data_sharing/data_sharing_templates/__init__.py +1 -0
  24. aimodelshare/data_sharing/data_sharing_templates/buildspec.txt +15 -0
  25. aimodelshare/data_sharing/data_sharing_templates/codebuild_policies.txt +129 -0
  26. aimodelshare/data_sharing/data_sharing_templates/codebuild_trust_relationship.txt +12 -0
  27. aimodelshare/data_sharing/download_data.py +620 -0
  28. aimodelshare/data_sharing/share_data.py +373 -0
  29. aimodelshare/data_sharing/utils.py +8 -0
  30. aimodelshare/deploy_custom_lambda.py +246 -0
  31. aimodelshare/documentation/Makefile +20 -0
  32. aimodelshare/documentation/karma_sphinx_theme/__init__.py +28 -0
  33. aimodelshare/documentation/karma_sphinx_theme/_version.py +2 -0
  34. aimodelshare/documentation/karma_sphinx_theme/breadcrumbs.html +70 -0
  35. aimodelshare/documentation/karma_sphinx_theme/layout.html +172 -0
  36. aimodelshare/documentation/karma_sphinx_theme/search.html +50 -0
  37. aimodelshare/documentation/karma_sphinx_theme/searchbox.html +14 -0
  38. aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css +2 -0
  39. aimodelshare/documentation/karma_sphinx_theme/static/css/custom.css.map +1 -0
  40. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css +2751 -0
  41. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.css.map +1 -0
  42. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css +2 -0
  43. aimodelshare/documentation/karma_sphinx_theme/static/css/theme.min.css.map +1 -0
  44. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.eot +0 -0
  45. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.svg +32 -0
  46. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.ttf +0 -0
  47. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff +0 -0
  48. aimodelshare/documentation/karma_sphinx_theme/static/font/fontello.woff2 +0 -0
  49. aimodelshare/documentation/karma_sphinx_theme/static/js/theme.js +68 -0
  50. aimodelshare/documentation/karma_sphinx_theme/theme.conf +9 -0
  51. aimodelshare/documentation/make.bat +35 -0
  52. aimodelshare/documentation/requirements.txt +2 -0
  53. aimodelshare/documentation/source/about.rst +18 -0
  54. aimodelshare/documentation/source/advanced_features.rst +137 -0
  55. aimodelshare/documentation/source/competition.rst +218 -0
  56. aimodelshare/documentation/source/conf.py +58 -0
  57. aimodelshare/documentation/source/create_credentials.rst +86 -0
  58. aimodelshare/documentation/source/example_notebooks.rst +132 -0
  59. aimodelshare/documentation/source/functions.rst +151 -0
  60. aimodelshare/documentation/source/gettingstarted.rst +390 -0
  61. aimodelshare/documentation/source/images/creds1.png +0 -0
  62. aimodelshare/documentation/source/images/creds2.png +0 -0
  63. aimodelshare/documentation/source/images/creds3.png +0 -0
  64. aimodelshare/documentation/source/images/creds4.png +0 -0
  65. aimodelshare/documentation/source/images/creds5.png +0 -0
  66. aimodelshare/documentation/source/images/creds_file_example.png +0 -0
  67. aimodelshare/documentation/source/images/predict_tab.png +0 -0
  68. aimodelshare/documentation/source/index.rst +110 -0
  69. aimodelshare/documentation/source/modelplayground.rst +132 -0
  70. aimodelshare/exceptions.py +11 -0
  71. aimodelshare/generatemodelapi.py +1270 -0
  72. aimodelshare/iam/codebuild_policy.txt +129 -0
  73. aimodelshare/iam/codebuild_trust_relationship.txt +12 -0
  74. aimodelshare/iam/lambda_policy.txt +15 -0
  75. aimodelshare/iam/lambda_trust_relationship.txt +12 -0
  76. aimodelshare/json_templates/__init__.py +1 -0
  77. aimodelshare/json_templates/api_json.txt +155 -0
  78. aimodelshare/json_templates/auth/policy.txt +1 -0
  79. aimodelshare/json_templates/auth/role.txt +1 -0
  80. aimodelshare/json_templates/eval/policy.txt +1 -0
  81. aimodelshare/json_templates/eval/role.txt +1 -0
  82. aimodelshare/json_templates/function/policy.txt +1 -0
  83. aimodelshare/json_templates/function/role.txt +1 -0
  84. aimodelshare/json_templates/integration_response.txt +5 -0
  85. aimodelshare/json_templates/lambda_policy_1.txt +15 -0
  86. aimodelshare/json_templates/lambda_policy_2.txt +8 -0
  87. aimodelshare/json_templates/lambda_role_1.txt +12 -0
  88. aimodelshare/json_templates/lambda_role_2.txt +16 -0
  89. aimodelshare/leaderboard.py +174 -0
  90. aimodelshare/main/1.txt +132 -0
  91. aimodelshare/main/1B.txt +112 -0
  92. aimodelshare/main/2.txt +153 -0
  93. aimodelshare/main/3.txt +134 -0
  94. aimodelshare/main/4.txt +128 -0
  95. aimodelshare/main/5.txt +109 -0
  96. aimodelshare/main/6.txt +105 -0
  97. aimodelshare/main/7.txt +144 -0
  98. aimodelshare/main/8.txt +142 -0
  99. aimodelshare/main/__init__.py +1 -0
  100. aimodelshare/main/authorization.txt +275 -0
  101. aimodelshare/main/eval_classification.txt +79 -0
  102. aimodelshare/main/eval_lambda.txt +1709 -0
  103. aimodelshare/main/eval_regression.txt +80 -0
  104. aimodelshare/main/lambda_function.txt +8 -0
  105. aimodelshare/main/nst.txt +149 -0
  106. aimodelshare/model.py +1543 -0
  107. aimodelshare/modeluser.py +215 -0
  108. aimodelshare/moral_compass/README.md +408 -0
  109. aimodelshare/moral_compass/__init__.py +65 -0
  110. aimodelshare/moral_compass/_version.py +3 -0
  111. aimodelshare/moral_compass/api_client.py +601 -0
  112. aimodelshare/moral_compass/apps/__init__.py +69 -0
  113. aimodelshare/moral_compass/apps/ai_consequences.py +540 -0
  114. aimodelshare/moral_compass/apps/bias_detective.py +714 -0
  115. aimodelshare/moral_compass/apps/ethical_revelation.py +898 -0
  116. aimodelshare/moral_compass/apps/fairness_fixer.py +889 -0
  117. aimodelshare/moral_compass/apps/judge.py +888 -0
  118. aimodelshare/moral_compass/apps/justice_equity_upgrade.py +853 -0
  119. aimodelshare/moral_compass/apps/mc_integration_helpers.py +820 -0
  120. aimodelshare/moral_compass/apps/model_building_game.py +1104 -0
  121. aimodelshare/moral_compass/apps/model_building_game_beginner.py +687 -0
  122. aimodelshare/moral_compass/apps/moral_compass_challenge.py +858 -0
  123. aimodelshare/moral_compass/apps/session_auth.py +254 -0
  124. aimodelshare/moral_compass/apps/shared_activity_styles.css +349 -0
  125. aimodelshare/moral_compass/apps/tutorial.py +481 -0
  126. aimodelshare/moral_compass/apps/what_is_ai.py +853 -0
  127. aimodelshare/moral_compass/challenge.py +365 -0
  128. aimodelshare/moral_compass/config.py +187 -0
  129. aimodelshare/placeholders/model.onnx +0 -0
  130. aimodelshare/placeholders/preprocessor.zip +0 -0
  131. aimodelshare/playground.py +1968 -0
  132. aimodelshare/postprocessormodules.py +157 -0
  133. aimodelshare/preprocessormodules.py +373 -0
  134. aimodelshare/pyspark/1.txt +195 -0
  135. aimodelshare/pyspark/1B.txt +181 -0
  136. aimodelshare/pyspark/2.txt +220 -0
  137. aimodelshare/pyspark/3.txt +204 -0
  138. aimodelshare/pyspark/4.txt +187 -0
  139. aimodelshare/pyspark/5.txt +178 -0
  140. aimodelshare/pyspark/6.txt +174 -0
  141. aimodelshare/pyspark/7.txt +211 -0
  142. aimodelshare/pyspark/8.txt +206 -0
  143. aimodelshare/pyspark/__init__.py +1 -0
  144. aimodelshare/pyspark/authorization.txt +258 -0
  145. aimodelshare/pyspark/eval_classification.txt +79 -0
  146. aimodelshare/pyspark/eval_lambda.txt +1441 -0
  147. aimodelshare/pyspark/eval_regression.txt +80 -0
  148. aimodelshare/pyspark/lambda_function.txt +8 -0
  149. aimodelshare/pyspark/nst.txt +213 -0
  150. aimodelshare/python/my_preprocessor.py +58 -0
  151. aimodelshare/readme.md +26 -0
  152. aimodelshare/reproducibility.py +181 -0
  153. aimodelshare/sam/Dockerfile.txt +8 -0
  154. aimodelshare/sam/Dockerfile_PySpark.txt +24 -0
  155. aimodelshare/sam/__init__.py +1 -0
  156. aimodelshare/sam/buildspec.txt +11 -0
  157. aimodelshare/sam/codebuild_policies.txt +129 -0
  158. aimodelshare/sam/codebuild_trust_relationship.txt +12 -0
  159. aimodelshare/sam/codepipeline_policies.txt +173 -0
  160. aimodelshare/sam/codepipeline_trust_relationship.txt +12 -0
  161. aimodelshare/sam/spark-class.txt +2 -0
  162. aimodelshare/sam/template.txt +54 -0
  163. aimodelshare/tools.py +103 -0
  164. aimodelshare/utils/__init__.py +78 -0
  165. aimodelshare/utils/optional_deps.py +38 -0
  166. aimodelshare/utils.py +57 -0
  167. aimodelshare-0.3.7.dist-info/METADATA +298 -0
  168. aimodelshare-0.3.7.dist-info/RECORD +171 -0
  169. aimodelshare-0.3.7.dist-info/WHEEL +5 -0
  170. aimodelshare-0.3.7.dist-info/licenses/LICENSE +5 -0
  171. aimodelshare-0.3.7.dist-info/top_level.txt +1 -0
@@ -0,0 +1,687 @@
1
+ """
2
+ Beginner Mode: Model Building Game (Ètica en Joc) - Justice & Equity Challenge
3
+
4
+ Purpose:
5
+ A simplified, scaffolded version of the full model building app for first-time or low-tech learners.
6
+
7
+ Structure:
8
+ - Factory: create_model_building_game_beginner_app()
9
+ - Launcher: launch_model_building_game_beginner_app()
10
+ """
11
+
12
+ import os
13
+ import random
14
+ import contextlib
15
+ from io import StringIO
16
+
17
+ import numpy as np
18
+ import pandas as pd
19
+ import requests
20
+ import gradio as gr
21
+
22
+ from sklearn.model_selection import train_test_split
23
+ from sklearn.preprocessing import StandardScaler
24
+ from sklearn.impute import SimpleImputer
25
+ from sklearn.compose import ColumnTransformer
26
+ from sklearn.pipeline import Pipeline
27
+ from sklearn.preprocessing import OneHotEncoder
28
+ from sklearn.linear_model import LogisticRegression
29
+ from sklearn.tree import DecisionTreeClassifier
30
+ from sklearn.ensemble import RandomForestClassifier
31
+ from sklearn.neighbors import KNeighborsClassifier
32
+
33
+ try:
34
+ from aimodelshare.playground import Competition
35
+ except ImportError:
36
+ raise ImportError("Install dependencies: pip install aimodelshare aim-widgets")
37
+
38
+ # ---------------------------------------------------------------------
39
+ # Configuration
40
+ # ---------------------------------------------------------------------
41
+ MY_PLAYGROUND_ID = "https://cf3wdpkg0d.execute-api.us-east-1.amazonaws.com/prod/m"
42
+
43
+ MODEL_TYPES = {
44
+ "The Balanced Generalist": {
45
+ "builder": lambda: LogisticRegression(max_iter=500, random_state=42, class_weight="balanced"),
46
+ "card": "A solid default that learns general patterns. Good first choice."
47
+ },
48
+ "The Rule-Maker": {
49
+ "builder": lambda: DecisionTreeClassifier(random_state=42, class_weight="balanced"),
50
+ "card": "Creates if/then rules. Easy to understand; may miss subtle patterns."
51
+ },
52
+ "The 'Nearest Neighbor'": {
53
+ "builder": lambda: KNeighborsClassifier(),
54
+ "card": "Compares each case to similar past ones. Simple pattern matching."
55
+ },
56
+ "The Deep Pattern-Finder": {
57
+ "builder": lambda: RandomForestClassifier(random_state=42, class_weight="balanced"),
58
+ "card": "Many trees working together. Powerful; can overfit if too complex."
59
+ }
60
+ }
61
+
62
+ DEFAULT_MODEL = "The Balanced Generalist"
63
+ TEAM_NAMES = [
64
+ "The Moral Champions", "The Justice League", "The Data Detectives",
65
+ "The Ethical Explorers", "The Fairness Finders", "The Accuracy Avengers"
66
+ ]
67
+ CURRENT_TEAM_NAME = random.choice(TEAM_NAMES)
68
+
69
+ BASIC_NUMERIC = [
70
+ "priors_count", "juv_fel_count", "juv_misd_count",
71
+ "juv_other_count", "days_b_screening_arrest"
72
+ ]
73
+ BASIC_CATEGORICAL = ["c_charge_desc"]
74
+ OPTIONAL_FEATURE = "age" # Only unlock at final rank (Explorer)
75
+
76
+ MAX_ROWS = 4000
77
+ TOP_N_CHARGES = 40
78
+ np.random.seed(42)
79
+
80
+ # Globals (initialized at launch)
81
+ playground = None
82
+ X_TRAIN_RAW = None
83
+ X_TEST_RAW = None
84
+ Y_TRAIN = None
85
+ Y_TEST = None
86
+
87
+ # ---------------------------------------------------------------------
88
+ # Data Loading
89
+ # ---------------------------------------------------------------------
90
+ def load_and_prep_data():
91
+ url = "https://raw.githubusercontent.com/propublica/compas-analysis/master/compas-scores-two-years.csv"
92
+ df = pd.read_csv(StringIO(requests.get(url).text))
93
+
94
+ if df.shape[0] > MAX_ROWS:
95
+ df = df.sample(n=MAX_ROWS, random_state=42)
96
+
97
+ if "c_charge_desc" in df.columns:
98
+ top_vals = df["c_charge_desc"].value_counts().head(TOP_N_CHARGES).index
99
+ df["c_charge_desc"] = df["c_charge_desc"].apply(
100
+ lambda v: v if (pd.notna(v) and v in top_vals) else "OTHER"
101
+ )
102
+
103
+ needed = BASIC_NUMERIC + BASIC_CATEGORICAL + [OPTIONAL_FEATURE]
104
+ for c in needed:
105
+ if c not in df.columns:
106
+ df[c] = np.nan
107
+
108
+ X = df[needed]
109
+ y = df["two_year_recid"]
110
+
111
+ X_train, X_test, y_train, y_test = train_test_split(
112
+ X, y, test_size=0.25, random_state=42, stratify=y
113
+ )
114
+ return X_train, X_test, y_train, y_test
115
+
116
+ # ---------------------------------------------------------------------
117
+ # Helper Functions
118
+ # ---------------------------------------------------------------------
119
+ def safe_int(value, default=1):
120
+ if value is None:
121
+ return default
122
+ try:
123
+ return int(value)
124
+ except (ValueError, TypeError):
125
+ return default
126
+
127
+ def get_model_card(name):
128
+ return MODEL_TYPES.get(name, {}).get("card", "No description available.")
129
+
130
+ def tune_model(model, complexity_level):
131
+ lvl = int(complexity_level)
132
+ if isinstance(model, LogisticRegression):
133
+ model.C = {1: 0.5, 2: 1.0, 3: 3.0}.get(lvl, 1.0)
134
+ elif isinstance(model, DecisionTreeClassifier):
135
+ model.max_depth = {1: 3, 2: 6, 3: None}.get(lvl, 6)
136
+ elif isinstance(model, RandomForestClassifier):
137
+ model.max_depth = {1: 5, 2: 10, 3: None}.get(lvl, 10)
138
+ model.n_estimators = {1: 30, 2: 60, 3: 100}.get(lvl, 60)
139
+ elif isinstance(model, KNeighborsClassifier):
140
+ model.n_neighbors = {1: 25, 2: 10, 3: 5}.get(lvl, 10)
141
+ return model
142
+
143
+ def compute_rank_state(submissions, current_model, current_complexity, current_size, include_age):
144
+ """
145
+ Determine unlocked tools based on submission count.
146
+ """
147
+ if submissions == 0:
148
+ return {
149
+ "rank_msg": "### 🧑‍🎓 Rank: Trainee\nSubmit 1 model to unlock a second model.",
150
+ "models": [DEFAULT_MODEL],
151
+ "model_value": DEFAULT_MODEL,
152
+ "model_interactive": False,
153
+ "complexity_max": 2,
154
+ "complexity_value": min(current_complexity, 2),
155
+ "size_choices": ["Small (40%)", "Full (100%)"],
156
+ "size_value": "Small (40%)",
157
+ "age_enabled": False,
158
+ "age_checked": False
159
+ }
160
+ elif submissions == 1:
161
+ return {
162
+ "rank_msg": "### 🚀 Rank Up: Junior\nRule-Maker + Complexity Level 3 unlocked.",
163
+ "models": [DEFAULT_MODEL, "The Rule-Maker"],
164
+ "model_value": current_model if current_model in [DEFAULT_MODEL, "The Rule-Maker"] else DEFAULT_MODEL,
165
+ "model_interactive": True,
166
+ "complexity_max": 3,
167
+ "complexity_value": min(current_complexity, 3),
168
+ "size_choices": ["Small (40%)", "Full (100%)"],
169
+ "size_value": current_size,
170
+ "age_enabled": False,
171
+ "age_checked": False
172
+ }
173
+ else:
174
+ return {
175
+ "rank_msg": "### 🌟 Rank: Explorer\nAll models + optional 'Age' feature unlocked.",
176
+ "models": list(MODEL_TYPES.keys()),
177
+ "model_value": current_model if current_model in MODEL_TYPES else DEFAULT_MODEL,
178
+ "model_interactive": True,
179
+ "complexity_max": 3,
180
+ "complexity_value": current_complexity,
181
+ "size_choices": ["Small (40%)", "Full (100%)"],
182
+ "size_value": current_size,
183
+ "age_enabled": True,
184
+ "age_checked": include_age
185
+ }
186
+
187
+ def summarize_leaderboard(team_name, username):
188
+ if playground is None:
189
+ return pd.DataFrame(), pd.DataFrame(), "Playground not connected.", 0.0
190
+
191
+ try:
192
+ df = playground.get_leaderboard()
193
+ if df is None or df.empty or "accuracy" not in df.columns:
194
+ return pd.DataFrame(), pd.DataFrame(), "No submissions yet.", 0.0
195
+
196
+ # Team summary (condensed)
197
+ team_df = pd.DataFrame()
198
+ if "Team" in df.columns:
199
+ team_df = (
200
+ df.groupby("Team")["accuracy"]
201
+ .agg(Best="max", Avg="mean", Subs="count")
202
+ .reset_index()
203
+ .sort_values("Best", ascending=False)
204
+ )
205
+ team_df["Best"] = team_df["Best"].round(4)
206
+ team_df["Avg"] = team_df["Avg"].round(4)
207
+
208
+ # Individual summary
209
+ user_best = df.groupby("username")["accuracy"].max().reset_index().rename(columns={"accuracy": "Best"})
210
+ user_best["Best"] = user_best["Best"].round(4)
211
+ user_best = user_best.sort_values("Best", ascending=False).reset_index(drop=True)
212
+
213
+ # Feedback
214
+ latest_acc = 0.0
215
+ feedback = "Submit a model to appear on the leaderboard."
216
+ my_subs = df[df["username"] == username].sort_values("timestamp", ascending=False)
217
+ if not my_subs.empty:
218
+ latest_acc = my_subs.iloc[0]["accuracy"]
219
+ feedback = f"Your latest accuracy: {latest_acc:.4f}"
220
+ if len(my_subs) > 1:
221
+ prev = my_subs.iloc[1]["accuracy"]
222
+ diff = latest_acc - prev
223
+ if diff > 0.0001:
224
+ feedback += f" (Improved +{diff:.4f})"
225
+ elif diff < -0.0001:
226
+ feedback += f" (Down -{abs(diff):.4f})"
227
+ else:
228
+ feedback += " (No change)"
229
+ return team_df, user_best, feedback, latest_acc
230
+ except Exception as e:
231
+ return pd.DataFrame(), pd.DataFrame(), f"Error loading leaderboard: {e}", 0.0
232
+
233
+ def run_beginner_experiment(
234
+ model_name,
235
+ complexity_level,
236
+ size_choice,
237
+ include_age,
238
+ team_name,
239
+ last_accuracy,
240
+ submissions,
241
+ username
242
+ ):
243
+ # ---- Normalize transient/invalid inputs (Gradio 5.x safety) ----
244
+ if not model_name or model_name not in MODEL_TYPES:
245
+ model_name = DEFAULT_MODEL
246
+ size_choice = size_choice if size_choice in ["Small (40%)", "Full (100%)"] else "Small (40%)"
247
+ include_age = bool(include_age)
248
+
249
+ # Coerce slider value to safe integer
250
+ complexity_level = safe_int(complexity_level, 2)
251
+
252
+ log = (
253
+ f"▶ Experiment\n"
254
+ f"Model: {model_name}\n"
255
+ f"Complexity: {complexity_level}\n"
256
+ f"Data Size: {size_choice}\n"
257
+ f"Include Age: {'Yes' if include_age else 'No'}\n"
258
+ )
259
+
260
+ if playground is None:
261
+ state = compute_rank_state(submissions, model_name, complexity_level, size_choice, include_age)
262
+ return (
263
+ log + "\nERROR: Playground not connected.",
264
+ "Playground connection failed.",
265
+ pd.DataFrame(),
266
+ pd.DataFrame(),
267
+ last_accuracy,
268
+ submissions,
269
+ state["rank_msg"],
270
+ gr.update(choices=state["models"], value=state["model_value"], interactive=state["model_interactive"]),
271
+ gr.update(minimum=1, maximum=state["complexity_max"], value=state["complexity_value"]),
272
+ gr.update(choices=state["size_choices"], value=state["size_value"]),
273
+ gr.update(interactive=state["age_enabled"], value=state["age_checked"])
274
+ )
275
+
276
+ try:
277
+ # Data sampling
278
+ frac = 0.4 if "Small" in size_choice else 1.0
279
+ if frac == 1.0:
280
+ X_sample = X_TRAIN_RAW
281
+ y_sample = Y_TRAIN
282
+ else:
283
+ X_sample = X_TRAIN_RAW.sample(frac=frac, random_state=42)
284
+ y_sample = Y_TRAIN.loc[X_sample.index]
285
+ log += f"Rows used: {len(X_sample)} ({int(frac*100)}% of training set)\n"
286
+
287
+ # Features
288
+ numeric = list(BASIC_NUMERIC)
289
+ categorical = list(BASIC_CATEGORICAL)
290
+ if include_age:
291
+ numeric.append(OPTIONAL_FEATURE)
292
+ log += f"Features: {', '.join(numeric + categorical)}\n"
293
+
294
+ num_tf = Pipeline([
295
+ ("imputer", SimpleImputer(strategy="median")),
296
+ ("scale", StandardScaler())
297
+ ])
298
+ cat_tf = Pipeline([
299
+ ("imputer", SimpleImputer(strategy="constant", fill_value="missing")),
300
+ ("onehot", OneHotEncoder(handle_unknown="ignore", sparse_output=False))
301
+ ])
302
+
303
+ ct = ColumnTransformer([
304
+ ("num", num_tf, numeric),
305
+ ("cat", cat_tf, categorical)
306
+ ])
307
+
308
+ X_train_processed = ct.fit_transform(X_sample)
309
+ X_test_processed = ct.transform(X_TEST_RAW)
310
+ log += "Preprocessing complete.\n"
311
+
312
+ base = MODEL_TYPES[model_name]["builder"]()
313
+ tuned = tune_model(base, complexity_level)
314
+ tuned.fit(X_train_processed, y_sample)
315
+ log += "Model trained.\n"
316
+
317
+ preds = tuned.predict(X_test_processed)
318
+ desc = f"{model_name} (C:{complexity_level} Size:{'Full' if frac==1.0 else 'Small'} Age:{include_age})"
319
+ tags = f"team:{team_name},mode:beginner"
320
+
321
+ playground.submit_model(
322
+ model=tuned,
323
+ preprocessor=ct,
324
+ prediction_submission=preds,
325
+ input_dict={"description": desc, "tags": tags},
326
+ custom_metadata={"Team": team_name, "Beginner_Mode": 1}
327
+ )
328
+ log += "Submitted to leaderboard.\n"
329
+
330
+ team_df, indiv_df, feedback, latest_acc = summarize_leaderboard(team_name, username)
331
+ new_submissions = submissions + 1
332
+ state = compute_rank_state(new_submissions, model_name, complexity_level, size_choice, include_age)
333
+
334
+ reflection = (
335
+ f"### 🔍 What Just Happened?\n"
336
+ f"- You trained: {model_name}\n"
337
+ f"- Complexity setting: {complexity_level}\n"
338
+ f"- Data amount: {'All data' if frac==1.0 else 'Partial data'}\n"
339
+ f"- Optional Age included: {'Yes' if include_age else 'No'}\n\n"
340
+ f"Latest Accuracy: {latest_acc:.4f}\n"
341
+ f"Tip: Try changing one setting at a time to learn cause and effect."
342
+ )
343
+
344
+ return (
345
+ log,
346
+ reflection + "\n\n" + feedback,
347
+ team_df,
348
+ indiv_df,
349
+ latest_acc,
350
+ new_submissions,
351
+ state["rank_msg"],
352
+ gr.update(choices=state["models"], value=state["model_value"], interactive=state["model_interactive"]),
353
+ gr.update(minimum=1, maximum=state["complexity_max"], value=state["complexity_value"]),
354
+ gr.update(choices=state["size_choices"], value=state["size_value"]),
355
+ gr.update(interactive=state["age_enabled"], value=state["age_checked"])
356
+ )
357
+
358
+ except Exception as e:
359
+ err = f"ERROR: {e}"
360
+ state = compute_rank_state(submissions, model_name, complexity_level, size_choice, include_age)
361
+ return (
362
+ log + err,
363
+ err,
364
+ pd.DataFrame(),
365
+ pd.DataFrame(),
366
+ last_accuracy,
367
+ submissions,
368
+ state["rank_msg"],
369
+ gr.update(choices=state["models"], value=state["model_value"], interactive=state["model_interactive"]),
370
+ gr.update(minimum=1, maximum=state["complexity_max"], value=state["complexity_value"]),
371
+ gr.update(choices=state["size_choices"], value=state["size_value"]),
372
+ gr.update(interactive=state["age_enabled"], value=state["age_checked"])
373
+ )
374
+
375
+ def initial_load(username):
376
+ team_df, indiv_df, feedback, _ = summarize_leaderboard(CURRENT_TEAM_NAME, username)
377
+ state = compute_rank_state(0, DEFAULT_MODEL, 2, "Small (40%)", False)
378
+ return (
379
+ get_model_card(DEFAULT_MODEL),
380
+ team_df,
381
+ indiv_df,
382
+ state["rank_msg"],
383
+ gr.update(choices=state["models"], value=state["model_value"], interactive=state["model_interactive"]),
384
+ gr.update(minimum=1, maximum=state["complexity_max"], value=state["complexity_value"]),
385
+ gr.update(choices=state["size_choices"], value=state["size_value"]),
386
+ gr.update(interactive=state["age_enabled"], value=state["age_checked"])
387
+ )
388
+
389
+ # ---------------------------------------------------------------------
390
+ # Gradio App Factory
391
+ # ---------------------------------------------------------------------
392
+ def create_model_building_game_beginner_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
393
+ css = """
394
+ .panel {
395
+ background:#fff;
396
+ padding:18px;
397
+ border-radius:16px;
398
+ border:2px solid #e5e7eb;
399
+ margin-bottom:16px;
400
+ }
401
+ .highlight-box {
402
+ background:#e0f2fe;
403
+ padding:18px;
404
+ border-radius:12px;
405
+ border:2px solid #0284c7;
406
+ }
407
+ .log-box textarea {
408
+ font-family: monospace !important;
409
+ font-size: 13px !important;
410
+ }
411
+ """
412
+
413
+ with gr.Blocks(theme=gr.themes.Soft(primary_hue=theme_primary_hue), css=css) as demo:
414
+ username = os.environ.get("username")
415
+ # Loading screen
416
+ with gr.Column(visible=False) as loading_screen:
417
+ gr.Markdown(
418
+ """
419
+ <div style='text-align:center; padding:80px 0;'>
420
+ <h2 style='color:#6b7280;'>⏳ Loading...</h2>
421
+ </div>
422
+ """
423
+ )
424
+
425
+ # Step 1: Intro
426
+ with gr.Column(visible=True) as step_1:
427
+ gr.Markdown("<h1 style='text-align:center;'>🎯 Beginner Mode: Your First AI Experiments</h1>")
428
+ gr.HTML(
429
+ f"""
430
+ <div class='highlight-box'>
431
+ <p><b>Welcome!</b> You joined <b>Team: {CURRENT_TEAM_NAME}</b>.</p>
432
+ <p>This simplified mode helps you learn the experiment loop step-by-step.</p>
433
+ <ul style='font-size:16px;'>
434
+ <li>Pick a model strategy</li>
435
+ <li>Set complexity</li>
436
+ <li>Choose how much data to use</li>
437
+ <li>Submit & observe the leaderboard</li>
438
+ </ul>
439
+ <p style='margin-top:10px;'>You will unlock more tools by submitting models.</p>
440
+ </div>
441
+ """
442
+ )
443
+ step_1_next = gr.Button("Start Building ▶️", variant="primary", size="lg")
444
+
445
+ # Step 2: Main Workspace
446
+ with gr.Column(visible=False) as step_2:
447
+ gr.Markdown("<h1 style='text-align:center;'>🛠️ Build a Model</h1>")
448
+ rank_message_display = gr.Markdown("Rank loading...")
449
+
450
+ # Hidden states (buffer all dynamic inputs)
451
+ team_state = gr.State(CURRENT_TEAM_NAME)
452
+ last_acc_state = gr.State(0.0)
453
+ submissions_state = gr.State(0)
454
+
455
+ model_state = gr.State(DEFAULT_MODEL)
456
+ complexity_state = gr.State(2)
457
+ size_state = gr.State("Small (40%)")
458
+ age_state = gr.State(False)
459
+
460
+ with gr.Row():
461
+ with gr.Column(scale=1):
462
+ with gr.Group():
463
+ gr.Markdown("### 1️⃣ Choose Model")
464
+ model_radio = gr.Radio(label="Model", choices=[], interactive=False, value=None)
465
+ model_card = gr.Markdown(get_model_card(DEFAULT_MODEL))
466
+
467
+ with gr.Group():
468
+ gr.Markdown("### 2️⃣ Set Complexity")
469
+ complexity_slider = gr.Slider(
470
+ minimum=1, maximum=2, step=1, value=2,
471
+ label="Complexity",
472
+ info="Higher = deeper patterns, but risk of overfitting."
473
+ )
474
+
475
+ with gr.Group():
476
+ gr.Markdown("### 3️⃣ Data Size")
477
+ size_radio = gr.Radio(
478
+ choices=["Small (40%)", "Full (100%)"],
479
+ value="Small (40%)",
480
+ label="Training Data Amount"
481
+ )
482
+
483
+ with gr.Group():
484
+ gr.Markdown("### 4️⃣ Optional Feature (Ethics)")
485
+ age_checkbox = gr.Checkbox(label="Include Age Feature", value=False, interactive=False)
486
+ gr.Markdown(
487
+ "> Age can improve accuracy but raises fairness concerns. Use thoughtfully."
488
+ )
489
+
490
+ with gr.Group():
491
+ gr.Markdown("### 5️⃣ Submit")
492
+ submit_btn = gr.Button("🔬 Train & Submit", variant="primary")
493
+ experiment_log = gr.Textbox(
494
+ label="Run Log",
495
+ lines=10,
496
+ interactive=False,
497
+ elem_classes=["log-box"],
498
+ placeholder="Your experiment steps will appear here..."
499
+ )
500
+ show_details = gr.Checkbox(label="Show Technical Details", value=False)
501
+ details_box = gr.Markdown(visible=False)
502
+
503
+ with gr.Column(scale=1):
504
+ gr.Markdown("### 📊 Feedback & Leaderboards")
505
+ feedback_md = gr.Markdown("Submit a model to see feedback.")
506
+ team_table = gr.DataFrame(value=pd.DataFrame(), label="Team Summary", interactive=False)
507
+ indiv_table = gr.DataFrame(value=pd.DataFrame(), label="Engineer Summary", interactive=False)
508
+ refresh_btn = gr.Button("🔄 Refresh Leaderboard")
509
+
510
+ step_2_next = gr.Button("Finish & Reflect ▶️", variant="secondary")
511
+
512
+ # Step 3: Completion
513
+ with gr.Column(visible=False) as step_3:
514
+ gr.Markdown("<h1 style='text-align:center;'>✅ Beginner Section Complete</h1>")
515
+ gr.HTML(
516
+ """
517
+ <div class='highlight-box'>
518
+ <p><b>Great job!</b> You now understand the core experiment loop:</p>
519
+ <ol style='font-size:16px;'>
520
+ <li>Pick a model strategy</li>
521
+ <li>Adjust complexity / data amount</li>
522
+ <li>Submit and observe accuracy</li>
523
+ <li>Iterate to improve</li>
524
+ </ol>
525
+ <p>Next: Try Advanced Mode or explore ethical trade-offs in later sections.</p>
526
+ <h2 style='text-align:center;'>👇 SCROLL DOWN FOR NEXT SECTION 👇</h2>
527
+ </div>
528
+ """
529
+ )
530
+ step_3_back = gr.Button("◀️ Back to Workspace")
531
+
532
+ # Navigation logic
533
+ all_steps = [step_1, step_2, step_3, loading_screen]
534
+
535
+ def nav(to_show, from_show):
536
+ def _go():
537
+ updates = {loading_screen: gr.update(visible=True)}
538
+ for s in all_steps:
539
+ if s != loading_screen:
540
+ updates[s] = gr.update(visible=False)
541
+ yield updates
542
+
543
+ updates = {to_show: gr.update(visible=True)}
544
+ for s in all_steps:
545
+ if s != to_show:
546
+ updates[s] = gr.update(visible=False)
547
+ yield updates
548
+ return _go
549
+
550
+ step_1_next.click(
551
+ fn=nav(step_2, step_1),
552
+ inputs=None,
553
+ outputs=all_steps,
554
+ show_progress="full",
555
+ js="()=>{window.scrollTo({top:0,behavior:'smooth'})}"
556
+ )
557
+ step_2_next.click(
558
+ fn=nav(step_3, step_2),
559
+ inputs=None,
560
+ outputs=all_steps,
561
+ show_progress="full",
562
+ js="()=>{window.scrollTo({top:0,behavior:'smooth'})}"
563
+ )
564
+ step_3_back.click(
565
+ fn=nav(step_2, step_3),
566
+ inputs=None,
567
+ outputs=all_steps,
568
+ show_progress="full",
569
+ js="()=>{window.scrollTo({top:0,behavior:'smooth'})}"
570
+ )
571
+
572
+ # Interactions
573
+
574
+ # Keep the model card in sync
575
+ model_radio.change(fn=get_model_card, inputs=model_radio, outputs=model_card)
576
+ # Mirror Radio into state (coerce None -> DEFAULT_MODEL)
577
+ model_radio.change(fn=lambda v: v or DEFAULT_MODEL, inputs=model_radio, outputs=model_state)
578
+
579
+ # Mirror Slider into state
580
+ complexity_slider.change(fn=lambda v: v, inputs=complexity_slider, outputs=complexity_state)
581
+ # Mirror size Radio into state (coerce None -> default)
582
+ size_radio.change(fn=lambda v: v or "Small (40%)", inputs=size_radio, outputs=size_state)
583
+ # Mirror Checkbox into state (coerce None -> False)
584
+ age_checkbox.change(fn=lambda v: bool(v), inputs=age_checkbox, outputs=age_state)
585
+
586
+ def toggle_details(show):
587
+ if not show:
588
+ return gr.update(visible=False, value="")
589
+ tech_md = """
590
+ ### 🧪 Technical Details
591
+ - Preprocessing: Numeric → Median Impute + StandardScaler; Categorical → Constant Impute + OneHot
592
+ - Metric: Accuracy (correct predictions / total)
593
+ - Models available: LogisticRegression, DecisionTree, KNN, RandomForest (phased unlock)
594
+ """
595
+ return gr.update(visible=True, value=tech_md)
596
+
597
+ show_details.change(toggle_details, show_details, details_box)
598
+
599
+ # Use **states** as inputs for submit
600
+ submit_btn.click(
601
+ fn=run_beginner_experiment,
602
+ inputs=[
603
+ model_state, # buffered radio
604
+ complexity_state, # buffered slider
605
+ size_state, # buffered radio
606
+ age_state, # buffered checkbox
607
+ team_state,
608
+ last_acc_state,
609
+ submissions_state,
610
+ gr.State(username)
611
+ ],
612
+ outputs=[
613
+ experiment_log,
614
+ feedback_md,
615
+ team_table,
616
+ indiv_table,
617
+ last_acc_state,
618
+ submissions_state,
619
+ rank_message_display,
620
+ model_radio,
621
+ complexity_slider,
622
+ size_radio,
623
+ age_checkbox
624
+ ],
625
+ show_progress="full",
626
+ js="()=>{window.scrollTo({top:0,behavior:'smooth'})}"
627
+ )
628
+
629
+ refresh_btn.click(
630
+ fn=lambda tm, u: summarize_leaderboard(tm, u)[:2],
631
+ inputs=[team_state, gr.State(username)],
632
+ outputs=[team_table, indiv_table]
633
+ )
634
+
635
+ # Initial load
636
+ demo.load(
637
+ fn=lambda u: initial_load(u),
638
+ inputs=[gr.State(username)],
639
+ outputs=[
640
+ model_card,
641
+ team_table,
642
+ indiv_table,
643
+ rank_message_display,
644
+ model_radio,
645
+ complexity_slider,
646
+ size_radio,
647
+ age_checkbox
648
+ ]
649
+ )
650
+
651
+ return demo
652
+
653
+ # ---------------------------------------------------------------------
654
+ # Launcher
655
+ # ---------------------------------------------------------------------
656
+ def launch_model_building_game_beginner_app(height: int = 1100, share: bool = False, debug: bool = False):
657
+ global playground, X_TRAIN_RAW, X_TEST_RAW, Y_TRAIN, Y_TEST
658
+ if playground is None:
659
+ try:
660
+ playground = Competition(MY_PLAYGROUND_ID)
661
+ except Exception as e:
662
+ print(f"WARNING: Could not connect to playground: {e}")
663
+ playground = None
664
+
665
+ if X_TRAIN_RAW is None:
666
+ X_TRAIN_RAW, X_TEST_RAW, Y_TRAIN, Y_TEST = load_and_prep_data()
667
+
668
+ app = create_model_building_game_beginner_app()
669
+ port = int(os.environ.get("PORT", 8080))
670
+ with contextlib.redirect_stdout(open(os.devnull, "w")), contextlib.redirect_stderr(open(os.devnull, "w")):
671
+ app.launch(share=share, inline=True, debug=debug, height=height, server_name="0.0.0.0", server_port=port)
672
+
673
+ # ---------------------------------------------------------------------
674
+ # Entrypoint
675
+ # ---------------------------------------------------------------------
676
+ if __name__ == "__main__":
677
+ print("Initializing Beginner Mode...")
678
+ try:
679
+ playground = Competition(MY_PLAYGROUND_ID)
680
+ print("Playground connected.")
681
+ except Exception as e:
682
+ print(f"Playground connection failed: {e}")
683
+ playground = None
684
+
685
+ X_TRAIN_RAW, X_TEST_RAW, Y_TRAIN, Y_TEST = load_and_prep_data()
686
+ print("Launching Beginner Mode App...")
687
+ create_model_building_game_beginner_app().launch(share=False,debug=True)