/** * Fetches the details of the question results, grouped by tag for this user. * Data is returned as an array with the calculations so that that it can * be used in any render that's needed (HTML, PDF or email) * * @return Array A nested array containing a list of tags, the tag details, and a list of the scores summarising how the user did for each tag. */ function fetch_quizzes_questionResultsByTag() { $tagBucketList = array(); // Use the cached version if we have it. if ($this->cached_resultsByTag) { return $this->cached_resultsByTag; } // ### 1 - Check to make sure we have questions if (empty($this->unitQuizDetails) || empty($this->unitQuizDetails->questions) || empty($this->unitQuizProgress->quiz_data)) { $this->cached_resultsByTag = $tagBucketList; return $tagBucketList; } // ### 1A - Ensure that the questions all have tags (random questions will not have these tags, hence fetching // them here). foreach ($this->unitQuizDetails->questions as $questionID => $questionObj) { // Tags should always exist for normal questions. So this will just be triggered for randoms. // This is as practically efficient as we can make it. if (!isset($questionObj->tags)) { $this->unitQuizDetails->questions[$questionID]->tags = WPCW_questions_tags_getTagsForQuestion($questionID); } } // ### 2 - Build a list of tags used by the questions foreach ($this->unitQuizDetails->questions as $questionID => $questionObj) { // ### 3 - Got some tags for this question if (!empty($questionObj->tags)) { $userResponse = array('got_right' => false); // ### 4 - Get the question from the user's responses. Can't assume it's // there, hence the initialisation above. if (isset($this->unitQuizProgress->quiz_data[$questionID])) { $userResponse = $this->unitQuizProgress->quiz_data[$questionID]; } $isQuestionRight = 'yes' == $userResponse['got_right']; // ###Ê5 - Work out if an open-ended question or not. $isQuestionOpen = false; if ('open' == $questionObj->question_type || 'upload' == $questionObj->question_type) { $isQuestionOpen = true; } // ### 5 - For each tag, add the tag details to the list, and then // add the association for this question. foreach ($questionObj->tags as $tagObj) { // Is the tag already in the bucket? if (isset($tagBucketList[$tagObj->question_tag_id])) { // Append the response from the user. $tagBucketList[$tagObj->question_tag_id]['question_responses'][$questionID] = $userResponse; // Increase question count $tagBucketList[$tagObj->question_tag_id]['question_count']++; // Increase correct count (if correct) $tagBucketList[$tagObj->question_tag_id]['score_correct_questions'] += $isQuestionRight ? 1 : 0; // Increase question open count $tagBucketList[$tagObj->question_tag_id]['question_open_count'] += $isQuestionOpen ? 1 : 0; } else { $tagBucketList[$tagObj->question_tag_id] = array('question_open_count' => $isQuestionOpen ? 1 : 0, 'question_count' => 1, 'score_correct_questions' => $isQuestionRight ? 1 : 0, 'score_total' => 0, 'tag_details' => $tagObj, 'question_responses' => array($questionID => $userResponse)); } } // end of foreach } // Check of question tags } // end of questionforeach // ### 6 - Now we need to calculate the total for each of these tags using the questions responses we have. if (!empty($tagBucketList)) { foreach ($tagBucketList as $tagID => $tagDetails) { $tagBucketList[$tagID]['score_total'] = WPCW_quizzes_calculateGradeForQuiz($tagDetails['question_responses'], 0); // Don't need responses now. unset($tagBucketList[$tagID]['question_responses']); } } // Update cache to save time later. $this->cached_resultsByTag = $tagBucketList; return $tagBucketList; }
/** * Function to show a list of questions in the question pool for use by a standard page or the AJAX thickbox. * * @param Integer $itemsPerPage The number of items to show on each table page. * @param Array $paramSrc The array of parameters to use for filtering/searching the question pool. * @param String $actionMode The type of mode we're in (ajax or std). * @param PageBuilder $page The current page object (optional). */ function WPCW_questionPool_showPoolTable($itemsPerPage, $paramSrc, $actionMode = 'std', $page = false) { global $wpcwdb, $wpdb; $wpdb->show_errors(); // AJAX loader if ('ajax' == $actionMode) { printf('<img src="%simg/ajax_loader.gif" class="wpcw_loader" style="display: none;" />', WPCW_plugin_getPluginPath()); } // Check to see if we've got questions to process if ('std' == $actionMode) { WPCW_showPage_QuestionPool_processActionForm($page); } $paging_pageWanted = WPCW_arrays_getValue($paramSrc, 'pagenum') + 0; if ($paging_pageWanted == 0) { $paging_pageWanted = 1; } // Handle the sorting and filtering $orderBy = WPCW_arrays_getValue($paramSrc, 'orderby'); $ordering = WPCW_arrays_getValue($paramSrc, 'order'); // Validate ordering switch ($orderBy) { case 'question_question': case 'question_type': break; // Default and question_id //case 'question_id': // Default and question_id //case 'question_id': default: $orderBy = 'qs.question_id'; break; } // Create opposite ordering for reversing it. $ordering_opposite = false; switch ($ordering) { case 'desc': $ordering_opposite = 'asc'; break; case 'asc': $ordering_opposite = 'desc'; break; default: $ordering = 'desc'; $ordering_opposite = 'asc'; break; } // Was a search string specified? Or a specific item? $searchString = WPCW_arrays_getValue($paramSrc, 's'); // Create WHERE string based search - Title or Description of Quiz $SQL_WHERE = false; if ($searchString) { $SQL_WHERE = $wpdb->prepare(" AND question_question LIKE %s", '%' . $searchString . '%'); } $summaryPageURL = admin_url('admin.php?page=WPCW_showPage_QuestionPool'); // Show the form for searching ?> <form id="wpcw_questions_search_box" method="get" action="<?php echo $summaryPageURL; ?> "> <p class="search-box"> <label class="screen-reader-text" for="wpcw_questions_search_input"><?php _e('Search Questions', 'wp_courseware'); ?> </label> <input id="wpcw_questions_search_input" type="text" value="<?php echo $searchString; ?> " name="s"/> <input class="button" type="submit" value="<?php _e('Search Questions', 'wp_courseware'); ?> "/> <input type="hidden" name="page" value="WPCW_showPage_QuestionPool" /> </p> </form> <?php $SQL_TAG_FILTER = false; $tagFilter = intval(WPCW_arrays_getValue($paramSrc, 'filter', false)); // See if we have any tag filtering to do. if ($tagFilter > 0) { // Ensure we add the tag mapping table to the query $SQL_TAG_FILTER = "\n\t\t\tLEFT JOIN {$wpcwdb->question_tag_mapping} qtm ON qtm.question_id = qs.question_id\t\n\t\t"; $SQL_WHERE .= $wpdb->prepare("\n\t\t\tAND qtm.tag_id = %d\n\t\t\tAND qs.question_question IS NOT NULL \n\t\t", $tagFilter); // } $SQL_PAGING = "\n\t\t\tSELECT COUNT(*) as question_count \n\t\t\tFROM {$wpcwdb->quiz_qs} qs\n\t\t\t{$SQL_TAG_FILTER}\n\t\t\tWHERE question_type <> 'random_selection'\n\t\t\t{$SQL_WHERE} \n\t\t"; $paging_resultsPerPage = $itemsPerPage; $paging_totalCount = $wpdb->get_var($SQL_PAGING); $paging_recordStart = ($paging_pageWanted - 1) * $paging_resultsPerPage + 1; $paging_recordEnd = $paging_pageWanted * $paging_resultsPerPage; $paging_pageCount = ceil($paging_totalCount / $paging_resultsPerPage); $paging_sqlStart = $paging_recordStart - 1; // Show search message - that a search has been tried. if ($searchString) { printf('<div class="wpcw_search_count">%s "%s" (%s %s) (<a href="%s">%s</a>)</div>', __('Search results for', 'wp_courseware'), htmlentities($searchString), $paging_totalCount, _n('result', 'results', $paging_totalCount, 'wp_courseware'), $summaryPageURL, __('reset', 'wp_courseware')); } // Do main query $SQL = "SELECT * \n\t\t\tFROM {$wpcwdb->quiz_qs} qs\t\t\t\n\t\t\t{$SQL_TAG_FILTER}\t\t\t\n\t\t\tWHERE question_type <> 'random_selection'\n\t\t\t{$SQL_WHERE}\n\t\t\tORDER BY {$orderBy} {$ordering}\n\t\t\tLIMIT {$paging_sqlStart}, {$paging_resultsPerPage}\t\t\t \n\t\t\t"; // These are already checked, so they are safe, hence no prepare() // Generate paging code $baseURL = WPCW_urls_getURLWithParams($summaryPageURL, 'pagenum') . "&pagenum="; $questions = $wpdb->get_results($SQL); $tbl = new TableBuilder(); $tbl->attributes = array('id' => 'wpcw_tbl_question_pool', 'class' => 'widefat wpcw_tbl'); // Checkbox Col //$tblCol = new TableColumn(false, 'question_selection'); //$tblCol->cellClass = "question_selection wpcw_center"; //$tbl->addColumn($tblCol); // Wanting sorting links... in standard mode if ('std' == $actionMode) { // Checkbox field (no name, as we'll use jQuery to do a check all) $tblCol = new TableColumn('<input type="checkbox" />', 'question_id_cb'); $tblCol->cellClass = "wpcw_center wpcw_select_cb"; $tblCol->headerClass = "wpcw_center wpcw_select_cb"; $tbl->addColumn($tblCol); // ID - sortable $sortableLink = sprintf('<a href="%s&order=%s&orderby=question_id"><span>%s</span><span class="sorting-indicator"></span></a>', $baseURL, 'question_id' == $orderBy ? $ordering_opposite : 'asc', __('ID', 'wp_courseware')); // ID - render $tblCol = new TableColumn($sortableLink, 'question_id'); $tblCol->headerClass = 'question_id' == $orderBy ? 'sorted ' . $ordering : 'sortable'; $tblCol->cellClass = "question_id"; $tbl->addColumn($tblCol); // Question - sortable $sortableLink = sprintf('<a href="%s&order=%s&orderby=question_question"><span>%s</span><span class="sorting-indicator"></span></a>', $baseURL, 'question_question' == $orderBy ? $ordering_opposite : 'asc', __('Question', 'wp_courseware')); // Question - render $tblCol = new TableColumn($sortableLink, 'question_question'); $tblCol->headerClass = 'question_question' == $orderBy ? 'sorted ' . $ordering : 'sortable'; $tblCol->cellClass = "question_question"; $tbl->addColumn($tblCol); // Question Type - sortable $sortableLink = sprintf('<a href="%s&order=%s&orderby=question_type"><span>%s</span><span class="sorting-indicator"></span></a>', $baseURL, 'question_type' == $orderBy ? $ordering_opposite : 'asc', __('Question Type', 'wp_courseware')); // Question Type - render $tblCol = new TableColumn($sortableLink, 'question_type'); $tblCol->headerClass = ('question_type' == $orderBy ? 'sorted ' . $ordering : 'sortable') . ' wpcw_center'; $tblCol->cellClass = "question_type"; $tbl->addColumn($tblCol); } else { $tblCol = new TableColumn(__('ID', 'wp_courseware'), 'question_id'); $tblCol->cellClass = "question_id"; $tbl->addColumn($tblCol); $tblCol = new TableColumn(__('Question', 'wp_courseware'), 'question_question'); $tblCol->cellClass = "question_question"; $tbl->addColumn($tblCol); $tblCol = new TableColumn(__('Question Type', 'wp_courseware'), 'question_type'); $tblCol->cellClass = "question_type"; $tbl->addColumn($tblCol); } $tblCol = new TableColumn(__('Associated Quizzes', 'wp_courseware'), 'associated_quizzes'); $tblCol->headerClass = "wpcw_center"; $tblCol->cellClass = "associated_quizzes wpcw_center"; $tbl->addColumn($tblCol); $tblCol = new TableColumn(__('Tags', 'wp_courseware'), 'question_tags'); $tblCol->cellClass = "question_tags wpcw_center"; $tbl->addColumn($tblCol); // Actions $tblCol = new TableColumn(__('Actions', 'wp_courseware'), 'actions'); $tblCol->cellClass = "actions actions_right"; $tblCol->headerClass = "actions_right"; $tbl->addColumn($tblCol); // Stores course details in a mini cache to save lots of MySQL lookups. $miniCourseDetailCache = array(); // Format row data and show it. if ($questions) { $odd = false; foreach ($questions as $singleQuestion) { $data = array(); // URLs $editURL = admin_url('admin.php?page=WPCW_showPage_ModifyQuestion&question_id=' . $singleQuestion->question_id); // Maintain paging where possible. $deleteURL = $baseURL . '&action=delete&question_id=' . $singleQuestion->question_id; // Basic Details $data['question_id'] = $singleQuestion->question_id; $data['question_type'] = WPCW_quizzes_getQuestionTypeName($singleQuestion->question_type); $data['question_id_cb'] = sprintf('<input type="checkbox" name="question_%d" />', $singleQuestion->question_id); // Association Count $data['associated_quizzes'] = $singleQuestion->question_usage_count; // Actions - Std mode if ('std' == $actionMode) { // Edit by clicking $data['question_question'] = sprintf('<a href="%s">%s</a>', $editURL, $singleQuestion->question_question); $data['actions'] = '<ul class="wpcw_action_link_list">'; $data['actions'] .= sprintf('<li><a href="%s" class="button-primary">%s</a></li>', $editURL, __('Edit', 'wp_courseware')); $data['actions'] .= sprintf('<li><a href="%s" class="button-secondary wpcw_action_link_delete_question wpcw_action_link_delete" rel="%s">%s</a></li>', $deleteURL, __('Are you sure you wish to delete this question? This cannot be undone.', 'wp_courseware'), __('Delete', 'wp_courseware')); $data['actions'] .= '</ul>'; } else { if ('ajax' == $actionMode) { // No Edit by clicking $data['question_question'] = $singleQuestion->question_question . sprintf('<span class="wpcw_action_status wpcw_action_status_added">%s</span>', __('Added', 'wp_courseware')); $data['actions'] = '<ul class="wpcw_action_link_list">'; $data['actions'] .= sprintf('<li><a href="#" class="button-primary wpcw_tb_action_add" data-questionnum="%d">%s</a></li>', $singleQuestion->question_id, __('Add To Quiz', 'wp_courseware')); $data['actions'] .= '</ul>'; } } // Tags $data['question_tags'] = sprintf('<span class="wpcw_quiz_details_question_tags" data-questionid="%d" id="wpcw_quiz_details_question_tags_%d">', $singleQuestion->question_id, $singleQuestion->question_id); $data['question_tags'] .= WPCW_questions_tags_render($singleQuestion->question_id, WPCW_questions_tags_getTagsForQuestion($singleQuestion->question_id)); $data['question_tags'] .= '</span>'; // Odd/Even row colouring. $odd = !$odd; $tbl->addRow($data, $odd ? 'alternate' : ''); } } else { // No questions - show error in table. $tbl->addRowObj(new RowDataSimple('wpcw_center wpcw_none_found', __('There are currently no questions to show.', 'wp_courseware'), 7)); } // Add the form for the start of the multiple-add $formWrapper_start = false; if ('std' == $actionMode) { // Set the action URL to preserve parameters that we have. $formWrapper_start = sprintf('<form method="POST" action="%s">', WPCW_urls_getURLWithParams($summaryPageURL, 'pagenum')); } // Create tag filter (uses a form) $tagFilter = WPCW_questions_tags_createTagFilter($tagFilter, 'WPCW_showPage_QuestionPool'); // Work out paging and filtering $paging = WPCW_tables_showPagination($baseURL, $paging_pageWanted, $paging_pageCount, $paging_totalCount, $paging_recordStart, $paging_recordEnd, $tagFilter); // Show the actions $formWrapper_end = false; if ('std' == $actionMode) { $formWrapper_end = WPCW_showPage_QuestionPool_actionForm(); // Form tag - needed for processing $formWrapper_end .= '</form>'; } // Finally show table return $paging . $formWrapper_start . $tbl->toString() . $formWrapper_end . $paging; }