muhalwan commited on
Commit
a0d554d
·
1 Parent(s): aa81953
Files changed (1) hide show
  1. app.py +189 -120
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import logging
2
  from typing import Optional, Tuple
3
 
@@ -180,76 +181,138 @@ def generate_predictions(
180
  ).mean() ** 0.5
181
  total_actual = courses_with_actual["actual_enrollment"].sum()
182
  total_predicted = courses_with_actual["predicted_enrollment"].sum()
 
 
 
183
 
184
- summary = f"""## {year} Semester {semester_name} - Validation Against Actual Data
185
-
186
- ### Historical Validation
187
- - **Status:** Comparing predictions against actual enrollment data
188
- - **Courses Validated:** {len(courses_with_actual)} courses
189
-
190
- ### Prediction Accuracy
191
- | Metric | Value |
192
- |--------|-------|
193
- | Prediction MAE | {comparison_mae:.2f} students |
194
- | Prediction RMSE | {comparison_rmse:.2f} students |
195
- | Overall Accuracy | {(1 - abs(total_predicted - total_actual) / total_actual) * 100:.1f}% |
196
-
197
- ### Enrollment Summary
198
- | Category | Actual | Predicted | Difference |
199
- |----------|--------|-----------|------------|
200
- | Total Students | {int(total_actual)} | {int(total_predicted)} | {int(total_predicted - total_actual):+d} |
201
- | Courses Analyzed | {len(courses_with_actual)} | {len(courses_with_actual)} | - |
202
-
203
- ### Model Baseline (Cross-Validation)
204
- | Metric | Value |
205
- |--------|-------|
206
- | Backtest MAE | {metrics["mae"]:.2f} students |
207
- | Backtest RMSE | {metrics["rmse"]:.2f} students |
208
-
209
- ### Recommendation Summary
210
- | Item | Value |
211
- |------|-------|
212
- | Courses Recommended to Open | {total_to_open} |
213
- | Total Quota Needed | {total_seats} seats |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  """
215
  else:
216
- summary = f"""## {year} Semester {semester_name}
217
-
218
- ### Limited Validation Data
219
- Data semester ada, tetapi tidak ditemukan mata kuliah pilihan yang cocok untuk perbandingan.
220
-
221
- ### Model Performance (Backtest)
222
- | Metric | Value |
223
- |--------|-------|
224
- | MAE | {metrics["mae"]:.2f} students |
225
- | RMSE | {metrics["rmse"]:.2f} students |
226
-
227
- ### Recommendations
228
- | Item | Value |
229
- |------|-------|
230
- | Courses to Open | {total_to_open} |
231
- | Total Seats | {total_seats} |
232
- | Estimated Total Students | {int(predictions["predicted_enrollment"].sum())} |
 
 
 
 
 
 
 
 
 
233
  """
234
  else:
235
- summary = f"""## {year} Semester {semester_name} - Future Prediction
236
-
237
- ### Forward-Looking Forecast
238
- - **Status:** No actual enrollment data available
239
- - **Type:** Predictive forecast based on historical trends
240
-
241
- ### Model Performance (Historical Backtest)
242
- | Metric | Value |
243
- |--------|-------|
244
- | MAE | {metrics["mae"]:.2f} students |
245
- | RMSE | {metrics["rmse"]:.2f} students |
246
-
247
- ### Forecast Summary
248
- | Category | Value |
249
- |----------|-------|
250
- | Courses to Open | {total_to_open} |
251
- | Total Seats | {total_seats} |
252
- | Estimated Total Students | {int(predictions["predicted_enrollment"].sum())} |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  """
254
 
255
  # Prepare all predictions display
@@ -265,26 +328,26 @@ Data semester ada, tetapi tidak ditemukan mata kuliah pilihan yang cocok untuk p
265
  ]
266
  ].copy()
267
  all_predictions_display.columns = [
268
- "Course Code",
269
- "Course Name",
270
- "Predicted",
271
- "Quota",
272
  "Status",
273
  "Confidence",
274
  "Strategy",
275
  ]
276
- all_predictions_display["Predicted"] = all_predictions_display[
277
- "Predicted"
278
- ].round(1)
279
- all_predictions_display["Quota"] = all_predictions_display["Quota"].astype(int)
280
 
281
  # Map status to plain text
282
  all_predictions_display["Status"] = all_predictions_display["Status"].map(
283
- {"BUKA": "OPEN", "TUTUP": "CLOSE"}
284
  )
