File size: 6,826 Bytes
5d2b004
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
"""
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")