From 9742c273defe5105a5b48ab0bd8c6c1315d6934c Mon Sep 17 00:00:00 2001 From: Wojtek Date: Thu, 15 Oct 2026 12:22:11 +0200 Subject: [PATCH] Add frontend test scoring --- README.md | 3 +- .../sql/updates/mysql/1.7.0.sql | 1 + com_testquestions.xml | 2 +- .../language/en-GB/com_testquestions.ini | 7 +++ .../src/Controller/DisplayController.php | 10 ++++ .../src/Model/QuestionsModel.php | 2 +- .../src/View/Questions/HtmlView.php | 49 +++++++++++++++++++ .../tmpl/questions/default.php | 48 +++++++++++++++--- 8 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql diff --git a/README.md b/README.md index 24264b4..a4a42b2 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Szkielet komponentu Joomla 6 do gromadzenia pytań testowych. - frontendowy widok pytań filtrowany po symbolu arkusza egzaminacyjnego, - pozycję menu „Arkusz egzaminacyjny” z opcjonalnym symbolem arkusza, - listę wyboru dostępnych symboli arkuszy na stronie klienckiej, +- możliwość rozwiązania arkusza i wyświetlenia wyniku oraz odpowiedzi poprawnych i zaznaczonych, - opcjonalny symbol testu, rok egzaminu i numer pytania z arkusza, - wskazanie poprawnej odpowiedzi, - opcjonalne zdjęcie wybierane z Media Managera, @@ -21,6 +22,6 @@ Szkielet komponentu Joomla 6 do gromadzenia pytań testowych. ## Instalacja -W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.6.3). +W Joomla wybierz **System → Install → Install from Folder** i wskaż ten katalog. Manifestem instalacyjnym jest `com_testquestions.xml` (wersja 1.7.0). Po instalacji komponent jest dostępny w panelu administratora jako **Pytania testowe**. diff --git a/administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql b/administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql new file mode 100644 index 0000000..c16724d --- /dev/null +++ b/administrator/components/com_testquestions/sql/updates/mysql/1.7.0.sql @@ -0,0 +1 @@ +-- Add client-side test completion and result calculation. diff --git a/com_testquestions.xml b/com_testquestions.xml index edc3bbe..5f2fca2 100644 --- a/com_testquestions.xml +++ b/com_testquestions.xml @@ -4,7 +4,7 @@ 2026-10-14 Komponent GNU General Public License version 2 or later - 1.6.3 + 1.7.0 COM_TESTQUESTIONS_DESCRIPTION com_testquestions Komponent\Component\Testquestions diff --git a/components/com_testquestions/language/en-GB/com_testquestions.ini b/components/com_testquestions/language/en-GB/com_testquestions.ini index aa75e78..5e582de 100644 --- a/components/com_testquestions/language/en-GB/com_testquestions.ini +++ b/components/com_testquestions/language/en-GB/com_testquestions.ini @@ -6,6 +6,13 @@ COM_TESTQUESTIONS_NO_TEST_QUESTIONS="Nie znaleziono pytań dla podanego arkusza. COM_TESTQUESTIONS_TEST_SHEET="Arkusz" COM_TESTQUESTIONS_TIME_LIMIT="Czas" COM_TESTQUESTIONS_POINTS="Punkty" +COM_TESTQUESTIONS_FINISH_TEST="Zakończ test" +COM_TESTQUESTIONS_RESULT="Wynik" +COM_TESTQUESTIONS_ANSWER_CORRECT="Poprawna odpowiedź" +COM_TESTQUESTIONS_ANSWER_INCORRECT="Niepoprawna odpowiedź" +COM_TESTQUESTIONS_CORRECT_ANSWER="Poprawna odpowiedź" +COM_TESTQUESTIONS_CORRECT_PATTERN="Wzorzec poprawnej odpowiedzi" +COM_TESTQUESTIONS_SELECTED_ANSWER="Zaznaczona odpowiedź" COM_TESTQUESTIONS_MENU_ITEM_TITLE="Arkusz egzaminacyjny" COM_TESTQUESTIONS_MENU_ITEM_DESCRIPTION="Wyświetla pytania z wybranego arkusza egzaminacyjnego." COM_TESTQUESTIONS_MENU_TEST_SYMBOL_DESC="Opcjonalnie wybierz symbol arkusza, który ma być wyświetlany po wejściu z menu." diff --git a/components/com_testquestions/src/Controller/DisplayController.php b/components/com_testquestions/src/Controller/DisplayController.php index 5800747..7737332 100644 --- a/components/com_testquestions/src/Controller/DisplayController.php +++ b/components/com_testquestions/src/Controller/DisplayController.php @@ -5,8 +5,18 @@ namespace Komponent\Component\Testquestions\Site\Controller; defined('_JEXEC') or die; use Joomla\CMS\MVC\Controller\BaseController; +use Joomla\CMS\Session\Session; class DisplayController extends BaseController { protected $default_view = 'questions'; + + public function check() + { + if (!Session::checkToken()) { + throw new \RuntimeException('Invalid token', 403); + } + + return $this->display(); + } } diff --git a/components/com_testquestions/src/Model/QuestionsModel.php b/components/com_testquestions/src/Model/QuestionsModel.php index 818d2c0..c3e15cb 100644 --- a/components/com_testquestions/src/Model/QuestionsModel.php +++ b/components/com_testquestions/src/Model/QuestionsModel.php @@ -22,7 +22,7 @@ class QuestionsModel extends ListModel { $db = Factory::getContainer()->get('DatabaseDriver'); $query = $db->getQuery(true) - ->select(['question_type', 'question', 'answer_a', 'answer_b', 'answer_c', 'answer_d', 'answer_open', 'correct_answer', 'image', 'time_limit', 'points']) + ->select(['id', 'test_symbol', 'question_type', 'question', 'answer_a', 'answer_b', 'answer_c', 'answer_d', 'answer_open', 'correct_answer', 'image', 'time_limit', 'points']) ->from($db->quoteName('#__testquestions_questions')) ->where($db->quoteName('published') . ' = 1') ->order($db->quoteName('ordering') . ' ASC, ' . $db->quoteName('id') . ' ASC'); diff --git a/components/com_testquestions/src/View/Questions/HtmlView.php b/components/com_testquestions/src/View/Questions/HtmlView.php index fa735db..c6364ee 100644 --- a/components/com_testquestions/src/View/Questions/HtmlView.php +++ b/components/com_testquestions/src/View/Questions/HtmlView.php @@ -5,12 +5,14 @@ namespace Komponent\Component\Testquestions\Site\View\Questions; defined('_JEXEC') or die; use Joomla\CMS\MVC\View\HtmlView as BaseHtmlView; +use Joomla\CMS\Factory; class HtmlView extends BaseHtmlView { protected $items; protected $symbol; protected $symbols; + protected $results; public function formatSheetLabel($sheet) { @@ -43,6 +45,53 @@ class HtmlView extends BaseHtmlView $this->items = $this->get('Items'); $this->symbol = $this->get('State')->get('filter.test_symbol'); $this->symbols = $this->get('TestSymbols'); + $this->results = $this->buildResults($this->items); return parent::display($tpl); } + + protected function buildResults(array $items) + { + $input = Factory::getApplication()->getInput(); + + if ($input->getCmd('task') !== 'check' || $input->getMethod() !== 'POST') { + return null; + } + + $answers = (array) $input->post->get('answers', [], 'array'); + $results = []; + $correct = 0; + + foreach ($items as $item) { + $selected = isset($answers[$item->id]) ? trim((string) $answers[$item->id]) : ''; + $isCorrect = false; + + if ($item->question_type === 'open') { + $patterns = preg_split('/\s*\|\s*/', (string) $item->answer_open, -1, PREG_SPLIT_NO_EMPTY); + foreach ($patterns as $pattern) { + if (mb_strtolower(trim($pattern), 'UTF-8') === mb_strtolower($selected, 'UTF-8')) { + $isCorrect = true; + break; + } + } + } else { + $isCorrect = (string) $item->correct_answer === $selected; + } + + if ($isCorrect) { + $correct++; + } + + $results[$item->id] = [ + 'selected' => $selected, + 'correct' => $isCorrect, + ]; + } + + return [ + 'items' => $results, + 'correct' => $correct, + 'total' => count($items), + 'percentage' => count($items) ? round(($correct / count($items)) * 100, 2) : 0, + ]; + } } diff --git a/components/com_testquestions/tmpl/questions/default.php b/components/com_testquestions/tmpl/questions/default.php index f720447..70140db 100644 --- a/components/com_testquestions/tmpl/questions/default.php +++ b/components/com_testquestions/tmpl/questions/default.php @@ -23,8 +23,17 @@ use Joomla\CMS\HTML\HTMLHelper;
symbol !== '') : ?>