285
 
286
  all_predictions_display = all_predictions_display.sort_values(
287
- "Predicted", ascending=False
288
  )
289
 
290
  # Prepare comparison table if actual data exists
@@ -339,27 +402,25 @@ Data semester ada, tetapi tidak ditemukan mata kuliah pilihan yang cocok untuk p
339
  ].copy()
340
 
341
  comparison_display.columns = [
342
- "Course Code",
343
- "Course Name",
344
- "Actual",
345
- "Predicted",
346
  "Error",
347
  "Abs Error",
348
- "Accuracy %",
349
  "Strategy",
350
  ]
351
 
352
- comparison_display["Actual"] = comparison_display["Actual"].astype(int)
353
- comparison_display["Predicted"] = comparison_display["Predicted"].round(
354
- 1
355
- )
356
  comparison_display["Error"] = comparison_display["Error"].round(1)
357
  comparison_display["Abs Error"] = comparison_display["Abs Error"].round(
358
  1
359
  )
360
- comparison_display["Accuracy %"] = comparison_display[
361
- "Accuracy %"
362
- ].round(1)
363
 
364
  comparison_display = comparison_display.sort_values(
365
  "Abs Error", ascending=False
@@ -404,25 +465,26 @@ def get_data_info() -> str:
404
  elective_courses = courses[courses["kategori_mk"] == "P"]
405
 
406
  info = f"""
407
- ## Dataset Information
408
-
409
- ### Course Catalog
410
- | Item | Value |
411
- |------|-------|
412
- | Total Courses | {len(courses)} |
413
- | Elective Courses | {len(elective_courses)} |
414
- | Mandatory Courses | {len(courses) - len(elective_courses)} |
415
-
416
- ### Student Population
417
- | Item | Value |
418
- |------|-------|
419
- | Years Available | {students["thn"].min()} - {students["thn"].max()} |
420
- | Total Records | {len(students)} |
421
-
422
- ### Data Source
423
- | Item | Value |
424
- |------|-------|
425
- | File | {_config.data.FILE_PATH} |
 
426
  """
427
  return info
428
 
@@ -439,16 +501,14 @@ if not init_success:
439
 
440
  # Create Gradio Interface
441
  with gr.Blocks(title="SKS Enrollment Predictor") as demo:
442
- # Clean header
443
  gr.Markdown("# Course Enrollment Predictor")
444
 
445
- with gr.Row(equal_height=True):
446
  # Left panel - Controls
447
- with gr.Column(scale=1, min_width=280):
448
- gr.Markdown("#### Semester Target")
449
-
450
  year_input = gr.Number(
451
- label="Year",
452
  value=2025,
453
  precision=0,
454
  minimum=2020,
@@ -467,19 +527,28 @@ with gr.Blocks(title="SKS Enrollment Predictor") as demo:
467
  size="lg",
468
  )
469
 
 
 
470
  # Data info section
471
  with gr.Accordion("Dataset Info", open=False):
472
- data_info_output = gr.Markdown()
473
  demo.load(fn=get_data_info, inputs=[], outputs=data_info_output)
474
 
475
  # Right panel - Results
476
  with gr.Column(scale=3):
477
- summary_output = gr.Markdown(
478
- value="Pilih tahun dan semester, lalu klik Generate Predictions"
 
 
 
 
 
479
  )
480
 
 
 
481
  # Predictions table
482
- gr.Markdown("#### Predictions")
483
  all_predictions_output = gr.Dataframe(
484
  label="",
485
  wrap=True,
@@ -487,9 +556,9 @@ with gr.Blocks(title="SKS Enrollment Predictor") as demo:
487
  )
488
 
489
  # Comparison section
490
- with gr.Accordion("Validation Results", open=False) as comparison_accordion:
491
  comparison_info = gr.Markdown(
492
- value="Validation data appears when actual enrollment is available",
493
  )
494
  comparison_output = gr.Dataframe(
495
  label="",
@@ -512,7 +581,7 @@ with gr.Blocks(title="SKS Enrollment Predictor") as demo:
512
  all_predictions,
513
  gr.update(open=True),
514
  gr.update(
515
- value=f"Validated against {len(comparison)} courses",
516
  ),
517
  gr.update(value=comparison),
518
  )
@@ -523,7 +592,7 @@ with gr.Blocks(title="SKS Enrollment Predictor") as demo:
523
  all_predictions,
524
  gr.update(open=False),
525
  gr.update(
526
- value="No validation data available for future predictions",
527
  ),
528
  gr.update(value=None),
529
  )
 
