Spaces:
Running
Running
| import streamlit as st | |
| from cert_study_app.services.learning_lab_service import ( | |
| PRACTICE_TASKS, | |
| certification_for_track, | |
| lessons_for_track, | |
| quizzes_for_track, | |
| certifications_for_track, | |
| active_tracks, | |
| normalize_track_id, | |
| ) | |
| from cert_study_app.services.learning_progress_service import ( | |
| completed_steps, | |
| save_preferred_track, | |
| spaced_review_count, | |
| spaced_review_due_today, | |
| ) | |
| from cert_study_app.ui.common import go_to, selected_lab_track | |
| def render_concept_mode_home(): | |
| track_id = selected_lab_track() | |
| certification = certification_for_track(track_id) | |
| lessons = lessons_for_track(track_id) | |
| quizzes_list = quizzes_for_track(track_id) | |
| session_done = completed_steps(track_id) | |
| lesson_done = "lesson" in session_done | |
| quiz_done = "quiz" in session_done | |
| st.subheader("๐ ๊ฐ๋ ๊ณต๋ถ") | |
| st.caption(f"{certification.get('name', '')} ๋๋น ยท ์ด๋ก ์นด๋๋ฅผ ๋ณด๊ณ ํ์ธ ํด์ฆ๋ก ์ดํด๋๋ฅผ ์ ๊ฒํฉ๋๋ค") | |
| st.info("๐ก ์ด ์น์ ์ **์ง์ ์ ์ํ ๊ฐ๋ ํ์ต ์ฝํ ์ธ **์ ๋๋ค. ์๋ '์ํ ์ค๋น'์ ๋คํ ๋ฌธ์ ์๋ ๋ณ๊ฐ์ ๋๋ค. ๊ฐ๋ ์ ์ดํดํ ๋ค ์ํ ์ค๋น๋ก ๋์ด๊ฐ๋ฉด ํจ๊ณผ์ ์ ๋๋ค.", icon=None) | |
| with st.container(border=True): | |
| done_chip = "<span class='cert-chip cert-chip-done'>โ ์๋ฃ</span>" if lesson_done else "" | |
| st.markdown(f"**์ด๋ก ์นด๋** {done_chip}", unsafe_allow_html=True) | |
| st.caption(f"{len(lessons)}๊ฐ ์นด๋ ยท ํต์ฌ ๊ฐ๋ ์ ์ ๋ฆฌํฉ๋๋ค. ๋ชจ๋ฅด๋ ๊ฒ ์์ผ๋ฉด ๋ค์ ์นด๋๋ก ์ด์ด๊ฐ๋๋ค.") | |
| btn_label = "์ด์ด์ ๋ณด๊ธฐ" if lesson_done else "์์ํ๊ธฐ" | |
| btn_type = "secondary" if lesson_done else "primary" | |
| if st.button(btn_label, type=btn_type, use_container_width=True, key="concept_lesson_btn"): | |
| go_to("์ด๋ก ํ์ต") | |
| with st.container(border=True): | |
| done_chip = "<span class='cert-chip cert-chip-done'>โ ์๋ฃ</span>" if quiz_done else "" | |
| st.markdown(f"**ํ์ธ ํด์ฆ** {done_chip}", unsafe_allow_html=True) | |
| st.caption(f"{len(quizzes_list)}๋ฌธ์ ยท ๋ฐฉ๊ธ ๋ณธ ๊ฐ๋ ์ ์งง์ ํด์ฆ๋ก ์ ๊ฒํฉ๋๋ค.") | |
| btn_label = "๋ค์ ํ๊ธฐ" if quiz_done else "ํด์ฆ ํ๊ธฐ" | |
| btn_type = "secondary" if quiz_done else "primary" | |
| if st.button(btn_label, type=btn_type, use_container_width=True, key="concept_quiz_btn"): | |
| go_to("ํ์ธ ํด์ฆ") | |
| if lesson_done and quiz_done: | |
| st.success("์ค๋ ๊ฐ๋ ๊ณต๋ถ๋ฅผ ๋ง์ณค์ต๋๋ค. ์ค์ต์ด๋ ์ํ ์ค๋น๋ก ์ด์ด๊ฐ ์ ์์ต๋๋ค.") | |
| def render_practice_mode_home(): | |
| track_id = selected_lab_track() | |
| certification = certification_for_track(track_id) | |
| practices = [t for t in PRACTICE_TASKS if t.track == track_id and t.status == "approved"] | |
| completed = st.session_state.lab_completed_practices | |
| done_ids = completed & {t.id for t in practices} | |
| session_done = completed_steps(track_id) | |
| apply_done = "apply" in session_done | |
| st.subheader("๐ฅ ์ค์ต") | |
| st.caption(f"{certification.get('name', '')} ๋๋น ยท ๋ช ๋ น์ด๋ ๋๊ตฌ๋ฅผ ์ง์ ์คํํด ๋ด ๋๋ค") | |
| if not practices: | |
| st.info( | |
| "์ด Track์ ์์ง ์ค์ต ๊ณผ์ ๋ฅผ ์ค๋น ์ค์ ๋๋ค. " | |
| "Linux Track์ ์ ํํ๋ฉด LFCS ๋ช ๋ น์ด ์ค์ต์ ๋ฐ๋ก ์์ํ ์ ์์ต๋๋ค." | |
| ) | |
| return | |
| progress_pct = len(done_ids) / len(practices) if practices else 0 | |
| st.progress(progress_pct, text=f"์ ์ฒด ์ง๋ {len(done_ids)}/{len(practices)} ์๋ฃ") | |
| if apply_done: | |
| st.success("์ค๋ ์ค์ต์ ์๋ฃํ์ต๋๋ค.") | |
| btn_label = "์ค์ต ์ด์ด๊ฐ๊ธฐ" if len(done_ids) > 0 else "์ค์ต ์์ํ๊ธฐ" | |
| btn_type = "secondary" if apply_done else "primary" | |
| if st.button(btn_label, type=btn_type, use_container_width=True): | |
| go_to("์ค์ตํ๊ธฐ") | |
| with st.expander("์ค์ต ๊ฐ์ด๋", expanded=False): | |
| st.write( | |
| "๋ช ๋ น์ด๋ฅผ ์ง์ ์ ๋ ฅํ๊ณ ์ฑ์ ์ ๋ฐ์ต๋๋ค. " | |
| "ํํธ๋ฅผ ๋ณด๋ฉด ํ์ต ํจ๊ณผ๊ฐ ์ค์ด๋๋ ์ต๋ํ ํผ์ ๋จผ์ ์๋ํด ๋ณด์ธ์. " | |
| "ํ๋ ค๋ ๋ฐ๋ก ๋ค์ ๋ฌธ์ ๋ก ๋์ด๊ฐ์ง ๋ง๊ณ ์ ๋ต ๋ช ๋ น์ด๋ฅผ ํ ๋ฒ ์ง์ ์ณ๋ณด๋ ๊ฑธ ๊ถ์ฅํฉ๋๋ค." | |
| ) | |
| def render_exam_prep_home(exams): | |
| st.subheader("๐ ์ํ ์ค๋น") | |
| st.caption("๋คํ ๋ฌธ์ ๋ก ์ค์ ๊ฐ๊ฐ์ ์ตํ๊ณ , ํ๋ฆฐ ๋ฌธ์ ๋ ๊ฐ๊ฒฉ ๋ฐ๋ณต์ผ๋ก ์์ ํ ๋ด ๊ฒ์ผ๋ก ๋ง๋ญ๋๋ค.") | |
| st.info("๐ก ์ฌ๊ธฐ๋ **์ค์ ์ํ ํ์ ๋ฌธ์ ํ์ด** ๊ณต๊ฐ์ ๋๋ค. ๊ฐ๋ ์ด ์์ง ์ต์ํ์ง ์๋ค๋ฉด '๊ฐ๋ ๊ณต๋ถ'๋ฅผ ๋จผ์ ํ์ธ์.", icon=None) | |
| due_count = spaced_review_count() | |
| if due_count > 0: | |
| with st.container(border=True): | |
| st.markdown(f"**๐ ๊ฐ๊ฒฉ ๋ณต์ต ยท {due_count}๋ฌธ์ ๋๊ธฐ**") | |
| st.caption("ํ๋ ธ๋ ๋ฌธ์ ๊ฐ ์ค๋ ๋ณต์ต ๊ธฐํ์ด ๋์ต๋๋ค. ๋ณต์ต๋ถํฐ ํ๋ฉด ์ฅ๊ธฐ ๊ธฐ์ต ํจ์จ์ด ๋์์ง๋๋ค.") | |
| if st.button(f"๋ณต์ต ์์ ({due_count}๋ฌธ์ )", type="primary", use_container_width=True): | |
| due_ids = spaced_review_due_today(limit=1) | |
| if due_ids: | |
| st.session_state.question_id = due_ids[0] | |
| st.session_state.exam_source = None | |
| st.session_state.selected = None | |
| st.session_state.last_result = None | |
| go_to("์๊ฒฉ์ฆ ๋ฌธ์ ") | |
| st.markdown('<div class="cert-section-title">์ํ๋ณ ๋ฌธ์ ํ์ด</div>', unsafe_allow_html=True) | |
| az_exam = next((e for e in exams if e.get("source") == "AZ-104"), None) | |
| with st.container(border=True): | |
| tc, bc = st.columns([4, 1]) | |
| tc.markdown("**AZ-104** ยท Microsoft Azure Administrator") | |
| if az_exam: | |
| tc.caption(f"๋ฌธ์ ์ํ {az_exam['count']}๋ฌธํญ ยท ์ค๋น๋จ") | |
| else: | |
| tc.caption("์ค๋น๋จ ยท ๋ฌธ์ ์๋ฅผ ๋ถ๋ฌ์ค๋ ์ค") | |
| if bc.button("์์", key="exam_az104", use_container_width=True): | |
| st.session_state.exam_source = "AZ-104" | |
| go_to("์๊ฒฉ์ฆ ๋ฌธ์ ") | |
| with st.container(border=True): | |
| tc, bc = st.columns([4, 1]) | |
| tc.markdown("**LFCS** ยท Linux Foundation Certified Sysadmin") | |
| tc.caption("๋ฌธ์ ์ํ ์ค๋น ์ค ยท ํ์ฌ๋ ์ค์ต์ผ๋ก ๋๋น") | |
| if bc.button("์ค์ต์ผ๋ก ์ด๋", key="exam_lfcs", use_container_width=True): | |
| go_to("์ค์ต") | |
| st.markdown('<div class="cert-section-title">๊ฐ๋ ๋ชจ์์ํ</div>', unsafe_allow_html=True) | |
| with st.container(border=True): | |
| tc, bc = st.columns([4, 1]) | |
| tc.markdown("**๐งช ๊ฐ๋ ๋ชจ์์ํ** ยท ์ง์ ์ ์ ํด์ฆ ๊ธฐ๋ฐ") | |
| tc.caption("์ด๋ก ยทCLI ํด์ฆ๋ฅผ ๋๋ค ์ถ์ , ํ์ด๋จธ ์์ ยท ๋คํ ๋ฌธ์ ์ ๋ณ๊ฐ") | |
| if bc.button("์์", key="exam_concept_mock", use_container_width=True): | |
| go_to("์ํ ๋ชจ๋") | |
| 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("์ทจ์ฝ ๊ฐ๋ ํ์ต") | |
| def render_exam_overview(exams, selected_exam): | |
| st.subheader("์ํ ํํฉ") | |
| total_questions = sum(exam["count"] for exam in exams) | |
| col1, col2, col3 = st.columns(3) | |
| col1.metric("์ํ", f"{len(exams)}๊ฐ") | |
| col2.metric("์ ์ฒด ๋ฌธํญ", f"{total_questions}๊ฐ") | |
| col3.metric("์ ํ ๋ฌธํญ", f"{selected_exam['count']}๊ฐ" if selected_exam else f"{total_questions}๊ฐ") | |
| if not exams: | |
| st.info("์์ง ๋ฑ๋ก๋ ์ํ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ์ ๋ก๋์์ ์ํ๋ช ์ ์ง์ ํ๊ณ PDF๋ฅผ ์ ์ฌํด ์ฃผ์ธ์.") | |
| return | |
| st.dataframe( | |
| [ | |
| { | |
| "์ํ": exam["name"], | |
| "๋ฌธํญ ์": exam["count"], | |
| "์ฒซ ๋ฌธํญ": exam["first_question_id"], | |
| "๋ง์ง๋ง ๋ฌธํญ": exam["last_question_id"], | |
| } | |
| for exam in exams | |
| ], | |
| hide_index=True, | |
| use_container_width=True, | |
| ) | |