Krika commited on
Commit
36e77cd
Β·
2 Parent(s): 4dd9ca12f3fcdf

Merge branch 'main' of https://github.com/Alalalallalalalalalalalal/Frontend

Browse files
Files changed (1) hide show
  1. phase/Student_view/lesson.py +268 -121
phase/Student_view/lesson.py CHANGED
@@ -37,6 +37,8 @@ _SS_DEFAULTS = {
37
 
38
  }
39
 
 
 
40
  def _ensure_state():
41
  for k, v in _SS_DEFAULTS.items():
42
  if k not in st.session_state:
@@ -273,14 +275,12 @@ def _render_catalog():
273
  margin-bottom: 1rem;
274
  height: 100%;
275
  }
276
-
277
  .card-header {
278
  display: flex;
279
  justify-content: space-between;
280
  align-items: flex-start;
281
  margin-bottom: 1rem;
282
  }
283
-
284
  .level-badge {
285
  background: #f8f9fa;
286
  color: #6c757d;
@@ -289,26 +289,18 @@ def _render_catalog():
289
  font-size: 0.875rem;
290
  font-weight: 500;
291
  }
292
-
293
- .card-icon {
294
- font-size: 1.5rem;
295
- color: #28a745;
296
- }
297
-
298
  .card-title {
299
  font-size: 1.25rem;
300
  font-weight: 600;
301
  color: #212529;
302
  margin: 0.5rem 0;
303
  }
304
-
305
  .card-description {
306
  color: #6c757d;
307
  font-size: 0.95rem;
308
  line-height: 1.4;
309
  margin-bottom: 1rem;
310
  }
311
-
312
  .card-meta {
313
  display: flex;
314
  justify-content: space-between;
@@ -317,17 +309,14 @@ def _render_catalog():
317
  color: #6c757d;
318
  font-size: 0.875rem;
319
  }
320
-
321
  .meta-item {
322
  display: flex;
323
  align-items: center;
324
  gap: 0.25rem;
325
  }
326
-
327
  .learn-section {
328
  margin: 1rem 0;
329
  }
330
-
331
  .learn-title {
332
  font-size: 0.75rem;
333
  font-weight: 600;
@@ -336,80 +325,58 @@ def _render_catalog():
336
  letter-spacing: 0.5px;
337
  margin-bottom: 0.5rem;
338
  }
339
-
340
  .learn-item {
341
  color: #28a745;
342
  font-size: 0.9rem;
343
  margin-bottom: 0.25rem;
344
  }
345
-
346
- .primary-button {
347
- background: #28a745 !important;
348
- color: white !important;
349
  border: none !important;
350
  border-radius: 6px !important;
351
  padding: 0.75rem 1.5rem !important;
352
  font-weight: 500 !important;
353
- width: 100% !important;
354
  margin-top: 1rem !important;
355
  }
356
-
 
 
 
357
  .secondary-button {
358
  background: #f8f9fa !important;
359
  color: #495057 !important;
360
  border: 1px solid #dee2e6 !important;
361
- border-radius: 6px !important;
362
- padding: 0.75rem 1.5rem !important;
363
- font-weight: 500 !important;
364
- width: 100% !important;
365
- margin-top: 1rem !important;
366
  }
367
  </style>
368
  """, unsafe_allow_html=True)
369
 
370
  level = st.session_state.get("level", "beginner")
371
 
372
- # Create columns for cards
373
  cols = st.columns(2, gap="large")
374
 
375
  for i, mod in enumerate(MODULES_META[level]):
376
  with cols[i % 2]:
377
- # Create card container
378
  with st.container():
379
  st.markdown(f"""
380
  <div class="card">
381
  <div class="card-header">
382
  <span class="level-badge">{mod['difficulty']}</span>
383
- <span class="card-icon">{mod['icon']}</span>
384
  </div>
385
-
386
  <h3 class="card-title">{mod['title']}</h3>
387
  <p class="card-description">{mod['description']}</p>
388
-
389
  <div class="card-meta">