1
+ # Version: 3.1 - Dark theme UI with white text
2
  import logging
3
  from typing import Optional, Tuple
4
 
 
181
  ).mean() ** 0.5
182
  total_actual = courses_with_actual["actual_enrollment"].sum()
183
  total_predicted = courses_with_actual["predicted_enrollment"].sum()
184
+ accuracy_pct = (
185
+ 1 - abs(total_predicted - total_actual) / total_actual
186
+ ) * 100
187
 
188
+ diff_color = (
189
+ "#4ade80" if total_predicted - total_actual >= 0 else "#f87171"
190
+ )
191
+
192
+ summary = f"""
193
+ <div style="padding: 24px;">
194
+ <div style="margin-bottom: 24px;">
195
+ <h2 style="margin: 0 0 8px 0; color: #fff; font-size: 24px; font-weight: 600;">{year} Semester {semester_name}</h2>
196
+ <p style="color: #9ca3af; margin: 0; font-size: 14px;">Validasi prediksi terhadap data aktual</p>
197
+ </div>
198
+
199
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px;">
200
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #4ade80;">
201
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">Akurasi</div>
202
+ <div style="font-size: 28px; font-weight: 700; color: #4ade80;">{accuracy_pct:.1f}%</div>
203
+ </div>
204
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #60a5fa;">
205
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">MAE</div>
206
+ <div style="font-size: 28px; font-weight: 700; color: #60a5fa;">{comparison_mae:.2f}</div>
207
+ </div>
208
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #a78bfa;">
209
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">RMSE</div>
210
+ <div style="font-size: 28px; font-weight: 700; color: #a78bfa;">{comparison_rmse:.2f}</div>
211
+ </div>
212
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #fb923c;">
213
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">MK Divalidasi</div>
214
+ <div style="font-size: 28px; font-weight: 700; color: #fb923c;">{len(courses_with_actual)}</div>
215
+ </div>
216
+ </div>
217
+
218
+ <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;">
219
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px;">
220
+ <h4 style="margin: 0 0 16px 0; color: #fff; font-size: 14px; font-weight: 600;">Ringkasan Enrollment</h4>
221
+ <div style="display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #334155;">
222
+ <span style="color: #9ca3af;">Total Aktual</span>
223
+ <span style="font-weight: 600; color: #fff;">{int(total_actual)}</span>
224
+ </div>
225
+ <div style="display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #334155;">
226
+ <span style="color: #9ca3af;">Total Prediksi</span>
227
+ <span style="font-weight: 600; color: #fff;">{int(total_predicted)}</span>
228
+ </div>
229
+ <div style="display: flex; justify-content: space-between; padding: 12px 0;">
230
+ <span style="color: #9ca3af;">Selisih</span>
231
+ <span style="font-weight: 600; color: {diff_color};">{int(total_predicted - total_actual):+d}</span>
232
+ </div>
233
+ </div>
234
+
235
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px;">
236
+ <h4 style="margin: 0 0 16px 0; color: #fff; font-size: 14px; font-weight: 600;">Rekomendasi</h4>
237
+ <div style="display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #334155;">
238
+ <span style="color: #9ca3af;">MK Dibuka</span>
239
+ <span style="font-weight: 600; color: #fff;">{total_to_open}</span>
240
+ </div>
241
+ <div style="display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid #334155;">
242
+ <span style="color: #9ca3af;">Total Kuota</span>
243
+ <span style="font-weight: 600; color: #fff;">{total_seats}</span>
244
+ </div>
245
+ <div style="display: flex; justify-content: space-between; padding: 12px 0;">
246
+ <span style="color: #9ca3af;">Backtest MAE</span>
247
+ <span style="font-weight: 600; color: #fff;">{metrics["mae"]:.2f}</span>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ </div>
252
  """
253
  else:
