示例#1
0
$line_width = 0.177;
// about half point
$circle_radius = $line_width * 11;
$circle_width = 2 * $circle_radius + $line_width;
$circle_shift = $circle_width + $line_width;
$circle_half_width = $circle_width / 2;
$align_mark_color = array(0, 0, 0);
$align_mark_width = $line_width * 7;
$align_mark_lenght = $line_width * 22;
$align_mark_shift = $line_width * 8;
$row_height = $circle_width + 8 * $line_width;
$line_style = array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color' => $grid_color);
// define barcode style
$bstyle = array('position' => '', 'align' => 'C', 'stretch' => false, 'fitwidth' => false, 'cellfitalign' => '', 'border' => false, 'hpadding' => 0, 'vpadding' => 0, 'fgcolor' => array(0, 0, 0), 'bgcolor' => false, 'text' => false);
// get test data
$testdata = F_getTestData($test_id);
$test_random_questions_select = F_getBoolean($testdata['test_random_questions_select']);
$test_random_questions_order = F_getBoolean($testdata['test_random_questions_order']);
$test_questions_order_mode = intval($testdata['test_questions_order_mode']);
$test_random_answers_select = F_getBoolean($testdata['test_random_answers_select']);
$test_random_answers_order = F_getBoolean($testdata['test_random_answers_order']);
$test_answers_order_mode = intval($testdata['test_answers_order_mode']);
$random_questions = ($test_random_questions_select or $test_random_questions_order);
$sql_answer_position = '';
if (!$test_random_answers_order and $test_answers_order_mode == 0) {
    $sql_answer_position = ' AND answer_position>0';
}
$sql_questions_order_by = '';
switch ($test_questions_order_mode) {
    case 0:
        // position
/**
 * Display a textarea for user's comment.<br>
 * @param $test_id (int) test ID
 * @return string XHTML code
 * @since 4.0.000 (2006-10-01)
 */
function F_testComment($test_id)
{
    require_once '../config/tce_config.php';
    global $db, $l;
    $test_id = intval($test_id);
    $td = F_getTestData($test_id);
    $user_id = intval($_SESSION['session_user_id']);
    $str = '';
    // user's comment
    if (F_getBoolean($td['test_comment_enabled'])) {
        // get user's test comment
        $comment = '';
        $sql = 'SELECT testuser_comment
		FROM ' . K_TABLE_TEST_USER . '
		WHERE testuser_user_id=' . $user_id . '
			AND testuser_test_id=' . $test_id . '
		LIMIT 1';
        if ($r = F_db_query($sql, $db)) {
            if ($m = F_db_fetch_array($r)) {
                $comment = $m['testuser_comment'];
            }
        } else {
            F_display_db_error();
        }
        $str .= '<label for="testcomment">' . $l['w_comment'] . '</label><br />';
        $str .= '<textarea cols="' . K_ANSWER_TEXTAREA_COLS . '" rows="4" name="testcomment" id="testcomment" class="answertext" title="' . $l['h_testcomment'] . '">' . $comment . '</textarea><br />' . K_NEWLINE;
    }
    return $str;
}
/**
 * Import user's test data from OMR.
 * @param $user_id (int) user ID.
 * @param $date (string) date-time field.
 * @param $omr_testdata (array) Array containing test data.
 * @param $omr_answers (array) Array containing test answers (from OMR).
 * @return boolean TRUE in case of success, FALSE otherwise.
 */
function F_importOMRTestData($user_id, $date, $omr_testdata, $omr_answers)
{
    require_once '../config/tce_config.php';
    require_once '../../shared/code/tce_functions_test.php';
    global $db, $l;
    // check arrays
    if (count($omr_testdata) > count($omr_answers) + 1) {
        // arrays must contain the same amount of questions
        return false;
    }
    $test_id = intval($omr_testdata[0]);
    $user_id = intval($user_id);
    $time = strtotime($date);
    $date = date(K_TIMESTAMP_FORMAT, $time);
    $dateanswers = date(K_TIMESTAMP_FORMAT, $time + 1);
    // check user's group
    if (F_count_rows(K_TABLE_USERGROUP . ', ' . K_TABLE_TEST_GROUPS . ' WHERE usrgrp_group_id=tstgrp_group_id AND tstgrp_test_id=' . $test_id . ' AND usrgrp_user_id=' . $user_id . ' LIMIT 1') == 0) {
        return false;
    }
    // get test data
    $testdata = F_getTestData($test_id);
    // 1. delete previous test data
    $sqld = 'DELETE FROM ' . K_TABLE_TEST_USER . ' WHERE testuser_test_id=' . $test_id . ' AND testuser_user_id=' . $user_id . '';
    if (!($rd = F_db_query($sqld, $db))) {
        F_display_db_error();
    }
    // 2. create new user's test entry
    // ------------------------------
    $sql = 'INSERT INTO ' . K_TABLE_TEST_USER . ' (
		testuser_test_id,
		testuser_user_id,
		testuser_status,
		testuser_creation_time,
		testuser_comment
		) VALUES (
		' . $test_id . ',
		' . $user_id . ',
		4,
		\'' . $date . '\',
		\'OMR\'
		)';
    if (!($r = F_db_query($sql, $db))) {
        F_display_db_error(false);
        return false;
    } else {
        // get inserted ID
        $testuser_id = F_db_insert_id($db, K_TABLE_TEST_USER, 'testuser_id');
    }
    // 3. create test log entries
    $num_questions = count($omr_testdata) - 1;
    // for each question on array
    for ($q = 1; $q <= $num_questions; ++$q) {
        $question_id = intval($omr_testdata[$q][0]);
        $num_answers = count($omr_testdata[$q][1]);
        // get question data
        $sqlq = 'SELECT question_type, question_difficulty FROM ' . K_TABLE_QUESTIONS . ' WHERE question_id=' . $question_id . ' LIMIT 1';
        if ($rq = F_db_query($sqlq, $db)) {
            if ($mq = F_db_fetch_array($rq)) {
                // question scores
                $question_right_score = $testdata['test_score_right'] * $mq['question_difficulty'];
                $question_wrong_score = $testdata['test_score_wrong'] * $mq['question_difficulty'];
                $question_unanswered_score = $testdata['test_score_unanswered'] * $mq['question_difficulty'];
                // add question
                $sqll = 'INSERT INTO ' . K_TABLE_TESTS_LOGS . ' (
					testlog_testuser_id,
					testlog_question_id,
					testlog_score,
					testlog_creation_time,
					testlog_display_time,
					testlog_reaction_time,
					testlog_order,
					testlog_num_answers
					) VALUES (
					' . $testuser_id . ',
					' . $question_id . ',
					' . $question_unanswered_score . ',
					\'' . $date . '\',
					\'' . $date . '\',
					1,
					' . $q . ',
					' . $num_answers . '
					)';
                if (!($rl = F_db_query($sqll, $db))) {
                    F_display_db_error(false);
                    return false;
                }
                $testlog_id = F_db_insert_id($db, K_TABLE_TESTS_LOGS, 'testlog_id');
                // set initial question score
                if ($mq['question_type'] == 1) {
                    // MCSA
                    $qscore = $question_unanswered_score;
                } else {
                    // MCMA
                    $qscore = 0;
                }
                $unanswered = true;
                // for each question on array
                for ($a = 1; $a <= $num_answers; ++$a) {
                    $answer_id = intval($omr_testdata[$q][1][$a]);
                    if (isset($omr_answers[$q][$a])) {
                        $answer_selected = $omr_answers[$q][$a];
                        //-1, 0, 1
                    } else {
                        $answer_selected = -1;
                    }
                    // add answer
                    $sqli = 'INSERT INTO ' . K_TABLE_LOG_ANSWER . ' (
						logansw_testlog_id,
						logansw_answer_id,
						logansw_selected,
						logansw_order
						) VALUES (
						' . $testlog_id . ',
						' . $answer_id . ',
						' . $answer_selected . ',
						' . $a . '
						)';
                    if (!($ri = F_db_query($sqli, $db))) {
                        F_display_db_error(false);
                        return false;
                    }
                    // calculate question score
                    if ($mq['question_type'] < 3) {
                        // MCSA or MCMA
                        // check if the answer is right
                        $answer_isright = false;
                        $sqla = 'SELECT answer_isright FROM ' . K_TABLE_ANSWERS . ' WHERE answer_id=' . $answer_id . ' LIMIT 1';
                        if ($ra = F_db_query($sqla, $db)) {
                            if ($ma = F_db_fetch_array($ra)) {
                                $answer_isright = F_getBoolean($ma['answer_isright']);
                                switch ($mq['question_type']) {
                                    case 1:
                                        // MCSA - Multiple Choice Single Answer
                                        if ($answer_selected == 1) {
                                            $unanswered = false;
                                            if ($answer_isright) {
                                                $qscore = $question_right_score;
                                            } else {
                                                $qscore = $question_wrong_score;
                                            }
                                        }
                                        break;
                                    case 2:
                                        // MCMA - Multiple Choice Multiple Answer
                                        if ($answer_selected == -1) {
                                            $qscore += $question_unanswered_score;
                                        } elseif ($answer_selected == 0) {
                                            $unanswered = false;
                                            if ($answer_isright) {
                                                $qscore += $question_wrong_score;
                                            } else {
                                                $qscore += $question_right_score;
                                            }
                                        } elseif ($answer_selected == 1) {
                                            $unanswered = false;
                                            if ($answer_isright) {
                                                $qscore += $question_right_score;
                                            } else {
                                                $qscore += $question_wrong_score;
                                            }
                                        }
                                        break;
                                }
                            }
                        } else {
                            F_display_db_error(false);
                            return false;
                        }
                    }
                }
                // end for each answer
                if ($mq['question_type'] == 2) {
                    // MCMA
                    // normalize score
                    if (F_getBoolean($testdata['test_mcma_partial_score'])) {
                        // use partial scoring for MCMA and ORDER questions
                        $qscore = round($qscore / $num_answers, 3);
                    } else {
                        // all-or-nothing points
                        if ($qscore >= $question_right_score * $num_answers) {
                            // right
                            $qscore = $question_right_score;
                        } elseif ($qscore == $question_unanswered_score * $num_answers) {
                            // unanswered
                            $qscore = $question_unanswered_score;
                        } else {
                            // wrong
                            $qscore = $question_wrong_score;
                        }
                    }
                }
                if ($unanswered) {
                    $change_time = '';
                } else {
                    $change_time = $dateanswers;
                }
                // update question score
                $sqll = 'UPDATE ' . K_TABLE_TESTS_LOGS . ' SET
					testlog_score=' . $qscore . ',
					testlog_change_time=' . F_empty_to_null($change_time) . ',
					testlog_reaction_time=1000
					WHERE testlog_id=' . $testlog_id . '';
                if (!($rl = F_db_query($sqll, $db))) {
                    F_display_db_error();
                    return false;
                }
            }
        } else {
            F_display_db_error(false);
            return false;
        }
    }
    // end for each question
    return true;
}
/**
* Returns raw statistic array for the selected test.
* @param $test_id (int) test ID.
* @param $group_id (int) group ID - if greater than zero, filter stats for the specified user group.
* @param $user_id (int) user ID - if greater than zero, filter stats for the specified user.
* @param $startdate (int) start date ID - if greater than zero, filter stats for the specified starting date
* @param $enddate (int) end date ID - if greater than zero, filter stats for the specified ending date
* @param $testuser_id (int) test-user ID - if greater than zero, filter stats for the specified test-user.
* @param $data (array) Array of existing data to be merged with the current one.
* @param $pubmode (boolean) If true filter the results for the public interface.
* return $data array containing test statistics.
*/
function F_getRawTestStat($test_id, $group_id = 0, $user_id = 0, $startdate = 0, $enddate = 0, $testuser_id = 0, $data = array(), $pubmode = false)
{
    require_once '../config/tce_config.php';
    require_once '../../shared/code/tce_functions_authorization.php';
    require_once '../../shared/code/tce_functions_test.php';
    global $db, $l;
    $test_id = intval($test_id);
    $group_id = intval($group_id);
    $user_id = intval($user_id);
    $testuser_id = intval($testuser_id);
    // query to calculate total number of questions
    $sqltot = K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS;
    $sqltb = K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_ANSWERS . ', ' . K_TABLE_LOG_ANSWER;
    $sqlm = K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_QUESTIONS . ', ' . K_TABLE_SUBJECTS . ', ' . K_TABLE_MODULES . '';
    // apply filters
    $sqlw = 'WHERE testlog_testuser_id=testuser_id';
    $sqlansw = 'WHERE logansw_answer_id=answer_id AND logansw_testlog_id=testlog_id AND testlog_testuser_id=testuser_id';
    if ($test_id > 0) {
        $sqlw .= ' AND testuser_test_id=' . $test_id . '';
        $sqlansw .= ' AND testuser_test_id=' . $test_id . '';
    } elseif ($pubmode) {
        $test_ids_results = F_getTestIDResults($test_id, $user_id);
        $sqlw .= ' AND testuser_test_id IN (' . $test_ids_results . ')';
        $sqlansw .= ' AND testuser_test_id IN (' . $test_ids_results . ')';
    }
    if ($user_id > 0) {
        $sqltot .= ', ' . K_TABLE_USERS;
        $sqltb .= ', ' . K_TABLE_USERS;
        $sqlm .= ', ' . K_TABLE_USERS;
        $sqlw .= ' AND testuser_user_id=user_id AND user_id=' . $user_id . '';
        $sqlansw .= ' AND testuser_user_id=user_id AND user_id=' . $user_id . '';
        if ($testuser_id > 0) {
            $sqlw .= ' AND testuser_id=' . $testuser_id . '';
            $sqlansw .= ' AND testuser_id=' . $testuser_id . '';
        }
    } elseif ($group_id > 0) {
        $sqltot .= ', ' . K_TABLE_USERS . ', ' . K_TABLE_USERGROUP;
        $sqltb .= ', ' . K_TABLE_USERS . ', ' . K_TABLE_USERGROUP;
        $sqlm .= ', ' . K_TABLE_USERS . ', ' . K_TABLE_USERGROUP;
        $sqlw .= ' AND testuser_user_id=user_id AND usrgrp_user_id=user_id AND usrgrp_group_id=' . $group_id . '';
        $sqlansw .= ' AND testuser_user_id=user_id AND usrgrp_user_id=user_id AND usrgrp_group_id=' . $group_id . '';
    }
    if (!empty($startdate)) {
        $startdate_time = strtotime($startdate);
        $startdate = date(K_TIMESTAMP_FORMAT, $startdate_time);
        $sqlw .= ' AND testuser_creation_time>=\'' . $startdate . '\'';
        $sqlansw .= ' AND testuser_creation_time>=\'' . $startdate . '\'';
    }
    if (!empty($enddate)) {
        $enddate_time = strtotime($enddate);
        $enddate = date(K_TIMESTAMP_FORMAT, $enddate_time);
        $sqlw .= ' AND testuser_creation_time<=\'' . $enddate . '\'';
        $sqlansw .= ' AND testuser_creation_time<=\'' . $enddate . '\'';
    }
    // check if a specific test is selected or not
    if ($test_id == 0) {
        $test_ids = array();
        $sqlt = 'SELECT testuser_test_id FROM ' . $sqltot . ' ' . $sqlw . ' GROUP BY testuser_test_id ORDER BY testuser_test_id';
        if ($rt = F_db_query($sqlt, $db)) {
            while ($mt = F_db_fetch_assoc($rt)) {
                // check user's authorization
                if (F_isAuthorizedUser(K_TABLE_TESTS, 'test_id', $mt['testuser_test_id'], 'test_user_id')) {
                    $test_ids[] = $mt['testuser_test_id'];
                }
            }
        } else {
            F_display_db_error();
        }
        foreach ($test_ids as $tid) {
            // select test IDs
            $data = F_getRawTestStat($tid, $group_id, $user_id, $startdate, $enddate, $testuser_id, $data);
        }
        return $data;
    }
    $testdata = F_getTestData($test_id);
    // array to be returned
    if (!isset($data['qstats'])) {
        // total number of questions
        $data['qstats'] = array('recurrence' => 0, 'recurrence_perc' => 0, 'average_score' => 0, 'average_score_perc' => 0, 'average_time' => 0, 'right' => 0, 'right_perc' => 0, 'wrong' => 0, 'wrong_perc' => 0, 'unanswered' => 0, 'unanswered_perc' => 0, 'undisplayed' => 0, 'undisplayed_perc' => 0, 'unrated' => 0, 'unrated_perc' => 0, 'qnum' => 0, 'module' => array());
    }
    $sql = 'SELECT
		module_id,
		subject_id,
		question_id,
		module_name,
		subject_name,
		subject_description,
		question_description,';
    if ($user_id > 0 and $testuser_id > 0) {
        $sql .= ' testlog_score,
			testlog_user_ip,
			testlog_display_time,
			testlog_change_time,
			testlog_reaction_time,
			testlog_answer_text,
			question_type,
			question_explanation,';
    }
    $sql .= ' COUNT(question_id) AS recurrence,
		AVG(testlog_score) AS average_score,
		AVG(testlog_change_time - testlog_display_time) AS average_time,
		MIN(question_type) AS question_type,
		MIN(question_difficulty) AS question_difficulty';
    $sql .= ' FROM ' . $sqlm;
    $sql .= ' WHERE testlog_testuser_id=testuser_id AND question_id=testlog_question_id AND subject_id=question_subject_id AND module_id=subject_module_id';
    if ($test_id > 0) {
        $sql .= ' AND testuser_test_id=' . $test_id . '';
    }
    if ($testuser_id > 0) {
        $sql .= ' AND testuser_id=' . $testuser_id . '';
    }
    if ($user_id > 0) {
        $sql .= ' AND testuser_user_id=user_id AND user_id=' . $user_id . '';
    } elseif ($group_id > 0) {
        $sql .= ' AND testuser_user_id=user_id AND usrgrp_user_id=user_id AND usrgrp_group_id=' . $group_id . '';
    }
    if (!empty($startdate)) {
        $sql .= ' AND testuser_creation_time>=\'' . $startdate . '\'';
    }
    if (!empty($enddate)) {
        $sql .= ' AND testuser_creation_time<=\'' . $enddate . '\'';
    }
    $sql .= ' GROUP BY module_id, subject_id, question_id, module_name, subject_name, subject_description, question_description';
    if ($user_id > 0 and $testuser_id > 0) {
        $sql .= ', testlog_score, testlog_user_ip, testlog_display_time, testlog_change_time, testlog_reaction_time, testlog_answer_text, question_type, question_explanation';
    } else {
        $sql .= ' ORDER BY module_name, subject_name, question_description';
    }
    if ($r = F_db_query($sql, $db)) {
        while ($m = F_db_fetch_array($r)) {
            if (!isset($data['qstats']['module']['\'' . $m['module_id'] . '\''])) {
                $data['qstats']['module']['\'' . $m['module_id'] . '\''] = array('id' => $m['module_id'], 'name' => $m['module_name'], 'recurrence' => 0, 'recurrence_perc' => 0, 'average_score' => 0, 'average_score_perc' => 0, 'average_time' => 0, 'right' => 0, 'right_perc' => 0, 'wrong' => 0, 'wrong_perc' => 0, 'unanswered' => 0, 'unanswered_perc' => 0, 'undisplayed' => 0, 'undisplayed_perc' => 0, 'unrated' => 0, 'unrated_perc' => 0, 'qnum' => 0, 'subject' => array());
            }
            if (!isset($data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\''])) {
                $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\''] = array('id' => $m['subject_id'], 'name' => $m['subject_name'], 'description' => $m['subject_description'], 'recurrence' => 0, 'recurrence_perc' => 0, 'average_score' => 0, 'average_score_perc' => 0, 'average_time' => 0, 'right' => 0, 'right_perc' => 0, 'wrong' => 0, 'wrong_perc' => 0, 'unanswered' => 0, 'unanswered_perc' => 0, 'undisplayed' => 0, 'undisplayed_perc' => 0, 'unrated' => 0, 'unrated_perc' => 0, 'qnum' => 0, 'question' => array());
            }
            $question_max_score = $testdata['test_score_right'] * $m['question_difficulty'];
            $question_half_score = $question_max_score / 2;
            $qright = F_count_rows($sqltot, $sqlw . ' AND testlog_question_id=' . $m['question_id'] . ' AND testlog_score>' . $question_half_score . '');
            $qwrong = F_count_rows($sqltot, $sqlw . ' AND testlog_question_id=' . $m['question_id'] . ' AND testlog_score<=' . $question_half_score . '');
            $qunanswered = F_count_rows($sqltot, $sqlw . ' AND testlog_question_id=' . $m['question_id'] . ' AND testlog_change_time IS NULL');
            $qundisplayed = F_count_rows($sqltot, $sqlw . ' AND testlog_question_id=' . $m['question_id'] . ' AND testlog_display_time IS NULL');
            $qunrated = F_count_rows($sqltot, $sqlw . ' AND testlog_question_id=' . $m['question_id'] . ' AND testlog_score IS NULL');
            if (stripos($m['average_time'], ':') !== FALSE) {
                // PostgreSQL returns formatted time, while MySQL returns the number of seconds
                $m['average_time'] = strtotime($m['average_time']);
            }
            $num_all_answers = F_count_rows($sqltb, $sqlansw . ' AND testlog_question_id=' . $m['question_id']);
            if (!isset($data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\''])) {
                $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\''] = array('id' => $m['question_id'], 'description' => $m['question_description'], 'type' => $m['question_type'], 'difficulty' => $m['question_difficulty'], 'recurrence' => 0, 'recurrence_perc' => 0, 'average_score' => 0, 'average_score_perc' => 0, 'average_time' => 0, 'right' => 0, 'right_perc' => 0, 'wrong' => 0, 'wrong_perc' => 0, 'unanswered' => 0, 'unanswered_perc' => 0, 'undisplayed' => 0, 'undisplayed_perc' => 0, 'unrated' => 0, 'unrated_perc' => 0, 'qnum' => 0, 'anum' => 0, 'answer' => array());
            }
            // average score ratio
            if ($question_max_score > 0) {
                $average_score_perc = $m['average_score'] / $question_max_score;
            } else {
                $average_score_perc = 0;
            }
            // sum values for questions
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['qnum'] += 1;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['recurrence'] += $m['recurrence'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['average_score'] += $m['average_score'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['average_score_perc'] += $average_score_perc;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['average_time'] += $m['average_time'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['right'] += $qright;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['wrong'] += $qwrong;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['unanswered'] += $qunanswered;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['undisplayed'] += $qundisplayed;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['unrated'] += $qunrated;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['anum'] += $num_all_answers;
            // sum values for subject
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['qnum'] += 1;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['recurrence'] += $m['recurrence'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['average_score'] += $m['average_score'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['average_score_perc'] += $average_score_perc;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['average_time'] += $m['average_time'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['right'] += $qright;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['wrong'] += $qwrong;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['unanswered'] += $qunanswered;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['undisplayed'] += $qundisplayed;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['unrated'] += $qunrated;
            // sum values for module
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['qnum'] += 1;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['recurrence'] += $m['recurrence'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['average_score'] += $m['average_score'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['average_score_perc'] += $average_score_perc;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['average_time'] += $m['average_time'];
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['right'] += $qright;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['wrong'] += $qwrong;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['unanswered'] += $qunanswered;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['undisplayed'] += $qundisplayed;
            $data['qstats']['module']['\'' . $m['module_id'] . '\'']['unrated'] += $qunrated;
            // sum totals
            $data['qstats']['qnum'] += 1;
            $data['qstats']['recurrence'] += $m['recurrence'];
            $data['qstats']['average_score'] += $m['average_score'];
            $data['qstats']['average_score_perc'] += $average_score_perc;
            $data['qstats']['average_time'] += $m['average_time'];
            $data['qstats']['right'] += $qright;
            $data['qstats']['wrong'] += $qwrong;
            $data['qstats']['unanswered'] += $qunanswered;
            $data['qstats']['undisplayed'] += $qundisplayed;
            $data['qstats']['unrated'] += $qunrated;
            // get answer statistics
            $sqlaa = 'SELECT answer_id, answer_description, COUNT(answer_id) AS recurrence';
            if ($user_id > 0 and $testuser_id > 0) {
                $sqlaa .= ', logansw_position, logansw_selected, answer_isright, answer_position, answer_explanation';
            }
            $sqlaa .= ' FROM ' . $sqltb . '';
            $sqlaw = ' WHERE testlog_testuser_id=testuser_id
					AND logansw_testlog_id=testlog_id
					AND answer_id=logansw_answer_id
					AND answer_question_id=' . $m['question_id'] . '';
            if ($test_id > 0) {
                $sqlaw .= ' AND testuser_test_id=' . $test_id . '';
            }
            if ($user_id > 0) {
                $sqlaw .= ' AND testuser_user_id=' . $user_id . '';
            }
            if ($testuser_id > 0) {
                $sqlaw .= ' AND testuser_id=' . $testuser_id . '';
            }
            if ($user_id > 0) {
                $sqlaw .= ' AND testuser_user_id=user_id AND user_id=' . $user_id . '';
            } elseif ($group_id > 0) {
                $sqlaw .= ' AND testuser_user_id=user_id AND usrgrp_user_id=user_id AND usrgrp_group_id=' . $group_id . '';
            }
            if (!empty($startdate)) {
                $sql .= ' AND testuser_creation_time>=\'' . $startdate . '\'';
            }
            if (!empty($enddate)) {
                $sql .= ' AND testuser_creation_time<=\'' . $enddate . '\'';
            }
            $sqlab = ' GROUP BY answer_id, answer_description';
            if ($user_id > 0 and $testuser_id > 0) {
                $sqlab .= ', logansw_position, logansw_selected, answer_isright, answer_position, answer_explanation';
            }
            $sqlab .= ' ORDER BY answer_description';
            $sqla = $sqlaa . $sqlaw . $sqlab;
            if ($ra = F_db_query($sqla, $db)) {
                while ($ma = F_db_fetch_array($ra)) {
                    $aright = F_count_rows($sqltb, $sqlaw . ' AND answer_id=' . $ma['answer_id'] . ' AND ((answer_isright=\'0\' AND logansw_selected=0) OR (answer_isright=\'1\' AND logansw_selected=1) OR (answer_position IS NOT NULL AND logansw_position IS NOT NULL AND answer_position=logansw_position))');
                    $awrong = F_count_rows($sqltb, $sqlaw . ' AND answer_id=' . $ma['answer_id'] . ' AND ((answer_isright=\'0\' AND logansw_selected=1) OR (answer_isright=\'1\' AND logansw_selected=0) OR (answer_position IS NOT NULL AND answer_position!=logansw_position))');
                    $aunanswered = F_count_rows($sqltb, $sqlaw . ' AND answer_id=' . $ma['answer_id'] . ' AND logansw_selected=-1');
                    if (!isset($data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['answer']['\'' . $ma['answer_id'] . '\''])) {
                        $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['answer']['\'' . $ma['answer_id'] . '\''] = array('id' => $ma['answer_id'], 'description' => $ma['answer_description'], 'recurrence' => 0, 'recurrence_perc' => 0, 'right' => 0, 'right_perc' => 0, 'wrong' => 0, 'wrong_perc' => 0, 'unanswered' => 0, 'unanswered_perc' => 0);
                    }
                    $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['answer']['\'' . $ma['answer_id'] . '\'']['recurrence'] += $ma['recurrence'];
                    $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['answer']['\'' . $ma['answer_id'] . '\'']['right'] += $aright;
                    $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['answer']['\'' . $ma['answer_id'] . '\'']['wrong'] += $awrong;
                    $data['qstats']['module']['\'' . $m['module_id'] . '\'']['subject']['\'' . $m['subject_id'] . '\'']['question']['\'' . $m['question_id'] . '\'']['answer']['\'' . $ma['answer_id'] . '\'']['unanswered'] += $aunanswered;
                }
            } else {
                F_display_db_error();
            }
        }
    } else {
        F_display_db_error();
    }
    return $data;
}
示例#5
0
/**
 * Export all question statistics of the selected test to XML.
 * @author Nicola Asuni
 * @since 2010-05-10
 * @param $test_id (int) test ID
 * @return XML data
 */
function F_xml_export_question_stats($test_id)
{
    global $l, $db;
    require_once '../config/tce_config.php';
    require_once '../../shared/code/tce_authorization.php';
    require_once '../code/tce_functions_auth_sql.php';
    $boolean = array('false', 'true');
    $type = array('single', 'multiple', 'text', 'ordering');
    $xml = '';
    // XML data to be returned
    $xml .= '<' . '?xml version="1.0" encoding="UTF-8" ?' . '>' . K_NEWLINE;
    $xml .= '<tcexamquestionstats version="' . K_TCEXAM_VERSION . '">' . K_NEWLINE;
    $xml .= K_TAB . '<header';
    $xml .= ' lang="' . K_USER_LANG . '"';
    $xml .= ' date="' . date(K_TIMESTAMP_FORMAT) . '">' . K_NEWLINE;
    $xml .= K_TAB . '</header>' . K_NEWLINE;
    $xml .= K_TAB . '<body>' . K_NEWLINE;
    // get test data
    $testdata = F_getTestData($test_id);
    // get total number of questions for the selected test
    $num_questions = F_count_rows(K_TABLE_TESTS_LOGS . ', ' . K_TABLE_TEST_USER, 'WHERE testlog_testuser_id=testuser_id AND testuser_test_id=' . $test_id . '');
    // output questions stats
    $sqlr = 'SELECT
			question_id,
			COUNT(question_id) AS recurrence,
			AVG(testlog_score) AS average_score,
			AVG(testlog_change_time - testlog_display_time) AS average_time,
			min(question_difficulty) AS question_difficulty
		FROM ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_TEST_USER . ', ' . K_TABLE_QUESTIONS . '
		WHERE testlog_testuser_id=testuser_id
			AND testlog_question_id=question_id
			AND testuser_test_id=' . $test_id . '
		GROUP BY question_id
		ORDER BY recurrence DESC,average_score DESC';
    if ($rr = F_db_query($sqlr, $db)) {
        while ($mr = F_db_fetch_array($rr)) {
            $xml .= K_TAB . K_TAB . '<question>' . K_NEWLINE;
            // get the question max score
            $question_max_score = $testdata['test_score_right'] * $mr['question_difficulty'];
            $qsttestdata = F_getQuestionTestStat($test_id, $mr['question_id']);
            $xml .= K_TAB . K_TAB . K_TAB . '<id>' . $mr['question_id'] . '</id>' . K_NEWLINE;
            $question_description = '';
            $sqlrq = 'SELECT question_description FROM ' . K_TABLE_QUESTIONS . ' WHERE question_id=' . $mr['question_id'] . '';
            if ($rrq = F_db_query($sqlrq, $db)) {
                if ($mrq = F_db_fetch_array($rrq)) {
                    $question_description = $mrq['question_description'];
                }
            } else {
                F_display_db_error();
            }
            $xml .= K_TAB . K_TAB . K_TAB . '<description>' . F_text_to_xml($question_description) . '</description>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<recurrence>' . $mr['recurrence'] . '</recurrence>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<recurrence_percent>' . F_formatXMLPercentage($mr['recurrence'] / $num_questions) . '</recurrence_percent>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<points>' . number_format($mr['average_score'], 3, '.', '') . '</points>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<points_percent>' . F_formatXMLPercentage($mr['average_score'] / $question_max_score) . '</points_percent>' . K_NEWLINE;
            if (stripos($mr['average_time'], ':') !== FALSE) {
                // PostgreSQL returns formatted time, while MySQL returns the number of seconds
                $mr['average_time'] = strtotime($mr['average_time']);
            }
            $xml .= K_TAB . K_TAB . K_TAB . '<time>' . date('i:s', $mr['average_time']) . '</time>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<correct>' . $qsttestdata['right'] . '</correct>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<correct_percent>' . F_formatXMLPercentage($qsttestdata['right'] / $qsttestdata['num']) . '</correct_percent>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<wrong>' . $qsttestdata['wrong'] . '</wrong>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<wrong_percent>' . F_formatXMLPercentage($qsttestdata['wrong'] / $qsttestdata['num']) . '</wrong_percent>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<unanswered>' . $qsttestdata['unanswered'] . '</unanswered>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<unanswered_percent>' . F_formatXMLPercentage($qsttestdata['unanswered'] / $qsttestdata['num']) . '</unanswered_percent>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<undisplayed>' . $qsttestdata['undisplayed'] . '</undisplayed>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<undisplayed_percent>' . F_formatXMLPercentage($qsttestdata['undisplayed'] / $qsttestdata['num']) . '</undisplayed_percent>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<unrated>' . $qsttestdata['unrated'] . '</unrated>' . K_NEWLINE;
            $xml .= K_TAB . K_TAB . K_TAB . '<unrated_percent>' . F_formatXMLPercentage($qsttestdata['unrated'] / $qsttestdata['num']) . '</unrated_percent>' . K_NEWLINE;
            // answers statistics
            $sqla = 'SELECT *
				FROM ' . K_TABLE_ANSWERS . '
				WHERE answer_question_id=' . $mr['question_id'] . '
				ORDER BY answer_id';
            if ($ra = F_db_query($sqla, $db)) {
                while ($ma = F_db_fetch_array($ra)) {
                    $xml .= K_TAB . K_TAB . K_TAB . '<answer>' . K_NEWLINE;
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<id>' . $ma['answer_id'] . '</id>' . K_NEWLINE;
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<description>' . F_text_to_xml($ma['answer_description']) . '</description>' . K_NEWLINE;
                    $num_all_answers = F_count_rows(K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_ANSWERS . ', ' . K_TABLE_LOG_ANSWER . ' WHERE logansw_answer_id=answer_id AND logansw_testlog_id=testlog_id AND testlog_testuser_id=testuser_id AND testuser_test_id=' . $test_id . ' AND testlog_question_id=' . $mr['question_id'] . '');
                    $num_answers = F_count_rows(K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_ANSWERS . ', ' . K_TABLE_LOG_ANSWER . ' WHERE answer_id=' . $ma['answer_id'] . ' AND logansw_answer_id=answer_id AND logansw_testlog_id=testlog_id AND testlog_testuser_id=testuser_id AND testuser_test_id=' . $test_id . ' AND testlog_question_id=' . $mr['question_id'] . '');
                    $right_answers = F_count_rows(K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_ANSWERS . ', ' . K_TABLE_LOG_ANSWER . ' WHERE answer_id=' . $ma['answer_id'] . ' AND logansw_answer_id=answer_id AND logansw_testlog_id=testlog_id AND testlog_testuser_id=testuser_id AND testuser_test_id=' . $test_id . ' AND testlog_question_id=' . $mr['question_id'] . ' AND ((answer_isright=\'0\' AND logansw_selected=0) OR (answer_isright=\'1\' AND logansw_selected=1) OR (answer_position IS NOT NULL AND logansw_position IS NOT NULL AND answer_position=logansw_position))');
                    $wrong_answers = F_count_rows(K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_ANSWERS . ', ' . K_TABLE_LOG_ANSWER . ' WHERE answer_id=' . $ma['answer_id'] . ' AND logansw_answer_id=answer_id AND logansw_testlog_id=testlog_id AND testlog_testuser_id=testuser_id AND testuser_test_id=' . $test_id . ' AND testlog_question_id=' . $mr['question_id'] . ' AND ((answer_isright=\'0\' AND logansw_selected=1) OR (answer_isright=\'1\' AND logansw_selected=0) OR (answer_position IS NOT NULL AND answer_position!=logansw_position))');
                    $unanswered = F_count_rows(K_TABLE_TEST_USER . ', ' . K_TABLE_TESTS_LOGS . ', ' . K_TABLE_ANSWERS . ', ' . K_TABLE_LOG_ANSWER . ' WHERE answer_id=' . $ma['answer_id'] . ' AND logansw_answer_id=answer_id AND logansw_testlog_id=testlog_id AND testlog_testuser_id=testuser_id AND testuser_test_id=' . $test_id . ' AND testlog_question_id=' . $mr['question_id'] . ' AND logansw_selected=-1');
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<recurrence>' . $num_answers . '</recurrence>' . K_NEWLINE;
                    $perc = 0;
                    if ($num_all_answers > 0) {
                        $perc = $num_answers / $num_all_answers;
                    }
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<recurrence_percent>' . F_formatXMLPercentage($perc) . '</recurrence_percent>' . K_NEWLINE;
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<correct>' . $right_answers . '</correct>' . K_NEWLINE;
                    $perc = 0;
                    if ($num_answers > 0) {
                        $perc = $right_answers / $num_answers;
                    }
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<correct_percent>' . F_formatXMLPercentage($perc) . '</correct_percent>' . K_NEWLINE;
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<wrong>' . $wrong_answers . '</wrong>' . K_NEWLINE;
                    $perc = 0;
                    if ($num_answers > 0) {
                        $perc = round($wrong_answers / $num_answers);
                    }
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<wrong_percent>' . F_formatXMLPercentage($perc) . '</wrong_percent>' . K_NEWLINE;
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<unanswered>' . $unanswered . '</unanswered>' . K_NEWLINE;
                    $perc = 0;
                    if ($num_answers > 0) {
                        $perc = round($unanswered / $num_answers);
                    }
                    $xml .= K_TAB . K_TAB . K_TAB . K_TAB . '<unanswered_percent>' . F_formatXMLPercentage($perc) . '</unanswered_percent>' . K_NEWLINE;
                    $xml .= K_TAB . K_TAB . K_TAB . '</answer>' . K_NEWLINE;
                }
            } else {
                F_display_db_error();
            }
            $xml .= K_TAB . K_TAB . '</question>' . K_NEWLINE;
        }
    } else {
        F_display_db_error();
    }
    $xml .= K_TAB . '</body>' . K_NEWLINE;
    $xml .= '</tcexamquestionstats>' . K_NEWLINE;
    return $xml;
}
/**
 * Sends email test reports to users.
 * @author Nicola Asuni
 * @since 2005-02-24
 * @param $test_id (int) TEST ID
 * @param $user_id (int) USER ID (0 means all users)
 * @param $group_id (int) GROUP ID (0 means all groups)
 * @param $mode (int) type of report to send: 0=detailed report; 1=summary report (without questions)
 */
function F_send_report_emails($test_id, $user_id = 0, $group_id = 0, $mode = 0)
{
    global $l, $db;
    require_once '../config/tce_config.php';
    require_once '../../shared/code/tce_functions_test.php';
    require_once '../../shared/code/tce_functions_test_stats.php';
    require_once '../../shared/code/tce_class_mailer.php';
    require_once 'tce_functions_user_select.php';
    $test_id = intval($test_id);
    $user_id = intval($user_id);
    $group_id = intval($group_id);
    $mode = intval($mode);
    if (!F_isAuthorizedUser(K_TABLE_TESTS, 'test_id', $test_id, 'test_user_id')) {
        return;
    }
    if (!F_isAuthorizedEditorForUser($user_id)) {
        return;
    }
    if (!F_isAuthorizedEditorForGroup($group_id)) {
        return;
    }
    // Instantiate C_mailer class
    $mail = new C_mailer();
    //Load default values
    $mail->language = $l;
    $mail->Priority = $emailcfg['Priority'];
    $mail->ContentType = $emailcfg['ContentType'];
    $mail->Encoding = $emailcfg['Encoding'];
    $mail->WordWrap = $emailcfg['WordWrap'];
    $mail->Mailer = $emailcfg['Mailer'];
    $mail->Sendmail = $emailcfg['Sendmail'];
    $mail->UseMSMailHeaders = $emailcfg['UseMSMailHeaders'];
    $mail->Host = $emailcfg['Host'];
    $mail->Port = $emailcfg['Port'];
    $mail->Helo = $emailcfg['Helo'];
    $mail->SMTPAuth = $emailcfg['SMTPAuth'];
    $mail->SMTPSecure = $emailcfg['SMTPSecure'];
    $mail->Username = $emailcfg['Username'];
    $mail->Password = $emailcfg['Password'];
    $mail->Timeout = $emailcfg['Timeout'];
    $mail->SMTPDebug = $emailcfg['SMTPDebug'];
    $mail->PluginDir = $emailcfg['PluginDir'];
    $mail->Sender = $emailcfg['Sender'];
    $mail->From = $emailcfg['From'];
    $mail->FromName = $emailcfg['FromName'];
    if ($emailcfg['Reply']) {
        $mail->AddReplyTo($emailcfg['Reply'], $emailcfg['ReplyName']);
    }
    $mail->CharSet = $l['a_meta_charset'];
    if (!$mail->CharSet) {
        $mail->CharSet = $emailcfg['CharSet'];
    }
    $mail->Subject = $l['t_result_user'];
    $mail->IsHTML(TRUE);
    // Set message type to HTML.
    $email_num = 0;
    // count emails;
    if ($user_id == 0) {
        // for each user on selected test
        $sql = 'SELECT user_id, user_name, user_email, user_firstname, user_lastname, testuser_creation_time
				FROM ' . K_TABLE_TEST_USER . ', ' . K_TABLE_USERS . '
				WHERE testuser_user_id=user_id
					AND testuser_test_id=' . $test_id . '
					AND testuser_status>0';
        if ($group_id > 0) {
            $sql .= ' AND testuser_user_id IN (SELECT usrgrp_user_id FROM ' . K_TABLE_USERGROUP . ' WHERE usrgrp_group_id=' . $group_id . ')';
        }
    } else {
        // select only one test of one user
        $sql = 'SELECT user_id, user_name, user_email, user_firstname, user_lastname, testuser_creation_time
				FROM ' . K_TABLE_TEST_USER . ', ' . K_TABLE_USERS . '
				WHERE testuser_user_id=user_id
					AND testuser_user_id=' . $user_id . '
					AND testuser_test_id=' . $test_id . '
					AND testuser_status>0
				LIMIT 1';
    }
    // get test data
    $testdata = F_getTestData($test_id);
    if ($r = F_db_query($sql, $db)) {
        while ($m = F_db_fetch_array($r)) {
            if (strlen($m['user_email']) > 3) {
                // get user's test stats
                $usrtestdata = F_getUserTestStat($test_id, $m['user_id']);
                // set HTML header
                $mail->Body = $emailcfg['MsgHeader'];
                // compose alternate TEXT message
                $mail->AltBody = '' . $l['t_result_user'] . ' [' . $m['testuser_creation_time'] . ']' . K_NEWLINE;
                $mail->AltBody .= $l['w_test'] . ': ' . $testdata['test_name'] . K_NEWLINE;
                $passmsg = '';
                if ($testdata['test_score_threshold'] > 0) {
                    $mail->AltBody .= $l['w_test_score_threshold'] . ': ' . $testdata['test_score_threshold'];
                    if ($usrtestdata['score'] >= $testdata['test_score_threshold']) {
                        $passmsg = ' - ' . $l['w_passed'];
                    } else {
                        $passmsg = ' - ' . $l['w_not_passed'];
                    }
                    $mail->AltBody .= K_NEWLINE;
                }
                $mail->AltBody .= $l['w_score'] . ': ' . $usrtestdata['score'] . ' (' . round(100 * $usrtestdata['score'] / $usrtestdata['max_score']) . '%)' . $passmsg . K_NEWLINE;
                $mail->AltBody .= $l['w_answers_right'] . ': ' . $usrtestdata['right'] . ' (' . round(100 * $usrtestdata['right'] / $usrtestdata['all']) . '%)' . K_NEWLINE;
                $mail->AltBody .= $l['w_answers_wrong'] . ': ' . $usrtestdata['wrong'] . ' (' . round(100 * $usrtestdata['wrong'] / $usrtestdata['all']) . '%)' . K_NEWLINE;
                $mail->AltBody .= $l['w_questions_unanswered'] . ': ' . $usrtestdata['unanswered'] . ' (' . round(100 * $usrtestdata['unanswered'] / $usrtestdata['all']) . '%)' . K_NEWLINE;
                $mail->AltBody .= $l['w_questions_undisplayed'] . ': ' . $usrtestdata['undisplayed'] . ' (' . round(100 * $usrtestdata['undisplayed'] / $usrtestdata['all']) . '%)' . K_NEWLINE;
                if ($mode == 0) {
                    // create PDF doc
                    $pdf_content = file_get_contents(K_PATH_HOST . K_PATH_TCEXAM . 'admin/code/tce_pdf_results.php?mode=3&testid=' . $test_id . '&groupid=0&userid=' . $m['user_id'] . '&email=' . md5(date('Y') . K_RANDOM_SECURITY . $test_id . $m['user_id']));
                    // attach doc
                    $doc_name = 'test_' . date('Ymd', strtotime($m['testuser_creation_time'])) . '_' . $test_id . '_' . $m['user_id'] . '.pdf';
                    $mail->AddStringAttachment($pdf_content, $doc_name, $emailcfg['AttachmentsEncoding'], 'application/octet-stream');
                    $mail->AltBody .= K_NEWLINE . $l['w_attachment'] . ': ' . $doc_name . K_NEWLINE;
                }
                // convert alternate text to HTML
                $mail->Body .= str_replace(K_NEWLINE, '<br />' . K_NEWLINE, $mail->AltBody);
                // add HTML footer
                $mail->Body .= $emailcfg['MsgFooter'];
                //--- Elaborate user Templates ---
                $mail->Body = str_replace('#CHARSET#', $l['a_meta_charset'], $mail->Body);
                $mail->Body = str_replace('#LANG#', $l['a_meta_language'], $mail->Body);
                $mail->Body = str_replace('#LANGDIR#', $l['a_meta_dir'], $mail->Body);
                $mail->Body = str_replace('#EMAIL#', $m['user_email'], $mail->Body);
                $mail->Body = str_replace('#USERNAME#', htmlspecialchars($m['user_name'], ENT_NOQUOTES, $l['a_meta_charset']), $mail->Body);
                $mail->Body = str_replace('#USERFIRSTNAME#', htmlspecialchars($m['user_firstname'], ENT_NOQUOTES, $l['a_meta_charset']), $mail->Body);
                $mail->Body = str_replace('#USERLASTNAME#', htmlspecialchars($m['user_lastname'], ENT_NOQUOTES, $l['a_meta_charset']), $mail->Body);
                // add a "To" address
                $mail->AddAddress($m['user_email'], $m['user_name']);
                $email_num++;
                $progresslog = '' . $email_num . '. ' . $m['user_email'] . ' [' . $m['user_name'] . ']';
                //output user data
                if (!$mail->Send()) {
                    //send email to user
                    $progresslog .= ' [' . $l['t_error'] . ']';
                    //display error message
                }
                $mail->ClearAddresses();
                // Clear all addresses for next loop
                $mail->ClearAttachments();
                // Clears all previously set filesystem, string, and binary attachments
            } else {
                $progresslog = '[' . $l['t_error'] . '] ' . $m['user_name'] . ': ' . $l['m_unknown_email'] . '';
                //output user data
            }
            echo '' . $progresslog . '<br />' . K_NEWLINE;
            //output processed emails
            flush();
            // force browser output
        }
    } else {
        F_display_db_error(false);
    }
    $mail->ClearAddresses();
    // Clear all addresses for next loop
    $mail->ClearCustomHeaders();
    // Clears all custom headers
    $mail->ClearAllRecipients();
    // Clears all recipients assigned in the TO, CC and BCC
    $mail->ClearAttachments();
    // Clears all previously set filesystem, string, and binary attachments
    $mail->ClearReplyTos();
    // Clears all recipients assigned in the ReplyTo array
    return;
}