390
- <div class="meta-item">
391
- <span>πŸ•’</span>
392
- <span>{mod['duration']}</span>
393
- </div>
394
-
395
  </div>
396
-
397
  <div class="learn-section">
398
- <div class="learn-title">YOU'LL LEARN</div>
399
  """, unsafe_allow_html=True)
400
-
401
- # Add learning objectives
402
- for topic in mod['topics']:
403
- st.markdown(f'<div class="learn-item">β€’ {topic}</div>', unsafe_allow_html=True)
404
-
405
- st.markdown("</div>", unsafe_allow_html=True)
406
-
407
- # Keep the expander as requested
408
  with st.expander("πŸ“‹ All Topics"):
409
  for topic in mod['topics']:
410
  st.write(f"β€’ {topic}")
411
-
412
- # Action button
413
  button_class = mod.get('button_style', 'primary')
414
  button_text = "Start Learning" if button_class == 'primary' else "Review Module"
415
 
@@ -418,7 +385,6 @@ def _render_catalog():
418
  key=f"start_{level}_{mod['id']}",
419
  help=f"Begin the {mod['title']} module"
420
  ):
421
- # Your existing button logic here
422
  st.session_state.module_id = mod["id"]
423
  st.session_state.topic_idx = 0
424
  st.session_state.mode = "lesson"
@@ -426,6 +392,9 @@ def _render_catalog():
426
 
427
  st.markdown("</div>", unsafe_allow_html=True)
428
 
 
 
 
429
  def _get_topics(level: str, module_id: int) -> List[Tuple[str, str]]:
430
  """
431
  Build the six-topic plan from metadata titles, try backend for each,
@@ -449,97 +418,275 @@ def _get_topics(level: str, module_id: int) -> List[Tuple[str, str]]:
449
 
450
 
451
  def _render_lesson():
452
- ensure_quiz_state() # make sure quiz keys exist
453
-
454
- level = st.session_state.level
455
- module_id = st.session_state.module_id
456
  if module_id is None:
457
  st.session_state.mode = "catalog"
458
  st.rerun()
459
 
460
  mod = next(m for m in MODULES_META[level] if m["id"] == module_id)
461
 
