Kentlo's picture
Sync from GitHub b098d17dbed1a6386c212d28b4bfa6138ad59d75
88c7275 verified
Raw
History Blame Contribute Delete
19.6 kB
from datetime import date
import streamlit as st
from sqlalchemy import func
from cert_study_app.config import DEFAULT_USER
from cert_study_app.db import SessionLocal
from cert_study_app.models import Attempt, Question
from cert_study_app.services.learning_lab_service import (
PRACTICE_TASKS,
active_tracks,
certification_for_track,
certifications_for_track,
lessons_for_track,
normalize_track_id,
quizzes_for_track,
track_by_id,
track_progress,
)
from cert_study_app.services.learning_progress_service import (
completed_steps,
mark_learning_step,
next_day_recommendation,
save_preferred_track,
spaced_review_count,
spaced_review_due_today,
streak_days,
study_units,
weekly_summary,
record_activity,
)
from cert_study_app.services.question_concept_service import concept_label
from cert_study_app.ui.common import go_to, selected_lab_track
def focus_apply_step(track_id: str) -> tuple[str, str, str, str]:
if track_id == "linux":
return (
"์‹ค์Šต ์ ์šฉ",
"๋ฐฉ๊ธˆ ๋ณธ ๊ฐœ๋…์„ ๋ช…๋ น์–ด ์‹ค์Šต์œผ๋กœ ๋ฐ”๋กœ ์จ๋ด…๋‹ˆ๋‹ค.",
"์‹ค์Šตํ•˜๊ธฐ",
"์‹ค์Šตํ•˜๊ธฐ",
)
if track_id == "azure":
return (
"๋ฌธ์ œ ์ ์šฉ",
"๊ฐœ๋…์„ AZ-104 ๋ฌธ์ œ์— ๋ฐ”๋กœ ์ ์šฉํ•ด ๋ด…๋‹ˆ๋‹ค.",
"๋ฌธ์ œ ํ’€๊ธฐ",
"์ž๊ฒฉ์ฆ ๋ฌธ์ œ",
)
return (
"๋ฌธ์„œ ์ ์šฉ",
"๊ณต์‹ ๋ฌธ์„œ ์นด๋“œ์™€ ํ€ด์ฆˆ๋ฅผ ์ด์–ด ๋ณด๋ฉฐ ๋„๊ตฌ ๊ฐœ๋…์„ ๊ตณํž™๋‹ˆ๋‹ค.",
"์ด๋ก  ์ด์–ด๋ณด๊ธฐ",
"์ด๋ก  ํ•™์Šต",
)
def focus_step_flow(track_id: str) -> list[tuple[str, str, str, str, str]]:
lessons = lessons_for_track(track_id)
quizzes = quizzes_for_track(track_id)
apply_title, apply_description, apply_label, apply_target = focus_apply_step(track_id)
return [
("lesson", "๊ฐœ๋… ์ดํ•ด", f"์นด๋“œ {min(1, len(lessons))}๊ฐœ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ณ , ์›ํ•˜๋ฉด ๊ณ„์† ๋‹ค์Œ ์นด๋“œ๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.", "๊ฐœ๋… ๋ณด๊ธฐ", "์ด๋ก  ํ•™์Šต"),
("quiz", "๋ฐ”๋กœ ํ™•์ธ", f"{min(3, len(quizzes))}๊ฐœ๋กœ ์ดํ•ด๋„๋ฅผ ์ ๊ฒ€ํ•œ ๋’ค ๊ณ„์† ํ’€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.", "ํ™•์ธ ํ€ด์ฆˆ ํ’€๊ธฐ", "ํ™•์ธ ํ€ด์ฆˆ"),
("apply", apply_title, apply_description, apply_label, apply_target),
("review", "์˜ค๋‹ต ์ •๋ฆฌ", "์˜ค๋‹ต 1๊ฐœ๋ถ€ํ„ฐ ๋ณด๊ณ , ๋” ๋ณต์Šตํ•˜๋ฉด ๋ˆ„์  ํ•™์Šต๋Ÿ‰์œผ๋กœ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.", "์˜ค๋‹ต ๋ณต์Šต์œผ๋กœ ์ด๋™", "์˜ค๋‹ต๋…ธํŠธ"),
]
def render_home(exams):
tracks = active_tracks()
track_ids = [t["id"] for t in tracks]
track_labels = [t["name"] for t in tracks]
from cert_study_app.services.learning_progress_service import preferred_track
current_track_id = normalize_track_id(st.session_state.get("lab_track", preferred_track()))
current_idx = track_ids.index(current_track_id) if current_track_id in track_ids else 0
selected_label = st.radio("", track_labels, index=current_idx, horizontal=True)
track_id = track_ids[track_labels.index(selected_label)]
if track_id != st.session_state.get("lab_track"):
save_preferred_track(track_id)
st.session_state.lab_track = track_id
certification = certification_for_track(track_id)
session_done = completed_steps(track_id)
streak = streak_days()
units = study_units()
due_count = spaced_review_count()
_, _, apply_action_label, apply_target = focus_apply_step(track_id)
concept_done = {"lesson", "quiz"} <= session_done
practice_done = "apply" in session_done
review_done = "review" in session_done
done_count = sum([concept_done, practice_done, review_done])
all_done = done_count == 3
if not concept_done:
next_label = "๊ฐœ๋… ๊ณต๋ถ€ ์‹œ์ž‘ํ•˜๊ธฐ โ†’" if "lesson" not in session_done else "ํ™•์ธ ํ€ด์ฆˆ ์ด์–ด๊ฐ€๊ธฐ โ†’"
next_page = "๊ฐœ๋…๊ณต๋ถ€"
elif not practice_done:
next_label = f"{apply_action_label} โ†’"
next_page = "์‹ค์Šต" if track_id != "azure" else "์‹œํ—˜์ค€๋น„"
elif not review_done:
next_label = "์˜ค๋‹ต ๋ณต์Šตํ•˜๊ธฐ โ†’"
next_page = "์˜ค๋‹ต๋…ธํŠธ"
else:
next_label = "์‹œํ—˜ ๋ฌธ์ œ ๋” ํ’€๊ธฐ โ†’"
next_page = "์‹œํ—˜์ค€๋น„"
celebrate_key = f"_celebrated_{track_id}_{date.today().isoformat()}"
if all_done and not st.session_state.get(celebrate_key):
st.session_state[celebrate_key] = True
st.balloons()
streak_text = f"๐Ÿ”ฅ {streak}์ผ ์—ฐ์† ํ•™์Šต ์ค‘" if streak > 0 else "์˜ค๋Š˜ ์ฒซ ํ•™์Šต์„ ์‹œ์ž‘ํ•ด๋ณด์„ธ์š”"
step_label = "์˜ค๋Š˜ ๋ชฉํ‘œ ๋‹ฌ์„ฑ! ๐ŸŽ‰" if all_done else f"์˜ค๋Š˜ {done_count}/3 ๋‹จ๊ณ„ ์™„๋ฃŒ"
bar_pct = done_count / 3 * 100
cert_name = certification.get("name", "")
st.markdown(
f"""
<div class="cert-hero">
<div class="cert-hero-track">{cert_name} ๋Œ€๋น„</div>
<div class="cert-hero-streak">{streak_text}</div>
<div class="cert-hero-bar-wrap">
<div class="cert-hero-bar-fill" style="width:{bar_pct:.0f}%"></div>
</div>
<div class="cert-hero-bar-label">{step_label}</div>
</div>
<script>
(function() {{
function connect() {{
try {{
var doc = window.parent.document;
var heroes = doc.querySelectorAll('.cert-hero');
heroes.forEach(function(hero) {{
hero.style.borderBottomLeftRadius = '0';
hero.style.borderBottomRightRadius = '0';
hero.style.marginBottom = '0';
var el = hero;
while (el && el.getAttribute && el.getAttribute('data-testid') !== 'element-container') el = el.parentElement;
if (!el) return;
var sib = el.nextElementSibling;
for (var i = 0; i < 4 && sib; i++) {{
var btn = sib.querySelector('button[data-testid="stBaseButton-primary"]');
if (btn) {{
btn.style.borderTopLeftRadius = '0';
btn.style.borderTopRightRadius = '0';
break;
}}
sib = sib.nextElementSibling;
}}
}});
}} catch(e) {{}}
}}
setTimeout(connect, 120);
setTimeout(connect, 600);
}})();
</script>
""",
unsafe_allow_html=True,
)
if all_done:
if st.button("๐ŸŽ‰ ์˜ค๋Š˜ ์™„๋ฃŒ! ์ถ”๊ฐ€ ๋ฌธ์ œ ๋” ํ’€๊ธฐ", type="primary", use_container_width=True):
go_to("์ž๊ฒฉ์ฆ ๋ฌธ์ œ")
else:
if st.button(next_label, type="primary", use_container_width=True):
if next_page == apply_target and track_id == "azure":
st.session_state.exam_source = "AZ-104"
go_to(next_page)
due_class = "cert-stat-card alert" if due_count > 0 else "cert-stat-card"
due_value = str(due_count) if due_count > 0 else "โ€”"
st.markdown(
f"""
<div class="cert-stats-row">
<div class="{due_class}">
<div class="cert-stat-value">{due_value}</div>
<div class="cert-stat-label">๊ฐ„๊ฒฉ ๋ณต์Šต ๋Œ€๊ธฐ</div>
</div>
<div class="cert-stat-card">
<div class="cert-stat-value">{units:.1f}</div>
<div class="cert-stat-label">์˜ค๋Š˜ ํ™œ๋™ ๋‹จ์œ„</div>
</div>
<div class="cert-stat-card">
<div class="cert-stat-value">{streak}</div>
<div class="cert-stat-label">์—ฐ์† ํ•™์Šต์ผ</div>
</div>
</div>
""",
unsafe_allow_html=True,
)
st.markdown('<div class="cert-section-title">๋ฐ”๋กœ ๊ฐ€๊ธฐ</div>', unsafe_allow_html=True)
col1, col2 = st.columns(2)
if col1.button("๐Ÿ“– ๊ฐœ๋… ๊ณต๋ถ€", use_container_width=True):
go_to("๊ฐœ๋…๊ณต๋ถ€")
if col2.button("๐Ÿ–ฅ ์‹ค์Šต", use_container_width=True):
go_to("์‹ค์Šต")
col3, col4 = st.columns(2)
if col3.button("๐Ÿ“‹ ์‹œํ—˜ ์ค€๋น„", use_container_width=True):
go_to("์‹œํ—˜์ค€๋น„")
if col4.button("ํ•™์Šต ํ˜„ํ™ฉ", use_container_width=True):
go_to("๋Œ€์‹œ๋ณด๋“œ")
render_spaced_review_panel()
def render_spaced_review_panel():
due_count = spaced_review_count()
if due_count == 0:
return
with st.container(border=True):
st.markdown(f"#### ๊ฐ„๊ฒฉ ๋ณต์Šต ยท ์˜ค๋Š˜ {due_count}๋ฌธ์ œ ๋Œ€๊ธฐ")
st.caption("ํ‹€๋ฆฐ ๋ฌธ์ œ๋ฅผ 1โ†’3โ†’7โ†’14โ†’30์ผ ๊ฐ„๊ฒฉ์œผ๋กœ ์žฌ์ถœ์ œํ•ฉ๋‹ˆ๋‹ค. 3ํšŒ ์—ฐ์† ์ •๋‹ต์ด๋ฉด ์™„์ „ ํ•™์Šต์œผ๋กœ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.")
due_ids = spaced_review_due_today(limit=5)
for qid in due_ids:
if st.button(f"๋ฌธ์ œ #{qid} ํ’€๊ธฐ", key=f"spaced_go_{qid}", use_container_width=True):
st.session_state.question_id = qid
st.session_state.exam_source = None
st.session_state.selected = None
st.session_state.last_result = None
go_to("์ž๊ฒฉ์ฆ ๋ฌธ์ œ")
def render_dashboard(exams):
st.subheader("๋Œ€์‹œ๋ณด๋“œ")
st.caption("์ง„๋„์™€ ์ถ”์ฒœ ๋ณต์Šต์€ ์—ฌ๊ธฐ์—์„œ๋งŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.")
render_today_plan(exams)
render_spaced_review_panel()
render_weak_recommendations()
def render_today_plan(exams):
total_questions = sum(exam["count"] for exam in exams)
track_id = selected_lab_track()
track = track_by_id(track_id)
certification = certification_for_track(track_id)
lessons = lessons_for_track(track_id)
quizzes = quizzes_for_track(track_id)
practices = [task for task in PRACTICE_TASKS if task.track == track_id and task.status == "approved"]
persisted_steps = completed_steps(track_id)
progress = track_progress(
track_id,
set(st.session_state.lab_completed_lessons),
set(st.session_state.lab_completed_quizzes),
set(st.session_state.lab_completed_practices),
)
week = weekly_summary()
streak = streak_days()
with st.container(border=True):
st.markdown("### ์ด์–ด์„œ ๊ณต๋ถ€")
st.caption(f"{track['name']} ์ค‘์‹ฌ ยท ๋ชฉํ‘œ ์ž๊ฒฉ์ฆ: {certification['name']}")
col1, col2, col3 = st.columns(3)
col1.metric("์ด๋ก  ์นด๋“œ", f"{min(3, len(lessons))}๊ฐœ")
col2.metric("ํ™•์ธ ํ€ด์ฆˆ", f"{min(5, len(quizzes))}๋ฌธ์ œ")
if track_id == "tool_docs":
third_label = "Docs ๋ณต์Šต"
third_value = "1๊ฐœ"
else:
third_label = "์˜ค๋‹ต ๋ณต์Šต"
third_value = "1๊ฐœ"
col3.metric(third_label, third_value)
wrong_count = len([item for item in st.session_state.lab_wrong_notes if item.get("track") == track_id])
st.caption(f"์˜ค๋‹ต ๋ณต์Šต {wrong_count}๊ฐœ ยท ๋“ฑ๋ก๋œ ์ž๊ฒฉ์ฆ ๋ฌธ์ œ {total_questions}๊ฐœ")
st.progress(progress["percent"] / 100 if progress["total"] else 0, text=f"{track['name']} ์ง„ํ–‰๋ฅ  {progress['completed']}/{progress['total']}")
focus_step_count = len(persisted_steps & {"lesson", "quiz", "apply", "review"})
st.progress(focus_step_count / 4, text=f"Focus ์ง„๋„ {focus_step_count}/4")
metric1, metric2, metric3 = st.columns(3)
metric1.metric("์—ฐ์† ํ•™์Šต", f"{streak}์ผ")
metric2.metric("์˜ค๋Š˜ ํ™œ๋™", f"{study_units():.1f}๋‹จ์œ„")
metric3.metric("์ด๋ฒˆ ์ฃผ ๋ˆ„์ ", f"{week['study_units']:.1f}๋‹จ์œ„")
st.caption(next_day_recommendation(track_id))
def render_weak_recommendations():
db = SessionLocal()
try:
rows = (
db.query(Question.category, Question.subcategory, func.count(Attempt.id))
.join(Question, Attempt.question_id == Question.id)
.filter(Attempt.user_id == DEFAULT_USER, Attempt.note_type == "wrong")
.group_by(Question.category, Question.subcategory)
.order_by(func.count(Attempt.id).desc())
.limit(3)
.all()
)
if not rows:
return
with st.container(border=True):
st.markdown("### ์˜ค๋Š˜ ์ถ”์ฒœ ๋ณต์Šต")
for category, subcategory, count in rows:
st.caption(f"{concept_label(category, subcategory)} ยท ์˜ค๋‹ต {count}ํšŒ")
if st.button("์˜ค๋‹ต ์ถ”์ฒœ์œผ๋กœ ๋ณต์Šต", use_container_width=True):
go_to("์˜ค๋‹ต๋…ธํŠธ")
finally:
db.close()
def render_focus_progress_flow(track_id: str):
track = track_by_id(track_id)
certification = certification_for_track(track_id)
session_done = completed_steps(track_id)
session_steps = focus_step_flow(track_id)
_, _, _, apply_target = focus_apply_step(track_id)
st.caption(f"{track['name']} Track ยท {certification['name']} ยท ๊ฐœ๋…๋ถ€ํ„ฐ ์˜ค๋‹ต๊นŒ์ง€ ์ˆœ์„œ๋Œ€๋กœ ์ด์–ด๊ฐ‘๋‹ˆ๋‹ค.")
done_count = len(session_done & {step[0] for step in session_steps})
st.progress(done_count / len(session_steps), text=f"์ง„๋„ ํ๋ฆ„ {done_count}/{len(session_steps)} ยท ์˜ค๋Š˜ ํ™œ๋™ {study_units():.1f}๋‹จ์œ„")
if st.button("๊ณ„์† ์ด์–ด๊ฐ€๊ธฐ", type="primary", use_container_width=True):
if "lesson" not in session_done:
go_to("์ด๋ก  ํ•™์Šต")
if "quiz" not in session_done:
go_to("ํ™•์ธ ํ€ด์ฆˆ")
if "apply" not in session_done:
if track_id == "azure":
st.session_state.exam_source = "AZ-104"
go_to(apply_target)
if "review" not in session_done:
go_to("์˜ค๋‹ต๋…ธํŠธ")
go_to("์ด๋ก  ํ•™์Šต")
for index, (step_id, title, description, action_label, target_page) in enumerate(session_steps, 1):
with st.container(border=True):
done = step_id in session_done
st.markdown(f"### {index}. {'์™„๋ฃŒ ยท ' if done else ''}{title}")
st.write(description)
col1, col2 = st.columns([1, 1])
if col1.button(action_label, type="primary" if not done else "secondary", use_container_width=True, key=f"today_go_{step_id}"):
if step_id == "apply" and track_id == "azure":
st.session_state.exam_source = "AZ-104"
go_to(target_page)
if col2.button("์™„๋ฃŒ ์ฒดํฌ", use_container_width=True, key=f"today_done_{step_id}", disabled=done):
mark_learning_step(track_id, step_id)
st.rerun()
if done_count == len(session_steps):
st.success("์ง„๋„ ํ๋ฆ„์„ ์ง€๋‚˜์™”์Šต๋‹ˆ๋‹ค. ๋” ๊ณต๋ถ€ํ•˜๋ฉด ์˜ค๋Š˜ ํ™œ๋™๋Ÿ‰์— ๊ณ„์† ๋”ํ•ด์ง‘๋‹ˆ๋‹ค.")
def render_continue_study():
st.subheader("Focus")
track_id = selected_lab_track()
render_focus_progress_flow(track_id)
def render_focus_mode():
st.subheader("Focus ๊ณต๋ถ€")
track_id = selected_lab_track()
track = track_by_id(track_id)
certification = certification_for_track(track_id)
st.caption(f"{track['name']} Track ยท {certification['name']} ยท ์ง„๋„๋ฅผ ์ด์–ด๊ฐ€๊ฑฐ๋‚˜, ์ง€๊ธˆ ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ณจ๋ผ ๊ณต๋ถ€ํ•ฉ๋‹ˆ๋‹ค.")
focus_mode = st.radio("๊ณต๋ถ€ ๋ฐฉ์‹", ["์ง„๋„ ์ด์–ด๊ฐ€๊ธฐ", "์›ํ•˜๋Š” ๊ฒƒ๋งŒ ํ•˜๊ธฐ"], horizontal=True)
if focus_mode == "์ง„๋„ ์ด์–ด๊ฐ€๊ธฐ":
render_focus_progress_flow(track_id)
return
focus_options = ["๊ฐœ๋…", "ํ™•์ธ", "์ ์šฉ", "์˜ค๋‹ต", "๋กœ๋“œ๋งต"]
selected_focus = st.radio("์ง€๊ธˆ ํ•  ๊ฒƒ", focus_options, horizontal=True)
apply_target = focus_apply_step(track_id)[3]
focus_targets = {
"๊ฐœ๋…": "์ด๋ก  ํ•™์Šต",
"ํ™•์ธ": "ํ™•์ธ ํ€ด์ฆˆ",
"์ ์šฉ": apply_target,
"์˜ค๋‹ต": "์˜ค๋‹ต๋…ธํŠธ",
"๋กœ๋“œ๋งต": "๋กœ๋“œ๋งต",
}
focus_descriptions = {
"๊ฐœ๋…": "์ƒˆ ๊ฐœ๋…์„ ๋จผ์ € ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํ—ท๊ฐˆ๋ฆฌ๋Š” ์šฉ์–ด์™€ ์ž์ฃผ ํ‹€๋ฆฌ๋Š” ํฌ์ธํŠธ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์ข‹์Šต๋‹ˆ๋‹ค.",
"ํ™•์ธ": "์งง์€ ํ€ด์ฆˆ๋กœ ๋ฐฉ๊ธˆ ์•„๋Š”์ง€ ๋ฐ”๋กœ ์ ๊ฒ€ํ•ฉ๋‹ˆ๋‹ค.",
"์ ์šฉ": "Linux๋Š” ์‹ค์Šต, Azure๋Š” ๋ฌธ์ œ ์ ์šฉ, Tool Docs๋Š” ๋ฌธ์„œ ์นด๋“œ ๋ณต์Šต์œผ๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.",
"์˜ค๋‹ต": "ํ‹€๋ฆฐ ๋ฌธ์ œ์™€ ์•ฝํ•œ ๊ฐœ๋…์„ ๋‹ค์‹œ ๋ด…๋‹ˆ๋‹ค.",
"๋กœ๋“œ๋งต": "์ง€๊ธˆ ๊ณต๋ถ€ํ•˜๋Š” Track์˜ ์ „์ฒด ์ˆœ์„œ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.",
}
st.info(focus_descriptions[selected_focus])
if st.button(f"{selected_focus} ์‹œ์ž‘", type="primary", use_container_width=True):
go_to(focus_targets[selected_focus])
if track_id == "tool_docs":
st.info("Tool Docs๋Š” ๊ณต์‹ ๋ฌธ์„œ ์š”์•ฝ ์นด๋“œ์™€ ํ™•์ธ ํ€ด์ฆˆ๋ฅผ ๋ฐ˜๋ณตํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์šด์˜ํ•ฉ๋‹ˆ๋‹ค.")
elif track_id == "azure":
st.info("AZ-104 ๋ฌธ์ œํ’€์ด์— ๋ชฐ์ž…ํ•˜๋ ค๋ฉด `Exam`์„ ์‚ฌ์šฉํ•˜์„ธ์š”.")
def render_exam_study_mode():
st.subheader("Exam")
st.caption("์ž๊ฒฉ์ฆ ์ง‘์ค‘ ๋ชจ๋“œ์ž…๋‹ˆ๋‹ค. ๋จผ์ € Track๊ณผ ์‹œํ—˜์„ ๊ณ ๋ฅธ ๋’ค ํ˜„์žฌ ์ค€๋น„ ์ƒํƒœ์— ๋งž๊ฒŒ ๊ณต๋ถ€ํ•ฉ๋‹ˆ๋‹ค.")
exam_tracks = [track for track in active_tracks() if track["id"] in {"azure", "linux"}]
track_labels = [track["name"] for track in exam_tracks]
current_track = normalize_track_id(st.session_state.get("lab_track", "linux"))
track_index = next((index for index, track in enumerate(exam_tracks) if track["id"] == current_track), 0)
selected_track_label = st.selectbox("Track", track_labels, index=track_index, key="exam_track_selector")
selected_track = exam_tracks[track_labels.index(selected_track_label)]
st.session_state.lab_track = selected_track["id"]
save_preferred_track(selected_track["id"])
certifications = certifications_for_track(selected_track["id"])
cert_labels = [f"{cert['name']} ยท {cert['study_mode']}" for cert in certifications]
selected_cert_label = st.selectbox("์ž๊ฒฉ์ฆ", cert_labels, key="exam_certification_selector")
certification = certifications[cert_labels.index(selected_cert_label)]
if certification["id"] == "az-104":
st.session_state.exam_source = "AZ-104"
else:
st.session_state.exam_source = None
readiness_messages = {
"ready_with_questions": "๋ฌธ์ œ์€ํ–‰์ด ์ค€๋น„๋˜์–ด ์žˆ์–ด ๋ฌธ์ œํ’€์ด์™€ ์„ธ๋ถ€๊ฐœ๋… ๋ฐ˜๋ณต์„ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.",
"practice_based": "๋ฌธ์ œ์€ํ–‰์€ ์•„์ง ์—†์ง€๋งŒ, ์‹ค์Šต ๊ณผ์ œ์™€ ๋ช…๋ น์–ด ์ˆ˜ํ–‰์œผ๋กœ ์‹œํ—˜ ๋Œ€๋น„๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
"concept_quiz_based": "๋ฌธ์ œ์€ํ–‰์€ ์•„์ง ์—†์ง€๋งŒ, ๊ฐœ๋… ์นด๋“œ์™€ ํ™•์ธ ํ€ด์ฆˆ๋กœ ํ•„๊ธฐํ˜• ๋Œ€๋น„๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.",
}
st.info(readiness_messages.get(certification.get("readiness"), "ํ•™์Šต ์ž๋ฃŒ๋ฅผ ์ค€๋น„ ์ค‘์ž…๋‹ˆ๋‹ค."))
col1, col2 = st.columns(2)
if col1.button("๋ฌธ์ œํ’€์ด", type="primary", use_container_width=True):
if certification["id"] == "lfcs":
st.toast("LFCS ๋ฌธ์ œ์€ํ–‰์€ ์•„์ง ์ค€๋น„ ์ค‘์ž…๋‹ˆ๋‹ค. ์‹ค์Šตํ˜• ํ•™์Šต์œผ๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.")
go_to("์‹ค์Šตํ•˜๊ธฐ")
if certification["id"] == "linux-master":
st.toast("๋ฆฌ๋ˆ…์Šค๋งˆ์Šคํ„ฐ ๋ฌธ์ œ์€ํ–‰์€ ์•„์ง ์ค€๋น„ ์ค‘์ž…๋‹ˆ๋‹ค. ํ™•์ธ ํ€ด์ฆˆ๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.")
go_to("ํ™•์ธ ํ€ด์ฆˆ")
go_to("์ž๊ฒฉ์ฆ ๋ฌธ์ œ")
if col2.button("์‹œํ—˜ ๋ชจ๋“œ ์„ค์ •", use_container_width=True):
go_to("์‹œํ—˜ ๋ชจ๋“œ")
col3, col4 = st.columns(2)
if col3.button("์„ธ๋ถ€๊ฐœ๋… ๋ฐ˜๋ณต", use_container_width=True):
if certification["id"] == "lfcs":
go_to("๋กœ๋“œ๋งต")
if certification["id"] == "linux-master":
go_to("์ด๋ก  ํ•™์Šต")
go_to("์ž๊ฒฉ์ฆ ๋ฌธ์ œ")
if col4.button("์˜ค๋‹ต ๋ณต์Šต", use_container_width=True):
go_to("์˜ค๋‹ต๋…ธํŠธ")