function test_auto_marking_sc($request) { Authenticator::assert_manager_or_professor($request->cookies['authToken']); $msg = new Messages($GLOBALS['locale'], '/new-question/errors'); try { $model = new Model(); $raw_input = $request->getBody(); $content_type = explode(';', $request->type)[0]; if ($content_type !== 'application/json') { Util::output_errors_and_die($msg->_('invalid-format'), 415); } $input_data = json_decode($raw_input, true); if (empty($input_data) || !isset($input_data['question']) || !isset($input_data['source-code']) || !is_string($input_data['source-code'])) { Util::output_errors_and_die($msg->_('invalid-format'), 400); } $extra = !empty($input_data['extra']) ? $input_data['extra'] : []; $qd = $input_data['question']; set_empty_if_undefined($qd['type']); if ($qd['type'] != 'source-code') { Util::output_errors_and_die('', 400); } $q = new QuestionSC($qd, Question::FROM_USER, $extra); $q->mark_automatically(array('source-code' => $input_data['source-code']), $log, $result); http_response_code(200); header('Content-Type: application/json'); echo my_json_encode($result); } catch (DatabaseException $e) { Util::output_errors_and_die($e->getMessage(), 503); } catch (Exception $e) { Util::output_errors_and_die($e->getMessage(), 400); } }
public function from_user($qd, $extra = []) { parent::from_user($qd, $extra); $err = []; if ($this->auto_marking_method == 'binary') { if (!isset($qd['answer']) || empty($qd['answer'])) { $err['answer'] = $this->msg->_('sa-missing-answer'); } elseif (!Validator::validate_answer($qd['answer'])) { $err['answer'] = $this->msg->_('answer-too-long'); } else { $this->raw_answer = $qd['answer']; } $r = Validator::build_regex($qd['answer'], 'iu', true); if (!$r) { $err['answer'] = $this->msg->_('invalid-regex', [$qd['answer']]); } else { $this->answer = $r; } } else { set_empty_if_undefined($qd['answer']); if (!Validator::validate_answer($qd['answer'])) { $err['answer'] = $this->msg->_('answer-too-long'); } else { $this->answer = $qd['answer']; } } if ($err) { throw new Exception(my_json_encode($err)); } }
function test_auto_marking($request) { Authenticator::assert_manager_or_professor($request->cookies['authToken']); $msg = new Messages($GLOBALS['locale'], '/new-question/errors'); try { $model = new Model(); $raw_input = $request->getBody(); $content_type = explode(';', $request->type)[0]; if ($content_type !== 'application/json') { Util::output_errors_and_die($msg->_('invalid-format'), 415); } $input_data = json_decode($raw_input, true); if (empty($input_data) || !isset($input_data['question']) || !isset($input_data['studentAnswer'])) { Util::output_errors_and_die($msg->_('invalid-format'), 400); } $extra = !empty($input_data['extra']) ? $input_data['extra'] : []; $qd = $input_data['question']; set_empty_if_undefined($qd['type']); if (!Validator::validate_question_type($qd['type'])) { Util::output_errors_and_die($msg->_('invalid-type'), 400); } switch ($qd['type']) { case 'short-answer': $q = new QuestionSA($qd, Question::FROM_USER, $extra); break; case 'essay': $q = new QuestionES($qd, Question::FROM_USER, $extra); break; case 'multiple-choice': $q = new QuestionMC($qd, Question::FROM_USER, $extra); break; case 'matching': $q = new QuestionMA($qd, Question::FROM_USER, $extra); break; case 'fitb-type': $q = new QuestionFT($qd, Question::FROM_USER, $extra); break; case 'fitb-select': $q = new QuestionFS($qd, Question::FROM_USER, $extra); break; case 'source-code': $q = new QuestionSC($qd, Question::FROM_USER, $extra); break; } http_response_code(200); header('Content-Type: application/json'); $mark = $q->mark_automatically($input_data['studentAnswer'], $log); foreach ($log as $i => $line) { $log[$i] = $msg->_('/auto-marking/' . $line[0], $line[1]); } $log = implode('<br/>', $log); echo my_json_encode(array('log' => $log, 'mark' => $mark)); } catch (DatabaseException $e) { Util::output_errors_and_die($e->getMessage(), 503); } catch (Exception $e) { Util::output_errors_and_die($e->getMessage(), 400); } }
function create_session($request) { $raw_input = $request->getBody(); $content_type = explode(';', $request->type)[0]; switch ($content_type) { case 'application/json': $input_data = json_decode($raw_input, true); break; case 'application/x-www-form-urlencoded': $input_data = array(); parse_str($raw_input, $input_data); break; default: Util::output_errors_and_die('', 415); } if ($input_data === null) { Util::output_errors_and_die('', 400); } set_empty_if_undefined($input_data['username_or_email']); set_empty_if_undefined($input_data['password']); $msg = new Messages($GLOBALS['locale'], '/signin'); try { $model = new Model(); $user_data = $model->is_valid_user($input_data['username_or_email'], $input_data['password']); if (!$user_data) { Util::output_errors_and_die($msg->_('invalid-username-pw'), 403); } switch ($user_data['status']) { case 'pending-activation': Util::output_errors_and_die($msg->_('pending-activation'), 403); break; case 'pending-approval': Util::output_errors_and_die($msg->_('pending-approval'), 403); break; case 'banned': Util::output_errors_and_die($msg->_('banned'), 403); break; case 'active': $token = generate_token($user_data); $now = new DateTime('now'); $expires_at = clone $now; $expires_at->add(new DateInterval('P7D')); $model->insert_auth_token($user_data['user_id'], $token, $now, $expires_at); http_response_code(201); $output = array('token' => $token, 'expires_at' => $expires_at->format('Y-m-d H:i:s')); setcookie('authToken', $token, $expires_at->getTimestamp(), '/', '', $secure = true, $httponly = true); header('Content-Type: application/json'); echo my_json_encode($output); die; break; } } catch (DatabaseException $e) { Util::output_errors_and_die($e->getMessage(), 503); } catch (Exception $e) { Util::output_errors_and_die($e->getMessage(), 400); } }
function create_user($request) { $raw_input = $request->getBody(); $content_type = explode(';', $request->type)[0]; switch ($content_type) { case 'application/json': $input_data = json_decode($raw_input, true); break; case 'application/x-www-form-urlencoded': $input_data = array(); parse_str($raw_input, $input_data); break; default: Util::output_errors_and_die('', 415); } if ($input_data === null) { Util::output_errors_and_die('', 400); } $fields = array('full_name' => '', 'email' => '', 'gender' => '', 'birth_date' => '', 'username' => '', 'role' => ''); $user_data = array(); foreach ($fields as $f => $v) { if (array_key_exists($f, $input_data)) { if (!is_string($input_data[$f])) { Util::output_errors_and_die('', 400); } $user_data[$f] = trim($input_data[$f]); } else { $user_data[$f] = ''; } } if (isset($input_data['password'])) { if (!is_string($input_data['password'])) { Util::output_errors_and_die('', 400); } $user_data['password'] = $input_data['password']; } set_empty_if_undefined($user_data['password']); try { $model = new Model(); $uid = $model->create_user($user_data); } catch (DatabaseException $e) { Util::output_errors_and_die($e->getMessage(), 503); } catch (ConflictException $e) { Util::output_errors_and_die($e->getMessage(), 409); } catch (Exception $e) { Util::output_errors_and_die($e->getMessage(), 400); } if ($uid) { http_response_code(201); header('Content-Type: text/plain'); echo '/users/' . $uid; die; } else { Util::output_errors_and_die('', 400); } }
function create_question($request, $assignment_id = null) { Authenticator::assert_manager_or_professor($request->cookies['authToken']); $msg = new Messages($GLOBALS['locale']); try { $model = new Model(); $raw_input = $request->getBody(); $content_type = explode(';', $request->type)[0]; if ($content_type !== 'application/json') { Util::output_errors_and_die('', 415); } $input_data = json_decode($raw_input, true); if (empty($input_data)) { Util::output_errors_and_die('', 400); } set_empty_if_undefined($input_data['type']); if (!Validator::validate_question_type($input_data['type'])) { Util::output_errors_and_die($msg->_('invalid-type'), 400); } switch ($input_data['type']) { case 'short-answer': $q = new QuestionSA($input_data, Question::FROM_USER); break; case 'essay': $q = new QuestionES($input_data, Question::FROM_USER); break; case 'multiple-choice': $q = new QuestionMC($input_data, Question::FROM_USER); break; case 'matching': $q = new QuestionMA($input_data, Question::FROM_USER); break; case 'fitb-type': $q = new QuestionFT($input_data, Question::FROM_USER); break; case 'fitb-select': $q = new QuestionFS($input_data, Question::FROM_USER); break; case 'source-code': $q = new QuestionSC($input_data, Question::FROM_USER); break; } $qid = $model->create_question($q); header('Content-Type: text/plain'); echo '/question_bank/questions/' . $qid; http_response_code(201); die; } catch (ConflictException $e) { Util::output_errors_and_die($e->getMessage(), 409); } catch (DatabaseException $e) { Util::output_errors_and_die($e->getMessage(), 503); } catch (Exception $e) { Util::output_errors_and_die($e->getMessage(), 400); } }
function test_question($request) { Authenticator::assert_manager_or_professor($request->cookies['authToken']); $msg = new Messages($GLOBALS['locale'], '/new-question/errors'); try { $model = new Model(); $raw_input = $request->getBody(); $content_type = explode(';', $request->type)[0]; if ($content_type !== 'application/json') { Util::output_errors_and_die($msg->_('invalid-format'), 415); } $input_data = json_decode($raw_input, true); if (empty($input_data)) { Util::output_errors_and_die($msg->_('invalid-format'), 400); } set_empty_if_undefined($input_data['type']); if (!Validator::validate_question_type($input_data['type'])) { Util::output_errors_and_die($msg->_('invalid-type'), 400); } switch ($input_data['type']) { case 'short-answer': $q = new QuestionSA($input_data, Question::FROM_USER); break; case 'essay': $q = new QuestionES($input_data, Question::FROM_USER); break; case 'multiple-choice': $q = new QuestionMC($input_data, Question::FROM_USER); break; case 'matching': $q = new QuestionMA($input_data, Question::FROM_USER); break; case 'fitb-type': $q = new QuestionFT($input_data, Question::FROM_USER); break; case 'fitb-select': $q = new QuestionFS($input_data, Question::FROM_USER); break; case 'source-code': $q = new QuestionSC($input_data, Question::FROM_USER); break; } http_response_code(200); header('Content-Type: application/json'); echo my_json_encode($q->to_auto_marking_test(true, true)); } catch (DatabaseException $e) { Util::output_errors_and_die($e->getMessage(), 503); } catch (Exception $e) { Util::output_errors_and_die($e->getMessage(), 400); } }
public function from_user($qd, $extra = []) { parent::from_user($qd, $extra); $err = []; set_empty_if_undefined($qd['answer']); if (!Validator::validate_answer($qd['answer'])) { $err['answer'] = $this->msg->_('answer-too-long'); } else { $this->answer = $qd['answer']; } if ($err) { throw new Exception(my_json_encode($err)); } }
function create_programming_language($pl_data, $existing_id = null) { if (empty($pl_data)) { throw new Exception('-'); } $this->conn->beginTransaction(); if ($existing_id) { // ensure that it exists $sql = 'SELECT COUNT(*) AS n FROM `programming_language` WHERE programming_language_id = ?;'; $s = $this->conn->prepare($sql); if (!$s) { $this->conn->rollBack(); throw new DatabaseException($this->conn->errorInfo()[2]); } if (!$s->execute(array($existing_id))) { $this->conn->rollBack(); throw new DatabaseException($s->errorInfo()[2]); } $n = $s->fetch(PDO::FETCH_ASSOC)['n']; if ($n < 1) { $this->conn->rollBack(); return false; } $sql = 'DELETE FROM `compiler_flag` WHERE programming_language_id = ?;'; $s = $this->conn->prepare($sql); if (!$s) { $this->conn->rollBack(); throw new DatabaseException($this->conn->errorInfo()[2]); } if (!$s->execute(array($existing_id))) { $this->conn->rollBack(); throw new DatabaseException($s->errorInfo()[2]); } } $fields = array('display_name', 'default_extension', 'editor_mode', 'check_command', 'compile_command', 'run_command'); foreach ($fields as $f) { set_empty_if_undefined($pl_data[$f]); if (!is_string($pl_data[$f])) { throw new Exception(); } $pl_data[$f] = trim($pl_data[$f]); } $pl_data['default_extension'] = trim(preg_replace('/^\\.+/', '', trim($pl_data['default_extension']))); $err = array(); $this->msg->set_root('/new-pl/errors'); if (!Validator::validate_pl_display_name($pl_data['display_name'])) { $err['display_name'] = $this->msg->_('display_name', array(1, 32)); } if (!Validator::validate_pl_extension($pl_data['default_extension'])) { $err['default_extension'] = $this->msg->_('default_extension', array(1, 7)); } if (!Validator::validate_pl_editor_mode($pl_data['editor_mode'])) { $err['editor_mode'] = $this->msg->_('editor_mode', array(31)); } if (!Validator::validate_pl_command($pl_data['check_command'], false)) { $err['check_command'] = $this->msg->_('check_command', array(127)); } if (!Validator::validate_pl_command($pl_data['compile_command'], false)) { $err['compile_command'] = $this->msg->_('compile_command', array(127)); } if (!Validator::validate_pl_command($pl_data['run_command'], true)) { $err['run_command'] = $this->msg->_('run_command', array(1, 127)); } if (!empty($err)) { $this->conn->rollBack(); throw new Exception(my_json_encode($err)); } if (!$existing_id) { $sql1 = 'INSERT IGNORE INTO `programming_language` (display_name, default_extension, editor_mode, check_command, compile_command, run_command) VALUES (:display_name, :default_extension, :editor_mode, :check_command, :compile_command, :run_command);'; } else { $sql1 = 'UPDATE `programming_language` SET display_name = :display_name, default_extension = :default_extension, editor_mode = :editor_mode, check_command = :check_command, compile_command = :compile_command, run_command = :run_command WHERE programming_language_id = :pl_id;'; } $s = $this->conn->prepare($sql1); if (!$s) { throw new DatabaseException($this->conn->errorInfo()[2]); $this->conn->rollBack(); } $s->bindValue(':display_name', $pl_data['display_name']); $s->bindValue(':default_extension', $pl_data['default_extension']); $s->bindValue(':editor_mode', null_if_empty_string($pl_data['editor_mode'])); $s->bindValue(':check_command', null_if_empty_string($pl_data['check_command'])); $s->bindValue(':compile_command', null_if_empty_string($pl_data['compile_command'])); $s->bindValue(':run_command', $pl_data['run_command']); if ($existing_id) { $s->bindValue(':pl_id', $existing_id); } if (!$s->execute()) { $this->conn->rollBack(); throw new DatabaseException($s->errorInfo()[2]); } if (!$existing_id and $s->rowCount() < 1) { $this->conn->rollBack(); $err[] = array('display_name' => $this->msg->_('display_name.exists')); throw new ConflictException(my_json_encode($err)); } $lid = $existing_id ? $existing_id : $this->conn->lastInsertId(); if (!isset($pl_data['compiler_flags'])) { $pl_data['compiler_flags'] = array(); } $sql2 = 'INSERT INTO `compiler_flag` (flag, description, programming_language_id, enabled_by_default) VALUES '; $sql2_items = array(); $arguments2 = array(); foreach ($pl_data['compiler_flags'] as $cf) { set_empty_if_undefined($cf['flag']); set_empty_if_undefined($cf['description']); if (!isset($cf['enabled_by_default'])) { $cf['enabled_by_default'] = false; } if (!Validator::validate_pl_flag($cf['flag'])) { $err['flag'] = $this->msg->_('flag', [1, 31]); } if (!Validator::validate_boolean($cf['enabled_by_default'])) { $l = array(1, 31); $err['enabled_by_default'] = $this->msg->_('enabled_by_default', $l); } if (!Validator::validate_pl_flag_description($cf['description'])) { $err['description'] = $this->msg->_('flag_description', [63]); } $sql2_items[] = ' (?, ?, ?, ?)'; $arguments2[] = $cf['flag']; $arguments2[] = null_if_empty_string($cf['description']); $arguments2[] = $lid; $arguments2[] = (int) $cf['enabled_by_default']; } if (!empty($err)) { $this->conn->rollBack(); throw new Exception(my_json_encode($err)); } if ($arguments2) { $sql2 .= implode(', ', $sql2_items) . ';'; $s = $this->conn->prepare($sql2); if (!$s) { $this->conn->rollBack(); throw new DatabaseException($this->conn->errorInfo()[2]); } if (!$s->execute($arguments2)) { $this->conn->rollBack(); throw new DatabaseException($s->errorInfo()[2]); } } $this->conn->commit(); return $lid; }
protected function from_user($qd, $extra = array()) { $msg = $this->msg; $msg->set_root('/new-question/errors'); $err = array(); $this->is_template = true; $this->assignment_id = null; if (!isset($qd['title'])) { $qd['title'] = ''; } if (!Validator::validate_question_title($qd['title'])) { $err['title'] = $msg->_('title', [1, 127]); } $this->title = $qd['title']; if (!isset($qd['programming_language_id'])) { $this->programming_language_id = null; $this->programming_language_name = null; } else { $pl = $this->model->get_programming_language($qd['programming_language_id']); if (!$pl) { $err['programming_language_id'] = $msg->_('pl_id'); } $this->programming_language_id = $qd['programming_language_id']; $this->programming_language_name = $pl['display_name']; $this->programming_language_data = $pl; } if (!isset($qd['topic_ids'])) { $this->topic_ids = []; } elseif (!is_array($qd['topic_ids'])) { $err['topic_ids'] = $msg->_('invalid-format'); } else { $this->topic_ids = []; $this->topic_names = []; $all_topics = array(); foreach ($this->model->get_topics()['items'] as $t) { $all_topics[$t['topic_id']] = $t['name']; } foreach (array_unique($qd['topic_ids']) as $id) { if (!array_key_exists($id, $all_topics)) { if (!isset($err['topic_ids'])) { $err['topic_ids'] = ''; } $err['topic_ids'] .= $msg->_('topic_ids', [$id]) . ' '; } else { $this->topic_ids[] = $id; $this->topic_names[] = $all_topics[$id]; } } } if (isset($qd['new_topics'])) { if (!is_array($qd['topic_ids'])) { $err['new_topics'] = $msg->_('invalid-format'); } else { foreach ($qd['new_topics'] as $topic) { if (!Validator::validate_topic($topic)) { $err['new_topics'] = $msg->_('/new-topic/error', array(1, 127)); } elseif (in_array($topic, $all_topics)) { $this->topic_ids[] = array_search($topic, $all_topics); $this->topic_names[] = $topic; } else { $this->topic_ids[] = null; $this->topic_names[] = $topic; } } } } set_empty_if_undefined($qd['level']); if (!Validator::validate_level($qd['level'])) { $err['level'] = $msg->_('level'); } else { $this->level = $qd['level']; } if (!isset($qd['auto_marking_method']) || $qd['auto_marking_method'] === 'none') { $qd['auto_marking_method'] = null; } elseif (in_array($qd['auto_marking_method'], $this->valid_amm, true)) { $this->auto_marking_method = $qd['auto_marking_method']; } else { $err['auto_marking_method'] = $msg->_('amm', array('nMethods' => count($this->valid_amm), 'methods' => implode('; ', $this->valid_amm))); } set_empty_if_undefined($qd['body']); $qd['body'] = Validator::clean_HTML($qd['body']); if (!Validator::validate_question_body($qd['body'])) { $err['body'] = $msg->_('body-too-long'); } else { $this->body = $qd['body']; } if ($err) { throw new Exception(my_json_encode($err)); } }
public function from_user($qd, $extra = array()) { parent::from_user($qd, $extra); $err = []; if (!isset($qd['answers'])) { $qd['answers'] = array(); } if (!is_array($qd['answers'])) { $err['answers'] = $msg->_('invalid-format'); } if (!empty($extra)) { if (!is_array($extra) || count($extra) != count($qd['answers'])) { $k = '/new-question/errors/invalid-format'; throw new Exception($this->msg->_($k)); } } else { $extra = false; } foreach ($qd['answers'] as $i => $a) { if (!is_array($a) || !(!empty($a['text']) && isset($a['fixed']) && isset($a['correct'])) || !is_string($a['text']) || !Validator::validate_boolean($a['fixed']) || !Validator::validate_boolean($a['correct'])) { $err['answers'] = $this->msg->_('invalid-format'); break; } if (!$extra) { $hex_strings = []; do { $hex_id = '#' . random_hex_string(8); } while (in_array($hex_id, $hex_strings)); // just in case $hex_strings[] = $hex_id; } else { $hex_id = $extra[$i]; } $this->answers[] = array('text' => $a['text'], 'correct' => $a['correct'], 'fixed' => $a['fixed'], 'hex_id' => $hex_id); $this->answers_by_hex_id[$hex_id] = array('text' => $a['text'], 'correct' => $a['correct'], 'fixed' => $a['fixed']); if ($a['correct']) { $this->correct_answers++; } $this->total_answers++; } if ($this->total_answers == 0) { $err['answers'] = $this->msg->_('mc-at-least-one'); } if ($err) { throw new Exception(my_json_encode($err)); } set_empty_if_undefined($qd['min_answers']); set_empty_if_undefined($qd['max_answers']); if (!is_int($qd['min_answers'])) { $err['min_answers'] = $this->msg->_('invalid-format'); } elseif (!($qd['min_answers'] >= 0) || !($qd['min_answers'] <= $this->correct_answers)) { $err['min_answers'] = $this->msg->_('mc-min'); } elseif ($qd['min_answers'] == $this->total_answers) { $err['min-answers'] = $this->msg->_('mc-min-lt-total'); } else { $this->min_answers = $qd['min_answers']; } if (!is_int($qd['max_answers'])) { $err['max_answers'] = $this->msg->_('invalid-format'); } elseif (!($qd['max_answers'] >= $this->correct_answers) || !($qd['max_answers'] <= $this->total_answers)) { $err['max_answers'] = $this->msg->_('mc-max'); } elseif ($qd['max_answers'] == 0) { $err['max-answers'] = $this->msg->_('mc-max-gt-zero'); } else { $this->max_answers = $qd['max_answers']; } if ($err) { throw new Exception(my_json_encode($err)); } }