462
- st.markdown(f"### {mod['title']}")
463
- if mod.get("description"):
464
- st.caption(mod["description"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
 
466
  topics = _get_topics(level, module_id)
 
467
 
468
  if not topics:
469
- plan = _topic_plan(level, module_id)
470
- topics = [(title, _fallback_text(title, module_id, i + 1)) for i, (title, _) in enumerate(plan)]
471
- st.session_state.topics_cache[(level, module_id)] = topics
472
-
473
- with st.container(border=True):
474
- progress = (st.session_state.topic_idx + 1) / max(1, len(topics))
475
- st.progress(progress, text=f"Unit {st.session_state.topic_idx + 1} of {len(topics)}")
476
-
477
- t_title, t_text = topics[st.session_state.topic_idx]
478
 
479
- # Special Quiz placeholder
480
- if t_title.strip().lower() == "quiz":
481
- with st.spinner("Generating quiz…"):
482
- ok = start_quiz(level, module_id, mod["title"])
483
- if ok:
484
- st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
 
485
  else:
486
- st.error("Quiz could not be generated. Please try again or skip.")
487
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
 
489
- st.subheader(t_title)
490
- source_note = "Default" if FALLBACK_TAG in t_text else "Backend"
491
- st.caption(f"Source: {source_note}")
492
- if t_text:
493
- cleaned = t_text.replace(FALLBACK_TAG, "")
494
- st.markdown(cleaned, unsafe_allow_html=True)
495
-
496
- takeaways = _extract_takeaways(cleaned)
497
- if takeaways:
498
- st.markdown("#### Key Takeaways")
499
- for it in takeaways:
500
- st.write("βœ… ", it)
501
- else:
502
- st.info("Content coming soon.")
503
 
504
- col1, col2, col3 = st.columns([1, 1, 1])
505
- with col1:
506
- if st.button("← Previous", disabled=st.session_state.topic_idx == 0):
507
- st.session_state.topic_idx -= 1
508
- st.rerun()
509
- with col2:
510
- if st.button("Back to Modules"):
511
- st.session_state.mode = "catalog"
512
- st.session_state.module_id = None
513
- st.rerun()
514
- with col3:
515
- is_last = st.session_state.topic_idx >= len(topics) - 1
516
- if is_last:
517
- if not st.session_state.get("_auto_quiz_started", False):
518
- st.session_state["_auto_quiz_started"] = True
519
- with st.spinner("Generating quiz…"):
520
- ok = start_quiz(level, module_id, mod["title"])
521
- if ok:
522
  st.rerun()
523
- else:
524
- st.error("Quiz could not be generated. Please try again.")
525
  else:
526
- if st.button("Take Lesson Quiz β†’"):
527
- with st.spinner("Generating quiz…"):
528
- ok = start_quiz(level, module_id, mod["title"])
529
- if ok:
530
- st.rerun()
531
- else:
532
- st.error("Quiz could not be generated. Please try again.")
533
- else:
534
- if st.button("Next β†’"):
535
- st.session_state.topic_idx += 1
536
- st.rerun()
537
-
538
- with st.expander("Module Units", expanded=False):
 
 
 
 
 
 
 
 
 
 
 
 
539
  for i, (tt, _) in enumerate(topics):
540
- label = f"{i+1}. {tt}"
541
- st.button(label, key=f"jump_{i}", on_click=lambda j=i: st.session_state.update({"topic_idx": j}) or st.rerun())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
 
 
 
 
 
 
 
543
 
544
 
545
  def _render_results():
 
37
 
38
  }
39
 
40
+
41
+
42
  def _ensure_state():
43
  for k, v in _SS_DEFAULTS.items():
44
  if k not in st.session_state:
 
275
  margin-bottom: 1rem;
276
  height: 100%;
277
  }
 
278
  .card-header {
279
  display: flex;
280
  justify-content: space-between;
281
  align-items: flex-start;
282
  margin-bottom: 1rem;
283
  }
 
284
  .level-badge {
285
  background: #f8f9fa;
286
  color: #6c757d;
 
289
  font-size: 0.875rem;
290
  font-weight: 500;
291
  }
 
 
 
 
 
 
292
  .card-title {
293
  font-size: 1.25rem;
294
  font-weight: 600;
295
  color: #212529;
296
  margin: 0.5rem 0;
297
  }
 
298
  .card-description {
299
  color: #6c757d;
300
  font-size: 0.95rem;
301
  line-height: 1.4;
302
  margin-bottom: 1rem;
303
  }
 
304
  .card-meta {
305
  display: flex;
306
  justify-content: space-between;
 
309
  color: #6c757d;
310
  font-size: 0.875rem;
311
  }
 
312
  .meta-item {
313
  display: flex;
314
  align-items: center;
315
  gap: 0.25rem;
316
  }
 
317
  .learn-section {
318
  margin: 1rem 0;
319
  }
 
320
  .learn-title {
321
  font-size: 0.75rem;
322
  font-weight: 600;
 
325
  letter-spacing: 0.5px;
326
  margin-bottom: 0.5rem;
327
  }
 
328
  .learn-item {
329
  color: #28a745;
330
  font-size: 0.9rem;
331
  margin-bottom: 0.25rem;
332
  }
333
+ .primary-button, .secondary-button {
334
+ width: 100% !important;
 
 
335
  border: none !important;
336
  border-radius: 6px !important;
337
  padding: 0.75rem 1.5rem !important;
338
  font-weight: 500 !important;
 
339
  margin-top: 1rem !important;
340
  }
341
+ .primary-button {
342
+ background: #28a745 !important;
343
+ color: white !important;
344
+ }
345
  .secondary-button {
346
  background: #f8f9fa !important;
347
  color: #495057 !important;
348
  border: 1px solid #dee2e6 !important;
 
 
 
 
 
349
  }
