initial development checkpoint 3
This commit is contained in:
parent
b4c1a23ce2
commit
54377708b0
|
@ -1,4 +1,5 @@
|
||||||
from db.object_pool import ObjectPool
|
from db.object_pool import ObjectPool
|
||||||
|
from db.response_option import ResponseOptionPool
|
||||||
|
|
||||||
class Question:
|
class Question:
|
||||||
def init(self, sID, title, max_time, test_id):
|
def init(self, sID, title, max_time, test_id):
|
||||||
|
@ -24,7 +25,10 @@ class Question:
|
||||||
def get_test_id(self):
|
def get_test_id(self):
|
||||||
return self.test_id
|
return self.test_id
|
||||||
|
|
||||||
def render_short(self):
|
def get_time_label(self):
|
||||||
|
if int(self.max_time) == 0:
|
||||||
|
return "Час на відповідь не обмежений"
|
||||||
|
|
||||||
total_time = self.max_time
|
total_time = self.max_time
|
||||||
|
|
||||||
hours = total_time // 3600
|
hours = total_time // 3600
|
||||||
|
@ -36,6 +40,7 @@ class Question:
|
||||||
seconds = total_time
|
seconds = total_time
|
||||||
|
|
||||||
total_label = []
|
total_label = []
|
||||||
|
|
||||||
if hours:
|
if hours:
|
||||||
total_label.append(f"{hours} год.")
|
total_label.append(f"{hours} год.")
|
||||||
if minutes:
|
if minutes:
|
||||||
|
@ -43,23 +48,17 @@ class Question:
|
||||||
if seconds:
|
if seconds:
|
||||||
total_label.append(f"{seconds} c.")
|
total_label.append(f"{seconds} c.")
|
||||||
|
|
||||||
total_time = " ".join(total_label)
|
return "На відповідь є " + " ".join(total_label)
|
||||||
|
|
||||||
if int(self.max_time) > 0:
|
def get_response_option_short_list(self, cur):
|
||||||
return f'<div class="question-short"><a class="question-link" href="/index.py?mode=view-question&id={self.id}"><span class="sub-label">#{self.id}</span><span class="main-label">{self.title}</span></a><span class="sub-title">На відповідь є {total_time}</span></div>'
|
rop = ResponseOptionPool(cur)
|
||||||
else:
|
return "<br>".join([i.render_short() for i in rop.select_by_question_id(self.id)])
|
||||||
return f'<div class="question-short"><a class="question-link" href="/index.py?mode=view-question&id={self.id}"><span class="sub-label">#{self.id}</span><span class="main-label">{self.title}</span></a><span class="sub-title">Час на відповідь не обмежений</span></div>'
|
|
||||||
|
|
||||||
'''
|
def render_short(self, cur):
|
||||||
class QuestionPool:
|
time_label = self.get_time_label()
|
||||||
def __init__(self):
|
response_options = self.get_response_option_short_list(cur)
|
||||||
self.pool = []
|
|
||||||
|
|
||||||
def load_from_db(self, cur):
|
return f'<div class="question-short"><a class="question-link" href="/index.py?mode=view-question&id={self.id}"><span class="sub-label">#{self.id}</span><span class="main-label">{self.title}</span></a><span class="sub-title">{time_label}</span><div class="response-option-short-list">{response_options}</div><div class="controls"><a class="sub-button" href="/index.py?mode=edit-question&id={self.id}">Редагувати</a><a class="scary-button" href="/index.py?mode=delete-question&id={self.id}">Видалити</a></div></div>'
|
||||||
db.execute("SELECT * FROM response_option;")
|
|
||||||
self.pool = [ResponseOption().init_from_data(i) for i in cur]
|
|
||||||
return self
|
|
||||||
'''
|
|
||||||
|
|
||||||
class QuestionPool:
|
class QuestionPool:
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
|
|
|
@ -28,7 +28,7 @@ class ResponseOption:
|
||||||
if self.correctness:
|
if self.correctness:
|
||||||
c_mark = "+ "
|
c_mark = "+ "
|
||||||
else:
|
else:
|
||||||
c_mark = " "
|
c_mark = "- "
|
||||||
|
|
||||||
return f'<span class="response-option-short">{c_mark} {self.label}</span>'
|
return f'<span class="response-option-short">{c_mark} {self.label}</span>'
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ class QuestionList:
|
||||||
qp = QuestionPool(self.cursor)
|
qp = QuestionPool(self.cursor)
|
||||||
|
|
||||||
if test_id:
|
if test_id:
|
||||||
rendered_questions = [i.render_short() for i in qp.select_by_test_id(test_id)]
|
rendered_questions = [i.render_short(self.cursor) for i in qp.select_by_test_id(test_id)]
|
||||||
else:
|
else:
|
||||||
rendered_questions = [i.render_short() for i in qp.iter()]
|
rendered_questions = [i.render_short(self.cursor) for i in qp.iter()]
|
||||||
|
|
||||||
return "\n".join(rendered_questions)
|
return "\n".join(rendered_questions)
|
||||||
|
|
|
@ -23,8 +23,11 @@ class View:
|
||||||
"view-test": self.render_view_test,
|
"view-test": self.render_view_test,
|
||||||
"create-question": self.render_create_question,
|
"create-question": self.render_create_question,
|
||||||
"edit-question": self.render_edit_question,
|
"edit-question": self.render_edit_question,
|
||||||
|
"delete-question": self.render_delete_question,
|
||||||
"view-question": self.render_view_question,
|
"view-question": self.render_view_question,
|
||||||
"create-response-option": self.render_create_response_option,
|
"create-response-option": self.render_create_response_option,
|
||||||
|
"edit-response-option": self.render_edit_response_option,
|
||||||
|
"delete-response-option": self.render_delete_response_option,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_db_connection(self):
|
def get_db_connection(self):
|
||||||
|
@ -110,6 +113,7 @@ class View:
|
||||||
subheader = f'<a class="generic-button" href="?mode=create-question&test_id={self.args["id"]}">Додати запитання</a>'
|
subheader = f'<a class="generic-button" href="?mode=create-question&test_id={self.args["id"]}">Додати запитання</a>'
|
||||||
|
|
||||||
content = QuestionList(cur).render(self.args['id'])
|
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
|
return header, subheader, content
|
||||||
|
|
||||||
|
@ -146,12 +150,32 @@ class View:
|
||||||
<label for="title">Текст запитання:</label>
|
<label for="title">Текст запитання:</label>
|
||||||
<input type="text" name="title" placeholder="Введіть запитання..." value="{escape_html(question_title)}" required><br>
|
<input type="text" name="title" placeholder="Введіть запитання..." value="{escape_html(question_title)}" required><br>
|
||||||
<label for="mtime">Максимальний час на відповідь (в сек):</label>
|
<label for="mtime">Максимальний час на відповідь (в сек):</label>
|
||||||
<input type="number" name="mtime" placeholder="Наприклад, 120"><br>
|
<input type="number" name="mtime" placeholder="Наприклад, 120" value="{question_max_time}"><br>
|
||||||
<input type="submit" value="Зберегти зміни">
|
<input type="submit" value="Зберегти зміни">
|
||||||
</form>'''
|
</form>'''
|
||||||
|
|
||||||
return header, subheader, content
|
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'''<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>'''
|
||||||
|
|
||||||
|
return header, subheader, content
|
||||||
|
|
||||||
def render_view_question(self, cur):
|
def render_view_question(self, cur):
|
||||||
cur.execute(f"SELECT title FROM question WHERE id = {self.args['id']};")
|
cur.execute(f"SELECT title FROM question WHERE id = {self.args['id']};")
|
||||||
question_name = next(iter(cur), [None])[0]
|
question_name = next(iter(cur), [None])[0]
|
||||||
|
@ -169,6 +193,14 @@ class View:
|
||||||
|
|
||||||
content = ResponseOptionList(cur).render(self.args['id'])
|
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
|
return header, subheader, content
|
||||||
|
|
||||||
def render_create_response_option(self, cur):
|
def render_create_response_option(self, cur):
|
||||||
|
@ -183,3 +215,33 @@ class View:
|
||||||
</form>'''
|
</form>'''
|
||||||
|
|
||||||
return header, subheader, content
|
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" 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'''<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>'''
|
||||||
|
|
||||||
|
return header, subheader, content
|
||||||
|
|
|
@ -30,7 +30,7 @@ if not 'title' in args:
|
||||||
print("Content-Type: text/plain; charset=UTF-8\r\n\r\nВи не зазначили текст запитання\r\n")
|
print("Content-Type: text/plain; charset=UTF-8\r\n\r\nВи не зазначили текст запитання\r\n")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if not 'mtime' in args:
|
if (not 'mtime' in args) or (not args['mtime']):
|
||||||
args['mtime'] = 0
|
args['mtime'] = 0
|
||||||
|
|
||||||
cur = db_connection.cursor()
|
cur = db_connection.cursor()
|
||||||
|
|
|
@ -57,7 +57,8 @@ header .top-half .view-test-main-tag {
|
||||||
header .lower-half a.generic-button,
|
header .lower-half a.generic-button,
|
||||||
header .lower-half a.sub-button,
|
header .lower-half a.sub-button,
|
||||||
main * a.sub-button,
|
main * a.sub-button,
|
||||||
main * a.scary-button
|
main * a.scary-button,
|
||||||
|
main a.return-button
|
||||||
{
|
{
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
|
@ -81,7 +82,8 @@ header .lower-half a.generic-button:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
header .lower-half a.sub-button,
|
header .lower-half a.sub-button,
|
||||||
main * a.sub-button
|
main * a.sub-button,
|
||||||
|
main a.return-button
|
||||||
{
|
{
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
}
|
}
|
||||||
|
@ -110,7 +112,7 @@ main .response-option
|
||||||
{
|
{
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
background: var(--blue-gradient-glassy);
|
background: var(--blue-gradient-glassy);
|
||||||
margin-top: 16px;
|
margin-bottom: 16px;
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
@ -149,6 +151,21 @@ main * .sub-title {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main * .response-option-short-list
|
||||||
|
{
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main * .response-option-short
|
||||||
|
{
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 7px;
|
||||||
|
background: #F1F1F1;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
main .response-option a.response-option-mark {
|
main .response-option a.response-option-mark {
|
||||||
font-size: 16pt;
|
font-size: 16pt;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
@ -160,3 +177,14 @@ main .response-option a.response-option-mark {
|
||||||
border: solid 1px;
|
border: solid 1px;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main .return-button-centerer
|
||||||
|
{
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
main a.return-button
|
||||||
|
{
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import mariadb as mdb
|
||||||
|
import traceback
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
from httputils import parse_query, escape_sql_string
|
||||||
|
|
||||||
|
def readfile(path):
|
||||||
|
if os.path.exists(path):
|
||||||
|
return open(path).read()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
db_connection = mdb.connect(**args)
|
||||||
|
|
||||||
|
args = parse_query(os.environ['QUERY_STRING'])
|
||||||
|
|
||||||
|
if not 'id' in args:
|
||||||
|
print("Content-Type: text/plain; charset=UTF-8\r\n\r\nВи не зазначили ідентифікатор запитання\r\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
cur = db_connection.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(f"SELECT test.id FROM test JOIN question ON test.id = question.tstID WHERE question.id = {args['id']};")
|
||||||
|
test_id = iter(cur).__next__()[0]
|
||||||
|
|
||||||
|
cur.execute(f"DELETE FROM question WHERE id = {args['id']};")
|
||||||
|
db_connection.commit()
|
||||||
|
|
||||||
|
print(f"Location: /index.py?mode=view-test&id={test_id}\r\n\r\n")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Content-Type: text/plain; charset=UTF-8\r\n\r\nНе вдалося видалити запитання ({e})\r\n{traceback.format_exc()}\r\n")
|
|
@ -0,0 +1,41 @@
|
||||||
|
import mariadb as mdb
|
||||||
|
import traceback
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
from httputils import parse_query, escape_sql_string
|
||||||
|
|
||||||
|
def readfile(path):
|
||||||
|
if os.path.exists(path):
|
||||||
|
return open(path).read()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
db_connection = mdb.connect(**args)
|
||||||
|
|
||||||
|
args = parse_query(os.environ['QUERY_STRING'])
|
||||||
|
|
||||||
|
if not 'id' in args:
|
||||||
|
print("Content-Type: text/plain; charset=UTF-8\r\n\r\nВи не зазначили ідентифікатор варіанту відповіді\r\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
cur = db_connection.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(f"SELECT question.id FROM question JOIN response_option ON question.id = response_option.qstID WHERE response_option.id = {args['id']};")
|
||||||
|
quest_id = iter(cur).__next__()[0]
|
||||||
|
|
||||||
|
cur.execute(f"DELETE FROM response_option WHERE id = {args['id']};")
|
||||||
|
db_connection.commit()
|
||||||
|
|
||||||
|
print(f"Location: /index.py?mode=view-question&id={quest_id}\r\n\r\n")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Content-Type: text/plain; charset=UTF-8\r\n\r\nНе вдалося видалити варіант відповіді ({e})\r\n{traceback.format_exc()}\r\n")
|
|
@ -0,0 +1,45 @@
|
||||||
|
import mariadb as mdb
|
||||||
|
import traceback
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
from httputils import parse_query, escape_sql_string
|
||||||
|
|
||||||
|
def readfile(path):
|
||||||
|
if os.path.exists(path):
|
||||||
|
return open(path).read()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
db_connection = mdb.connect(**args)
|
||||||
|
|
||||||
|
args = parse_query(os.environ['QUERY_STRING'])
|
||||||
|
|
||||||
|
if not 'id' in args:
|
||||||
|
print("Content-Type: text/plain; charset=UTF-8\r\n\r\nВи не зазначили ідентифікатор варіанту відповіді\r\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if not 'label' in args:
|
||||||
|
print("Content-Type: text/plain; charset=UTF-8\r\n\r\nВи не зазначили текст варіанту відповіді\r\n")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
cur = db_connection.cursor()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute(f"UPDATE response_option SET label = '{escape_sql_string(args['label'])}' WHERE id = {args['id']};")
|
||||||
|
db_connection.commit()
|
||||||
|
|
||||||
|
cur.execute(f"SELECT question.id FROM question JOIN response_option ON question.id = response_option.qstID WHERE response_option.id = {args['id']};")
|
||||||
|
|
||||||
|
quest_id = iter(cur).__next__()[0]
|
||||||
|
print(f"Location: /index.py?mode=view-question&id={quest_id}\r\n\r\n")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Content-Type: text/plain; charset=UTF-8\r\n\r\nНе вдалося створити нове запитання ({e})\r\n{traceback.format_exc()}\r\n")
|
Loading…
Reference in New Issue