sairaj2 commited on
Commit
6c3d963
·
verified ·
1 Parent(s): d137754

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. server/app.py +5 -1
  2. server/environment.py +5 -3
  3. server/static/index.html +908 -0
server/app.py CHANGED
@@ -12,7 +12,7 @@ import sys, os, uuid, logging, dataclasses, enum, time, threading
12
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13
 
14
  from fastapi import FastAPI, HTTPException, Header, Request
15
- from fastapi.responses import JSONResponse, RedirectResponse, HTMLResponse, Response
16
  from fastapi.middleware.cors import CORSMiddleware
17
  from contextlib import asynccontextmanager
18
  from typing import Dict, Any, Optional, List
@@ -1399,6 +1399,10 @@ async def schema():
1399
  },
1400
  }
1401
 
 
 
 
 
1402
  @app.get("/datasets", tags=["Info"])
1403
  async def datasets():
1404
  try: return {"total_examples": _get_default_env().dataset_loader.get_total_examples()}
 
12
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13
 
14
  from fastapi import FastAPI, HTTPException, Header, Request
15
+ from fastapi.responses import JSONResponse, RedirectResponse, HTMLResponse, Response, FileResponse
16
  from fastapi.middleware.cors import CORSMiddleware
17
  from contextlib import asynccontextmanager
18
  from typing import Dict, Any, Optional, List
 
1399
  },
1400
  }
1401
 
1402
+ @app.get("/web", include_in_schema=False)
1403
+ async def web():
1404
+ return FileResponse("server/static/index.html")
1405
+
1406
  @app.get("/datasets", tags=["Info"])
1407
  async def datasets():
1408
  try: return {"total_examples": _get_default_env().dataset_loader.get_total_examples()}