350
  </style>
351
  """, unsafe_allow_html=True)
352
 
353
  level = st.session_state.get("level", "beginner")
354
 
 
355
  cols = st.columns(2, gap="large")
356
 
357
  for i, mod in enumerate(MODULES_META[level]):
358
  with cols[i % 2]:
 
359
  with st.container():
360
  st.markdown(f"""
361
  <div class="card">
362
  <div class="card-header">
363
  <span class="level-badge">{mod['difficulty']}</span>
 
364
  </div>
 
365
  <h3 class="card-title">{mod['title']}</h3>
366
  <p class="card-description">{mod['description']}</p>
 
367
  <div class="card-meta">
368
+ <div class="meta-item">πŸ•’ {mod['duration']}</div>
369
+ <div class="meta-item">πŸ‘₯ {2500 - i * 350}</div>
 
 
 
370
  </div>
 
371
  <div class="learn-section">
372
+ <div class="learn-title">You'll Learn</div>
373
  """, unsafe_allow_html=True)
374
+
375
+
 
 
 
 
 
 
376
  with st.expander("πŸ“‹ All Topics"):
377
  for topic in mod['topics']:
378
  st.write(f"β€’ {topic}")
379
+
 
380
  button_class = mod.get('button_style', 'primary')
381
  button_text = "Start Learning" if button_class == 'primary' else "Review Module"
382
 
 
385
  key=f"start_{level}_{mod['id']}",
386
  help=f"Begin the {mod['title']} module"
387
  ):
 
388
  st.session_state.module_id = mod["id"]
389
  st.session_state.topic_idx = 0
390
  st.session_state.mode = "lesson"
 
392
 
393
  st.markdown("</div>", unsafe_allow_html=True)
394
 
395
+ st.markdown("</div>", unsafe_allow_html=True)
396
+
397
+
398
  def _get_topics(level: str, module_id: int) -> List[Tuple[str, str]]:
399
  """
400
  Build the six-topic plan from metadata titles, try backend for each,
 
418
 
419
 
420
  def _render_lesson():
421
+ """Render the lesson interface"""
422
+ level = st.session_state.get("level", "beginner")
423
+ module_id = st.session_state.get("module_id")
424
+
425
  if module_id is None:
426
  st.session_state.mode = "catalog"
427
  st.rerun()
428
 
429
  mod = next(m for m in MODULES_META[level] if m["id"] == module_id)
430
 