: symbol, ENT_QUOTES, 'UTF-8'); ?>

+ results) : ?> +
+ : + results['correct']; ?> / results['total']; ?> + (results['percentage'], ENT_QUOTES, 'UTF-8'); ?>%) +
+ + items) : ?> +
items as $number => $item) : ?>

question; ?>

@@ -37,15 +46,42 @@ use Joomla\CMS\HTML\HTMLHelper;

question_type === 'open') : ?> - + -
    -
  1. answer_a; ?>
  2. -
  3. answer_b; ?>
  4. -
  5. answer_c; ?>
  6. -
  7. answer_d; ?>
  8. +
      + $answerField) : ?> + results && $this->results['items'][$item->id]['selected'] === (string) ($answerIndex + 1); ?> + results && (string) $item->correct_answer === (string) ($answerIndex + 1); ?> +
    1. + +
    2. +
    + results) : ?> +
    + results['items'][$item->id]['correct'] ? '✓ ' . \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_ANSWER_CORRECT') : '✗ ' . \Joomla\CMS\Language\Text::_('COM_TESTQUESTIONS_ANSWER_INCORRECT'); ?> + question_type === 'closed') : ?> + — : correct_answer); ?> + + — : answer_open, ENT_QUOTES, 'UTF-8'); ?> + +
    +
+ + + +
+