| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- <?php
- /**
- * TimeKeeper front page controller: shows the student's stats, checks a
- * submitted answer, and asks a question generator for the next question.
- *
- * Changes from the current repo version:
- * - removed adminDev(): for every Main Admin it ran on every page load,
- * printed debug dumps (the students' settings and stats, test numbers),
- * hard-coded two students' user names, set the time zone, and generated a
- * question from the unfinished "continents" generator
- * - checkAnswer() called delete(..., true): the third argument is e107's
- * debug flag, so the SQL was printed on the page after every answer.
- * It's gone, and the user name is escaped
- * - generators are loaded with require_once and an absolute path (include
- * plus ./relative paths depended on the working directory and could
- * redeclare a class)
- * - getQuestion(): generalKnowledge fell through into elements (no break);
- * countries/flags/capitalCities built an elements2 object after including
- * continents.php (class not found). Subjects that aren't built yet now
- * show a plain message instead
- * - getScores()/getLastQuestion(): MPREFIX instead of `e107.e107_...`, user
- * escaped, and a student with no answers this month gets zeros (not
- * null) for the stats
- * - showMessage(): user name escaped
- * - someone with neither the student nor the teacher class now sees a
- * message instead of an empty page
- */
- require_once __DIR__ . '/TkAnswerStore.php';
- class TK2 {
- private $data = [];
- private $subjectsList = [
- // Math
- [['numbers', 1, 0], // number sequences
- ['addition', 1, 10000], // Numeracy adding
- ['subtraction', 1, 20000], // Numeracy minus'
- ['multiplication', 1, 200000], // Numeracy times'
- ['division', 0, 0], // Numeracy deviding
- ['rounding', 0, 0], // Numeracy rounding
- ['worded_add', 0, 0], // Numeracy worded adding
- ['worded_sub', 0, 0], // Numeracy worded minusing
- ['worded_mult', 0, 0], // Numeracy worded timesing
- ['worded_devi', 0, 0], // Numeracy worded deviding
- ],
- // English
- [['spelling', 1, 0], // Plain spelling
- ['anagrams', 1, 4000000], // Shuffle letters in a word
- ],
- // General knowledge
- [['generalKnowledge', 0, 0], // Name kinda says it all
- ],
- // Science
- [['elements', 1, 150000], // Periodic table of elements
- ],
- // Geography
- [['continents', 0, 0], // Point to the continent
- ['countries', 0, 0], // Point to the country
- ['flags', 0, 0], // Name the country by the flag picture
- ['capitalCities', 0, 0], // Name the capital for a country
- ],
- // History
- // Art
- [['artists', 0, 0], // Name the artist for famous paintings
- ],
- // Technology
- // language
- [['malaySpelling', 0, 0], // This could be interesting
- ],
- // Health - Health and Physical Education
- [['nameBodyParts', 0, 0], // Name the body parts from the images or point to the part
- ['nameBodyOrgans', 0, 0], // Name the organs or what they do
- ['basicFirstAid', 0, 0], // Some BASIC things to know for first aid
- ],
- ];
- /**
- * Run the timekeeper functionality
- */
- public function run(): void {
- $isStudent = check_class("TIMEKEEPER_STUDENT");
- $isTeacher = check_class("TIMEKEEPER_TEACHER");
- if ($isStudent) {
- $this->student();
- }
- if ($isTeacher) {
- $this->teacher();
- }
- if (!$isStudent && !$isTeacher) {
- e107::getRender()->tablerender('TimeKeeper', USER ? 'Your account has no TimeKeeper role (student or teacher).' : 'Please log in to use TimeKeeper.');
- }
- }
- /**
- * Run the sequence for teachers
- */
- private function teacher(): void {
- }
- /**
- * Run the sequence for students
- */
- private function student(): void {
- $user = e107::getUser()->getLoginName();
- $this->checkAnswer($user);
- $this->getScores($user);
- $this->showMessage();
- // If the last question has not been answered ask again.
- if ($this->getLastQuestion()) {
- return;
- }
- $this->getSubject();
- $this->getQuestion();
- }
- /**
- * Check if the last question was answered and check the answer
- * @param string $user
- */
- private function checkAnswer($user): void {
- $subject = filter_input(INPUT_POST, 'subject');
- if (is_string($subject) && $subject !== '') {
- require_once __DIR__ . '/answerChecker2.php';
- $ac = new answerChecker2();
- $ac->checkQuestionSubject($subject, $user);
- $db = e107::getDb();
- $db->delete('tkLastQuestion', "user = '" . $db->escape($user) . "'");
- }
- }
- /**
- * Show the last question asked if it hasn't been answered.
- * @return bool True when a question was shown.
- */
- private function getLastQuestion(): bool {
- $db = e107::getDb();
- $lastQuestion = $db->retrieve("SELECT LastQuestionHtml FROM `" . MPREFIX . "tkLastQuestion` WHERE user = '" . $db->escape($this->data['user']) . "' ORDER BY id DESC LIMIT 1");
- if (!empty($lastQuestion)) {
- e107::getRender()->tablerender('Last Question', base64_decode($lastQuestion));
- return true;
- }
- return false;
- }
- /**
- * Get the question based on selected subject
- * @return void
- */
- private function getQuestion(): void {
- switch ($this->data['selectedSubject']) {
- case 'numbers':
- require_once __DIR__ . '/questionGenerators/math/numbers2.php';
- $questionGen = new numbers2($this->data['user']);
- $questionGen->getNumbers();
- break;
- case 'addition':
- case 'subtraction':
- case 'multiplication':
- case 'division':
- case 'rounding':
- require_once __DIR__ . '/questionGenerators/math/numeracy2.php';
- $questionGen = new numeracy2($this->data['user'], $this->data['selectedSubject']);
- $questionGen->getNumeracy();
- break;
- case 'worded_add':
- case 'worded_sub':
- case 'worded_mult':
- case 'worded_devi':
- case 'spelling':
- require_once __DIR__ . '/questionGenerators/english/spelling2.php';
- $questionGen = new spelling2($this->data['user']);
- $questionGen->getSpellingWord();
- break;
- case 'anagrams':
- require_once __DIR__ . '/questionGenerators/english/anagrams.php';
- $questionGen = new anagrams($this->data['user']);
- $questionGen->getanagram();
- break;
- case 'elements':
- require_once __DIR__ . '/questionGenerators/science/elements2.php';
- $questionGen = new elements2($this->data['user']);
- $questionGen->getElements();
- break;
- case 'generalKnowledge':
- case 'continents':
- case 'countries':
- case 'flags':
- case 'capitalCities':
- case 'artists':
- case 'malaySpelling':
- case 'nameBodyParts':
- case 'nameBodyOrgans':
- case 'basicFirstAid':
- default:
- // Not built yet (or unknown): none of these are in the random pool.
- e107::getRender()->tablerender('TimeKeeper', 'This subject is not available yet. Please refresh the page.');
- break;
- }
- }
- /**
- * Get the list of available subjects and select a random one.
- * @return void
- */
- private function getSubject(): void {
- $usableSubjects = [];
- foreach ($this->subjectsList as $subjects) {
- foreach ($subjects as $subject) {
- if ($subject[1] === 1 && $this->data['difficulty'] >= $subject[2]) {
- $usableSubjects[] = $subject[0];
- }
- }
- }
- if (empty($usableSubjects)) {
- $usableSubjects[] = 'numbers';
- }
- $this->data['selectedSubject'] = $usableSubjects[mt_rand(0, count($usableSubjects) - 1)];
- }
- /**
- * Show an initial stats message to the user.
- * @return void
- */
- private function showMessage(): void {
- $text = "You have answered " . (int) $this->data['cntr'] . " questions this month.<br/>"
- . "Your accuacy for the month is " . htmlspecialchars((string) $this->data['percent']) . "%.<br/>"
- . "Your difficulty is set at " . htmlspecialchars((string) $this->data['difficulty']) . ".<br/><br/>";
- e107::getRender()->tablerender('Your stats ' . htmlspecialchars(ucfirst($this->data['user'])), $text);
- }
- /**
- * Get the initial values needed to generate a sane question.
- * @param string $user
- * @return void
- */
- private function getScores($user): void {
- $db = e107::getDb();
- $u = $db->escape($user);
- $returnArray = [];
- $returnArray['totalQuestionCount'] = (int) $db->count('tkReport', '(id)', "username = '{$u}'");
- $db->gen("SELECT COUNT(id) AS cntr, CAST((SUM(correct)/COUNT(id)*100) AS DECIMAL(5,2)) AS percent
- FROM `" . MPREFIX . "tkReport` WHERE username = '{$u}'
- AND answereddatestamp >= UNIX_TIMESTAMP(CURRENT_DATE() - INTERVAL 1 MONTH)
- AND answereddatestamp <= UNIX_TIMESTAMP(CURRENT_DATE() + INTERVAL 1 DAY)");
- $row = $db->fetch();
- $returnArray['cntr'] = is_array($row) ? (int) $row['cntr'] : 0;
- $returnArray['percent'] = (is_array($row) && $row['percent'] !== null) ? (float) $row['percent'] : 0;
- $returnArray['user'] = $user;
- $returnArray['difficulty'] = $returnArray['totalQuestionCount'] * $returnArray['percent'];
- $this->data = $returnArray;
- }
- }
|