431
+ st.markdown("""
432
+ <style>
433
+ .lesson-header {
434
+ font-size: 2rem;
435
+ font-weight: 600;
436
+ color: #1f2937;
437
+ margin-bottom: 2rem;
438
+ }
439
+ .lesson-card {
440
+ background: white;
441
+ border: 1px solid #e5e7eb;
442
+ border-radius: 12px;
443
+ padding: 2rem;
444
+ margin-bottom: 1.5rem;
445
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
446
+ min-height: 400px;
447
+ }
448
+ .unit-header {
449
+ display: flex;
450
+ justify-content: space-between;
451
+ align-items: center;
452
+ margin-bottom: 1.5rem;
453
+ padding-bottom: 1rem;
454
+ border-bottom: 1px solid #f3f4f6;
455
+ }
456
+ .unit-info {
457
+ font-size: 0.875rem;
458
+ color: #6b7280;
459
+ font-weight: 500;
460
+ }
461
+ .read-time {
462
+ display: flex;
463
+ align-items: center;
464
+ gap: 0.5rem;
465
+ font-size: 0.875rem;
466
+ color: #6b7280;
467
+ }
468
+ .lesson-content {
469
+ font-size: 1rem;
470
+ line-height: 1.6;
471
+ color: #374151;
472
+ margin-bottom: 2rem;
473
+ }
474
+ .takeaways-section {
475
+ background: #f9fafb;
476
+ border: 1px solid #e5e7eb;
477
+ border-radius: 8px;
478
+ padding: 1.5rem;
479
+ margin-top: 2rem;
480
+ }
481
+ .takeaways-header {
482
+ display: flex;
483
+ align-items: center;
484
+ gap: 0.5rem;
485
+ font-size: 1.125rem;
486
+ font-weight: 600;
487
+ color: #1f2937;
488
+ margin-bottom: 1rem;
489
+ }
490
+ .takeaway-item {
491
+ display: flex;
492
+ align-items: flex-start;
493
+ gap: 0.75rem;
494
+ margin-bottom: 0.75rem;
495
+ font-size: 0.95rem;
496
+ color: #374151;
497
+ }
498
+ .takeaway-check {
499
+ color: #10b981;
500
+ font-size: 1.1rem;
501
+ margin-top: 0.1rem;
502
+ }
503
+ .sidebar-card {
504
+ background: white;
505
+ border: 1px solid #e5e7eb;
506
+ border-radius: 12px;
507
+ padding: 1.5rem;
508
+ margin-bottom: 1rem;
509
+ }
510
+ .progress-section h4 {
511
+ font-size: 1.1rem;
512
+ font-weight: 600;
513
+ color: #1f2937;
514
+ margin-bottom: 1rem;
515
+ }
516
+ .unit-item {
517
+ display: flex;
518
+ align-items: center;
519
+ gap: 0.75rem;
520
+ padding: 0.5rem 0;
521
+ font-size: 0.9rem;
522
+ }
523
+ .unit-active {
524
+ background: #10b981;
525
+ color: white;
526
+ padding: 0.25rem 0.75rem;
527
+ border-radius: 20px;
528
+ font-weight: 500;
529
+ }
530
+ .unit-inactive {
531
+ color: #6b7280;
532
+ }
533
+ .nav-buttons {
534
+ display: flex;
535
+ justify-content: space-between;
536
+ margin-top: 2rem;
537
+ gap: 1rem;
538
+ }
539
+ .btn-secondary {
540
+ background: #f3f4f6;
541
+ color: #374151;
542
+ border: 1px solid #d1d5db;
543
+ padding: 0.75rem 1.5rem;
544
+ border-radius: 8px;
545
+ font-weight: 500;
546
+ text-decoration: none;
547
+ }
548
+ .btn-primary {
549
+ background: #10b981;
550
+ color: white;
551
+ border: none;
552
+ padding: 0.75rem 1.5rem;
553
+ border-radius: 8px;
554
+ font-weight: 500;
555
+ }
556
+ </style>
557
+ """, unsafe_allow_html=True)
558
+
559
+ st.markdown(f'<div class="lesson-header">{mod["title"]}</div>', unsafe_allow_html=True)
560
 
561
  topics = _get_topics(level, module_id)
562
+ topic_idx = st.session_state.get("topic_idx", 0)
563
 
564
  if not topics:
565
+ st.error("No lesson content available for this module.")
566
+ return
 
 
 
 
 
 
 
567
 
568
+ col_main, col_sidebar = st.columns([2, 1])
569
+
570
+ with col_main:
571
+ t_title, t_text = topics[topic_idx]
572
+
573
+ st.markdown(f"""
574
+ <div class="lesson-card">
575
+ <div class="unit-header">
576
+ <span class="unit-info">Unit {topic_idx + 1} of {len(topics)}</span>
577
+ <div class="read-time">
578
+ <span>πŸ•</span>
579
+ <span>5-8 min read</span>
580
+ </div>
581
+ </div>
582
+ <div class="lesson-content">
583
+ """, unsafe_allow_html=True)
584
+
585
+ if t_text:
586
+ st.markdown(t_text.strip(), unsafe_allow_html=True)
587
  else:
