test-platform/server/cgi/view.py

332 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import mariadb as mdb
import json
import os
from test_list import TestList
from question_list import QuestionList
from response_option_list import ResponseOptionList
from httputils import escape_html
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
self.supported_modes = {
"test-list": self.render_test_list,
"create-test": self.render_create_test,
"edit-test": self.render_edit_test,
"delete-test": self.render_delete_test,
"view-test": self.render_view_test,
"create-question": self.render_create_question,
"edit-question": self.render_edit_question,
"delete-question": self.render_delete_question,
"view-question": self.render_view_question,
"create-response-option": self.render_create_response_option,
"edit-response-option": self.render_edit_response_option,
"delete-response-option": self.render_delete_response_option,
"generate-response-options": self.render_generate_response_options,
"view-stats": self.render_view_stats
}
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"
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)
def render_test_list(self, cur):
cur.execute("SELECT id FROM test;")
test_amount = len(list(cur))
header = f'<h2>Всього тестів: {test_amount}</h2>'
if 'search' in self.args:
search = self.args['search']
else:
search = ""
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>'
tl = TestList(cur)
if 'search' in self.args:
content = tl.render(self.args['search'])
else:
content = tl.render()
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>
<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="Зберегти">
</form>'''
return header, subheader, content
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
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>"
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">{test_name}</span>'
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>'
content = QuestionList(cur).render(self.args['id'])
content += '<div class="return-button-centerer"><a class="return-button" href="/index.py">Назад до переліку тестів</a></div>'
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>
<input type="submit" class="submit-button" value="Додати">
</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>
<input type="number" name="mtime" placeholder="Наприклад, 120" value="{question_max_time}"><br>
<input type="submit" class="submit-button" value="Зберегти">
</form>'''
return header, subheader, content
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]
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>'''
return header, subheader, content
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'])
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>'
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>
<input type="submit" class="submit-button" value="Додати">
</form>'''
return header, subheader, content
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>
<input type="submit" class="submit-button" value="Зберегти">
</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]
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>'
return header, subheader, content