server/environment.py CHANGED
@@ -88,7 +88,8 @@ class DataCleaningEnvironment(Environment[DataCleaningAction, DataCleaningObserv
88
  transform=None,
89
  config: Optional[EnvironmentConfig] = None,
90
  session_id: Optional[str] = None,
91
- dataset_generator: Optional["DatasetGenerator"] = None
 
92
  ):
93
  super().__init__(transform=transform)
94
 
@@ -96,8 +97,9 @@ class DataCleaningEnvironment(Environment[DataCleaningAction, DataCleaningObserv
96
  self.config = config or EnvironmentConfig()
97
  self.session_id = session_id or str(uuid.uuid4())[:8]
98
 
99
- # Dataset management
100
- self.dataset_generator = dataset_generator or DatasetGenerator()
 
101
 
102
  # Episode state
103
  self.episode_id: Optional[str] = None
 
88
  transform=None,
89
  config: Optional[EnvironmentConfig] = None,
90
  session_id: Optional[str] = None,
91
+ dataset_generator: Optional["DatasetGenerator"] = None,
92
+ dataset_loader: Optional["DatasetGenerator"] = None
93
  ):
94
  super().__init__(transform=transform)
95
 
 
97
  self.config = config or EnvironmentConfig()
98
  self.session_id = session_id or str(uuid.uuid4())[:8]
99
 
100
+ # Dataset management (support both parameter names for backwards compatibility)
101
+ self.dataset_generator = dataset_generator or dataset_loader or DatasetGenerator()
102
+ self.dataset_loader = self.dataset_generator
103
 
104
  # Episode state
105
  self.episode_id: Optional[str] = None
server/static/index.html ADDED
@@ -0,0 +1,908 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>OpenEnv Data Cleaner</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ :root {
15
+ --primary: #6366f1;
16
+ --primary-dark: #4f46e5;
17
+ --success: #22c55e;
18
+ --warning: #f59e0b;
19
+ --danger: #ef4444;
20
+ --bg: #0f172a;
21
+ --bg-card: #1e293b;
22
+ --bg-input: #334155;
23
+ --text: #f1f5f9;
24
+ --text-muted: #94a3b8;
25
+ --border: #475569;
26
+ }
27
+
28
+ body {
29
+ font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
30
+ background: var(--bg);
31
+ color: var(--text);
32
+ min-height: 100vh;
33
+ }
34
+
35
+ .container {
36
+ max-width: 1400px;
37
+ margin: 0 auto;
38
+ padding: 20px;
39
+ }
40
+
41
+ header {
42
+ text-align: center;
43
+ padding: 30px 0;
44
+ border-bottom: 1px solid var(--border);
45
+ margin-bottom: 30px;
46
+ }
47
+
48
+ header h1 {
49
+ font-size: 2.5rem;
50
+ background: linear-gradient(135deg, var(--primary), #a855f7);
51
+ -webkit-background-clip: text;
52
+ -webkit-text-fill-color: transparent;
53
+ margin-bottom: 10px;
54
+ }
55
+
56
+ header p {
57
+ color: var(--text-muted);
58
+ font-size: 1.1rem;
59
+ }
60
+
61
+ .status-bar {
62
+ display: flex;
63
+ gap: 20px;
64
+ justify-content: center;
65
+ flex-wrap: wrap;
66
+ margin-bottom: 30px;
67
+ }
68
+
69
+ .status-item {
70
+ background: var(--bg-card);
71
+ padding: 15px 25px;
72
+ border-radius: 12px;
73
+ border: 1px solid var(--border);
74
+ min-width: 150px;
75
+ text-align: center;
76
+ }
77
+
78
+ .status-item .label {
79
+ font-size: 0.8rem;
80
+ color: var(--text-muted);
81
+ text-transform: uppercase;
82
+ letter-spacing: 1px;
83
+ }
84
+
85
+ .status-item .value {
86
+ font-size: 1.3rem;
87
+ font-weight: 600;
88
+ margin-top: 5px;
89
+ }
90
+
91
+ .status-item .value.healthy { color: var(--success); }
92
+ .status-item .value.warning { color: var(--warning); }
93
+ .status-item .value.error { color: var(--danger); }
94
+
95
+ .grid {
96
+ display: grid;
97
+ grid-template-columns: 300px 1fr;
98
+ gap: 30px;
99
+ }
100
+
101
+ @media (max-width: 900px) {
102
+ .grid {
103
+ grid-template-columns: 1fr;
104
+ }
105
+ }
106
+
107
+ .sidebar {
108
+ display: flex;
109
+ flex-direction: column;
110
+ gap: 20px;
111
+ }
112
+
113
+ .card {
114
+ background: var(--bg-card);
115
+ border-radius: 16px;
116
+ padding: 24px;
117
+ border: 1px solid var(--border);
118
+ }
119
+
120
+ .card h3 {
121
+ font-size: 1.1rem;
122
+ margin-bottom: 16px;
123
+ color: var(--text);
124
+ display: flex;
125
+ align-items: center;
126
+ gap: 10px;
127
+ }
128
+
129
+ .card h3 .icon {
130
+ font-size: 1.3rem;
131
+ }
132
+
133
+ select, button {
134
+ width: 100%;
135
+ padding: 12px 16px;
136
+ border-radius: 10px;
137
+ border: 1px solid var(--border);
138
+ font-size: 0.95rem;
139
+ cursor: pointer;
140
+ transition: all 0.2s;
141
+ }
142
+
143
+ select {
144
+ background: var(--bg-input);
145
+ color: var(--text);
146
+ margin-bottom: 12px;
147
+ }
148
+
149
+ select:focus {
150
+ outline: none;
151
+ border-color: var(--primary);
152
+ }
153
+
154
+ button {
155
+ font-weight: 600;
156
+ display: flex;
157
+ align-items: center;
158
+ justify-content: center;
159
+ gap: 8px;
160
+ }
161
+
162
+ .btn-primary {
163
+ background: var(--primary);
164
+ color: white;
165
+ border: none;
166
+ }
167
+
168
+ .btn-primary:hover {
169
+ background: var(--primary-dark);
170
+ }
171
+
172
+ .btn-success {
173
+ background: var(--success);
174
+ color: white;
175
+ border: none;
176
+ }
177
+
178
+ .btn-success:hover {
179
+ background: #16a34a;
180
+ }
181
+
182
+ .btn-warning {
183
+ background: var(--warning);
184
+ color: white;
185
+ border: none;
186
+ }
187
+
188
+ .btn-warning:hover {
189
+ background: #d97706;
190
+ }
191
+
192
+ .btn-danger {
193
+ background: var(--danger);
194
+ color: white;
195
+ border: none;
196
+ }
197
+
198
+ .btn-danger:hover {
199
+ background: #dc2626;
200
+ }
201
+
202
+ .btn-secondary {
203
+ background: var(--bg-input);
204
+ color: var(--text);
205
+ }
206
+
207
+ .btn-secondary:hover {
208
+ background: var(--border);
209
+ }
210
+
211
+ .action-list {
212
+ display: flex;
213
+ flex-direction: column;
214
+ gap: 8px;
215
+ }
216
+
217
+ .action-btn {
218
+ background: var(--bg-input);
219
+ border: 1px solid var(--border);
220
+ color: var(--text);
221
+ padding: 10px 14px;
222
+ border-radius: 8px;
223
+ cursor: pointer;
224
+ transition: all 0.2s;
225
+ text-align: left;
226
+ font-size: 0.9rem;
227
+ }
228
+
229
+ .action-btn:hover {
230
+ background: var(--primary);
231
+ border-color: var(--primary);
232
+ }
233
+
234
+ .action-btn:disabled {
235
+ opacity: 0.5;
236
+ cursor: not-allowed;
237
+ }
238
+
239
+ .action-btn:disabled:hover {
240
+ background: var(--bg-input);
241
+ border-color: var(--border);
242
+ }
243
+
244
+ .dataset-table {
245
+ width: 100%;
246
+ border-collapse: collapse;
247
+ margin-top: 16px;
248
+ }
249
+
250
+ .dataset-table th,
251
+ .dataset-table td {
252
+ padding: 10px 14px;
253
+ text-align: left;
254
+ border-bottom: 1px solid var(--border);
255
+ }
256
+
257
+ .dataset-table th {
258
+ background: var(--bg-input);
259
+ font-weight: 600;
260
+ font-size: 0.85rem;
261
+ text-transform: uppercase;
262
+ letter-spacing: 0.5px;
263
+ }
264
+
265
+ .dataset-table tr:hover {
266
+ background: rgba(99, 102, 241, 0.1);
267
+ }
268
+
269
+ .null-count {
270
+ color: var(--warning);
271
+ font-weight: 600;
272
+ }
273
+
274
+ .log-container {
275
+ background: var(--bg-input);
276
+ border-radius: 10px;
277
+ padding: 16px;
278
+ max-height: 300px;
279
+ overflow-y: auto;
280
+ font-family: 'Courier New', monospace;
281
+ font-size: 0.85rem;
282
+ }
283
+
284
+ .log-entry {
285
+ padding: 4px 0;
286
+ border-bottom: 1px solid rgba(71, 85, 105, 0.3);
287
+ }
288
+
289
+ .log-entry:last-child {
290
+ border-bottom: none;
291
+ }
292
+
293
+ .log-entry .timestamp {
294
+ color: var(--text-muted);
295
+ margin-right: 10px;
296
+ }
297
+
298
+ .log-entry .action {
299
+ color: var(--primary);
300
+ font-weight: 600;
301
+ }
302
+
303
+ .log-entry .reward {
304
+ color: var(--success);
305
+ }
306
+
307
+ .log-entry .error {
308
+ color: var(--danger);
309
+ }
310
+
311
+ .param-form {
312
+ display: flex;
313
+ flex-direction: column;
314
+ gap: 10px;
315
+ margin-top: 12px;
316
+ }
317
+
318
+ .param-form label {
319
+ font-size: 0.85rem;
320
+ color: var(--text-muted);
321
+ }
322
+
323
+ .param-form input,
324
+ .param-form select {
325
+ background: var(--bg-input);
326
+ border: 1px solid var(--border);
327
+ color: var(--text);
328
+ padding: 8px 12px;
329
+ border-radius: 6px;
330
+ }
331
+
332
+ .param-form input:focus,
333
+ .param-form select:focus {
334
+ outline: none;
335
+ border-color: var(--primary);
336
+ }
337
+
338
+ .modal-overlay {
339
+ position: fixed;
340
+ top: 0;
341
+ left: 0;
342
+ right: 0;
343
+ bottom: 0;
344
+ background: rgba(0, 0, 0, 0.7);
345
+ display: flex;
346
+ align-items: center;
347
+ justify-content: center;
348
+ z-index: 1000;
349
+ }
350
+
351
+ .modal {
352
+ background: var(--bg-card);
353
+ border-radius: 16px;
354
+ padding: 30px;
355
+ max-width: 500px;
356
+ width: 90%;
357
+ border: 1px solid var(--border);
358
+ }
359
+
360
+ .modal h3 {
361
+ margin-bottom: 20px;
362
+ }
363
+
364
+ .modal-actions {
365
+ display: flex;
366
+ gap: 10px;
367
+ margin-top: 20px;
368
+ }
369
+
370
+ .modal-actions button {
371
+ flex: 1;
372
+ }
373
+
374
+ .score-display {
375
+ text-align: center;
376
+ padding: 20px;
377
+ }
378
+
379
+ .score-display .score {
380
+ font-size: 3rem;
381
+ font-weight: 700;
382
+ color: var(--primary);
383
+ }
384
+
385
+ .score-display .label {
386
+ color: var(--text-muted);
387
+ margin-top: 10px;
388
+ }
389
+
390
+ .hidden {
391
+ display: none;
392
+ }
393
+
394
+ .loading {
395
+ display: inline-block;
396
+ width: 20px;
397
+ height: 20px;
398
+ border: 2px solid var(--text-muted);
399
+ border-top-color: var(--primary);
400
+ border-radius: 50%;
401
+ animation: spin 0.8s linear infinite;
402
+ }
403
+
404
+ @keyframes spin {
405
+ to { transform: rotate(360deg); }
406
+ }
407
+
408
+ .task-info {
409
+ font-size: 0.9rem;
410
+ color: var(--text-muted);
411
+ margin-bottom: 16px;
412
+ }
413
+
414
+ .task-info .difficulty {
415
+ display: inline-block;
416
+ padding: 2px 8px;
417
+ border-radius: 4px;
418
+ font-size: 0.75rem;
419
+ font-weight: 600;
420
+ text-transform: uppercase;
421
+ }
422
+
423
+ .difficulty.easy { background: var(--success); color: white; }
424
+ .difficulty.medium { background: var(--warning); color: white; }
425
+ .difficulty.hard { background: var(--danger); color: white; }
426
+ </style>
427
+ </head>
428
+ <body>
429
+ <div class="container">
430
+ <header>
431
+ <h1>OpenEnv Data Cleaner</h1>
432
+ <p>AI-powered data cleaning environment with reinforcement learning</p>
433
+ </header>
434
+
435
+ <div class="status-bar">
436
+ <div class="status-item">
437
+ <div class="label">Status</div>
438
+ <div class="value" id="status-value">-</div>
439
+ </div>
440
+ <div class="status-item">
441
+ <div class="label">Task</div>
442
+ <div class="value" id="task-value">-</div>
443
+ </div>
444
+ <div class="status-item">
445
+ <div class="label">Steps</div>
446
+ <div class="value" id="step-value">0</div>
447
+ </div>
448
+ <div class="status-item">
449
+ <div class="label">Reward</div>
450
+ <div class="value" id="reward-value">0.00</div>
451
+ </div>
452
+ <div class="status-item">
453
+ <div class="label">Score</div>
454
+ <div class="value" id="score-value">-</div>
455
+ </div>
456
+ </div>
457
+
458
+ <div class="grid">
459
+ <div class="sidebar">
460
+ <div class="card">
461
+ <h3><span class="icon">📋</span> Task Selection</h3>
462
+ <select id="task-select">
463
+ <option value="employee_demo">📊 Employee Dataset - Demo</option>
464
+ <option value="easy_001">Easy - Basic Cleaning</option>
465
+ <option value="medium_001">Medium - Intermediate</option>
466
+ <option value="hard_001">Hard - Advanced Pipeline</option>
467
+ </select>
468
+ <button class="btn-primary" onclick="resetEnvironment()">
469
+ <span>🔄</span> Start New Task
470
+ </button>
471
+ <div class="task-info" id="task-info"></div>
472
+ </div>
473
+
474
+ <div class="card">
475
+ <h3><span class="icon">🔧</span> Actions</h3>
476
+ <div class="action-list" id="action-list">
477
+ <button class="action-btn" onclick="showActionModal('drop_nulls')">Drop Nulls</button>
478
+ <button class="action-btn" onclick="showActionModal('fill_nulls')">Fill Nulls</button>
479
+ <button class="action-btn" onclick="showActionModal('remove_duplicates')">Remove Duplicates</button>
480
+ <button class="action-btn" onclick="showActionModal('filter_rows')">Filter Rows</button>
481
+ <button class="action-btn" onclick="showActionModal('drop_columns')">Drop Columns</button>
482
+ <button class="action-btn" onclick="showActionModal('convert_types')">Convert Types</button>
483
+ <button class="action-btn" onclick="showActionModal('validate_email')">Validate Email</button>
484
+ <button class="action-btn" onclick="showActionModal('outlier_removal')">Outlier Removal</button>
485
+ <button class="action-btn" onclick="showActionModal('normalize')">Normalize</button>
486
+ </div>
487
+ <div style="margin-top: 16px; display: flex; flex-direction: column; gap: 8px;">
488
+ <button class="btn-warning" onclick="revertAction()">↩️ Revert Last</button>
489
+ <button class="btn-success" onclick="submitSolution()">✅ Submit Solution</button>
490
+ </div>
491
+ </div>
492
+ </div>
493
+
494
+ <div class="main-content">
495
+ <div class="card">
496
+ <h3><span class="icon">📊</span> Dataset Information</h3>
497
+ <div id="dataset-info">
498
+ <p style="color: var(--text-muted);">Start a task to view dataset information.</p>
499
+ </div>
500
+ <table class="dataset-table hidden" id="dataset-table">
501
+ <thead>
502
+ <tr>
503
+ <th>Column</th>
504
+ <th>Type</th>
505
+ <th>Null Count</th>
506
+ </tr>
507
+ </thead>
508
+ <tbody id="dataset-tbody"></tbody>
509
+ </table>
510
+ <button class="btn-secondary" onclick="showSampleData()" style="margin-top: 12px;">
511
+ <span>👁️</span> Preview Sample Data
512
+ </button>
513
+ </div>
514
+
515
+ <div class="card" style="margin-top: 20px;">
516
+ <h3><span class="icon">📝</span> Action Log</h3>
517
+ <div class="log-container" id="log-container">
518
+ <div class="log-entry">
519
+ <span class="timestamp">--:--:--</span>
520
+ <span>Waiting for task to start...</span>
521
+ </div>
522
+ </div>
523
+ </div>
524
+ </div>
525
+ </div>
526
+ </div>
527
+
528
+ <!-- Action Modal -->
529
+ <div class="modal-overlay hidden" id="action-modal">
530
+ <div class="modal">
531
+ <h3 id="modal-title">Action Parameters</h3>
532
+ <div class="param-form" id="param-form"></div>
533
+ <div class="modal-actions">
534
+ <button class="btn-secondary" onclick="closeModal()">Cancel</button>
535
+ <button class="btn-primary" onclick="executeAction()">Execute</button>
536
+ </div>
537
+ </div>
538
+ </div>
539
+
540
+ <!-- Score Modal -->
541
+ <div class="modal-overlay hidden" id="score-modal">
542
+ <div class="modal">
543
+ <div class="score-display">
544
+ <div class="label">Final Score</div>
545
+ <div class="score" id="final-score">0.00</div>
546
+ <div id="score-feedback" style="margin-top: 16px; color: var(--text-muted);"></div>
547
+ </div>
548
+ <div class="modal-actions">
549
+ <button class="btn-primary" onclick="closeScoreModal()">Close</button>
550
+ </div>
551
+ </div>
552
+ </div>
553
+
554
+ <script>
555
+ let currentAction = null;
556
+ let totalReward = 0;
557
+
558
+ // Initialize
559
+ document.addEventListener('DOMContentLoaded', () => {
560
+ loadTasks();
561
+ });
562
+
563
+ async function loadTasks() {
564
+ try {
565
+ const res = await fetch('/tasks');
566
+ const data = await res.json();
567
+ if (data.tasks?.length > 0) {
568
+ updateTaskInfo(data.tasks[0]);
569
+ }
570
+ } catch (e) {
571
+ console.error('Failed to load tasks:', e);
572
+ }
573
+ }
574
+
575
+ document.getElementById('task-select').addEventListener('change', (e) => {
576
+ fetch(`/tasks`).then(r => r.json()).then(data => {
577
+ const task = data.tasks.find(t => t.task_id === e.target.value);
578
+ updateTaskInfo(task);
579
+ });
580
+ });
581
+
582
+ function updateTaskInfo(task) {
583
+ if (!task) return;
584
+ const info = document.getElementById('task-info');
585
+ info.innerHTML = `
586
+ <span class="difficulty ${task.difficulty}">${task.difficulty}</span>
587
+ <p style="margin-top: 8px;">${task.description}</p>
588
+ <p style="margin-top: 8px;">Expected: ${(task.expected_actions || []).join(', ')}</p>
589
+ `;
590
+ }
591
+
592
+ async function resetEnvironment() {
593
+ const taskId = document.getElementById('task-select').value;
594
+ try {
595
+ addLog('Starting new task...', 'info');
596
+ const res = await fetch('/reset', {
597
+ method: 'POST',
598
+ headers: { 'Content-Type': 'application/json' },
599
+ body: JSON.stringify({ task_id: taskId })
600
+ });
601
+ const data = await res.json();
602
+
603
+ if (data.success || data.status === 'reset_complete') {
604
+ totalReward = 0;
605
+ updateStatus(data);
606
+ await loadDatasetInfo();
607
+ addLog(`Task '${taskId}' initialized`, 'success');
608
+ }
609
+ } catch (e) {
610
+ addLog(`Reset failed: ${e.message}`, 'error');
611
+ }
612
+ }
613
+
614
+ async function loadDatasetInfo() {
615
+ try {
616
+ const res = await fetch('/dataset');
617
+ const data = await res.json();
618
+
619
+ document.getElementById('task-value').textContent = data.task_id || '-';
620
+ document.getElementById('step-value').textContent = data.step_count || 0;
621
+
622
+ const tbody = document.getElementById('dataset-tbody');
623
+ tbody.innerHTML = '';
624
+
625
+ (data.columns || []).forEach(col => {
626
+ const tr = document.createElement('tr');
627
+ tr.innerHTML = `
628
+ <td>${col}</td>
629
+ <td>${data.dtypes?.[col] || '-'}</td>
630
+ <td class="null-count">${data.null_counts?.[col] || 0}</td>
631
+ `;
632
+ tbody.appendChild(tr);
633
+ });
634
+
635
+ document.getElementById('dataset-table').classList.remove('hidden');
636
+ document.getElementById('dataset-info').innerHTML = `
637
+ <p>Shape: ${data.shape?.[0] || 0} rows × ${data.shape?.[1] || 0} columns</p>
638
+ `;
639
+ } catch (e) {
640
+ console.error('Failed to load dataset info:', e);
641
+ }
642
+ }
643
+
644
+ function showActionModal(actionType) {
645
+ currentAction = actionType;
646
+ const modal = document.getElementById('action-modal');
647
+ const title = document.getElementById('modal-title');
648
+ const form = document.getElementById('param-form');
649
+
650
+ title.textContent = actionType.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
651
+
652
+ let paramsHtml = '';
653
+ switch (actionType) {
654
+ case 'drop_nulls':
655
+ paramsHtml = '<label>Column (optional, leave empty for all)</label><input type="text" id="param-column" placeholder="Column name">';
656
+ break;
657
+ case 'fill_nulls':
658
+ paramsHtml = `
659
+ <label>Column (optional)</label><input type="text" id="param-column" placeholder="Column name">
660
+ <label>Strategy</label>
661
+ <select id="param-strategy">
662
+ <option value="mean">Mean</option>
663
+ <option value="median">Median</option>
664
+ <option value="mode">Mode</option>
665
+ <option value="forward_fill">Forward Fill</option>
666
+ <option value="backward_fill">Backward Fill</option>
667
+ </select>
668
+ `;
669
+ break;
670
+ case 'remove_duplicates':
671
+ paramsHtml = '<label>Columns (optional, comma-separated)</label><input type="text" id="param-columns" placeholder="col1, col2">';
672
+ break;
673
+ case 'filter_rows':
674
+ paramsHtml = `
675
+ <label>Column</label><input type="text" id="param-column" placeholder="Column name">
676
+ <label>Operator</label>
677
+ <select id="param-operator">
678
+ <option value="==">==</option>
679
+ <option value="!=">!=</option>
680
+ <option value=">">></option>
681
+ <option value="<"><</option>
682
+ <option value=">=">>=</option>
683
+ <option value="<="><=</option>
684
+ <option value="contains">contains</option>
685
+ </select>
686
+ <label>Value</label><input type="text" id="param-value" placeholder="Value">
687
+ `;
688
+ break;
689
+ case 'drop_columns':
690
+ paramsHtml = '<label>Columns (comma-separated)</label><input type="text" id="param-columns" placeholder="col1, col2">';
691
+ break;
692
+ case 'convert_types':
693
+ paramsHtml = `
694
+ <label>Column</label><input type="text" id="param-column" placeholder="Column name">
695
+ <label>Data Type</label>
696
+ <select id="param-dtype">
697
+ <option value="str">String</option>
698
+ <option value="int">Integer</option>
699
+ <option value="float">Float</option>
700
+ <option value="datetime">Datetime</option>
701
+ </select>
702
+ `;
703
+ break;
704
+ case 'validate_email':
705
+ paramsHtml = `
706
+ <label>Column</label><input type="text" id="param-column" value="email" placeholder="Column name">
707
+ <label><input type="checkbox" id="param-drop_invalid"> Drop invalid emails</label>
708
+ `;
709
+ break;
710
+ case 'outlier_removal':
711
+ paramsHtml = `
712
+ <label>Column</label><input type="text" id="param-column" placeholder="Column name">
713
+ <label>IQR Multiplier</label><input type="number" id="param-multiplier" value="1.5" step="0.1">
714
+ `;
715
+ break;
716
+ case 'normalize':
717
+ paramsHtml = `
718
+ <label>Column</label><input type="text" id="param-column" placeholder="Column name">
719
+ <label>Method</label>
720
+ <select id="param-method">
721
+ <option value="minmax">Min-Max</option>
722
+ <option value="zscore">Z-Score</option>
723
+ </select>
724
+ `;
725
+ break;
726
+ }
727
+
728
+ form.innerHTML = paramsHtml;
729
+ modal.classList.remove('hidden');
730
+ }
731
+
732
+ function closeModal() {
733
+ document.getElementById('action-modal').classList.add('hidden');
734
+ currentAction = null;
735
+ }
736
+
737
+ async function executeAction() {
738
+ if (!currentAction) return;
739
+
740
+ const params = {};
741
+ const column = document.getElementById('param-column')?.value;
742
+ if (column) params.column = column;
743
+
744
+ const strategy = document.getElementById('param-strategy')?.value;
745
+ if (strategy) params.strategy = strategy;
746
+
747
+ const columns = document.getElementById('param-columns')?.value;
748
+ if (columns) params.columns = columns.split(',').map(c => c.trim());
749
+
750
+ const operator = document.getElementById('param-operator')?.value;
751
+ if (operator) params.operator = operator;
752
+
753
+ const value = document.getElementById('param-value')?.value;
754
+ if (value) params.value = value;
755
+
756
+ const dtype = document.getElementById('param-dtype')?.value;
757
+ if (dtype) params.dtype = dtype;
758
+
759
+ const multiplier = document.getElementById('param-multiplier')?.value;
760
+ if (multiplier) params.multiplier = parseFloat(multiplier);
761
+
762
+ const method = document.getElementById('param-method')?.value;
763
+ if (method) params.method = method;
764
+
765
+ const dropInvalid = document.getElementById('param-drop_invalid')?.checked;
766
+ if (dropInvalid) params.drop_invalid = true;
767
+
768
+ try {
769
+ addLog(`Executing: ${currentAction}...`, 'info');
770
+ const res = await fetch('/step', {
771
+ method: 'POST',
772
+ headers: { 'Content-Type': 'application/json' },
773
+ body: JSON.stringify({ action_type: currentAction, params })
774
+ });
775
+ const data = await res.json();
776
+
777
+ if (data.success || data.status === 'success') {
778
+ totalReward += (data.reward || data.data?.reward || 0);
779
+ document.getElementById('reward-value').textContent = totalReward.toFixed(4);
780
+ document.getElementById('step-value').textContent = (data.observation?.step_count || data.data?.observation?.step_count || 0);
781
+ addLog(`${currentAction}: reward=${(data.reward || data.data?.reward || 0).toFixed(4)}`, 'success');
782
+ await loadDatasetInfo();
783
+ } else {
784
+ addLog(`${currentAction} failed`, 'error');
785
+ }
786
+ } catch (e) {
787
+ addLog(`Error: ${e.message}`, 'error');
788
+ }
789
+
790
+ closeModal();
791
+ }
792
+
793
+ async function revertAction() {
794
+ try {
795
+ const res = await fetch('/revert', { method: 'POST' });
796
+ const data = await res.json();
797
+ addLog('Last action reverted', 'warning');
798
+ await loadDatasetInfo();
799
+ } catch (e) {
800
+ addLog(`Revert failed: ${e.message}`, 'error');
801
+ }
802
+ }
803
+
804
+ async function submitSolution() {
805
+ try {
806
+ addLog('Submitting solution...', 'info');
807
+ const res = await fetch('/submit', { method: 'POST' });
808
+ const data = await res.json();
809
+
810
+ const score = data.final_score || data.grade?.final_score || 0;
811
+ const feedback = data.grade?.feedback || '';
812
+ document.getElementById('final-score').textContent = score.toFixed(2);
813
+ document.getElementById('score-feedback').textContent = feedback;
814
+ document.getElementById('score-value').textContent = score.toFixed(2);
815
+ document.getElementById('score-modal').classList.remove('hidden');
816
+ addLog(`Submitted! Score: ${score.toFixed(2)}`, 'success');
817
+ } catch (e) {
818
+ addLog(`Submit failed: ${e.message}`, 'error');
819
+ }
820
+ }
821
+
822
+ function closeScoreModal() {
823
+ document.getElementById('score-modal').classList.add('hidden');
824
+ }
825
+
826
+ function updateStatus(data) {
827
+ document.getElementById('status-value').textContent = 'Ready';
828
+ document.getElementById('status-value').className = 'value healthy';
829
+ document.getElementById('task-value').textContent = data.task_id || '-';
830
+ }
831
+
832
+ function addLog(message, type = 'info') {
833
+ const container = document.getElementById('log-container');
834
+ const time = new Date().toLocaleTimeString();
835
+ const entry = document.createElement('div');
836
+ entry.className = 'log-entry';
837
+
838
+ let typeClass = '';
839
+ if (type === 'success') typeClass = 'reward';
840
+ else if (type === 'error') typeClass = 'error';
841
+
842
+ entry.innerHTML = `<span class="timestamp">${time}</span><span class="${typeClass}">${message}</span>`;
843
+ container.insertBefore(entry, container.firstChild);
844
+
845
+ // Keep only last 50 entries
846
+ while (container.children.length > 50) {
847
+ container.removeChild(container.lastChild);
848
+ }
849
+ }
850
+
851
+ async function showSampleData() {
852
+ try {
853
+ const res = await fetch('/dataset');
854
+ const data = await res.json();
855
+
856
+ if (!data.columns || data.columns.length === 0) {
857
+ addLog('No dataset loaded. Start a task first.', 'warning');
858
+ return;
859
+ }
860
+
861
+ // Create sample data modal
862
+ const modal = document.createElement('div');
863
+ modal.className = 'modal-overlay';
864
+ modal.id = 'sample-data-modal';
865
+
866
+ let tableHtml = '<table class="dataset-table"><thead><tr>';
867
+ data.columns.forEach(col => {
868
+ tableHtml += `<th>${col}</th>`;
869
+ });
870
+ tableHtml += '</tr></thead><tbody>';
871
+
872
+ // Show first 5 rows as sample
873
+ const sampleRows = Math.min(5, data.shape?.[0] || 0);
874
+ for (let i = 0; i < sampleRows; i++) {
875
+ tableHtml += '<tr>';
876
+ data.columns.forEach(col => {
877
+ const nullCount = data.null_counts?.[col] || 0;
878
+ const isNull = nullCount > 0 && Math.random() < (nullCount / (data.shape?.[0] || 1));
879
+ tableHtml += `<td>${isNull ? '<span style="color: var(--warning);">NULL</span>' : `Sample ${col} ${i+1}`}</td>`;
880
+ });
881
+ tableHtml += '</tr>';
882
+ }
883
+ tableHtml += '</tbody></table>';
884
+
885
+ modal.innerHTML = `
886
+ <div class="modal" style="max-width: 800px;">
887
+ <h3>📊 Sample Data Preview</h3>
888
+ <p style="color: var(--text-muted); margin-bottom: 16px;">
889
+ Showing ${sampleRows} sample rows from ${data.shape?.[0] || 0} total rows
890
+ </p>
891
+ <div style="overflow-x: auto;">
892
+ ${tableHtml}
893
+ </div>
894
+ <div class="modal-actions">
895
+ <button class="btn-primary" onclick="document.getElementById('sample-data-modal').remove()">Close</button>
896
+ </div>
897
+ </div>
898
+ `;
899
+
900
+ document.body.appendChild(modal);
901
+ addLog('Sample data preview opened', 'info');
902
+ } catch (e) {
903
+ addLog(`Failed to load sample data: ${e.message}`, 'error');
904
+ }
905
+ }
906
+ </script>
907
+ </body>
908
+ </html>