588
+ st.info("Content coming soon.")
589
+
590
+ if t_text:
591
+ takeaways = _extract_takeaways(t_text)
592
+ if takeaways:
593
+ st.markdown("""
594
+ <div class="takeaways-section" style="min-height: 150px;">
595
+ <div class="takeaways-header">
596
+ <span style="color: #10b981;">●</span>
597
+ <span>Key Takeaways</span>
598
+ </div>
599
+ """, unsafe_allow_html=True)
600
+
601
+ for takeaway in takeaways:
602
+ st.markdown(f"""
603
+ <div class="takeaway-item">
604
+ <span class="takeaway-check">βœ“</span>
605
+ <span>{takeaway}</span>
606
+ </div>
607
+ """, unsafe_allow_html=True)
608
+
609
+ st.markdown("</div>", unsafe_allow_html=True)
610
 
611
+ st.markdown("</div></div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
612
 
613
+ st.markdown('<div class="nav-buttons">', unsafe_allow_html=True)
614
+
615
+ col1, col2, col3 = st.columns([1, 1, 1])
616
+ with col1:
617
+ if st.button("← Previous", disabled=topic_idx == 0, key="prev_btn"):
618
+ st.session_state.topic_idx -= 1
619
+ st.rerun()
620
+
621
+ with col3:
622
+ is_last = topic_idx >= len(topics) - 1
623
+ if is_last:
624
+ if st.button("Complete Module β†’", key="complete_btn"):
625
+ st.session_state.mode = "catalog"
626
+ st.session_state.module_id = None
627
+ st.session_state.topic_idx = 0
628
+ st.success("Module completed! πŸŽ‰")
 
 
629
  st.rerun()
 
 
630
  else:
631
+ if st.button("Next Unit β†’", key="next_btn"):
632
+ st.session_state.topic_idx += 1
633
+ st.rerun()
634
+
635
+ st.markdown('</div>', unsafe_allow_html=True)
636
+
637
+ with col_sidebar:
638
+ # Module Progress Card
639
+ st.markdown("""
640
+ <div class="sidebar-card" style="min-height: 120px;">
641
+ <h4>Module Progress</h4>
642
+ """, unsafe_allow_html=True)
643
+
644
+ progress = (topic_idx + 1) / max(1, len(topics))
645
+ st.progress(progress)
646
+ st.markdown(f"<p style='color: #6b7280; font-size: 0.875rem; margin-top: 0.5rem;'>Unit {topic_idx + 1} of {len(topics)}</p>", unsafe_allow_html=True)
647
+
648
+ st.markdown("</div>", unsafe_allow_html=True)
649
+
650
+ # Module Units Card
651
+ st.markdown("""
652
+ <div class="sidebar-card" style="min-height: 300px;">
653
+ <h4>Module Units</h4>
654
+ """, unsafe_allow_html=True)
655
+
656
  for i, (tt, _) in enumerate(topics):
657
+ is_current = i == topic_idx
658
+ is_completed = i < topic_idx
659
+
660
+ if is_current:
661
+ st.markdown(f"""
662
+ <div class="unit-item">
663
+ <span style="background: #10b981; color: white; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.75rem;">●</span>
664
+ <span class="unit-active">{tt}</span>
665
+ </div>
666
+ """, unsafe_allow_html=True)
667
+ elif is_completed:
668
+ st.markdown(f"""
669
+ <div class="unit-item">
670
+ <span style="color: #10b981; font-size: 1.1rem;">βœ“</span>
671
+ <span style="color: #374151;">{tt}</span>
672
+ </div>
673
+ """, unsafe_allow_html=True)
674
+ else:
675
+ st.markdown(f"""
676
+ <div class="unit-item">
677
+ <span style="background: #e5e7eb; color: #9ca3af; width: 20px; height: 20px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.75rem;">β—‹</span>
678
+ <span class="unit-inactive">{tt}</span>
679
+ </div>
680
+ """, unsafe_allow_html=True)
681
+
682
+ st.markdown("</div>", unsafe_allow_html=True)
683
 
684
+ # Back to modules button
685
+ if st.button("← Back to Modules", key="back_modules"):
686
+ st.session_state.mode = "catalog"
687
+ st.session_state.module_id = None
688
+ st.session_state.topic_idx = 0
689
+ st.rerun()
690
 
691
 
692
  def _render_results():