254
+ summary = f"""
255
+ <div style="padding: 24px;">
256
+ <div style="margin-bottom: 24px;">
257
+ <h2 style="margin: 0 0 8px 0; color: #fff; font-size: 24px; font-weight: 600;">{year} Semester {semester_name}</h2>
258
+ <p style="color: #9ca3af; margin: 0; font-size: 14px;">Data semester ada, tetapi tidak ditemukan MK pilihan yang cocok</p>
259
+ </div>
260
+
261
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;">
262
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #60a5fa;">
263
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">MAE (Backtest)</div>
264
+ <div style="font-size: 28px; font-weight: 700; color: #60a5fa;">{metrics["mae"]:.2f}</div>
265
+ </div>
266
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #a78bfa;">
267
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">RMSE (Backtest)</div>
268
+ <div style="font-size: 28px; font-weight: 700; color: #a78bfa;">{metrics["rmse"]:.2f}</div>
269
+ </div>
270
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #4ade80;">
271
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">MK Dibuka</div>
272
+ <div style="font-size: 28px; font-weight: 700; color: #4ade80;">{total_to_open}</div>
273
+ </div>
274
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #fb923c;">
275
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">Total Kuota</div>
276
+ <div style="font-size: 28px; font-weight: 700; color: #fb923c;">{total_seats}</div>
277
+ </div>
278
+ </div>
279
+ </div>
280
  """
281
  else:
282
+ summary = f"""
283
+ <div style="padding: 24px;">
284
+ <div style="margin-bottom: 24px;">
285
+ <h2 style="margin: 0 0 8px 0; color: #fff; font-size: 24px; font-weight: 600;">{year} Semester {semester_name}</h2>
286
+ <p style="color: #9ca3af; margin: 0; font-size: 14px;">Prediksi masa depan berdasarkan tren historis</p>
287
+ </div>
288
+
289
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px;">
290
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #60a5fa;">
291
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">MAE (Backtest)</div>
292
+ <div style="font-size: 28px; font-weight: 700; color: #60a5fa;">{metrics["mae"]:.2f}</div>
293
+ </div>
294
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #a78bfa;">
295
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">RMSE (Backtest)</div>
296
+ <div style="font-size: 28px; font-weight: 700; color: #a78bfa;">{metrics["rmse"]:.2f}</div>
297
+ </div>
298
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #4ade80;">
299
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">MK Dibuka</div>
300
+ <div style="font-size: 28px; font-weight: 700; color: #4ade80;">{total_to_open}</div>
301
+ </div>
302
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; border-left: 4px solid #fb923c;">
303
+ <div style="font-size: 12px; color: #9ca3af; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;">Total Kuota</div>
304
+ <div style="font-size: 28px; font-weight: 700; color: #fb923c;">{total_seats}</div>
305
+ </div>
306
+ </div>
307
+
308
+ <div style="background: #1e293b; padding: 20px; border-radius: 12px; max-width: 300px;">
309
+ <h4 style="margin: 0 0 16px 0; color: #fff; font-size: 14px; font-weight: 600;">Estimasi Total</h4>
310
+ <div style="display: flex; justify-content: space-between; padding: 12px 0;">
311
+ <span style="color: #9ca3af;">Total Mahasiswa</span>
312
+ <span style="font-weight: 600; color: #fff;">{int(predictions["predicted_enrollment"].sum())}</span>
313
+ </div>
314
+ </div>
315
+ </div>
316
  """
317
 
318
  # Prepare all predictions display
 
328
  ]
329
  ].copy()
330
  all_predictions_display.columns = [
331
+ "Kode MK",
332
+ "Nama MK",
333
+ "Prediksi",
334
+ "Kuota",
335
  "Status",
336
  "Confidence",
337
  "Strategy",
338
  ]
339
+ all_predictions_display["Prediksi"] = all_predictions_display["Prediksi"].round(
340
+ 1
341
+ )
342
+ all_predictions_display["Kuota"] = all_predictions_display["Kuota"].astype(int)
343
 
344
  # Map status to plain text
345
  all_predictions_display["Status"] = all_predictions_display["Status"].map(
346
+ {"BUKA": "BUKA", "TUTUP": "TUTUP"}
347
  )
348
 
349
  all_predictions_display = all_predictions_display.sort_values(
350
+ "Prediksi", ascending=False
351
  )
352
 
353
  # Prepare comparison table if actual data exists
 
402
  ].copy()
403
 
404
  comparison_display.columns = [
405
+ "Kode MK",
406
+ "Nama MK",
407
+ "Aktual",
408
+ "Prediksi",
409
  "Error",
410
  "Abs Error",
411
+ "Akurasi %",
412
  "Strategy",
413
  ]
414
 
415
+ comparison_display["Aktual"] = comparison_display["Aktual"].astype(int)
416
+ comparison_display["Prediksi"] = comparison_display["Prediksi"].round(1)
 
 
417
  comparison_display["Error"] = comparison_display["Error"].round(1)
