""" Exam backend functions for AI Examiner Agent. """ import json import random import os from datetime import datetime from typing import TypedDict, Literal TOPICS = [ "Tokenization and text preprocessing", "Word embeddings (Word2Vec, GloVe, FastText)", "Recurrent neural networks (RNN, LSTM, GRU)", "Attention mechanism and Transformers", "BERT and pre-trained language models", "Named Entity Recognition (NER)", "Sentiment analysis", "Machine translation", "Text classification", "Language model evaluation metrics (BLEU, ROUGE, Perplexity)", ] STUDENTS = [ {"name": "Stanislav Androshchuk", "email": "Stanislav.Androshchuk.mKNSSh.2025@lpnu.ua"}, {"name": "Oleksandr Babilia", "email": "Oleksandr.Babilia.mKNSSh.2025@lpnu.ua"}, {"name": "Vitalii Bahrynets", "email": "Vitalii.Bahrynets.mKNSSh.2025@lpnu.ua"}, {"name": "Dmytro Betsa", "email": "Dmytro.Betsa.mKNSSh.2025@lpnu.ua"}, {"name": "Kateryna Bilyk", "email": "Kateryna.Bilyk.mKNSSh.2025@lpnu.ua"}, {"name": "Iryna Boiko", "email": "Iryna.Boiko.mKNSSh.2025@lpnu.ua"}, {"name": "Ihor Boklach", "email": "Ihor.Boklach.mKNSSh.2025@lpnu.ua"}, {"name": "Bohdan Boretskyi", "email": "Bohdan.Boretskyi.mKNSSh.2025@lpnu.ua"}, {"name": "Yaroslav Borys", "email": "Yaroslav.Borys.mKNSSh.2025@lpnu.ua"}, {"name": "Denys Brativnyk", "email": "Denys.Brativnyk.mKNSSh.2025@lpnu.ua"}, {"name": "Oleksandr Vlasiuk", "email": "Oleksandr.Vlasiuk.mKNSSh.2025@lpnu.ua"}, {"name": "Oleksandr Voznyi", "email": "Oleksandr.Voznyi.mKNSSh.2025@lpnu.ua"}, {"name": "Khrystyna Dolynska", "email": "Khrystyna.Dolynska.mKNSSh.2025@lpnu.ua"}, {"name": "Viktor Zharkivskyi", "email": "Viktor.Zharkivskyi.mKNSSh.2025@lpnu.ua"}, {"name": "Olena Kalenchuk", "email": "Olena.Kalenchuk.mKNSSh.2025@lpnu.ua"}, {"name": "Dmytro Kostinskyi", "email": "Dmytro.Kostinskyi.mKNSSh.2025@lpnu.ua"}, {"name": "Anastasiia Kudybovska", "email": "Anastasiia.Kudybovska.mKNSSh.2025@lpnu.ua"}, {"name": "Vladyslav Kuchynskyi", "email": "Vladyslav.Kuchynskyi.mKNSSh.2025@lpnu.ua"}, {"name": "Olena Litovska", "email": "Olena.Litovska.mKNSSh.2025@lpnu.ua"}, {"name": "Oleh Lozovyi", "email": "Oleh.Lozovyi.mKNSSh.2025@lpnu.ua"}, {"name": "Roman Maior", "email": "Roman.Maior.mKNSSh.2025@lpnu.ua"}, {"name": "Yevhen Makarenko", "email": "Yevhen.Makarenko.mKNSSh.2025@lpnu.ua"}, {"name": "Serhii Matsyshyn", "email": "Serhii.Matsyshyn.mKNSSh.2025@lpnu.ua"}, {"name": "Maksym Myna", "email": "Maksym.Myna.mKNSSh.2025@lpnu.ua"}, {"name": "Artem Mikanov", "email": "Artem.Mikanov.mKNSSh.2025@lpnu.ua"}, {"name": "Vitalii Mil", "email": "Vitalii.Mil.mKNSSh.2025@lpnu.ua"}, {"name": "Vladyslav Miniailo", "email": "Vladyslav.Miniailo.mKNSSh.2025@lpnu.ua"}, {"name": "Vladyslav Moiseienko", "email": "Vladyslav.Moiseienko.mKNSSh.2025@lpnu.ua"}, {"name": "Tymofii Nasobko", "email": "Tymofii.Nasobko.mKNSSh.2025@lpnu.ua"}, {"name": "Arsenii Ohar", "email": "Arsenii.Ohar.mKNSSh.2025@lpnu.ua"}, {"name": "Marta Oliinyk", "email": "Marta.Oliinyk.mKNSSh.2025@lpnu.ua"}, {"name": "Oleksii Oliinyk", "email": "Oleksii.Oliinyk.mKNSSh.2025@lpnu.ua"}, {"name": "Roman Omelchuk", "email": "Roman.Omelchuk.mKNSSh.2025@lpnu.ua"}, {"name": "Maksym Orlianskyi", "email": "Maksym.Orlianskyi.mKNSSh.2025@lpnu.ua"}, {"name": "Alina Pavliv", "email": "Alina.Pavliv.mKNSSh.2025@lpnu.ua"}, {"name": "Andrii Pytel", "email": "Andrii.Pytel.mKNSSh.2025@lpnu.ua"}, {"name": "Oleksii Postovyi", "email": "Oleksii.Postovyi.mKNSSh.2025@lpnu.ua"}, {"name": "Myroslav Pronyshyn", "email": "Myroslav.Pronyshyn.mKNSSh.2025@lpnu.ua"}, {"name": "Yurii Pukhta", "email": "Yurii.Pukhta.mKNSSh.2025@lpnu.ua"}, {"name": "Denys Ratushniak", "email": "Denys.Ratushniak.mKNSSh.2025@lpnu.ua"}, {"name": "Nazar Savitskyi", "email": "Nazar.Savitskyi.mKNSSh.2025@lpnu.ua"}, {"name": "Oleksandr Siryk", "email": "Oleksandr.Siryk.mKNSSh.2025@lpnu.ua"}, {"name": "Petro Slobodian", "email": "Petro.Slobodian.mKNSSh.2025@lpnu.ua"}, {"name": "Artem Somar", "email": "Artem.Somar.mKNSSh.2025@lpnu.ua"}, {"name": "Vladyslav Spivakov", "email": "Vladyslav.Spivakov.mKNSSh.2025@lpnu.ua"}, {"name": "Pavlo Stetsiuk", "email": "Pavlo.Stetsiuk.mKNSSh.2025@lpnu.ua"}, {"name": "Vladyslav Taraban", "email": "Vladyslav.Taraban.mKNSSh.2025@lpnu.ua"}, {"name": "Andrii Tarasov", "email": "Andrii.Tarasov.mKNSSh.2025@lpnu.ua"}, {"name": "Illia Feloniuk", "email": "Illia.Feloniuk.mKNSSh.2025@lpnu.ua"}, {"name": "Sviatoslav Shainoha", "email": "Sviatoslav.Shainoha.mKNSSh.2025@lpnu.ua"}, {"name": "Sviatoslav Shylkov", "email": "Sviatoslav.Shylkov.mKNSSh.2025@lpnu.ua"}, {"name": "Vitalii Yuzvyn", "email": "Vitalii.Yuzvyn.mKNSSh.2025@lpnu.ua"}, {"name": "Vladyslav Yakymchuk", "email": "Vladyslav.Yakymchuk.mKNSSh.2025@lpnu.ua"}, # demo {"name": "Test", "email": "test@test.com"}, ] DB_FILE = "exam_db.json" def _load_db() -> dict: if os.path.exists(DB_FILE): with open(DB_FILE, "r", encoding="utf-8") as f: return json.load(f) return {"exams": []} def _save_db(db: dict) -> None: with open(DB_FILE, "w", encoding="utf-8") as f: json.dump(db, f, ensure_ascii=False, indent=2) def start_exam(email: str, name: str) -> list[str]: student = next( (s for s in STUDENTS if s["email"].lower() == email.strip().lower()), None, ) if student is None: raise ValueError( f"Student with email '{email}' not found. Please check your email address." ) topics = random.sample(TOPICS, k=random.randint(2, 3)) db = _load_db() db["exams"].append({ "email": email, "name": name, "started_at": datetime.now().isoformat(), "topics": topics, "score": None, "finished_at": None, }) _save_db(db) print(f"[DB] Exam started for {name} <{email}>. Topics: {topics}") return topics _topic_queue: list[str] = [] def set_topic_queue(topics: list[str]) -> None: global _topic_queue _topic_queue = list(topics) def get_next_topic() -> str: if _topic_queue: return _topic_queue.pop(0) return "" class Message(TypedDict): role: Literal["system", "user", "tool_call"] content: str datetime: str def end_exam(email: str, score: float, history: list[Message]) -> None: db = _load_db() for exam in reversed(db["exams"]): if exam["email"].lower() == email.lower() and exam["score"] is None: exam["score"] = score exam["finished_at"] = datetime.now().isoformat() exam["history"] = history break _save_db(db) print(f"[DB] Exam finished for {email}. Score: {score}/10")