test-platform/server/cgi/view.py

332 lines
16 KiB
Python
Raw Normal View History

2024-05-25 19:18:26 +03:00
import mariadb as mdb
import json
import os
from test_list import TestList
2024-05-28 17:47:08 +03:00
from question_list import QuestionList
from response_option_list import ResponseOptionList
from httputils import escape_html
2024-05-25 19:18:26 +03:00
def readfile(path):
if os.path.exists(path):
return open(path).read()
class View:
def __init__(self, query_args, connector_data = {}):
self.args = query_args
self.connector_data = connector_data
self.db_connection = None
2024-05-28 17:47:08 +03:00
self.supported_modes = {
"test-list": self.render_test_list,
"create-test": self.render_create_test,
2024-06-01 19:45:47 +03:00
"edit-test": self.render_edit_test,
"delete-test": self.render_delete_test,
2024-05-28 17:47:08 +03:00
"view-test": self.render_view_test,
"create-question": self.render_create_question,
"edit-question": self.render_edit_question,
2024-06-01 13:49:13 +03:00
"delete-question": self.render_delete_question,
2024-05-28 17:47:08 +03:00
"view-question": self.render_view_question,
"create-response-option": self.render_create_response_option,
2024-06-01 13:49:13 +03:00
"edit-response-option": self.render_edit_response_option,
"delete-response-option": self.render_delete_response_option,
2024-06-01 19:45:47 +03:00
"generate-response-options": self.render_generate_response_options,
"view-stats": self.render_view_stats
2024-05-28 17:47:08 +03:00
}
2024-05-25 19:18:26 +03:00
def get_db_connection(self):
if not self.db_connection:
args = {"host": "127.0.0.1",
"port": 3306,
"user": "root",
"password": "",
"database": "test_holder"}
settings = json.loads(readfile("cgi/db-settings.json"))
args.update(settings)
args.update(self.connector_data)
self.db_connection = mdb.connect(**args)
return self.db_connection
def format_page(self, header, subheader, content):
base_layout = readfile("html/base_layout.html")
base_layout = base_layout.replace("%HEADER%", header)
base_layout = base_layout.replace("%SUBHEADER%", subheader)
base_layout = base_layout.replace("%CONTENT%", content)
return base_layout
def render_page(self):
dbc = self.get_db_connection()
cur = dbc.cursor()
if 'mode' in self.args:
mode = self.args['mode']
else:
mode = "test-list"
2024-05-28 17:47:08 +03:00
if mode not in self.supported_modes:
header = f"<h2>No such view mode: {mode}</h2>"
subheader = f'<a href="/index.py">Повернутися на головну сторінку</a>'
content = ""
else:
header, subheader, content = self.supported_modes[mode](cur)
dbc.close()
return self.format_page(header, subheader, content)
2024-05-25 19:18:26 +03:00
2024-05-28 17:47:08 +03:00
def render_test_list(self, cur):
cur.execute("SELECT id FROM test;")
test_amount = len(list(cur))
header = f'<h2>Всього тестів: {test_amount}</h2>'
2024-06-01 19:45:47 +03:00
if 'search' in self.args:
search = self.args['search']
else:
search = ""
2024-05-25 19:18:26 +03:00
2024-06-01 19:45:47 +03:00
subheader = f'<form action="/index.py"><input name="search" placeholder="Шукати тести" value="{search}"></form><br><a class="generic-button" href="?mode=create-test">Створити новий тест</a><a class="sub-button" href="?mode=view-stats">Переглянути статистику</a>'
2024-05-25 19:18:26 +03:00
2024-06-01 19:45:47 +03:00
tl = TestList(cur)
if 'search' in self.args:
content = tl.render(self.args['search'])
else:
content = tl.render()
2024-05-25 19:18:26 +03:00
2024-05-28 17:47:08 +03:00
return header, subheader, content
def render_create_test(self, cur):
header = f"<h2>Створити новий тест</h2>"
subheader = "<i>Введіть властивості нового тесту нижче</i>"
content = f'''<form action="/create-test.py">
<label for="name">Назва тесту:</label>
<input type="text" name="name" placeholder="Введіть назву..." required><br>
2024-06-01 19:45:47 +03:00
<input type="submit" class="submit-button" value="Створити">
</form>'''
return header, subheader, content
def render_edit_test(self, cur):
cur.execute(f"SELECT name FROM test WHERE id = {self.args['id']};")
test_name = next(iter(cur), [None])[0]
if not test_name:
header = f"<h2>Такого тесту не існує: {self.args['id']}</h2>"
subheader = f'<a href="/index.py">Повернутися на головну сторінку</a>'
content = ""
return header, subheader, content
header = f"<h2>Редагувати тест</h2>"
subheader = "<i>Вкажіть нові властивості тесту нижче</i>"
content = f'''<form action="/edit-test.py">
<input type="text" name="id" value="{self.args['id']}" style="display:none;">
<label for="name">Назва тесту:</label>
<input type="text" name="name" placeholder="Введіть назву..." value="{test_name}" required><br>
<input type="submit" class="submit-button" value="Зберегти">
2024-05-25 19:18:26 +03:00
</form>'''
2024-05-28 17:47:08 +03:00
return header, subheader, content
2024-06-01 19:45:47 +03:00
def render_delete_test(self, cur):
cur.execute(f"SELECT name FROM test WHERE id = {self.args['id']};")
test_name = next(iter(cur), [None])[0]
if not test_name:
header = f"<h2>Такого тесту не існує: {self.args['id']}</h2>"
subheader = f'<a href="/index.py">Повернутися на головну сторінку</a>'
content = ""
return header, subheader, content
header = f"<h2>Точно видалити цей тест?</h2>"
subheader = f"<i>{test_name}</i>"
content = f'''<div class="return-button-centerer"><a class="delete-button" href="/delete-test.py?id={self.args["id"]}">Так, видалити</a><br><a class="cancel-button" href="/">Ні, залишити</a></div>'''
return header, subheader, content
2024-05-28 17:47:08 +03:00
def render_view_test(self, cur):
cur.execute(f"SELECT name FROM test WHERE id = {self.args['id']};")
test_name = next(iter(cur), [None])[0]
if not test_name:
header = f"<h2>Такого тесту не існує: {self.args['id']}</h2>"
2024-05-25 19:18:26 +03:00
subheader = f'<a href="/index.py">Повернутися на головну сторінку</a>'
content = ""
2024-05-28 17:47:08 +03:00
return header, subheader, content
header = f'<span class="view-test-id-tag">#{self.args["id"]}</span><span class="view-test-main-tag">{test_name}</span>'
2024-06-01 19:45:47 +03:00
subheader = f'<a class="generic-button" href="?mode=create-question&test_id={self.args["id"]}">Додати запитання</a><a class="sub-button" href="?mode=edit-test&id={self.args["id"]}">Редагувати тест</a>'
2024-05-28 17:47:08 +03:00
content = QuestionList(cur).render(self.args['id'])
2024-06-01 13:49:13 +03:00
content += '<div class="return-button-centerer"><a class="return-button" href="/index.py">Назад до переліку тестів</a></div>'
2024-05-28 17:47:08 +03:00
return header, subheader, content
def render_create_question(self, cur):
header = f"<h2>Додати нове запитання</h2>"
subheader = "<i>Введіть властивості нового запитання нижче</i>"
content = f'''<form action="/create-question.py">
<input type="text" name="test_id" value="{self.args["test_id"]}" style="display:none;">
<label for="title">Текст запитання:</label>
<input type="text" name="title" placeholder="Введіть запитання..." required><br>
<label for="mtime">Максимальний час на відповідь (в сек):</label>
<input type="number" name="mtime" placeholder="Наприклад, 120"><br>
2024-06-01 19:45:47 +03:00
<input type="submit" class="submit-button" value="Додати">
2024-05-28 17:47:08 +03:00
</form>'''
return header, subheader, content
def render_edit_question(self, cur):
cur.execute(f"SELECT title, mtime FROM question WHERE id = {self.args['id']};")
question_title, question_max_time = next(iter(cur), [None, None])
if not question_title:
header = f"<h2>Такого запитання не існує: {self.args['id']}</h2>"
subheader = f'<a href="/index.py">Повернутися на головну сторінку</a>'
content = ""
return header, subheader, content
header = f"<h2>Змінити запитання</h2>"
subheader = "<i>Відредагуйте властивості запитання нижче</i>"
content = f'''<form action="/edit-question.py">
<input type="text" name="question_id" value="{self.args["id"]}" style="display:none;">
<label for="title">Текст запитання:</label>
<input type="text" name="title" placeholder="Введіть запитання..." value="{escape_html(question_title)}" required><br>
<label for="mtime">Максимальний час на відповідь (в сек):</label>
2024-06-01 13:49:13 +03:00
<input type="number" name="mtime" placeholder="Наприклад, 120" value="{question_max_time}"><br>
2024-06-01 19:45:47 +03:00
<input type="submit" class="submit-button" value="Зберегти">
2024-05-28 17:47:08 +03:00
</form>'''
return header, subheader, content
2024-06-01 13:49:13 +03:00
def render_delete_question(self, cur):
cur.execute(f"SELECT title FROM question WHERE id = {self.args['id']};")
question_title = next(iter(cur), [None])[0]
if not question_title:
header = f"<h2>Такого запитання не існує: {self.args['id']}</h2>"
subheader = f'<a href="/index.py">Повернутися на головну сторінку</a>'
content = ""
return header, subheader, content
header = f"<h2>Точно видалити запитання?</h2>"
subheader = f"<i>{question_title}</i>"
cur.execute(f"SELECT test.id FROM test JOIN question ON test.id = question.tstID WHERE question.id = {self.args['id']};")
test_id = iter(cur).__next__()[0]
2024-06-01 19:45:47 +03:00
content = f'''<div class="return-button-centerer"><a class="delete-button" href="/delete-question.py?id={self.args["id"]}">Так, видалити</a><br><a class="cancel-button" href="/index.py?mode=view-test&id={test_id}">Ні, залишити</a></div>'''
2024-06-01 13:49:13 +03:00
return header, subheader, content
2024-05-28 17:47:08 +03:00
def render_view_question(self, cur):
cur.execute(f"SELECT title FROM question WHERE id = {self.args['id']};")
question_name = next(iter(cur), [None])[0]
if not question_name:
header = f"<h2>Такого запитання не існує: {self.args['id']}</h2>"
subheader = f'<a href="/index.py">Повернутися на головну сторінку</a>'
content = ""
return header, subheader, content
header = f'<span class="view-test-id-tag">#{self.args["id"]}</span><span class="view-test-main-tag">{question_name}</span>'
subheader = f'<a class="generic-button" href="?mode=create-response-option&question_id={self.args["id"]}">Додати варіант відповіді</a>' + \
f'<a class="sub-button" href="?mode=edit-question&id={self.args["id"]}">Редагувати запитання</a>'
content = ResponseOptionList(cur).render(self.args['id'])
2024-06-01 13:49:13 +03:00
cur.execute(f"""SELECT test.id FROM test
JOIN question ON test.id = question.tstID
WHERE question.id = {self.args['id']};""")
test_id = iter(cur).__next__()[0]
content += f'<div class="return-button-centerer"><a class="return-button" href="/index.py?mode=view-test&id={test_id}">Назад до тесту</a></div>'
2024-05-28 17:47:08 +03:00
return header, subheader, content
def render_create_response_option(self, cur):
header = f"<h2>Додати новий варіант відповіді</h2>"
subheader = "<i>Введіть властивості варіанту відповіді нижче</i>"
content = f'''<form action="/create-response-option.py">
<input type="text" name="question_id" value="{self.args["question_id"]}" style="display:none;">
<label for="label">Текст відповіді:</label>
<input type="text" name="label" placeholder="Введіть текст відповіді..." required><br>
2024-06-01 19:45:47 +03:00
<input type="submit" class="submit-button" value="Додати">
2024-05-28 17:47:08 +03:00
</form>'''
return header, subheader, content
2024-06-01 13:49:13 +03:00
def render_edit_response_option(self, cur):
cur.execute(f"SELECT label FROM response_option WHERE id = {self.args['id']};")
respose_option_label = next(iter(cur), [None])[0]
header = f"<h2>Редагувати варіант відповіді</h2>"
subheader = "<i>Введіть властивості варіанту відповіді нижче</i>"
content = f'''<form action="/edit-response-option.py">
<input type="text" name="id" value="{self.args["id"]}" style="display:none;">
<label for="label">Текст відповіді:</label>
<input type="text" name="label" placeholder="Введіть текст відповіді..." value="{respose_option_label}" required><br>
2024-06-01 19:45:47 +03:00
<input type="submit" class="submit-button" value="Зберегти">
2024-06-01 13:49:13 +03:00
</form>'''
return header, subheader, content
def render_delete_response_option(self, cur):
cur.execute(f"SELECT label FROM response_option WHERE id = {self.args['id']};")
response_option_label = next(iter(cur), [None])[0]
header = f"<h2>Точно видалити цей варіант відповіді?</h2>"
subheader = f"<i>{response_option_label}</i>"
cur.execute(f"SELECT question.id FROM question JOIN response_option ON question.id = response_option.qstID WHERE response_option.id = {self.args['id']};")
quest_id = iter(cur).__next__()[0]
2024-06-01 19:45:47 +03:00
content = f'''<div class="return-button-centerer"><a class="delete-button" href="/delete-response-option.py?id={self.args["id"]}">Так, видалити</a><br><a class="cancel-button" href="/index.py?mode=view-question&id={quest_id}">Ні, залишити</a></div>'''
return header, subheader, content
def render_generate_response_options(self, cur):
header = f"<h2>Генерація варіантів відповідей</h2>"
subheader = f"<i>Вкажіть параметри генерації нижче</i>"
content = f'''<form action="/generate-response-options.py">
<input type="text" name="id" value="{self.args["id"]}" style="display:none;">
<label for="label">Кількість варіантів:</label>
<input type="number" name="amount" placeholder="Наприклад, 4" required><br>
<input type="submit" class="magic-button" value="Згенерувати">
</form>'''
return header, subheader, content
def render_view_stats(self, cur):
header = f"<h2>Статистика системи</h2>"
subheader = f'<a class="sub-button" href="/">Повернутися на головну сторінку</a>'
content = f''''''
test_count = TestList(cur).count()
content += f'<div class="piece-of-stats"><span class="header">Тести</span>Всього тестів: {test_count}</div>'
question_count = QuestionList(cur).count()
question_avg_time = QuestionList(cur).get_avg_time()
content += f'<div class="piece-of-stats"><span class="header">Запитання</span>Всього запитань: {question_count} (в середньому по {round(question_count/test_count, 3)} зап./тест)<br>В середньому на запитання дається {question_avg_time}</div>'
rol = ResponseOptionList(cur)
response_option_count = rol.count()
response_option_count_correct = rol.count(correct = True)
content += f'<div class="piece-of-stats"><span class="header">Варіанти відповідей</span>Всього варіантів відповідей: {response_option_count}<br>З-поміж них правильних: {response_option_count_correct} ({round(response_option_count_correct/response_option_count*100, 2)}%)</div>'
2024-06-01 13:49:13 +03:00
return header, subheader, content