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