418
  comparison_display["Abs Error"] = comparison_display["Abs Error"].round(
419
  1
420
  )
421
+ comparison_display["Akurasi %"] = comparison_display["Akurasi %"].round(
422
+ 1
423
+ )
424
 
425
  comparison_display = comparison_display.sort_values(
426
  "Abs Error", ascending=False
 
465
  elective_courses = courses[courses["kategori_mk"] == "P"]
466
 
467
  info = f"""
468
+ <div style="padding: 8px 0;">
469
+ <div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px;">
470
+ <div style="background: #1e293b; padding: 16px; border-radius: 8px; text-align: center;">
471
+ <div style="font-size: 11px; color: #9ca3af; margin-bottom: 6px;">Total MK</div>
472
+ <div style="font-size: 20px; font-weight: 700; color: #fff;">{len(courses)}</div>
473
+ </div>
474
+ <div style="background: #1e293b; padding: 16px; border-radius: 8px; text-align: center;">
475
+ <div style="font-size: 11px; color: #9ca3af; margin-bottom: 6px;">MK Pilihan</div>
476
+ <div style="font-size: 20px; font-weight: 700; color: #4ade80;">{len(elective_courses)}</div>
477
+ </div>
478
+ <div style="background: #1e293b; padding: 16px; border-radius: 8px; text-align: center;">
479
+ <div style="font-size: 11px; color: #9ca3af; margin-bottom: 6px;">MK Wajib</div>
480
+ <div style="font-size: 20px; font-weight: 700; color: #fff;">{len(courses) - len(elective_courses)}</div>
481
+ </div>
482
+ <div style="background: #1e293b; padding: 16px; border-radius: 8px; text-align: center;">
483
+ <div style="font-size: 11px; color: #9ca3af; margin-bottom: 6px;">Tahun Data</div>
484
+ <div style="font-size: 20px; font-weight: 700; color: #60a5fa;">{students["thn"].min()}-{students["thn"].max()}</div>
485
+ </div>
486
+ </div>
487
+ </div>
488
  """
489
  return info
490
 
 
501
 
502
  # Create Gradio Interface
503
  with gr.Blocks(title="SKS Enrollment Predictor") as demo:
504
+ # Header
505
  gr.Markdown("# Course Enrollment Predictor")
506
 
507
+ with gr.Row():
508
  # Left panel - Controls
509
+ with gr.Column(scale=1, min_width=300):
 
 
510
  year_input = gr.Number(
511
+ label="Tahun",
512
  value=2025,
513
  precision=0,
514
  minimum=2020,
 
527
  size="lg",
528
  )
529
 
530
+ gr.Markdown("---")
531
+
532
  # Data info section
533
  with gr.Accordion("Dataset Info", open=False):
534
+ data_info_output = gr.HTML()
535
  demo.load(fn=get_data_info, inputs=[], outputs=data_info_output)
536
 
537
  # Right panel - Results
538
  with gr.Column(scale=3):
539
+ summary_output = gr.HTML(
540
+ value="""
541
+ <div style="padding: 60px 40px; text-align: center; background: #1e293b; border-radius: 12px;">
542
+ <h3 style="color: #fff; margin: 0 0 8px 0; font-size: 18px; font-weight: 600;">Pilih tahun dan semester</h3>
543
+ <p style="color: #9ca3af; margin: 0; font-size: 14px;">Klik Generate Predictions untuk melihat hasil</p>
544
+ </div>
545
+ """
546
  )
547
 
548
+ gr.Markdown("---")
549
+
550
  # Predictions table
551
+ gr.Markdown("### Daftar Prediksi Mata Kuliah")
552
  all_predictions_output = gr.Dataframe(
553
  label="",
554
  wrap=True,
 
556
  )
557
 
558
  # Comparison section
559
+ with gr.Accordion("Detail Validasi", open=False) as comparison_accordion:
560
  comparison_info = gr.Markdown(
561
+ value="Data validasi muncul ketika data aktual tersedia",
562
  )
563
  comparison_output = gr.Dataframe(
564
  label="",
 
581
  all_predictions,
582
  gr.update(open=True),
583
  gr.update(
584
+ value=f"Validasi terhadap {len(comparison)} mata kuliah",
585
  ),
586
  gr.update(value=comparison),
587
  )
 
592
  all_predictions,
593
  gr.update(open=False),
594
  gr.update(
595
+ value="Tidak ada data validasi untuk prediksi masa depan",
596
  ),
597
  gr.update(value=None),
598
  )