import json import os import random import gradio as gr from dotenv import load_dotenv from constants import MODELS, MODELS_PATH, BOOKS, CLASSES, STUDENTS, MODEL_NAME_TO_CHOICE, BOOKS_FOR_CLASSES from utils import save_results, get_test_by_student_class_book load_dotenv() if os.getenv("ENV_TYPE") == "dev": MODELS["Test"] = "test" MODELS_PATH["Test"] = "test" BOOKS["Test"] = "test.json" MODEL_NAME_TO_CHOICE["test"] = "Test" def get_question(questions_data, current_question_index): question = questions_data[current_question_index] question_text = f"### Питання {current_question_index + 1}/{len(questions_data)}:\n#### {question['question']}" answers = [answer['answer'] for answer in question['answers']] random.shuffle(answers) return question_text, answers def load_questions(model, book, student_name, class_name): print("load_questions") if not model or not book or student_name is None or class_name is None: return ( "# Будь ласка, заповніть усі поля!", # question_output [], # answer_radio [], # questions_data_state ) model_path = MODELS_PATH[model] book_filename = BOOKS[book] file_path = os.path.join("questions", model_path, book_filename) if not os.path.exists(file_path): return ( f"Файл за шляхом {file_path} не знайдено.", # question_output [], # answer_radio [], # questions_data_state ) with open(file_path, "r", encoding="utf-8") as file: questions_data = json.load(file) if questions_data: question_text, answers = get_question(questions_data, 0) return ( question_text, # question_output answers, # answer_radio questions_data, # questions_data_state ) else: return ( "У файлі немає питань.", # question_output [], # answer_radio questions_data, # questions_data_state ) def get_next_question(selected_answer, questions_data, current_question_index, answers_log): print("get_next_question answers_log", answers_log) if not selected_answer: question_text, answers = get_question(questions_data, current_question_index) return ( question_text, # question_radio gr.update(choices=answers, value=None, interactive=True, visible=True), # answer_radio gr.update(visible=True), # next_button gr.update(visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button gr.update(visible=False), # rating_text gr.update(visible=False), # question_correct gr.update(visible=False), # answers_correct gr.update(visible=False), # interesting_question gr.update(visible=False, value=""), # feedback_questions_output current_question_index, # current_question_index_state answers_log, # answers_log_state "", # feedback_questions_state ) # Writing answer in log answers_log.append({ "selected": selected_answer, "isCorrect": any(answer['answer'] == selected_answer and answer['isCorrect'] for answer in questions_data[current_question_index]['answers']) }) print("get_next_question updated answers_log", answers_log) # Move to the next question current_question_index += 1 if current_question_index < len(questions_data): question_text, answers = get_question(questions_data, current_question_index) return ( question_text, # question_radio gr.update(choices=answers, value=None, interactive=True, visible=True), # answer_radio gr.update(visible=True), # next_button gr.update(visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button gr.update(visible=False), # rating_text gr.update(visible=False), # question_correct gr.update(visible=False), # answers_correct gr.update(visible=False), # interesting_question gr.update(visible=False, value=""), # feedback_questions_output current_question_index, # current_question_index_state answers_log, # answers_log_state "", # feedback_questions_state ) else: # All questions are completed — ask for feedback question_text = "#### Дякуємо за участь у тесті!\n#### Нижче можете переглянути ваші та правильні відповіді\n#### Залиште, будь ласка, відгук про тест в кінці сторінки.\n---" feedback_questions = prepare_questions_for_feedback(questions_data, answers_log) return ( question_text, # question_radio gr.update(visible=False, value=None), # answer_radio gr.update(visible=False), # next_button gr.update(visible=True), # feedback_input gr.update(visible=True), # submit_feedback_button gr.update(visible=True), # rating_text gr.update(visible=True), # question_correct gr.update(visible=True), # answers_correct gr.update(visible=True), # interesting_question gr.update(visible=True, value=feedback_questions), # feedback_questions_output 0, # current_question_index_state answers_log, # answers_log_state feedback_questions, # feedback_questions_state ) def summarize_results(student_name, class_name, model, book, feedback, question_correct, answers_correct, interesting_question, questions_data, answers_log, feedback_questions, available_models): print("summarize_results questions_data", questions_data) questions = [] if question_correct is None or answers_correct is None or interesting_question is None: return ( "### Залиште відгук про тест!", # question_output gr.update(visible=True), # feedback_input gr.update(visible=True), # submit_feedback_button gr.update(visible=True), # question_correct gr.update(visible=True), # answers_correct gr.update(visible=True), # interesting_question gr.update(visible=True), # rating_text gr.update(visible=True, value=feedback_questions), # feedback_questions_output gr.update(visible=True), # feedback_not_provided questions_data, # questions_data_state 0, # current_question_index_state answers_log, # answers_log_state feedback_questions, # feedback_questions_state available_models, # available_models_state ) print("summarize_results answers_log", answers_log) for question, answer in zip(questions_data, answers_log): questions.append({ "question": question, "answer": answer }) correct_answers_count = sum(1 for answer in answers_log if answer['isCorrect']) total_questions = len(questions_data) grade_12 = (correct_answers_count / total_questions) * 12 print("summarize_results questions_data before save", questions_data) save_results( student_name, class_name, MODELS[model], book, questions, feedback, question_correct, answers_correct, interesting_question, grade_12, correct_answers_count, ) summary = ( f"#### Тест для моделі {model} закінчено!\n\n" f"#### Ви відповіли правильно на {correct_answers_count} з {total_questions} питань.\n\n" f"#### Оцінка за 12-бальною шкалою: {grade_12:.2f}.\n\n" ) available_models_list = available_models print(available_models_list) updated_available_models = [m for m in available_models_list if m != model] if student_name != "Вчитель" else available_models_list print(list(updated_available_models)) print(updated_available_models) return ( summary, # question_output gr.update(visible=False, value=""), # feedback_input gr.update(visible=False), # submit_feedback_button gr.update(visible=False, value=None), # question_correct gr.update(visible=False, value=None), # answers_correct gr.update(visible=False, value=None), # interesting_question gr.update(visible=False), # rating_text gr.update(visible=False, value=""), # feedback_questions_output gr.update(visible=False), # feedback_not_provided gr.update(choices=list(updated_available_models), value=None, visible=True), # model_radio [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state updated_available_models, # available_models_state ) def prepare_questions_for_feedback(questions, answer_log): feedback = [] for i, question in enumerate(questions): question_text = f"#### Питання: {question['question']}" quote = f"#### Цитата: *{question['textPart']}*" user_answer = answer_log[i]['selected'] answers_text = "\n".join( [ f"- {'**(Правильна)**' if ans['isCorrect'] else ''} {'**(Обрана)**' if ans['answer'] == user_answer else ''} {ans['answer']}" for ans in question['answers'] ] ) feedback.append(f"{question_text}\n{quote}\n#### Відповіді:\n{answers_text}\n---") return "\n".join(feedback) def update_students(class_name, available_models): print('update_students: ', available_models) students = STUDENTS.get(class_name, []) return ( gr.update(choices=students, value=None, visible=True), # student_name_input gr.update(choices=list(BOOKS_FOR_CLASSES[class_name].keys()), value=None, visible=False), # book_radio gr.update(choices=available_models, value=None, visible=False), # model_radio gr.update(value=""), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state available_models, # available_models_state ) def handle_student_name_change(student_name, available_models, class_name): print("handle_student_name_change", available_models) if student_name == "Вчитель": available_models = list(MODELS.keys()) print("handle_student_name_change available_models: ", available_models) if not student_name: return ( gr.update(choices=list(BOOKS_FOR_CLASSES[class_name].keys()), value=None, visible=False), # book_radio gr.update(choices=available_models, value=None, visible=False), # model_radio gr.update(value=""), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state available_models, # available_models_state ) return ( gr.update(choices=list(BOOKS_FOR_CLASSES[class_name].keys()), value=None, visible=True), # book_radio gr.update(choices=available_models, value=None, visible=False), # model_radio gr.update(value=""), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state available_models, # available_models_state ) def filter_models(student_name, class_name, book, available_models): print('filter_models: ', available_models) if not book: return ( gr.update(choices=available_models, value=None, visible=False), # model_radio gr.update(value=""), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state available_models, # available_models_state ) if student_name == "Вчитель": return ( gr.update(choices=MODELS.keys(), value=None, visible=True), # model_radio gr.update(value=""), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state MODELS.keys(), # available_models_state ) tests = get_test_by_student_class_book(student_name, class_name, book) available_models_names = list(MODELS.values()) for test in tests: if test.get("model") in available_models_names: available_models_names.remove(test.get("model")) print("Available models (before update):", available_models_names) if not available_models_names: return gr.update(choices=[], label="Немає доступних моделей", value=None, visible=True) models_list = [] for model in list(available_models_names): models_list.append(MODEL_NAME_TO_CHOICE.get(model)) return ( gr.update(choices=models_list, value=None, visible=True), # model_radio gr.update(value=""), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state models_list, # available_models_state ) def handle_model_change(model, available_models): print("handle_model_change", available_models) print("handle_model_change") if model is not None: return ( gr.update(value=""), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state available_models, # available_models_state ) return ( gr.update(visible=True), # questions_output gr.update(choices=[], value=None, visible=False), # answer_radio gr.update(visible=False), # next_button gr.update(value="", visible=False), # feedback_questions_output gr.update(visible=False), # rating_text gr.update(visible=False), # feedback_not_provided gr.update(value=None, visible=False), # question_correct_radio gr.update(value=None, visible=False), # answers_correct_radio gr.update(value=None, visible=False), # interesting_question_radio gr.update(value="", visible=False), # feedback_input gr.update(visible=False), # submit_feedback_button [], # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state available_models, # available_models_state ) with gr.Blocks() as demo: with gr.Column(): gr.Markdown("## Оберіть модель та книгу, щоб завантажити питання") class_name_input = gr.Dropdown(choices=CLASSES, label="Ваш клас", value=None) student_name_input = gr.Dropdown(label="Ваше ім'я", value=None, visible=False) book_radio = gr.Radio([], label="Оберіть книгу", interactive=True, value=None, visible=False) model_radio = gr.Radio([], label="Оберіть модель", interactive=True, value=None, visible=False) load_button = gr.Button("Завантажити питання") question_output = gr.Markdown(label="Питання") answer_radio = gr.Radio([], label="Варіанти відповіді", interactive=True, visible=False) next_button = gr.Button("Наступне питання", visible=False) feedback_questions_output = gr.Markdown(label="Пройдені питання", value="Nothing", visible=False) rating_text = gr.Markdown( "### Шкала оцінювання:\n\n#### -2 — дуже погано\n\n#### -1 — погано\n\n#### 0 — задовільно\n\n#### 1 — добре\n\n#### 2 — відмінно", visible=False) feedback_not_provided = gr.Markdown("# Заповніть поля відгуку та залишіть оцінку!", visible=False) question_correct_radio = gr.Radio([-2, -1, 0, 1, 2], label="Чи коректно поставлені запитання?", visible=False, interactive=True) answers_correct_radio = gr.Radio([-2, -1, 0, 1, 2], label="Чи коректно поставлені варіанти відповідей?", visible=False, interactive=True) interesting_question_radio = gr.Radio([-2, -1, 0, 1, 2], label="Чи цікаві були запитання?", visible=False, interactive=True) feedback_input = gr.Textbox(label="Будь-який коментар про тест (за бажанням)", visible=False) submit_feedback_button = gr.Button("Завершити тест", visible=False) questions_data_state = gr.State([]) current_question_index_state = gr.State(0) answers_log_state = gr.State([]) # Log for saving answers feedback_questions_state = gr.State("") available_models_state = gr.State([]) class_name_input.change( update_students, inputs=[ class_name_input, available_models_state, ], outputs=[ student_name_input, book_radio, model_radio, question_output, answer_radio, next_button, feedback_questions_output, rating_text, feedback_not_provided, question_correct_radio, answers_correct_radio, interesting_question_radio, feedback_input, submit_feedback_button, questions_data_state, current_question_index_state, answers_log_state, feedback_questions_state, available_models_state, ] ) student_name_input.change( handle_student_name_change, inputs=[ student_name_input, available_models_state, class_name_input, ], outputs=[ book_radio, model_radio, question_output, answer_radio, next_button, feedback_questions_output, rating_text, feedback_not_provided, question_correct_radio, answers_correct_radio, interesting_question_radio, feedback_input, submit_feedback_button, questions_data_state, current_question_index_state, answers_log_state, feedback_questions_state, available_models_state, ] ) book_radio.change( filter_models, inputs=[ student_name_input, class_name_input, book_radio, available_models_state, ], outputs=[ model_radio, question_output, answer_radio, next_button, feedback_questions_output, rating_text, feedback_not_provided, question_correct_radio, answers_correct_radio, interesting_question_radio, feedback_input, submit_feedback_button, questions_data_state, current_question_index_state, answers_log_state, feedback_questions_state, available_models_state, ] ) model_radio.change( handle_model_change, inputs=[ model_radio, available_models_state, ], outputs=[ question_output, answer_radio, next_button, feedback_questions_output, rating_text, feedback_not_provided, question_correct_radio, answers_correct_radio, interesting_question_radio, feedback_input, submit_feedback_button, questions_data_state, current_question_index_state, answers_log_state, feedback_questions_state, available_models_state, ] ) def update_question(model, book, student_name, class_name, available_models): question, answers, questions_data = load_questions(model, book, student_name, class_name) print("update_question", questions_data) is_field_filled = len(answers) > 0 return ( question, # question_output gr.update(choices=answers, interactive=True, visible=is_field_filled), # answer_radio gr.update(visible=is_field_filled), # next_button gr.update(visible=False, value=""), # feedback_input gr.update(visible=False), # submit_feedback_button gr.update(visible=False), # rating_text gr.update(visible=False, value=None), # question_correct gr.update(visible=False, value=None), # answers_correct gr.update(visible=False, value=None), # interesting_question gr.update(visible=False, value=""), # feedback_questions_output gr.update(visible=False), # feedback_not_provided questions_data, # questions_data_state 0, # current_question_index_state [], # answers_log_state "", # feedback_questions_state available_models, # available_models_state ) load_button.click( update_question, inputs=[ model_radio, book_radio, student_name_input, class_name_input, available_models_state, ], outputs=[ question_output, answer_radio, next_button, feedback_input, submit_feedback_button, rating_text, question_correct_radio, answers_correct_radio, interesting_question_radio, feedback_questions_output, feedback_not_provided, questions_data_state, current_question_index_state, answers_log_state, feedback_questions_state, available_models_state, ] ) next_button.click( get_next_question, inputs=[ answer_radio, questions_data_state, current_question_index_state, answers_log_state ], outputs=[ question_output, answer_radio, next_button, feedback_input, submit_feedback_button, rating_text, question_correct_radio, answers_correct_radio, interesting_question_radio, feedback_questions_output, current_question_index_state, answers_log_state, feedback_questions_state, ] ) submit_feedback_button.click( summarize_results, inputs=[ student_name_input, class_name_input, model_radio, book_radio, feedback_input, question_correct_radio, answers_correct_radio, interesting_question_radio, questions_data_state, answers_log_state, feedback_questions_state, available_models_state, ], outputs=[ question_output, feedback_input, submit_feedback_button, question_correct_radio, answers_correct_radio, interesting_question_radio, rating_text, feedback_questions_output, feedback_not_provided, model_radio, questions_data_state, current_question_index_state, answers_log_state, feedback_questions_state, available_models_state, ] ) if __name__ == "__main__": demo.launch(server_name="0.0.0.0")