From 334a0fa8264d4ba3289739dc98fe9a5bd9b4f858 Mon Sep 17 00:00:00 2001
From: fago ' . t('@quiz that are not evaluated may have different score and grade when it is done.', array('@quiz' => QUIZ_NAME)) . ' ' . t('@quizzes that are not evaluated may have a different score and grade once evaluated.', array('@quiz' => QUIZ_NAME)) . 'gAK=>O7Mdb7(YI3>2OT+bWl{>h59
zT^IwWuU(?GIm7dt1~@+SA-QmhbRvleWRq!}il^p
' . t('No questions were found.') . '
'; - } - $header = $rows = array(); - $header = array( - 'title' => t('Quiz'), - 'creator' => t('Author'), - 'created' => t('Created'), - ); - if (user_access('access author stats')) { - $header['questions_count'] = t('Questions'); - $header['attempt_count'] = t('Attempts'); - } - $p = drupal_get_path('module', 'quiz_stats'); - $chart_icon = theme('image', array('path' => "$p/chart.png", 'width' => t('Charts'), 'height' => t('See charts'))); - foreach ($results as $result) { - $title_link = user_access('access author stats') ? 'node/' . $result['nid'] . '/statistics' : 'user/' . arg(1) . '/stats/'; - $row = array( - 'title' => l($chart_icon . ' ' . $result['title'], $title_link, array('html' => TRUE)), - 'creator' => l($result['name'], 'user/' . $result['uid']), - 'created' => format_date($result['created'], 'short'), - ); - if (user_access('access author stats')) { - $row['questions_count'] = quiz_get_number_of_questions($result['vid']); - $row['attempt_count'] = _quiz_get_attempt_count($result['nid']); - } - $rows[] = $row; - } - module_load_include('inc', 'quiz', 'quiz.pages'); - $cc = '' . t('Chart icon from !url', array('!url' => 'pinvoke.com')) . ''; - return theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => 'tablesorter'))) . $cc; -} - -/** - * Get the timestamps for the last days - * - * @param $num_days - * How many of the last days we need timestamps for - * @return - * Array of objects with timestamp and value. The value has '0' as its default value. - */ -function _quiz_get_last_days($num_days) { - $to_return = array(); - $now = REQUEST_TIME; - $one_day = 86400; - for ($i = 0; $i < $num_days; $i++) { - $timestamp = $now - ($one_day * $i); - $to_add = new stdClass(); - $to_add->timestamp = $timestamp; - $to_add->value = '0'; - $to_return[date('Y.m.j', $timestamp)] = $to_add; - } - return $to_return; -} diff --git a/includes/quiz_stats/quiz_stats.css b/includes/quiz_stats/quiz_stats.css deleted file mode 100644 index 533adfb..0000000 --- a/includes/quiz_stats/quiz_stats.css +++ /dev/null @@ -1,14 +0,0 @@ - -/* - * @file - * CSS file for quiz stats - */ - -.quiz-stats-chart-space { - margin-bottom:3px; -} - -.quiz-charts-title { - margin-top: 30px; - margin-bottom: 10px; -} \ No newline at end of file diff --git a/includes/quiz_stats/quiz_stats.info b/includes/quiz_stats/quiz_stats.info deleted file mode 100644 index 4bd1cc4..0000000 --- a/includes/quiz_stats/quiz_stats.info +++ /dev/null @@ -1,6 +0,0 @@ -name = Quiz Statistics -description = "Creates a report to compare and analyse the results of quiz attendees." -core = 7.x -package = Quiz Addon -dependencies[] = quiz -dependencies[] = chart diff --git a/includes/quiz_stats/quiz_stats.module b/includes/quiz_stats/quiz_stats.module deleted file mode 100644 index 455711b..0000000 --- a/includes/quiz_stats/quiz_stats.module +++ /dev/null @@ -1,145 +0,0 @@ -' . t('Module creates a report to analyse and compare the results of quiz attendees. The reports will be displayed visually using goolge chart API.') . ''; - } -} - -/** - * Implements hook_permission(). - */ -function quiz_stats_permission() { - $permission = array( - 'access user stats' => array( - 'title' => t('access user stats'), - ), - 'access author stats' => array( - 'title' => t('access author stats'), - ), - ); - return $permission; -} - -/** - * Implements hook_menu(). - */ -function quiz_stats_menu() { - $items['admin/quiz/reports/stats/creator'] = array( - 'title' => 'Quiz Statistics', - 'description' => 'Generates a report on quiz results for quiz creators.', - 'file' => 'quiz_stats.admin.inc', - 'page callback' => 'quiz_stats_get_basic_stats', - 'access arguments' => array('access author stats'), - 'type' => MENU_NORMAL_ITEM, - ); - $items['node/%node/statistics'] = array( - 'title' => 'Statistics', - 'description' => 'Generates a report on quiz results for quiz creators.', - 'file' => 'quiz_stats.admin.inc', - 'page callback' => 'quiz_stats_revision_selector_page', - 'page arguments' => array(1), - 'access callback' => 'quiz_type_confirm', - 'access arguments' => array(1, 'access user stats'), - 'type' => MENU_LOCAL_TASK, - 'weight' => 4, - ); - $items['node/%node/statistics/%'] = array( - 'title' => 'Statistics', - 'description' => 'Generates a report on quiz results for quiz creators.', - 'file' => 'quiz_stats.admin.inc', - 'page callback' => 'quiz_stats_get_adv_stats', - 'page arguments' => array(3), - 'access callback' => 'quiz_stats_validate_vid', - 'access arguments' => array(1, 3), - 'type' => MENU_CALLBACK, - 'weight' => 4, - ); - $items['user/%/stats'] = array( - 'title' => 'Result Statistics', - 'description' => 'Generates a report on quiz results for quiz creators.', - 'file' => 'quiz_stats.admin.inc', - 'page callback' => 'quiz_stats_get_basic_stats', - 'page arguments' => array(1), - 'access arguments' => array('access user stats'), - 'type' => MENU_LOCAL_TASK, - ); - $items['user/%/stats/%/view'] = array( - 'title' => 'Result Statistics', - 'file' => 'quiz_stats.admin.inc', - 'page callback' => 'quiz_stats_get_adv_stats', - 'page arguments' => array(3, 1), - 'access arguments' => array('access user stats'), - 'type' => MENU_CALLBACK, - ); - return $items; -} - -/** - * Implements hook_theme(). - */ -function quiz_stats_theme() { - $path = drupal_get_path('module', 'quiz_stats') . '/theme'; - return array( - 'quiz_stats_get_basic_stats' => array( - 'variables' => array('results' => NULL), - 'file' => 'quiz_stats.admin.inc', - ), - 'date_vs_takeup_count' => array( - 'variables' => array('takeup' => NULL), - 'file' => 'quiz_stats.admin.inc', - ), - 'get_quiz_status_chart' => array( - 'variables' => array('quiz' => NULL), - 'file' => 'quiz_stats.admin.inc', - ), - 'quiz_top_scorers' => array( - 'variables' => array('scorer' => NULL), - 'file' => 'quiz_stats.admin.inc', - ), - 'quiz_grade_range' => array( - 'variables' => array('range' => NULL), - 'file' => 'quiz_stats.admin.inc', - ), - 'quiz_stats_revision_selector' => array( - 'variables' => array('content' => NULL), - 'path' => $path, - 'template' => 'quiz_stats_revision_selector', - ), - 'quiz_stats_charts' => array( - 'variables' => array('charts' => NULL), - 'path' => $path, - 'template' => 'quiz_stats_charts', - ), - ); -} - -/** - * Validate that a node is of type quiz, and that the user has access to it, and that the vid is a vid of that quiz - * - * @param $quiz - * The quiz node - * @param $vid - * The version id - * @return - * TRUE if user has access - */ -function quiz_stats_validate_vid($quiz, $vid) { - if ($quiz->type != 'quiz') { - return FALSE; - } - if (!user_access('access author stats')) { - return FALSE; - } - return $quiz->nid == db_query('SELECT n.nid FROM {node} n INNER JOIN {node_revision} nr ON (n.nid = nr.nid) WHERE nr.vid = :vid', array(':vid' => $vid))->fetchField(); -} diff --git a/includes/quiz_stats/theme/quiz_stats_charts.tpl.php b/includes/quiz_stats/theme/quiz_stats_charts.tpl.php deleted file mode 100755 index 8901c75..0000000 --- a/includes/quiz_stats/theme/quiz_stats_charts.tpl.php +++ /dev/null @@ -1,39 +0,0 @@ -' . $chart['title'] . '' . "\n" - . $chart['chart']. "\n" - . $chart['explanation'] . "\n"; - $chart_found = TRUE; - } - } -} -_quiz_stats_print_chart($charts['takeup']); -_quiz_stats_print_chart($charts['top_scorers']); -_quiz_stats_print_chart($charts['status']); -_quiz_stats_print_chart($charts['grade_range']); -if (!$chart_found) { - echo t('There are no statistics for this quiz (or quiz revision). This is probably because nobody has yet run this quiz (or quiz revision). If the quiz has multiple revisions, it is possible that the other revisions do have statistics. If this is the last revision, taking the quiz should generate some statistics.'); -} -?> diff --git a/includes/quiz_stats/theme/quiz_stats_revision_selector.tpl.php b/includes/quiz_stats/theme/quiz_stats_revision_selector.tpl.php deleted file mode 100755 index 9c45aa0..0000000 --- a/includes/quiz_stats/theme/quiz_stats_revision_selector.tpl.php +++ /dev/null @@ -1,9 +0,0 @@ -' . $content['explanation'] . '' . "\n"; -print ''; -$counter = 1; -foreach ($content['links'] as $key => $value) { - print ' | '. l(t('revision !num', array('!num' => $counter++)), $value); -} -print ' |
'; -?> \ No newline at end of file diff --git a/includes/views/defaults/questions_by_quiz.view.inc b/includes/views/defaults/questions_by_quiz.view.inc deleted file mode 100644 index d97a895..0000000 --- a/includes/views/defaults/questions_by_quiz.view.inc +++ /dev/null @@ -1,292 +0,0 @@ -name = 'questions_by_quiz'; -$view->description = 'View of all the questions associated with a quiz'; -$view->tag = 'quiz'; -$view->view_php = ''; -$view->base_table = 'quiz_node_properties'; -$view->is_cacheable = FALSE; -$view->api_version = 2; -$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ -$handler = $view->new_display('default', 'Defaults', 'default'); -$handler->override_option('relationships', array( - 'parent_vid' => array( - 'label' => 'node', - 'required' => 1, - 'id' => 'parent_vid', - 'table' => 'quiz_node_relationship', - 'field' => 'parent_vid', - 'relationship' => 'none', - ), - 'child_vid' => array( - 'label' => 'question node revision', - 'required' => 0, - 'id' => 'child_vid', - 'table' => 'quiz_node_relationship', - 'field' => 'child_vid', - 'relationship' => 'none', - ), -)); -$handler->override_option('fields', array( - 'child_nid' => array( - 'label' => 'Question Node ID', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'set_precision' => FALSE, - 'precision' => 0, - 'decimal' => '.', - 'separator' => '', - 'prefix' => '', - 'suffix' => '', - 'exclude' => 1, - 'id' => 'child_nid', - 'table' => 'quiz_node_relationship', - 'field' => 'child_nid', - 'relationship' => 'none', - ), - 'title' => array( - 'label' => 'Question Title', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 1, - 'path' => 'node/[child_nid]', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'link_to_node' => 1, - 'exclude' => 0, - 'id' => 'title', - 'table' => 'node_revision', - 'field' => 'title', - 'relationship' => 'child_vid', - ), - 'type' => array( - 'label' => 'Question Type', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'link_to_node' => 0, - 'exclude' => 0, - 'id' => 'type', - 'table' => 'node', - 'field' => 'type', - 'relationship' => 'child_vid', - ), - 'question_status' => array( - 'label' => 'Question Status', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'type' => 'yes-no', - 'not' => 0, - 'exclude' => 0, - 'id' => 'question_status', - 'table' => 'quiz_node_relationship', - 'field' => 'question_status', - 'relationship' => 'none', - ), -)); -$handler->override_option('arguments', array( - 'nid' => array( - 'default_action' => 'not found', - 'style_plugin' => 'default_summary', - 'style_options' => array(), - 'wildcard' => 'all', - 'wildcard_substitution' => 'All', - 'title' => 'Questions', - 'breadcrumb' => '', - 'default_argument_type' => 'fixed', - 'default_argument' => '', - 'validate_type' => 'node', - 'validate_fail' => 'not found', - 'break_phrase' => 0, - 'not' => 0, - 'which_vid' => 'latest', - 'id' => 'nid', - 'table' => 'quiz_node_properties', - 'field' => 'nid', - 'validate_user_argument_type' => 'uid', - 'validate_user_roles' => array( - '2' => 0, - '3' => 0, - ), - 'relationship' => 'none', - 'default_options_div_prefix' => '', - 'default_argument_user' => 0, - 'default_argument_fixed' => '', - 'default_argument_php' => '', - 'validate_argument_node_type' => array( - 'quiz' => 'quiz', - 'certificate' => 0, - 'long_answer' => 0, - 'matching' => 0, - 'multichoice' => 0, - 'quiz_directions' => 0, - 'scale' => 0, - 'short_answer' => 0, - 'truefalse' => 0, - 'page' => 0, - 'story' => 0, - ), - 'validate_argument_node_access' => 0, - 'validate_argument_nid_type' => 'nid', - 'validate_argument_vocabulary' => array(), - 'validate_argument_type' => 'tid', - 'validate_argument_transform' => 0, - 'validate_user_restrict_roles' => 0, - 'validate_argument_php' => '', - ), -)); -$handler->override_option('access', array( - 'type' => 'none', -)); -$handler->override_option('cache', array( - 'type' => 'none', -)); -$handler->override_option('use_ajax', TRUE); -$handler->override_option('use_pager', 'mini'); -$handler->override_option('style_plugin', 'table'); -$handler->override_option('style_options', array( - 'grouping' => 'parent_vid', - 'override' => 1, - 'sticky' => 0, - 'order' => 'asc', - 'columns' => array( - 'timestamp' => 'parent_nid', - 'title' => 'title', - 'type' => 'type', - 'child_nid' => 'child_nid', - 'child_vid' => 'child_vid', - 'question_status' => 'question_status', - 'parent_nid' => 'timestamp', - 'parent_vid' => 'parent_vid', - ), - 'info' => array( - 'timestamp' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'title' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'type' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'child_nid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'child_vid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'question_status' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'parent_nid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'parent_vid' => array( - 'sortable' => 0, - 'separator' => '', - ), - ), - 'default' => 'title', -)); -$handler = $view->new_display('page', 'Questions by Specific Quiz', 'page_1'); -$handler->override_option('path', 'admin/quiz/questions_by_quiz/%'); -$handler->override_option('menu', array( - 'type' => 'none', - 'title' => 'Questions by Quiz', - 'description' => '', - 'weight' => '0', - 'name' => 'navigation', -)); -$handler->override_option('tab_options', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, -)); -$handler = $view->new_display('page', 'Page', 'page_2'); -$handler->override_option('path', 'admin/quiz/%/questions'); -$handler->override_option('menu', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, - 'name' => 'navigation', -)); -$handler->override_option('tab_options', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, -)); diff --git a/includes/views/defaults/questions_by_quiz_version.view.inc b/includes/views/defaults/questions_by_quiz_version.view.inc deleted file mode 100644 index 497d678..0000000 --- a/includes/views/defaults/questions_by_quiz_version.view.inc +++ /dev/null @@ -1,401 +0,0 @@ -name = 'questions_by_quiz_version'; -$view->description = 'View of all the questions associated with a particular revision of a quiz'; -$view->tag = 'quiz'; -$view->view_php = ''; -$view->base_table = 'quiz_node_properties'; -$view->is_cacheable = FALSE; -$view->api_version = 2; -$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ -$handler = $view->new_display('default', 'Defaults', 'default'); -$handler->override_option('relationships', array( - 'parent_vid' => array( - 'label' => 'node', - 'required' => 1, - 'id' => 'parent_vid', - 'table' => 'quiz_node_relationship', - 'field' => 'parent_vid', - 'relationship' => 'none', - ), - 'child_vid' => array( - 'label' => 'question node revision', - 'required' => 0, - 'id' => 'child_vid', - 'table' => 'quiz_node_relationship', - 'field' => 'child_vid', - 'relationship' => 'none', - ), -)); -$handler->override_option('fields', array( - 'parent_nid' => array( - 'label' => 'Quiz Node ID', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'set_precision' => FALSE, - 'precision' => 0, - 'decimal' => '.', - 'separator' => '', - 'prefix' => '', - 'suffix' => '', - 'exclude' => 0, - 'id' => 'parent_nid', - 'table' => 'quiz_node_relationship', - 'field' => 'parent_nid', - 'relationship' => 'none', - ), - 'parent_vid' => array( - 'label' => 'Quiz Node VID', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'set_precision' => FALSE, - 'precision' => 0, - 'decimal' => '.', - 'separator' => '', - 'prefix' => '', - 'suffix' => '', - 'exclude' => 0, - 'id' => 'parent_vid', - 'table' => 'quiz_node_relationship', - 'field' => 'parent_vid', - 'relationship' => 'none', - ), - 'timestamp' => array( - 'label' => 'Quiz Created', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'date_format' => 'small', - 'custom_date_format' => '', - 'exclude' => 0, - 'id' => 'timestamp', - 'table' => 'node_revision', - 'field' => 'timestamp', - 'relationship' => 'parent_vid', - ), - 'title' => array( - 'label' => 'Question Title', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'link_to_node' => 1, - 'exclude' => 0, - 'id' => 'title', - 'table' => 'node_revision', - 'field' => 'title', - 'relationship' => 'child_vid', - ), - 'type' => array( - 'label' => 'Question Type', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'link_to_node' => 0, - 'exclude' => 0, - 'id' => 'type', - 'table' => 'node', - 'field' => 'type', - 'relationship' => 'child_vid', - ), - 'child_nid' => array( - 'label' => 'Question Node ID', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'set_precision' => FALSE, - 'precision' => 0, - 'decimal' => '.', - 'separator' => '', - 'prefix' => '', - 'suffix' => '', - 'exclude' => 0, - 'id' => 'child_nid', - 'table' => 'quiz_node_relationship', - 'field' => 'child_nid', - 'relationship' => 'none', - ), - 'child_vid' => array( - 'label' => 'Question Node VID', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'link_class' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'target' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'empty' => '', - 'hide_empty' => 0, - 'empty_zero' => 0, - 'set_precision' => FALSE, - 'precision' => 0, - 'decimal' => '.', - 'separator' => '', - 'prefix' => '', - 'suffix' => '', - 'exclude' => 0, - 'id' => 'child_vid', - 'table' => 'quiz_node_relationship', - 'field' => 'child_vid', - 'relationship' => 'none', - ), - 'question_status' => array( - 'label' => 'Question Status', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'type' => 'yes-no', - 'not' => 0, - 'exclude' => 0, - 'id' => 'question_status', - 'table' => 'quiz_node_relationship', - 'field' => 'question_status', - 'relationship' => 'none', - ), -)); -$handler->override_option('arguments', array( - 'vid' => array( - 'default_action' => 'ignore', - 'style_plugin' => 'default_summary', - 'style_options' => array(), - 'wildcard' => 'all', - 'wildcard_substitution' => 'All', - 'title' => 'Questions on Quiz \'%1\'', - 'default_argument_type' => 'fixed', - 'default_argument' => '', - 'validate_type' => 'none', - 'validate_fail' => 'not found', - 'break_phrase' => 0, - 'not' => 0, - 'id' => 'vid', - 'table' => 'quiz_node_properties', - 'field' => 'vid', - 'relationship' => 'none', - 'default_options_div_prefix' => '', - 'default_argument_user' => 0, - 'default_argument_fixed' => '', - 'default_argument_php' => '', - 'validate_argument_node_type' => array( - 'long_answer' => 0, - 'multichoice' => 0, - 'quiz' => 0, - 'true_false' => 0, - 'page' => 0, - 'story' => 0, - ), - 'validate_argument_node_access' => 0, - 'validate_argument_nid_type' => 'nid', - 'validate_argument_vocabulary' => array(), - 'validate_argument_type' => 'tid', - 'user_argument_type' => '', - 'restrict_user_roles' => 0, - 'user_roles' => array(), - 'validate_argument_php' => '', - ), -)); -$handler->override_option('access', array( - 'type' => 'none', -)); -$handler->override_option('cache', array( - 'type' => 'none', -)); -$handler->override_option('use_ajax', TRUE); -$handler->override_option('style_plugin', 'table'); -$handler->override_option('style_options', array( - 'grouping' => 'parent_vid', - 'override' => 1, - 'sticky' => 0, - 'order' => 'asc', - 'columns' => array( - 'timestamp' => 'parent_nid', - 'title' => 'title', - 'type' => 'type', - 'child_nid' => 'child_nid', - 'child_vid' => 'child_vid', - 'question_status' => 'question_status', - 'parent_nid' => 'timestamp', - 'parent_vid' => 'parent_vid', - ), - 'info' => array( - 'timestamp' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'title' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'type' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'child_nid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'child_vid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'question_status' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'parent_nid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'parent_vid' => array( - 'sortable' => 0, - 'separator' => '', - ), - ), - 'default' => 'title', -)); -$handler = $view->new_display('page', 'Questions by Specific Quiz', 'page_1'); -$handler->override_option('path', 'admin/quiz/questions_by_quiz/%'); -$handler->override_option('menu', array( - 'type' => 'none', - 'title' => 'Questions by Quiz', - 'description' => '', - 'weight' => '0', - 'name' => 'navigation', -)); -$handler->override_option('tab_options', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, -)); diff --git a/includes/views/defaults/quiz_by_author.view.inc b/includes/views/defaults/quiz_by_author.view.inc deleted file mode 100644 index 1778be5..0000000 --- a/includes/views/defaults/quiz_by_author.view.inc +++ /dev/null @@ -1,267 +0,0 @@ -name = 'quiz_by_author'; -$view->description = 'View of all the quizzes owned by a particular user (\'author\').'; -$view->tag = 'quiz'; -$view->view_php = ''; -$view->base_table = 'quiz_node_properties'; -$view->is_cacheable = FALSE; -$view->api_version = 2; -$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ -$handler = $view->new_display('default', 'Defaults', 'default'); -$handler->override_option('relationships', array( - 'nid' => array( - 'label' => 'node', - 'required' => 1, - 'id' => 'nid', - 'table' => 'quiz_node_properties', - 'field' => 'nid', - 'relationship' => 'none', - ), -)); -$handler->override_option('fields', array( - 'nid' => array( - 'label' => 'Quiz Node NID', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'exclude' => 0, - 'id' => 'nid', - 'table' => 'quiz_node_properties', - 'field' => 'nid', - 'relationship' => 'none', - ), - 'vid' => array( - 'label' => 'Quiz Node VID', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'exclude' => 0, - 'id' => 'vid', - 'table' => 'quiz_node_properties', - 'field' => 'vid', - 'relationship' => 'none', - ), - 'quiz_close' => array( - 'label' => 'Quiz close time', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'date_format' => 'small', - 'custom_date_format' => '', - 'exclude' => 0, - 'id' => 'quiz_close', - 'table' => 'quiz_node_properties', - 'field' => 'quiz_close', - 'relationship' => 'none', - ), - 'quiz_open' => array( - 'label' => 'Quiz open time', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'date_format' => 'small', - 'custom_date_format' => '', - 'exclude' => 0, - 'id' => 'quiz_open', - 'table' => 'quiz_node_properties', - 'field' => 'quiz_open', - 'relationship' => 'none', - ), - 'time_limit' => array( - 'label' => 'Time limit', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'set_precision' => FALSE, - 'precision' => 0, - 'decimal' => '.', - 'separator' => ',', - 'prefix' => '', - 'suffix' => '', - 'exclude' => 0, - 'id' => 'time_limit', - 'table' => 'quiz_node_properties', - 'field' => 'time_limit', - 'relationship' => 'none', - ), - 'takes' => array( - 'label' => 'Allowed takes', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'exclude' => 0, - 'id' => 'takes', - 'table' => 'quiz_node_properties', - 'field' => 'takes', - 'relationship' => 'none', - ), -)); -$handler->override_option('arguments', array( - 'uid' => array( - 'default_action' => 'not found', - 'style_plugin' => 'default_summary', - 'style_options' => array(), - 'wildcard' => 'all', - 'wildcard_substitution' => 'All', - 'title' => 'Quizzes created by %1', - 'default_argument_type' => 'fixed', - 'default_argument' => '', - 'validate_type' => 'none', - 'validate_fail' => 'not found', - 'break_phrase' => 0, - 'not' => 0, - 'id' => 'uid', - 'table' => 'quiz_node_results', - 'field' => 'uid', - 'relationship' => 'none', - 'default_options_div_prefix' => '', - 'default_argument_user' => 0, - 'default_argument_fixed' => '', - 'default_argument_php' => '', - 'validate_argument_node_type' => array( - 'long_answer' => 0, - 'multichoice' => 0, - 'quiz' => 0, - 'true_false' => 0, - 'page' => 0, - 'story' => 0, - ), - 'validate_argument_node_access' => 0, - 'validate_argument_nid_type' => 'nid', - 'validate_argument_vocabulary' => array(), - 'validate_argument_type' => 'tid', - 'user_argument_type' => 'either', - 'restrict_user_roles' => 0, - 'user_roles' => array(), - 'validate_argument_php' => '', - ), -)); -$handler->override_option('access', array( - 'type' => 'none', -)); -$handler->override_option('style_plugin', 'table'); -$handler->override_option('style_options', array( - 'grouping' => 'nid', - 'override' => 1, - 'sticky' => 0, - 'order' => 'asc', - 'columns' => array( - 'nid' => 'nid', - 'vid' => 'vid', - 'quiz_close' => 'quiz_close', - 'quiz_open' => 'quiz_open', - 'time_limit' => 'time_limit', - ), - 'info' => array( - 'nid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'vid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'quiz_close' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'quiz_open' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'time_limit' => array( - 'sortable' => 0, - 'separator' => '', - ), - ), - 'default' => '-1', -)); -$handler = $view->new_display('page', 'Page', 'page_1'); -$handler->override_option('path', 'quiztest/byauthor/%'); -$handler->override_option('menu', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, - 'name' => 'navigation', -)); -$handler->override_option('tab_options', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, -)); diff --git a/includes/views/defaults/quiz_by_taker.view.inc b/includes/views/defaults/quiz_by_taker.view.inc deleted file mode 100644 index 5e2d7ad..0000000 --- a/includes/views/defaults/quiz_by_taker.view.inc +++ /dev/null @@ -1,311 +0,0 @@ -name = 'quiz_by_taker'; -$view->description = 'View of all the quizzes a user has taken'; -$view->tag = 'quiz'; -$view->view_php = ''; -$view->base_table = 'quiz_node_properties'; -$view->is_cacheable = FALSE; -$view->api_version = 2; -$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ -$handler = $view->new_display('default', 'Defaults', 'default'); -$handler->override_option('relationships', array( - 'vid' => array( - 'label' => 'quiz revision', - 'required' => 1, - 'id' => 'vid', - 'table' => 'quiz_node_results', - 'field' => 'vid', - 'relationship' => 'none', - ), -)); -$handler->override_option('fields', array( - 'title' => array( - 'label' => 'Title', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'link_to_node' => 0, - 'exclude' => 0, - 'id' => 'title', - 'table' => 'node_revision', - 'field' => 'title', - 'relationship' => 'vid', - ), - 'name' => array( - 'label' => 'Name', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'link_to_user' => 1, - 'overwrite_anonymous' => 0, - 'anonymous_text' => '', - 'exclude' => 0, - 'id' => 'name', - 'table' => 'users', - 'field' => 'name', - 'relationship' => 'vid', - ), - 'score' => array( - 'label' => 'Score', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'exclude' => 0, - 'id' => 'score', - 'table' => 'quiz_node_results', - 'field' => 'score', - 'relationship' => 'none', - ), - 'time_start' => array( - 'label' => 'Quiz Start Time.', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'date_format' => 'small', - 'custom_date_format' => '', - 'exclude' => 0, - 'id' => 'time_start', - 'table' => 'quiz_node_results', - 'field' => 'time_start', - 'relationship' => 'none', - ), - 'time_end' => array( - 'label' => 'Quiz End Time', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'html' => 0, - ), - 'date_format' => 'small', - 'custom_date_format' => '', - 'exclude' => 0, - 'id' => 'time_end', - 'table' => 'quiz_node_results', - 'field' => 'time_end', - 'relationship' => 'none', - ), - 'quiz_state' => array( - 'label' => 'Quiz State', - 'alter' => array( - 'alter_text' => 0, - 'text' => '', - 'make_link' => 0, - 'path' => '', - 'alt' => '', - 'prefix' => '', - 'suffix' => '', - 'help' => '', - 'trim' => 0, - 'max_length' => '', - 'word_boundary' => 1, - 'ellipsis' => 1, - 'strip_tags' => 0, - 'html' => 0, - ), - 'exclude' => 0, - 'id' => 'quiz_state', - 'table' => 'quiz_node_results', - 'field' => 'quiz_state', - 'relationship' => 'none', - ), -)); -$handler->override_option('arguments', array( - 'uid' => array( - 'default_action' => 'not found', - 'style_plugin' => 'default_summary', - 'style_options' => array(), - 'wildcard' => 'all', - 'wildcard_substitution' => 'All', - 'title' => 'Quizzes taken by %1', - 'default_argument_type' => 'fixed', - 'default_argument' => '', - 'validate_type' => 'none', - 'validate_fail' => 'not found', - 'break_phrase' => 0, - 'not' => 0, - 'allow_null' => 1, - 'id' => 'uid', - 'table' => 'quiz_node_results', - 'field' => 'uid', - 'relationship' => 'none', - 'default_options_div_prefix' => '', - 'default_argument_user' => 0, - 'default_argument_fixed' => '', - 'default_argument_php' => '', - 'validate_argument_node_type' => array( - 'multichoice' => 0, - 'quiz' => 0, - 'long_answer' => 0, - 'true_false' => 0, - 'page' => 0, - 'story' => 0, - 'wanky_group' => 0, - ), - 'validate_argument_node_access' => 0, - 'validate_argument_nid_type' => 'nid', - 'validate_argument_vocabulary' => array( - '1' => 0, - '2' => 0, - '3' => 0, - ), - 'validate_argument_type' => 'tid', - 'user_argument_type' => '', - 'restrict_user_roles' => 0, - 'user_roles' => array(), - 'validate_argument_php' => '', - 'validate_user_argument_type' => 'uid', - 'validate_user_roles' => array( - '2' => 0, - ), - 'validate_argument_transform' => 0, - 'validate_user_restrict_roles' => 0, - 'validate_argument_is_member' => 0, - ), -)); -$handler->override_option('filters', array( - 'nid' => array( - 'operator' => '=', - 'value' => '', - 'group' => '0', - 'exposed' => FALSE, - 'expose' => array( - 'operator' => FALSE, - 'label' => '', - ), - 'which_vid' => 'latest', - 'secondary' => 'results', - 'id' => 'nid', - 'table' => 'quiz_node_properties', - 'field' => 'nid', - 'relationship' => 'none', - 'secondary_group_by' => 'uid', - 'secondary_group_by_table' => 'quiz_node_results', - 'secondary_table' => 'quiz_node_results', - 'secondaries' => array( - 'secondary_group_by' => 'uid', - 'secondary_table' => 'quiz_node_results', - 'secondary_vid' => 'vid', - 'secondary_nid' => 'nid', - ), - ), -)); -$handler->override_option('access', array( - 'type' => 'none', -)); -$handler->override_option('style_plugin', 'table'); -$handler->override_option('style_options', array( - 'grouping' => 'vid', - 'override' => 1, - 'sticky' => 0, - 'order' => 'asc', - 'columns' => array( - 'time_end' => 'time_end', - 'nid' => 'nid', - 'vid' => 'vid', - 'time_start' => 'time_start', - 'score' => 'score', - 'uid' => 'uid', - ), - 'info' => array( - 'time_end' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'nid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'vid' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'time_start' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'score' => array( - 'sortable' => 0, - 'separator' => '', - ), - 'uid' => array( - 'sortable' => 0, - 'separator' => '', - ), - ), - 'default' => '-1', -)); -$handler = $view->new_display('page', 'Page', 'page_1'); -$handler->override_option('path', 'quiztest/bytaker/%'); -$handler->override_option('menu', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, - 'name' => 'navigation', -)); -$handler->override_option('tab_options', array( - 'type' => 'none', - 'title' => '', - 'description' => '', - 'weight' => 0, -)); diff --git a/includes/views/handlers/quiz_views_handler_argument_quiz_nid.inc b/includes/views/handlers/quiz_views_handler_argument_quiz_nid.inc deleted file mode 100644 index dee1efb..0000000 --- a/includes/views/handlers/quiz_views_handler_argument_quiz_nid.inc +++ /dev/null @@ -1,168 +0,0 @@ -nid_field = $this->definition['nid field']; - $this->vid_field = isset($this->definition['vid field']) ? $this->definition['vid field'] : 'vid'; - } - - function option_definition() { - $options = parent::option_definition(); - - $options['which_vid'] = array('default' => 'latest'); - - return $options; - } - - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - // Set weighting so that our element can sanely ordered - $form['title']['#weight'] = -2; - // $form['default_action']['#weight'] = -3; - // $form['wildcard']['#weight'] = -4; - // $form['wildcard_substitution']['#weight'] = -4; - $form['which_vid'] = array( - '#type' => 'select', - '#title' => t('Quiz revision(s) to use'), - '#options' => array( - 'latest' => t('Latest: most recent version of the quiz ONLY.'), - 'initial' => t('Initial: original version of the quiz ONLY.'), - 'all' => t('All: ALL versions of the quiz.') - ), - '#description' => t('The validator will transform the incoming quiz node id(s) into one or more quiz node revisions, depending on your selection.'), - '#default_value' => $this->options['which_vid'], - '#weight' => -1, - ); - } - - function title_query() { - $titles = array(); - $placeholders = implode(', ', array_fill(0, count($this->corresponding_vids), '%d')); - - // TODO Please convert this statement to the D7 database API syntax. - $result = db_query("SELECT n.title FROM {node_revision} n WHERE n.vid IN ($placeholders)", $this->corresponding_vids); - while ($term = $result->fetch()) { - $titles[] = check_plain($term->title); - } - return $titles; - } - - /** - * Override the default behavior of query() to introduce the medial step of - * retrieving vids from the provided nids. - */ - function query() { - $this->ensure_my_table(); - $this->break_phrase(); - - // Do subselect to get relevant vids, then add where clause - if ($this->subselect_vids()) { - $placeholders = implode(', ', array_fill(0, count($this->corresponding_vids), '%d')); - $this->query->add_where(0, "$this->table_alias.$this->nid_field IN ($placeholders)", $this->corresponding_vids); - } - else { - $this->query->add_where(0, "$this->table_alias.$this->nid_field", $this->corresponding_vids); - } - } - - /** - * Set up the argument with the vids extracted from nids. - * - * Needs to be done here, because this is the earliest stage at which we can - * guarantee the contents of $this->argument to be available. - * - * @param string $arg - * The argument, as delivered in the URL. - */ - function set_argument($arg) { - $this->argument = $arg; - if ($this->validate_arg($arg)) { - $this->break_phrase(); - $this->subselect_vids(); - return TRUE; - } - return FALSE; - } - - function break_phrase() { - if (!$this->phrase_broken) { - // Handle multiple argument inputs - if (!empty($this->options['break_phrase'])) { - views_break_phrase($this->argument, $this); - } - else { - $this->value = array($this->argument); - } - $this->phrase_broken = TRUE; - } - } - - /** - * Helper method to retrieve the vid(s) the final view query should actually - * be run against. - * - * Would be done in pre_query(), but $this->argument is not yet available at - * that time. So, called from set_argument(). - */ - function subselect_vids() { - $from = 'FROM {quiz_node_properties} qnp'; - $field = 'qnp.vid'; - if (count($this->value) > 1) { - // Guaranteed to produce multiple values; therefore may need the group by - $use_group_by = TRUE; - - $operator = empty($this->options['not']) ? 'IN' : 'NOT IN'; - $placeholders = implode(', ', array_fill(0, count($this->value), '%d')); - $where = "WHERE qnp.nid $operator ($placeholders)"; - } - else { - // Multiple values only possible with a NOT; only then do we need group by - $use_group_by = !empty($this->options['not']); - - $operator = empty($this->options['not']) ? '=' : '!='; - $where = "WHERE qnp.nid $operator :nid"; - } - - switch ($this->options['which_vid']) { - case 'initial': - case 'latest': - // SQL operation for getting a single vids based on view config settings - $operation = ($this->options['which_vid'] == 'initial' ? 'MIN' : 'MAX'); - // If we need a group by clause, tag it onto the end. - $where .= ($use_group_by) ? ' GROUP BY qnp.nid' : ''; - - // TODO Please convert this statement to the D7 database API syntax. - $result = db_query("SELECT $operation(qnp.vid) AS vid $from $where", array(':nid' => $this->value)); - break; - - case 'all': - // TODO Please convert this statement to the D7 database API syntax. - $result = db_query("SELECT qnp.vid AS vid $from $where", $this->value); - break; - } - - $this->nid_field = $this->vid_field; - - $this->corresponding_vids = array(); - while ($item = $result->fetch()) { - $this->corresponding_vids[] = $item->vid; - } - return (count($this->corresponding_vids) > 1); - } -} diff --git a/includes/views/handlers/quiz_views_handler_argument_user_uid_nullable.inc b/includes/views/handlers/quiz_views_handler_argument_user_uid_nullable.inc deleted file mode 100644 index 16e5cd5..0000000 --- a/includes/views/handlers/quiz_views_handler_argument_user_uid_nullable.inc +++ /dev/null @@ -1,43 +0,0 @@ - FALSE); - - return $options; - } - - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - unset($form['break_phrase']); - $form['allow_null'] = array( - '#type' => 'checkbox', - '#title' => t('Used for Quiz Status'), - '#description' => t('When this agument is used, this box must be checked if the Quiz Status field is to work properly.'), - '#default_value' => $this->options['allow_null'], - ); - } - - function query() { - $this->ensure_my_table(); - $operator = empty($this->options['not']) ? '=' : '!='; - $where = "$this->table_alias.$this->real_field"; - if ($this->options['allow_null']) { - $group = $this->query->set_where_group('AND', 'qnr_user'); - //$where .= " OR ISNULL($this->table_alias.$this->real_field)"; - } - else { - $group = 0; - } - - // By adding the ISNULL, joins can properly inform us about quiz state - $this->query->add_where($group, $where, $this->argument); - } -} diff --git a/includes/views/handlers/quiz_views_handler_field_number_questions.inc b/includes/views/handlers/quiz_views_handler_field_number_questions.inc deleted file mode 100644 index c57ccc4..0000000 --- a/includes/views/handlers/quiz_views_handler_field_number_questions.inc +++ /dev/null @@ -1,38 +0,0 @@ - TRUE, - ); - } - - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - $form['include_random'] = array( - '#type' => 'checkbox', - '#title' => t('Include random questions?'), - '#description' => t('Whether or not questions set to "random" should be included in the count.'), - '#default_value' => $this->options['include_random'], - ); - } - - function query() { - $this->ensure_my_table(); - $this->field_alias = 'question_count'; - $formula = "COUNT($this->table_alias.child_vid)"; - if ($this->options['include_random']) { - $properties_alias = $this->query->ensure_table('quiz_node_properties'); - - $formula .= " + (SELECT number_of_random_questions FROM {quiz_node_properties} WHERE vid = $this->table_alias.parent_vid)"; - } - $this->query->add_field(NULL, "$formula", $this->field_alias); - } -} diff --git a/includes/views/handlers/quiz_views_handler_field_question_status.inc b/includes/views/handlers/quiz_views_handler_field_question_status.inc deleted file mode 100644 index e27b649..0000000 --- a/includes/views/handlers/quiz_views_handler_field_question_status.inc +++ /dev/null @@ -1,22 +0,0 @@ -text_map = array( - QUESTION_RANDOM => t('Random'), // 'Random-ly' better? - QUESTION_ALWAYS => t('Always'), - QUESTION_NEVER => t('Never'), - ); - } - - function render($values) { - return $this->text_map[$values->{$this->field_alias}]; - } -} diff --git a/includes/views/handlers/quiz_views_handler_field_score_aggregate.inc b/includes/views/handlers/quiz_views_handler_field_score_aggregate.inc deleted file mode 100644 index 31f96d2..0000000 --- a/includes/views/handlers/quiz_views_handler_field_score_aggregate.inc +++ /dev/null @@ -1,30 +0,0 @@ -group_field = $this->definition['group field']; - } - - function option_definition() { - $options = parent::option_definition(); - return $options; - } - - function query() { - $this->ensure_my_table(); - $this->query->add_field($this->table_alias, $this->group_field); - $this->field_alias = $this->query->add_field( - NULL, "AVG($this->table_alias.$this->real_field)", - $this->table_alias . '__average', array('aggregate' => TRUE) - ); - $this->query->add_groupby("$this->table_alias.$this->group_field"); // nid is OK for average. Others maybe not so much - } -} diff --git a/includes/views/handlers/quiz_views_handler_field_takes.inc b/includes/views/handlers/quiz_views_handler_field_takes.inc deleted file mode 100644 index cd67eda..0000000 --- a/includes/views/handlers/quiz_views_handler_field_takes.inc +++ /dev/null @@ -1,15 +0,0 @@ -{$this->field_alias}; - if ((int) $value === 0) { - return 'Unlimited'; - } - return $value; - } -} diff --git a/includes/views/handlers/quiz_views_handler_field_time.inc b/includes/views/handlers/quiz_views_handler_field_time.inc deleted file mode 100755 index 2e36528..0000000 --- a/includes/views/handlers/quiz_views_handler_field_time.inc +++ /dev/null @@ -1,12 +0,0 @@ -{$this->field_alias}; - return _quiz_format_duration($value); - } -} diff --git a/includes/views/handlers/quiz_views_handler_field_user_quiz_state.inc b/includes/views/handlers/quiz_views_handler_field_user_quiz_state.inc deleted file mode 100644 index 99dd86b..0000000 --- a/includes/views/handlers/quiz_views_handler_field_user_quiz_state.inc +++ /dev/null @@ -1,30 +0,0 @@ -real_field in this situation? - // $this->real_field = time() . 'BETWEEN ' - } - // Note that the native field is time_start, and time_end is added as an - // additional field in the handler declaration - function query() { - $this->ensure_my_table(); - $this->query->add_field($this->table_alias, "time_end", 'is_finished'); - // $this->query->add_field($this->table_alias, time() . " BETWEEN $this->table_alias.time_start AND $this->table_alias.time_end", $this->field_alias); - } - - function render($values) { - if ($values->is_finished > 0) { - return t('Finished'); - } - else { - return t('In Progress'); - } - } -} diff --git a/includes/views/handlers/quiz_views_handler_filter_question_status.inc b/includes/views/handlers/quiz_views_handler_filter_question_status.inc deleted file mode 100644 index ab20657..0000000 --- a/includes/views/handlers/quiz_views_handler_filter_question_status.inc +++ /dev/null @@ -1,18 +0,0 @@ -value_options)) { - $this->value_title = t('Question Status in Quiz'); - $this->value_options = array( - QUESTION_RANDOM => t('Random'), // 'Random-ly' better? - QUESTION_ALWAYS => t('Always'), - QUESTION_NEVER => t('Never'), - ); - } - } -} diff --git a/includes/views/handlers/quiz_views_handler_filter_quiz_nid.inc b/includes/views/handlers/quiz_views_handler_filter_quiz_nid.inc deleted file mode 100644 index c715289..0000000 --- a/includes/views/handlers/quiz_views_handler_filter_quiz_nid.inc +++ /dev/null @@ -1,140 +0,0 @@ - array( - 'secondary_group_by' => 'nid', - 'secondary_table' => 'quiz_node_results', - 'secondary_vid' => 'vid', - 'secondary_nid' => 'nid', - ), - 'results' => array( - 'secondary_group_by' => 'uid', - 'secondary_table' => 'quiz_node_results', - 'secondary_vid' => 'vid', - 'secondary_nid' => 'nid', - ), - 'questions' => array( - 'secondary_group_by' => 'child_nid', - 'secondary_table' => 'quiz_node_relationship', - 'secondary_vid' => 'parent_vid', - 'secondary_nid' => 'parent_nid', - ), - ); - - function construct() { - parent::construct(); - $this->vid_field = !empty($this->definition['vid field']) ? $this->definition['vid field'] : 'vid'; - $this->group_by = $this->definition['group by']; - if (!empty($this->definition['secondary group by'])) { - $this->secondary_group_by = $this->definition['secondary group by']; - } - } - - function can_expose() { - return FALSE; - } - - function option_definition() { - $options = parent::option_definition(); - - $options['which_vid'] = array('default' => 'latest'); - $options['secondary'] = array('default' => 'none'); - - return $options; - } - - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - $form['which_vid'] = array( - '#type' => 'select', - '#title' => t('Quiz revision(s) to use'), - '#options' => array( - 'latest' => t('Latest: most recent version of the quiz ONLY.'), - 'initial' => t('Initial: original version of the quiz ONLY.'), - // 'all' => t('All: ALL versions of the quiz.') - ), - '#description' => t('The filter will limit score results for a particular quiz node id(s) into one or more quiz node revision ids, depending on your selection.'), - '#default_value' => $this->options['which_vid'], - ); - - $tables = array('none' => t('None')); - $tables['results'] = t('Use Quiz Results'); - $tables['questions'] = t('Use Quiz Questions'); - $form['secondary'] = array( - '#type' => 'radios', - '#title' => t('Secondary Grouping'), - '#options' => $tables, - '#default_value' => $this->options['secondary'], - '#description' => t('If you are looking to generate a list of either quiz questions or quiz results, you must select the appropriate option from this list.'), - ); - } - - function value_submit($form, &$form_state) { - $form_state['values']['options']['secondaries'] = $this->secondary_tables[$form_state['values']['options']['secondary']]; - } - - /** - * Override the default behavior of query() to introduce the medial step of - * retrieving vids from the provided nids. - */ - function query() { - $this->ensure_my_table(); - - foreach ($this->options['secondaries'] as $key => $value) { - if (!is_null($value)) { - $this->$key = $value; - } - } - - $this->group_by_table = !empty($this->definition['group by table']) ? - $this->query->ensure_table($this->definition['group by table']) : - $this->table_alias; - // $this->query->add_groupby("$this->group_by_table.$this->group_by"); - $this->query->add_groupby("$this->table_alias.$this->field"); - $this->query->add_groupby("$this->table_alias.$this->vid_field"); - - // if (!empty($this->secondary_group_by) || !empty($this->options['secondary_group_by'])) { - if (!empty($this->secondary_group_by)) { - $this->secondary_table_alias = $this->query->ensure_table($this->secondary_table); - $this->query->add_groupby("$this->secondary_table_alias.$this->secondary_group_by"); - } - $this->query->add_where(0, "$this->secondary_table_alias.$this->secondary_vid", $this->subselect()); - } - - /** - * Helper method to retrieve the vid(s) the final view query should actually - * be run against. - * - * Would be done in pre_query(), but $this->argument is not yet available at - * that time. So, called from set_argument(). - */ - function subselect() { - $operation = ($this->options['which_vid'] == 'initial' ? 'MIN' : 'MAX'); - $subalias = $this->secondary_table_alias . '__subselect'; - $subselect = "SELECT $operation($subalias.$this->secondary_vid) FROM "; - $subselect .= '{' . $this->secondary_table . "} $subalias WHERE "; - $subselect .= "$subalias.$this->secondary_group_by = $this->secondary_table_alias.$this->secondary_group_by AND "; - $subselect .= "$subalias.$this->secondary_nid = $this->secondary_table_alias.$this->secondary_nid"; - return $subselect; - - } -} diff --git a/includes/views/handlers/quiz_views_handler_filter_user_nullable.inc b/includes/views/handlers/quiz_views_handler_filter_user_nullable.inc deleted file mode 100644 index e4f0937..0000000 --- a/includes/views/handlers/quiz_views_handler_filter_user_nullable.inc +++ /dev/null @@ -1,45 +0,0 @@ - FALSE); - $options['use_current'] = array('default' => FALSE); - - return $options; - } - - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - $form['use_current'] = array( - '#type' => 'checkbox', - '#title' => t('Use Current User'), - '#description' => t('Filter using the current user; only quiz results owned by the current user will be shown.'), - '#default_value' => $this->options['use_current'], - ); - $form['allow_null'] = array( - '#type' => 'checkbox', - '#title' => t('Used for Quiz Status'), - '#description' => t('If you are using the Quiz Status, this box must be checked; otherwise, the field will not work properly.'), - '#default_value' => $this->options['allow_null'], - ); - } - - function admin_summary() { - return !empty($this->options['use_current']) ? t('Current User') : t('NOT Current User'); - } - - function query() { - $group = $this->query->set_where_group('AND', 'qnr_user'); - $this->ensure_my_table(); - - $operator = empty($this->options['use_current']) ? '!=' : '='; - // By adding the ISNULL, joins can properly inform us about quiz state - $this->query->add_where($group, "$this->table_alias.$this->real_field $operator ***CURRENT_USER*** OR ISNULL($this->table_alias.$this->real_field)", $this->argument); - } -} diff --git a/includes/views/handlers/quiz_views_handler_filter_user_quiz_state.inc b/includes/views/handlers/quiz_views_handler_filter_user_quiz_state.inc deleted file mode 100644 index eb0f1a7..0000000 --- a/includes/views/handlers/quiz_views_handler_filter_user_quiz_state.inc +++ /dev/null @@ -1,73 +0,0 @@ -states = array( - 'any' => t('All Results (Do not filter)'), - 'not_started' => t('Not Started'), - 'in_progress' => t('In Progress'), - 'finished' => t('Finished'), - ); - } - - function option_definition() { - $options = parent::option_definition(); - $options['value']['quiz_state'] = array('default' => 'finished'); - return $options; - } - - function value_form(&$form, &$form_state) { - $form['value'] = array(); - $form['value']['quiz_state'] = $this->base_form_item(); - } - - function base_form_item() { - return array( - '#type' => 'radios', - '#title' => t('Quiz State'), - '#description' => t('Output will be limited to only include quiz results in this state. If the filter is exposed, the value set here will be used as the default. Note that "Any" is only useful for exposed filters.'), - '#options' => $this->states, - '#default_value' => !empty($this->value['quiz_state']) ? $this->value['quiz_state'] : 'any', - ); - } - - function exposed_form(&$form, &$form_state) { - if (empty($this->options['exposed'])) { - return; - } - if (!empty($this->options['expose']['identifier'])) { - $value = $this->options['expose']['identifier']; - - $form[$value]['quiz_state'] = $this->base_form_item(); - unset($form[$value]['quiz_state']['#title'], $form[$value]['quiz_state']['#description']); - } - } - - function admin_summary() { - return 'is ' . $this->states[$this->value['quiz_state']]; - } - - function query() { - $val = is_array($this->value) ? $this->value['quiz_state'] : $this->value; - if (empty($val) || $val == 'any') { - return; - } - $this->ensure_my_table(); - if ($val == 'not_started') { - $this->query->add_where($this->options['group'], "$this->table_alias.time_end", NULL, "IS NULL"); - } - else { - $operator = $val == 'in_progress' ? '=' : '>'; - $this->query->add_where($this->options['group'], "$this->table_alias.time_end", 0, $operator); - } - } -} diff --git a/includes/views/handlers/quiz_views_handler_relationship_quiz_nid.inc b/includes/views/handlers/quiz_views_handler_relationship_quiz_nid.inc deleted file mode 100644 index 7ed06b6..0000000 --- a/includes/views/handlers/quiz_views_handler_relationship_quiz_nid.inc +++ /dev/null @@ -1,10 +0,0 @@ - array( - 'path' => QUIZ_VIEWS_DIR . '/handlers', - ), - 'handlers' => array( - // Fields - 'quiz_views_handler_field_takes' => array( - 'parent' => 'views_handler_field', - ), - 'quiz_views_handler_field_time' => array( - 'parent' => 'views_handler_field', - ), - 'quiz_views_handler_field_question_status' => array( - 'parent' => 'views_handler_field', - ), - 'quiz_views_handler_field_user_quiz_state' => array( - 'parent' => 'views_handler_field', - ), - 'quiz_views_handler_field_score_aggregate' => array( - 'parent' => 'views_handler_field_numeric', - ), - 'quiz_views_handler_field_number_questions' => array( - 'parent' => 'views_handler_field', - ), - // Filters - 'quiz_views_handler_filter_question_status' => array( - 'parent' => 'views_handler_filter_in_operator', - ), - 'quiz_views_handler_filter_quiz_nid' => array( - 'parent' => 'views_handler_filter', - ), - 'quiz_views_handler_filter_user_quiz_state' => array( - 'parent' => 'views_handler_filter', - ), - 'quiz_views_handler_filter_user_nullable' => array( - 'parent' => 'views_handler_filter', - ), - // Relationships - 'quiz_views_handler_relationship_vid_from_nid' => array( - 'parent' => 'views_handler_relationship', - ), - // Arguments - 'quiz_views_handler_argument_quiz_nid' => array( - 'parent' => 'views_handler_argument_numeric', - ), - 'quiz_views_handler_argument_user_uid_nullable' => array( - 'parent' => 'views_handler_argument_user_uid', - ), - ), - ); -} - -/** - * Implementation of hook_views_data(). - */ -function quiz_views_data() { - $data = array(); - // Quiz Node Properties (additions to Node) - $data['quiz_node_properties'] = array( - - // Table Definition - 'table' => array( - 'group' => t('Quiz'), - // Base tables: - 'base' => array( - 'field' => 'vid', - 'title' => t('Quiz'), - 'help' => t('Quizzes are collections of questions that are taken and scored.'), - 'weight' => 0, - ), - - // Allow attachment to a node: - 'join' => array( - // This is vid because vid is always more specific. FIXME changed to try to fix the joining problems - 'node' => array( - 'left_field' => 'nid', - 'field' => 'nid', - 'type' => 'INNER', - ), - 'node_revision' => array( - 'left_field' => 'vid', - 'field' => 'vid', - 'type' => 'INNER', - ), - ), - ), - - // Field definitions - 'nid' => array( - 'title' => t('Quiz Node NID'), - 'help' => t('The ID of the Quiz Node.'), - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'quiz_views_handler_argument_quiz_nid', - 'name field' => 'title', - 'name table' => 'node_revision', - 'numeric' => TRUE, - 'validate type' => 'vid', - 'nid field' => 'nid', - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_quiz_nid', - 'title' => t('Quiz Version'), - 'help' => t('Filter results to a specific subset of quiz versions.'), - 'group by' => 'vid', - // 'secondary group by' => 'vid', - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node', - 'field' => 'nid', - 'label' => t('node'), - ), - ), - 'vid' => array( - 'title' => t('Quiz Node VID'), - 'help' => t('The Revision ID of the Quiz Node.'), - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'name field' => 'title', - 'name table' => 'node_revision', - 'numeric' => TRUE, - 'validate type' => 'vid', - ), - // Related to NODE - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node_revision', - 'field' => 'vid', - 'label' => t('quiz node revision'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'number_of_random_questions' => array( - 'title' => t('Number of random questions'), - 'help' => t('The number of questions on this quiz that will be randomly selected.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'pass_rate' => array( - 'title' => t('Pass rate'), - 'help' => t('The percentage of questions that must be correct before the quiz is passed.'), - 'field' => array('handler' => 'views_handler_field_numeric', 'click sortable' => TRUE), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'shuffle' => array( - 'title' => t('Randomization'), - 'help' => t('Indicates how questions will be randomized.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'backwards_navigation' => array( - 'title' => t('Backwards navigation'), - 'help' => t('Indicates whether quiz takers can go back to previous questions.'), - 'field' => array( - 'handler' => 'views_handler_field_boolean', - 'click sortable' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'repeat_until_correct' => array( - 'title' => t('Repeat until correct'), - 'help' => t('Require the user to re-try the question until they answer it correctly.'), - 'field' => array( - 'handler' => 'views_handler_field_boolean', - 'click sortable' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - - 'feedback_time' => array( - 'title' => t('Feedback Time'), - 'help' => t('Indicates whether quiz takers will get feedback after every question.'), - 'field' => array( - 'handler' => 'views_handler_field_boolean', - 'click sortable' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'time_limit' => array( - 'title' => t('Time limit'), - 'help' => t('The time limit on a quiz.'), - 'field' => array( - 'handler' => 'quiz_views_handler_field_time', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'quiz_open' => array( - 'title' => t('Open time'), - 'help' => t('The first time a new quiz can be taken.'), - 'field' => array( - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_date', - 'numeric' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort_date'), - ), - 'quiz_close' => array( - 'title' => t('Close time'), - 'help' => t('The last time a new quiz can be taken.'), - 'field' => array( - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_date', - 'numeric' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort_date'), - ), - 'quiz_always' => array( - 'title' => t('Always Available'), - 'help' => t('Indicates whether quiz is always available (Open and Close time are ignored).'), - 'field' => array( - 'handler' => 'views_handler_field_boolean', - 'click sortable' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'takes' => array( - 'title' => t('Allowed takes'), - 'help' => t('The number of times a quiz can be taken.'), - 'field' => array( - 'handler' => 'quiz_views_handler_field_takes', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'tid' => array( - 'title' => t('Term ID'), - 'help' => t('The term ID used to select questions based on taxonomy.'), - 'field' => array( - 'handler' => 'views_handler_field_taxonomy', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - - // Related to TERM - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'taxonomy_term_data', - 'field' => 'tid', - 'label' => t('term'), - ), - ), // End tid - ); // End quiz_node_properties - - - $data['quiz_node_relationship'] = array( - 'table' => array( - 'group' => t('Quiz Questions'), - 'join' => array( - // This is vid because vid is always more specific. We never work with - // nid. - 'quiz_node_properties' => array( - 'left_field' => 'vid', - 'field' => 'parent_vid', - 'type' => 'INNER', - ), - ), - ), - 'parent_nid' => array( - 'title' => t('Quiz Node ID'), - 'help' => t('The node id of the quiz to which the question is attached.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - // This'll get us the node title for the title, which may or may not be - // preferable; that's the only difference between this handler and the - // standard numeric handler - 'handler' => 'views_handler_argument_node_nid', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_quiz_nid', - 'help' => t('Filter results to a particular subset of quiz versions.'), - 'nid field' => 'parent_nid', - 'vid field' => 'parent_vid', - 'secondary group' => 'child_nid', - ), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node', - 'field' => 'nid', - 'label' => t('Quiz node id'), - ), - 'sort' => array( - 'handler' => 'views_handler_sort', - ), - ), - 'parent_vid' => array( - 'title' => t('Quiz Node VID'), - 'help' => t('The node revision id of the quiz to which the question is attached.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - // This'll get us the node title for the title, which may or may not be preferable - 'handler' => 'views_handler_argument_node_vid', - 'numeric' => TRUE, - 'validate type' => 'vid', - ), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node_revision', - 'field' => 'vid', - 'label' => t('Quiz node revision'), - ), - 'sort' => array( - 'handler' => 'views_handler_sort', - ), - ), - 'child_nid' => array( - 'title' => t('Question Node ID'), - 'help' => t('The question node id.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - // This'll get us the node title for the title, which may or may not be - // preferable; that's the only difference between this handler and the - // standard numeric handler - 'handler' => 'views_handler_argument_node_nid', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node', - 'field' => 'nid', - 'label' => t('Question node nid'), - ), - 'sort' => array( - 'handler' => 'views_handler_sort', - ), - ), - 'child_vid' => array( - 'title' => t('Question Node VID'), - 'help' => t('The question node revision id.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - // This'll get us the node title for the title, which may or may not be preferable - 'handler' => 'views_handler_argument_node_vid', - 'numeric' => TRUE, - 'validate type' => 'vid', - ), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node_revision', - 'field' => 'vid', - 'label' => t('Question node revision'), - ), - 'sort' => array( - 'handler' => 'views_handler_sort', - ), - ), - 'question_status' => array( - 'title' => t('Question Status'), - 'help' => t('Field indicating the state of a question for the relevant quiz.'), - 'field' => array( - 'handler' => 'quiz_views_handler_field_question_status', - 'click sortable' => TRUE, - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_question_status', - 'help' => t('Filter results based on the question\'s status in the relevant quiz.'), - ), - 'sort' => array( - 'handler' => 'views_handler_sort', - ), - 'number_questions' => array( - 'title' => t('Number of Questions'), - 'help' => t('The number of questions assigned to the quiz.'), - 'field' => array( - 'handler' => 'quiz_views_handler_field_number_questions', - ), - 'sort' => array( - 'handler' => 'views_handler_sort', - ) - ), - ), - 'max_score' => array( - 'title' => t('Maximum possible score'), - 'help' => t('Maximum question score'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'weight' => array( - 'title' => t('Question weight'), - 'help' => t('Question weight or order'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - ); // End quiz_node_relationship - - - $data['quiz_node_results'] = array( - // Table Definition - 'table' => array( - 'group' => 'Quiz Results', - 'join' => array( - // This is vid because vid is always more specific. - 'quiz_node_properties' => array( - 'left_field' => 'vid', - 'field' => 'vid', - 'type' => 'LEFT', // I think... - ), - ), - ), - - // Field descriptions: - 'result_id' => array( - 'title' => t('Result ID'), - 'help' => t('The ID identifying a particular set of quiz results. This ID is unique with respect to the quiz nid and vid of the quiz, uid of the quizee, and the take of the quiz.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'result_id', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_numeric', - 'help' => t('Filter results to a particular result set'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'nid' => array( - 'real field' => 'vid', - 'title' => t('Quiz Node NID'), - 'help' => t('The ID of the Quiz Node.'), - 'argument' => array( - 'handler' => 'quiz_views_handler_argument_quiz_nid', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'vid', - 'nid field' => 'nid', - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_quiz_nid', - 'help' => t('Filter results to a particular subset of quiz versions.'), - 'nid field' => 'nid', - 'vid field' => 'vid', - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node', - 'field' => 'nid', - 'label' => t('node'), - ), - ), - 'vid' => array( - 'title' => t('Quiz Node VID'), - 'help' => t('The Version ID of the Quiz Node.'), - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'vid', - ), - // Related to NODE - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node_revision', - 'field' => 'vid', - 'label' => t('node revision'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'uid' => array( - 'title' => t('User'), - 'help' => t('The ID of the user who took this quiz.'), - 'field' => array( - 'handler' => 'views_handler_field_user', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'quiz_views_handler_argument_user_uid_nullable', - 'numeric' => TRUE, - 'validate type' => 'nid', - 'name field' => 'name', - 'name table' => 'users', - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_user_nullable', - 'help' => t('Filter on the user who owns the quiz results.'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - // Related to USERS - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'users', - 'field' => 'uid', - 'label' => t('user'), - ), - ), - 'time_start' => array( - 'title' => t('Quiz Start Time'), - 'help' => t('Time the quiz was started.'), - 'field' => array( - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_date', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'sort' => array('handler' => 'views_handler_sort_date'), - ), - 'time_end' => array( - 'title' => t('Quiz End Time'), - 'help' => t('Time the quiz was finished.'), - 'field' => array( - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_date', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'sort' => array('handler' => 'views_handler_sort_date'), - 'filter' => array( - 'handler' => 'views_handler_filter_date', - 'allow empty' => TRUE, - ), - ), - 'score' => array( - 'title' => t('Score'), - 'help' => t('Score on the Quiz.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'is_invalid' => array( - 'title' => t('Is Invalid'), - 'help' => t('Indicates whether or not a quiz result is valid. This is a simple database boolean; the meaning of the "valid" flag should vary according to your use case.'), - 'field' => array( - 'handler' => 'views_handler_field_boolean', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_boolean_operator', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'score_aggregate' => array( - 'title' => t('Score Aggregations'), - 'help' => t('Aggregate scores on quizzes using a variety of different algorithms.'), - 'real field' => 'score', - 'field' => array( - 'handler' => 'quiz_views_handler_field_score_aggregate', - 'float' => TRUE, - 'group field' => 'nid', - ), - ), - 'quiz_state' => array( - 'title' => t('Quiz State'), - 'help' => t('The state of the quiz for the provided user. Calculated on the fly; can be "Finished," "In Progress," or "Not Started".'), - 'field' => array( - 'handler' => 'quiz_views_handler_field_user_quiz_state', - 'click sortable' => TRUE, - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_user_quiz_state', - 'help' => t('Filter the results based on the state of quizzes with respect to users (i.e., whether they have started, finished, or are in progress on the quiz).'), - ), - ) - ); // End quiz_node_results - - - $data['quiz_node_results_answers'] = array( - // Table Definition - 'table' => array( - 'group' => 'Quiz Results by Question', - 'join' => array( - 'quiz_node_properties' => array( - 'left_table' => 'quiz_node_relationship', - 'left_field' => 'child_nid', - 'field' => 'question_nid', - ), - ), - ), - - // Field descriptions: - 'result_id' => array( - 'title' => t('Result ID'), - 'help' => t('The ID identifying a particular set of quiz results. This ID is unique with respect to the quiz nid and vid of the quiz, uid of the quizee, and the take of the quiz.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'result_id', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_numeric', - 'help' => t('Filter results to a particular result set'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'quiz_node_results', - 'field' => 'result_id', - 'base field' => 'result_id', - 'label' => t('Result'), - ), - ), - 'question_nid' => array( - 'title' => t('Question Node NID'), - 'help' => t('The ID of the Question Node.'), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_numeric', - 'help' => t('Filter results to a particular subset of question versions.'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node', - 'field' => 'nid', - 'label' => t('node'), - ), - ), - 'question_vid' => array( - 'title' => t('Question Node VID'), - 'help' => t('The Version ID of the Question Node.'), - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'vid', - ), - // Related to NODE - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node_revision', - 'field' => 'vid', - 'label' => t('node revision'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'answer_timestamp' => array( - 'title' => t('Quiz Answer Time'), - 'help' => t('Time the answer was provided.'), - 'field' => array( - 'handler' => 'views_handler_field_date', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_date', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'sort' => array('handler' => 'views_handler_sort_date'), - 'filter' => array( - 'handler' => 'views_handler_filter_date', - 'allow empty' => TRUE, - ), - ), - 'points_awarded' => array( - 'title' => t('Points awarded'), - 'help' => t('Points awarded for answer(s) given'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'is_correct' => array( - 'title' => t('Is Correct'), - 'help' => t('Indicates whether or not a quiz answer was correct.'), - 'field' => array( - 'handler' => 'views_handler_field_boolean', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_boolean_operator', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'is_skipped' => array( - 'title' => t('Is Skipped'), - 'help' => t('Indicates whether or not a quiz question was skipped.'), - 'field' => array( - 'handler' => 'views_handler_field_boolean', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_boolean_operator', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - ); // End quiz_node_results_answers - - - $data['quiz_multichoice_answers'] = array( - // Table Definition - 'table' => array( - 'group' => 'Quiz Multichoice Answers', - 'join' => array( - 'quiz_node_properties' => array( - 'left_table' => 'quiz_node_relationship', - 'left_field' => 'child_nid', - 'field' => 'question_nid', - ), - ), - ), - - // Field descriptions: - 'id' => array( - 'title' => t('Answer ID'), - 'help' => t('The ID identifying an individual answer.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'result_id', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_numeric', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'question_nid' => array( - 'title' => t('Answers Question Node NID'), - 'help' => t('The ID of the Question Node.'), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_numeric', - 'help' => t('Filter results to a particular subset of question versions.'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node', - 'field' => 'nid', - 'label' => t('node'), - ), - ), - 'question_vid' => array( - 'title' => t('Answers Question Node VID'), - 'help' => t('The Version ID of the Question Node.'), - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'vid', - ), - // Related to NODE - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node_revision', - 'field' => 'vid', - 'label' => t('node revision'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'answer' => array( - 'title' => t('Answer'), - 'help' => t('Answer text'), - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - ); // End quiz_multichoice_answers - - - $data['quiz_multichoice_user_answers'] = array( - // Table Definition - 'table' => array( - 'group' => 'Quiz Multichoice Answers', - 'join' => array( - 'quiz_node_properties' => array( - 'left_table' => 'quiz_node_relationship', - 'left_field' => 'child_nid', - 'field' => 'question_nid', - ), - ), - ), - - // Field descriptions: - 'id' => array( - 'title' => t('User answer set ID'), - 'help' => t('The ID identifying an answer set for a user.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'result_id', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_numeric', - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'result_id' => array( - 'title' => t('Result ID'), - 'help' => t('The ID identifying a particular set of quiz results. This ID is unique with respect to the quiz nid and vid of the quiz, uid of the quizee, and the take of the quiz.'), - 'field' => array( - 'handler' => 'views_handler_field_numeric', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'result_id', - ), - 'filter' => array( - 'handler' => 'views_handler_filter_numeric', - 'help' => t('Filter results to a particular result set'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'quiz_node_results', - 'field' => 'result_id', - 'base field' => 'result_id', - 'label' => t('Result'), - ), - ), - 'question_nid' => array( - 'title' => t('Result Question Node NID'), - 'help' => t('The ID of the Question Node.'), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'nid', - ), - 'filter' => array( - 'handler' => 'quiz_views_handler_filter_numeric', - 'help' => t('Filter results to a particular subset of question versions.'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node', - 'field' => 'nid', - 'label' => t('node'), - ), - ), - 'question_vid' => array( - 'title' => t('Result Question Node VID'), - 'help' => t('The Version ID of the Question Node.'), - 'field' => array( - 'handler' => 'views_handler_field', - 'click sortable' => TRUE, - ), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - //'name_field' => 'title', - 'numeric' => TRUE, - 'validate type' => 'vid', - ), - // Related to NODE - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'node_revision', - 'field' => 'vid', - 'label' => t('node revision'), - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - ); // End quiz_multichoice_user_answers - - $data['quiz_multichoice_user_answer_multi'] = array( - 'table' => array( - 'group' => t('Quiz Multichoice Answers'), - 'join' => array( - 'quiz_node_properties' => array( - 'left_table' => 'quiz_multichoice_user_answers', - 'left_field' => 'id', - 'field' => 'user_answer_id', - ), - ), - ), - - // Field descriptions: - 'user_answer_id' => array( - 'title' => t('User answer set id'), - 'help' => t("The ID of a user's answer set."), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - ), - 'filter' => array( - 'handler' => 'views_handler_filter_numeric', - 'help' => t("Filter results to a particular subset of a user's answer set."), - ), - 'sort' => array('handler' => 'views_handler_sort'), - ), - 'answer_id' => array( - 'title' => t('Answer id'), - 'help' => t("The ID of a user's answer."), - 'argument' => array( - 'handler' => 'views_handler_argument_numeric', - 'numeric' => TRUE, - ), - 'filter' => array( - 'handler' => 'views_handler_filter_numeric', - ), - 'sort' => array('handler' => 'views_handler_sort'), - 'relationship' => array( - 'handler' => 'views_handler_relationship', - 'base' => 'quiz_multichoice_answers', - 'field' => 'answer_id', - 'base field' => 'id', - 'label' => t('answer'), - ), - ), - ); // End quiz_multichoice_user_answer_multi - - return $data; -} diff --git a/includes/views/quiz.views_default.inc b/includes/views/quiz.views_default.inc deleted file mode 100644 index a0fe6d9..0000000 --- a/includes/views/quiz.views_default.inc +++ /dev/null @@ -1,21 +0,0 @@ - $file) { - require DRUPAL_ROOT . '/' . $path; - $views[$file->name] = $view; - } - return $views; -} diff --git a/question_types/long_answer/long_answer.admin.inc b/question_types/long_answer/long_answer.admin.inc deleted file mode 100644 index 6751c9a..0000000 --- a/question_types/long_answer/long_answer.admin.inc +++ /dev/null @@ -1,177 +0,0 @@ - $unscored)); -} - -/** - * Page handler for displaying a scoring form. - * This function is called directly from the menu router. It generates a form for - * scoring a quiz. - * - * @param $vid - * The VID of the question and answer to load. - * @param $rid - * The result ID of the answer to load. - * @return - * Text to display. - */ -function long_answer_edit_score($vid, $rid) { - - // We have to do the vid -> nid lookup ourselves because node_load uses only node.vid, - // and we need to be able to access old nodes in node_revision.vid. - $nid = db_query('SELECT nid FROM {node_revision} WHERE vid = :vid', array(':vid' => $vid))->fetchField(); - if (!$nid) { - drupal_not_found(); - return; - } - - $node = node_load($nid, $vid); - if (!$node || $node->type != 'long_answer') { - drupal_not_found(); - return; - } - - $answer = (object) long_answer_get_answer($node->nid, $node->vid, $rid); - if (!$answer) { - drupal_not_found(); - return; - } - if ($node->max_score > 0) { - $answer->rel_score = round($answer->score * $answer->rel_max_score / $node->max_score); - } - else { - $answer->rel_score = 0; - } - drupal_set_title(t('Score answer to "@title"', array('@title' => $node->title)), PASS_THROUGH); - return drupal_get_form('long_answer_score_form', $node, $answer); -} - -/** - * Build a form for scoring long-answer questions. - * - * @param $node - * The question node. - * @param $answer - * An object containing an answer to the question. This form is for scoring that answer. - * @return - * The form (as a FAPI array). - */ -function long_answer_score_form($form, $form_state, $node, $answer) { - if (!$node || $node->type != 'long_answer' || !$answer) { - drupal_not_found(); - return; - } - - // Set up the form - $form['question'] = array( - '#type' => 'item', - '#title' => t('Question'), - '#markup' => check_markup($node->body, $node->format), - ); - $form['rubric'] = array( - '#type' => 'item', - '#title' => t('Rubric'), - '#markup' => check_markup($node->rubric, variable_get('long_answer_markup_filter', FILTER_FORMAT_DEFAULT)), - ); - $form['show_max_score'] = array( - '#type' => 'item', - '#title' => t('Maximum Score'), - '#markup' => (int) $answer->rel_max_score, - ); - $form['score_answer'] = array( - '#type' => 'fieldset', - '#title' => t('Score answer'), - ); - $form['score_answer']['answer'] = array( - '#type' => 'item', - '#title' => t('Answer'), - '#markup' => check_markup($answer->answer, variable_get('long_answer_markup_filter', FILTER_FORMAT_DEFAULT)), - ); - $form['score_answer']['score'] = array( - '#type' => 'textfield', - '#title' => t('Score'), - '#description' => t('The score for this essay, between 0 and @max', array('@max' => $answer->rel_max_score)), - '#size' => 3, - '#maxlength' => 3, - '#default_value' => (int) $answer->rel_score, - '#required' => TRUE, - ); - $form['score_answer']['submit'] = array( - '#type' => 'submit', - '#value' => t('Save this score'), - ); - - // Save some work by keeping these. - $form['max_score'] = array( - '#type' => 'value', - '#value' => $node->max_score, - ); - $form['rel_max_score'] = array( - '#type' => 'value', - '#value' => $answer->rel_max_score, - ); - $form['question_nid'] = array( - '#type' => 'value', - '#value' => $answer->question_nid, - ); - $form['question_vid'] = array( - '#type' => 'value', - '#value' => $answer->question_vid, - ); - $form['result_id'] = array( - '#type' => 'value', - '#value' => $answer->result_id, - ); - - return $form; -} - -/** - * Validates the long answer score form - */ -function long_answer_score_form_validate($form, $form_state) { - // Check to make sure that entered score is not higher than max allowed score. - $max = (int) $form_state['values']['rel_max_score']; - $given = $form_state['values']['score']; - if (!_quiz_is_int($given, 0, $max)) { - $args = array('@score' => $given, '@max' => $max); - form_set_error('score', t('The given score (@score) must be an integer between 0 and @max', $args)); - } -} - -/** - * Submit handler for the long answer score form - */ -function long_answer_score_form_submit($form, &$form_state) { - $sql = 'SELECT nid, vid FROM {quiz_node_results} WHERE result_id = %d'; - $result = db_fetch_object(db_query('SELECT nid, vid FROM {quiz_node_results} WHERE result_id = :result_id', array(':result_id' => $form_state['values']['result_id']))); - - $quiz = node_load($result->nid, $result->vid); - - $nid = $form_state['values']['question_nid']; - $vid = $form_state['values']['question_vid']; - $rid = $form_state['values']['result_id']; - $score = $form_state['values']['score']; - - $result = long_answer_score_an_answer($quiz, $nid, $vid, $rid, $score); - - if ($result == 1) { - drupal_set_message(t('The score has been saved.')); - $form_state['redirect'] = 'admin/quiz/reports/score-long-answer'; - } - else { - drupal_set_message(t('Error saving the score. The selected answer was not scored.'), 'error'); - } -} diff --git a/question_types/long_answer/long_answer.classes.inc b/question_types/long_answer/long_answer.classes.inc deleted file mode 100644 index f0f65aa..0000000 --- a/question_types/long_answer/long_answer.classes.inc +++ /dev/null @@ -1,401 +0,0 @@ -node->feedback)) { - $this->node->feedback = ''; - } - if ($is_new || $this->node->revision == 1) { - $id = db_insert('quiz_long_answer_node_properties') - ->fields(array( - 'nid' => $this->node->nid, - 'vid' => $this->node->vid, - 'rubric' => $this->node->rubric, - )) - ->execute(); - } - else { - db_update('quiz_long_answer_node_properties') - ->fields(array('rubric' => $this->node->rubric)) - ->condition('nid', $this->node->nid) - ->condition('vid', $this->node->vid) - ->execute(); - } - } - - /** - * Implementation of validateNode - * - * @see QuizQuestion#validateNode($form) - */ - public function validateNode(array &$form) { } - - /** - * Implementation of delete - * - * @see QuizQuestion#delete($only_this_version) - */ - public function delete($only_this_version = FALSE) { - if ($only_this_version) { - db_delete('quiz_long_answer_user_answers') - ->condition('question_nid', $this->node->nid) - ->condition('question_vid', $this->node->vid) - ->execute(); - db_delete('quiz_long_answer_node_properties') - ->condition('nid', $this->node->nid) - ->condition('vid', $this->node->vid) - ->execute(); - } - else { - db_delete('quiz_long_answer_node_properties') - ->condition('nid', $this->node->nid) - ->execute(); - db_delete('quiz_long_answer_user_answers') - ->condition('question_nid', $this->node->nid) - ->execute(); - } - parent::delete($only_this_version); - } - - /** - * Implementation of getNodeProperties - * - * @see QuizQuestion#getNodeProperties() - */ - public function getNodeProperties() { - if (isset($this->nodeProperties)) { - return $this->nodeProperties; - } - $props = parent::getNodeProperties(); - - $res_a = db_query('SELECT rubric FROM {quiz_long_answer_node_properties} - WHERE nid = :nid AND vid = :vid', array(':nid' => $this->node->nid, ':vid' => $this->node->vid))->fetchAssoc(); - - if (is_array($res_a)) { - $props = array_merge($props, $res_a); - } - - $this->nodeProperties = $props; - return $props; - } - - /** - * Implementation of getNodeView - * - * @see QuizQuestion#getNodeView() - */ - public function getNodeView() { - $content = parent::getNodeView(); - if ($this->viewCanRevealCorrect()) { - $content['answers'] = array( - '#type' => 'item', - '#title' => t('Rubric'), - '#markup' => '' . t('A long-answer question is designed to provide the quiz taker a lengthy area to expand on ideas. - Common forms of long-answer questions include essays, single paragraph responses, hypothesis design problems, - outlines and summaries, and lengthier math problems - where the focus is on showing work rather than simply getting the correct answer.') . '
'; - } -} - -/** - * Implements hook_menu(). - */ -function long_answer_menu() { - $items['admin/quiz/reports/score-long-answer'] = array( - 'title' => 'Score long answer questions', - 'description' => 'Score the answers from quizzes that use long answer questions.', - 'page callback' => 'long_answer_view_unscored', - 'access arguments' => array('score any quiz', 'score own quiz'), - 'access callback' => 'quiz_access_multi_or', - 'type' => MENU_NORMAL_ITEM, - 'file' => 'long_answer.admin.inc', - ); - // Pass vid and rid to this path. - $items['admin/quiz/reports/score-long-answer/%/%'] = array( - 'title' => 'Score long answer response', - 'description' => 'Score a response to a long answer question.', - 'page callback' => 'long_answer_edit_score', - 'page arguments' => array(4, 5), - 'type' => MENU_NORMAL_ITEM, - 'access arguments' => array(4, 5), - 'access callback' => 'quiz_question_access_to_score', - 'file' => 'long_answer.admin.inc' - ); - - return $items; -} - -/** - * Implements hook_quiz_question_info(). - */ -function long_answer_quiz_question_info() { - return array( - 'long_answer' => array( - 'name' => t('Long answer question'), - 'description' => t('Quiz questions that allow a user to enter multiple paragraphs of text.'), - 'question provider' => 'LongAnswerQuestion', - 'response provider' => 'LongAnswerResponse', - 'module' => 'quiz_question', // All wrapper functions are in that module. - ), - ); -} - -/** - * Implements hook_config(). - */ -function long_answer_config() { - return FALSE; -} - -/** - * Implements hook_theme(). - */ -function long_answer_theme() { - return array( - 'long_answer_view_unscored' => array( - 'variables' => array('unscored' => array()), - 'path' => drupal_get_path('module', 'long_answer') . '/theme', - 'file' => 'long_answer.theme.inc', - ), - 'long_answer_response_form' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'long_answer') . '/theme', - 'file' => 'long_answer.theme.inc', - ), - 'long_answer_answering_form' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'long_answer') . '/theme', - 'template' => 'long-answer-answering-form', - ), - ); -} - -/** - * Set a score for a long answer question. - * - * This stores a score for a long answer question and marks that question as having been evaluated. - * The function updates all of the necessary data sources so that the individual answer results should be - * reflected in the total scoring table. - * - * @param $quiz - * Quiz node. - * @param $nid - * Node ID of question. - * @param $vid - * Version ID of question. - * @param $rid - * Result ID for the quiz results. - * @param $score - * The numeric score to assign the result. - * @param $update_total - * Shall the total score be updated? - * - * @return int - * Number of scores adjusted. If a change was made, this should be 1. - */ -function long_answer_score_an_answer($quiz, $nid, $vid, $rid, $score, $update_total = TRUE) { - // Quiz scoring information is spread out across three tables: - // 1. The module should retain its own scoring information in any case where scoring is non-trivial. - // 2. The Quiz moduleretains a limited amount of scoring information. - // 3. The Quiz module retains an overall score for a quiz. This is the percentage score for the combination of all - // questions on the quiz. - // - // We update all three. - - // First, we update the long answer table - $question_max_score = db_query('SELECT max_score FROM {quiz_question_properties} WHERE nid = :nid AND vid = :vid', array(':nid' => $nid, ':vid' => $vid))->FetchField(); - - $quiz_max_score = db_query('SELECT max_score FROM {quiz_node_relationship} WHERE parent_vid = :pvid AND child_vid = :cvid', array(':pvid' => $quiz->vid, ':cvid' => $vid))->fetchField(); - - $changed = db_update('quiz_long_answer_user_answers') - ->fields(array( - 'score' => $question_max_score / $quiz_max_score, - 'is_evaluated' => 1 - )) - ->condition('question_nid', $nid) - ->condition('question_vid', $vid) - ->condition('result_id', $rid) - ->execute(); - - $changed = db_affected_rows(); - - if ($changed > 0) { - // Second, we update the main quiz answers table - - // What do we do about the quiz_node_results_answers table? It assumes strict - // bivalence (is_correct). I guess we consider any essay with over 50% to be correct? - $max = db_query('SELECT max_score FROM {quiz_question_properties} WHERE vid = :vid', array(':vid' => $vid))->fetchField(); - if ($max <= 0) { - $is_correct = 0; - $points_awarded = 0; - } - else { - $is_correct = $score == $max ? 1 : 0; - $points_awarded = $score; - } - - db_update('quiz_node_results_answers') - ->fields(array( - 'points_awarded' => $points_awarded, - 'is_correct' => $is_correct, - )) - ->condition('question_vid', $vid) - ->condition('result_id', $rid) - ->execute(); - - // Third, we update the main quiz results table - if ($update_total) { - quiz_update_total_score($quiz, $rid); - } - } - - return $changed; -} - -/** - * Get the answer for a question. - * - * This stores a score for a long answer question and marks that question as having been evaluated. - * @param $nid - * Node ID of question. - * @param $vid - * Version ID of question. - * @param $rid - * Result ID for the quiz results. - * - * @return Assoc array - * An array if successful, or FALSE if no result could be found. The array contains the following properties: - *
- * answer_id; // The answer ID
- * answer; // The full text of the answer
- * is_evaluated; // 0 if the question has not been evaluated, 1 if it has
- * score; // The score the evaluator gave the user; this should be 0 if is_evaluated is 0.
- * question_vid
- * question_nid
- * result_id
- *
- */
-function long_answer_get_answer($question_nid, $question_vid, $result_id) {
- $result = db_query('SELECT answer_id, answer, la.is_evaluated, la.score, question_vid, question_nid, la.result_id, rel.max_score AS rel_max_score FROM {quiz_long_answer_user_answers} la
- JOIN {quiz_node_results} qnr ON (la.result_id = qnr.result_id)
- JOIN {quiz_node_relationship} rel ON (qnr.vid = rel.parent_vid AND rel.child_vid = la.question_vid)
- WHERE question_nid = :qnid AND question_vid = :qvid AND la.result_id = :rid', array(':qnid' => $question_nid, ':qvid' => $question_vid, ':rid' => $result_id))->fetchAssoc();
- return $result ? $result : FALSE;
-}
-
-/**
- * Given a quiz, return a list of all the unscored answers.
- *
- * @param $nid
- * Node ID for the quiz to check.
- * @param $vid
- * Version ID for the quiz to check.
- * @param $count
- * Number of items to return (default: 50).
- * @param $offset
- * Where in the results we should start (default: 0).
- *
- * @return
- * Indexed array of result IDs that need to be scored.
- */
-function long_answer_get_unscored_answers_by_question($nid, $vid, $count = 50, $offset = 0) {
- $results = db_query('SELECT result_id FROM {quiz_long_answer_user_answers}
- WHERE is_evaluated = :is_evaluated AND question_nid = :question_nid AND question_vid = :question_vid', array(':is_evaluated' => 0, ':question_nid' => $nid, ':question_vid' => $vid));
-
- $unscored = array();
- foreach ($results as $result) {
- $unscored[] = $result->result_id;
- }
- return $unscored;
-}
-
-
-/**
- * Get all quiz scores that have not yet been evaluated.
- *
- * @param $count
- * Number of items to return (default: 50).
- * @param $offset
- * Where in the results we should start (default: 0).
- *
- * @return
- * Array of objects describing unanswered questions. Each object will have result_id, question_nid, and question_vid.
- */
-function long_answer_get_all_unscored_answers($count = 50, $offset = 0) {
- $query = db_select('quiz_long_answer_user_answers', 'a');
- $query->fields('a', array('result_id', 'question_nid', 'question_vid'));
- $query->fields('r', array('title'));
- $query->fields('qnr', array('time_end', 'time_start', 'uid'));
- $query->join('node_revision', 'r', 'a.question_vid = r.vid');
- $query->join('quiz_node_results', 'qnr', 'a.result_id = qnr.result_id');
- $query->join('node', 'n', 'qnr.nid = n.nid');
- $query->condition('a.is_evaluated', 0);
-
- if (!user_access('score any quiz')) {
- $query->condition('n.uid', $_GLOBALS['user']->uid);
- }
-
- $unscored = array();
- foreach ($query->execute() as $result) {
- $unscored[] = $result;
- }
- return $unscored;
-}
-
-/**
- * Generate a list of long answer questions
- *
- * @return
- * array containing nid as key and title as value
- */
-function long_answer_questions_list() {
- $questions = array();
- $results = db_query('SELECT nid, title FROM {node} WHERE type = :type', array(':type' => 'long_answer'));
- foreach ($results as $result) {
- $questions[$result->nid] = check_plain($result->title);
- }
- return $questions;
-}
-
-/**
- * Submit function for the report form
- *
- * @param $values
- * The FAPI $form_state['values']
- */
-function long_answer_report_submit($values) {
- long_answer_score_an_answer($values['quiz'], $values['nid'], $values['vid'], $values['rid'], (int) $values['score'], FALSE);
-}
-
-/**
- * Validation function for the report form
- *
- * @param $values
- * The FAPI $form_state['values']
- * @param $form_key
- * Array key to add errors to
- */
-function long_answer_report_validate($values, $form_key) {
- $max = (int) $values['max_score'];
- // Check to make sure that entered score is not higher than max allowed score.
- if (!_quiz_is_int($values['score'], 0, $max)) {
- form_set_error($form_key . '][score', t('The score needs to be a number between @min and @max', array('@min' => 0, '@max' => $max)));
- }
-}
diff --git a/question_types/long_answer/long_answer.test b/question_types/long_answer/long_answer.test
deleted file mode 100644
index c7be57c..0000000
--- a/question_types/long_answer/long_answer.test
+++ /dev/null
@@ -1,353 +0,0 @@
- t('Long answer unit test'),
- 'description' => t('Suite of unit tests for verifying functionality for essay question functions.'),
- 'group' => t('Quiz'),
- );
- }
-
- /*
- * @function
- * generates a rand integer between the specified range
- *
- * @return
- * random Integer value
- */
- public function getRandSize() {
- return rand($this->min, $this->max);
- }
-
- /*
- * Implementing setUp() to enable truefalse module testing
- */
- function setUp() {
- parent::setUp('taxonomy', 'quiz', 'views', 'autoload', 'multichoice',
- 'quiz_directions', 'quiz_question', 'querypath', 'questions_import',
- 'short_answer', 'truefalse', 'long_answer', 'matching', 'questions_export');
-
- // Create and log in our test user. Should be cleaned up as I something
- // was wrong with permissions and I basically kept adding potentially
- // useful ones until it worked.
-
- $permission = array('administer site configuration', 'access administration pages',
- 'administer quiz', 'access quiz', 'administer blocks', 'import questions', 'create quiz',
- 'administer quiz configuration', 'use PHP for block visibility', 'administer blocks',
- 'create multichoice', 'edit any multichoice', 'administer taxonomy', 'allow multiple correct answers',
- 'allow any number of answers', 'export questions');
-
- // create user with a set of permission
- $user = $this->drupalCreateUser($permission);
- $this->assertTrue(is_object, t('Check that user has been created with specified permission.'));
- $this->drupalLogin($user);
-
- // create one quiz, which will be the default in the import form
- $quiz_settings = array();
- $quiz_settings['title'] = $this->randomName($this->getRandSize());
- $quiz_settings['comment'] = $this->randomName($this->getRandSize());
- $quiz_settings['type'] = 'quiz';
- //$this->drupalCreateNode($quiz_settings);
- }
-
- //public function testModuleIsEnabled() {
- // $this->assertTrue(in_array('long_answer', module_list(FALSE, TRUE, TRUE)), t('Test that module is installed.'));
- //}
-
- /**
- * Check that hook_quiz_question_info is working.
- */
- public function testLongAnswerQuizQuestionInfo() {
- $info = long_answer_quiz_question_info();
- $this->assertEqual(count($info), 1, t('Check that info was returned.'));
- $this->assertTrue(isset($info['long_answer']), t('Check that long answer question type exists.'));
- }
-
- /**
- * Utility function for creating a new test question.
- */
- public function createLongAnswerQuestion() {
- $this->title = $this->randomName($this->getRandSize());
- $this->body = $this->randomName($this->getRandSize());
- $this->max_score = $this->getRandSize();
-
- // array of node attributes
- $settings = array(
- 'type' => $this->question_node_type,
- 'title' => $this->title,
- 'body' => $this->body,
- 'maximum_score' => $this->max_score,
- 'revisions' => TRUE,
- );
-
- // create test drupal node
- return $this->drupalCreateNode($settings);;
- }
-
- /*
- public function createQuiz() {
- $title = 'Test Quiz';
- $body = 'This is a sample quiz.';
- $settings = array(
- 'type' => 'quiz',
- 'title' => $title,
- 'body' => $body,
- 'revisions' => TRUE,
- );
-
- $node = $this->drupalCreateNode($settings);
-
- return $node;
- }
- */
-
- /**
- * Run a bundle of Node API tests.
- *
- * This tests CRUD and revision functionality on a single node.
- */
- public function testNodeAPI() {
- $this->unitTestCreateQuestionNode();
- $this->unitTestCheckNodeProperties();
- $this->unitTestUpdateQuestionNode();
-
- $this->unitTestListQuestions();
-
- // Revisions
- $this->unitTestCreateQuestionRevision();
-
- $this->unitTestDeleteQuestionNode();
- }
-
- /**
- * Create and then update a node.
- */
- public function unitTestCreateQuestionNode() {
- $node = $this->createLongAnswerQuestion();
-
- if (!$node) {
- throw new Exception('Expected to have a node to work with.');
- }
- $this->nid1 = $node->nid;
- $this->assertTrue(!empty($node->title), t('check that node title is not empty @title', array('@title' => $node->title)));
- $this->assertTrue(!empty($this->title), t('check that original title is not empty @title', array('@title' => $this->title)));
- $this->assertEqual(strlen($node->title), strlen($this->title), t('check that length of original title and stored title are same.'));
-
- $this->assertEqual($node->title, $this->title, t('Title of stored node should equal the original title.'));
- $this->assertEqual($node->body, $this->body, t('Body of stored node should be equal to original body.'));
- $this->assertEqual($node->maximum_score, $this->max_score, t('Stored score should be the same as original score.'));
- $this->assertEqual($node->type, $this->question_node_type, t('Stored node type should be long_answer'));
- }
-
-
- /**
- * Test that the appropriate data was stored in the node properties table.
- */
- public function unitTestCheckNodeProperties() {
- $max = db_result(db_query('SELECT maximum_score FROM {quiz_long_answer_node_properties} WHERE nid = %d', $this->nid1));
- $this->assertEqual($max, $this->max_score, t('Test that max score was appropriately stored in database.'));
- }
-
- /**
- * Check that question exists in DB.
- */
- public function unitTestListQuestions() {
- $questions = long_answer_questions_list();
- $this->assertEqual(count($questions), 1, t('Verify that the question exists.'));
- }
-
- /**
- * Test updating of a question.
- */
- public function unitTestUpdateQuestionNode() {
- $newScore = 2;
- $node = node_load($this->nid1);
- $node->maximum_score = $newScore;
- node_save($node);
-
- $nodeCopy = node_load($node->nid, $node->vid);
- $this->assertEqual($nodeCopy->maximum_score, $newScore, t('Check that stored score is the same as newly assigned score.'));
-
- }
-
- /**
- * Test creation of node revision.
- */
- public function unitTestCreateQuestionRevision() {
- $node = node_load($this->nid1);
- $oldVid = $node->vid;
- $node->revision = 1;
- node_save($node);
-
- $oldMax = $node->maximum_score;
-
- $nodeCopy = node_load($node->nid, $node->vid);
- $this->assertNotEqual($nodeCopy->vid, $oldVid, t('Check that VID of new version is not the same as old VID.'));
- $this->assertEqual($oldMax, $nodeCopy->maximum_score, t('Check that new VID has an entry in node properties.'));
- }
-
- /**
- * Test deeting of question node.
- */
- public function unitTestDeleteQuestionNode() {
- $node = node_load($this->nid1);
- if ($node === FALSE) {
- throw new Exception("Expected fixture to have valid nid1");
- }
- unset($node);
-
- node_delete($this->nid1);
- $node = node_load($this->nid);
- $this->assertFalse($node, t('Test that node was deleted'));
- }
-
- /**
- * The main callback for answering a question.
- */
- public function testEvaluateQuestion() {
- //$result = long_answer_evaluate_question($question, 1);
-
- //$this->assertTrue((count($result) > 0), t('See if a result was returned from the evaluator.'));
- //$this->assertEqual($result->is_correct, 0, t('Test that answer was not marked correct.'));
- }
-
- /**
- * Test suite to write, update and delete answers.
- */
- public function testSaveDeleteScoreAnswer() {
- //$this->unitTestSaveAnswer();
- //$this->unitTestGetUnscoredAnswers();
- //$this->unitTestScoreAnswer();
- //$this->unitTestGetAnswer();
- //$this->unitTestDeleteAnswer();
- }
-
- /**
- * Test saving an answer.
- */
- public function unitTestSaveAnswer() {
- //$answer_id = long_answer_save_answer(1, 1, 1, 'THIS IS A TEST ANSWER');
-
- // FIXME: This should just select * from the table and check to make sure there is an entry.
- // We should not assume IDs are serial.
- //$this->assertEqual($answer_id, 1, 'Answer ID is set to 1 -- first answer');
- }
-
- /**
- * Test modifying (scoring) an answer.
- */
- public function unitTestScoreAnswer() {
- $change = long_answer_score_an_answer(1, 1, 1, 30);
- $this->assertEqual($change, 1, t('Only one row was changed.'));
-
- $unanswered = long_answer_get_all_unscored_answers();
- $this->assertEqual(count($unanswered), 0, t('There should be no unscored questions.'));
- }
-
- public function unitTestGetAnswer() {
- $answer = long_answer_get_answer(1, 1, 1);
- $this->assertTrue(($answer !== FALSE), t('Check that an answer was returned.'));
- $this->assertEqual($answer['is_evaluated'], 1, t('Check that answer is scored.'));
- $this->assertEqual($answer['answer_id'], 1, t('Check that this is first answer'));
- $this->assertEqual($answer['answer'], 'THIS IS A TEST ANSWER', t('Check that answer text is expected.'));
- }
-
- /**
- * Test deleting an answer.
- */
- public function unitTestDeleteAnswer() {
- //$removed = long_answer_delete_answer(1, 1, 1);
- //$this->assertEqual($removed, 1, t('A single answer should be deleted.'));
- }
-
- public function unitTestGetUnscoredAnswers() {
-
- $unanswered = long_answer_get_all_unscored_answers();
-
- drupal_set_message(__FUNCTION__ . ' Unanswered: ' . count($unanswered));
-
- $this->assertEqual(count($unanswered), 1, t('There should be only one unanswered question in *all* questions.'));
-
- $unanswered = long_answer_get_unscored_answers_by_question(1, 1);
- $this->assertEqual(count($unanswered), 1, t('There should be only one unanswered question for the quiz.'));
- }
-
-
-}
-
-/*
-// Functional tests are a pain in the butt when it comes to creating new nodes.
-class LongAnswerFunctionalTest extends DrupalWebTestCase {
- public function getInfo() {
- return array(
- 'name' => t('Long answer quiz question functional tests.'),
- 'description' => t('Suite of functional tests for verifying functionality for essay questions.'),
- 'group' => t('Quiz'),
- );
- }
-
- public function setUp() {
- parent::setUp('quiz', 'views', 'long_answer');
- }
-
- public function testEditScore() {
- $node = $this->createLongAnswerQuestion();
- $rid = 1;
- $answer_text = 'This is a test answer';
- $answer_id = long_answer_save_answer($node->nid, $node->vid, $rid, $answer_text);
-
- $form = long_answer_edit_score($node->vid, $rid);
-
- print_r($form);
-
- $pattern = '|' . $answer_text . '|';
- $this->assertText($form['answer']['#value'], check_markup($answer->answer), t('Test that answer was correctly loaded'));
- }
-
- public function testCreateQuestion() {
- $path = 'node/add/long-answer';
- $submit = 'Save';
-
- $data = array(
-
- );
- }
-
- public function testModifyQuestion() {
-
- }
-
- public function testAnswerQuestion() {
-
- }
-
- public function testScoringQuestion() {
-
- }
-}
-*/
diff --git a/question_types/long_answer/theme/long-answer-answering-form.tpl.php b/question_types/long_answer/theme/long-answer-answering-form.tpl.php
deleted file mode 100755
index b487a9c..0000000
--- a/question_types/long_answer/theme/long-answer-answering-form.tpl.php
+++ /dev/null
@@ -1,12 +0,0 @@
-
diff --git a/question_types/long_answer/theme/long_answer.theme.inc b/question_types/long_answer/theme/long_answer.theme.inc
deleted file mode 100644
index f245a76..0000000
--- a/question_types/long_answer/theme/long_answer.theme.inc
+++ /dev/null
@@ -1,47 +0,0 @@
-time_end > 0) {
- $rows[] = array(
- check_plain($item->title),
- date('Y-m-d H:i', $item->time_end),
- l(t('Score this response'), 'admin/quiz/reports/score-long-answer/' . $item->question_vid . '/' . $item->result_id),
- );
- }
- }
- $output .= !empty($rows) ? theme('table', array('header' => $header, 'rows' => $rows)) : t('There are no unscored essays.');
- return $output;
-}
-
-/**
- * Theme the long_answer response form
- *
- * @param $form
- * The response form
- */
-function theme_long_answer_response_form($variables) {
- $form = $variables['form'];
- return drupal_render_children($form);
-}
diff --git a/question_types/matching/matching.classes.inc b/question_types/matching/matching.classes.inc
deleted file mode 100644
index 7d31c87..0000000
--- a/question_types/matching/matching.classes.inc
+++ /dev/null
@@ -1,508 +0,0 @@
-node->match)) {
- $this->node->match = array();
- }
- }
-
- /**
- * Implementation of saveNodeProperties
- *
- * @see QuizQuestion#saveNodeProperties($is_new)
- */
- public function saveNodeProperties($is_new = FALSE) {
- // Loop through each question-answer combination
- foreach ($this->node->match as $match) {
- $match['feedback'] = (isset($match['feedback'])) ? $match['feedback'] : '';
- // match_id is not so it is a new question.
- if (empty($match['match_id']) || $this->node->revision) {
- if (!empty($match['question']) && !empty($match['answer'])) {
- $sql = "INSERT INTO {quiz_matching_node} (nid, vid, question, answer, feedback) VALUES (%d, %d, '%s', '%s', '%s')";
- // TODO Please review the conversion of this statement to the D7 database API syntax.
- /* db_query($sql, $this->node->nid, $this->node->vid, $match['question'], $match['answer'], $match['feedback']) */
- $id = db_insert('quiz_matching_node')
- ->fields(array(
- 'nid' => $this->node->nid,
- 'vid' => $this->node->vid,
- 'question' => $match['question'],
- 'answer' => $match['answer'],
- 'feedback' => $match['feedback'],
- ))
- ->execute();
- }
- }
- // match_id is set, user may remove or update existing question.
- else {
- if (empty($match['question']) && empty($match['answer'])) {
- // remove sub question.
- // TODO Please review the conversion of this statement to the D7 database API syntax.
- /* db_query("DELETE FROM {quiz_matching_node} WHERE match_id = %d", $match['match_id']) */
- db_delete('quiz_matching_node')
- ->condition('match_id', $match['match_id'])
- ->execute();
- }
- else {
- // update sub question.
- //$sql = "UPDATE {quiz_matching_node} SET question = '%s', answer = '%s', feedback = '%s' WHERE match_id = %d";
- // TODO Please review the conversion of this statement to the D7 database API syntax.
- /* db_query($sql, $match['question'], $match['answer'], $match['feedback'], $match['match_id']) */
- db_update('quiz_matching_node')
- ->fields(array(
- 'question' => $match['question'],
- 'answer' => $match['answer'],
- 'feedback' => $match['feedback'],
- ))
- ->condition('match_id', $match['match_id'])
- ->execute();
- }
- }
- }
- }
-
- /**
- * Implementation of validateNode
- *
- * @see QuizQuestion#validateNode($form)
- */
- public function validateNode(array &$form) {
- // No validation is required
- // The first two pairs are required in the form, if there are other errors we forgive them
- }
-
- /**
- * Implementation of delete
- *
- * @see QuizQuestion#delete($only_this_version)
- */
- public function delete($only_this_version = FALSE) {
- parent::delete($only_this_version);
- if ($only_this_version) {
- $match_id = db_query('SELECT match_id FROM {quiz_matching_node} WHERE nid = :nid AND vid = :vid', array(':nid' => $this->node->nid, ':vid' => $this->node->vid))->fetchCol();
- db_delete('quiz_matching_user_answers')
- ->condition('match_id', is_array($match_id) ? $match_id : array(0), 'IN')
- ->execute();
-
- db_delete('quiz_matching_node')
- ->condition('nid', $this->node->nid)
- ->condition('vid', $this->node->vid)
- ->execute();
- }
- // Delete all versions of this question.
- else {
- $match_id = db_query('SELECT match_id FROM {quiz_matching_node} WHERE nid = :nid', array(':nid' => $this->node->nid))->fetchCol();
- db_delete('quiz_matching_user_answers')
- ->condition('match_id', is_array($match_id) ? $match_id : array(0), 'IN')
- ->execute();
-
- db_delete('quiz_matching_node')
- ->condition('nid', $this->node->nid)
- ->execute();
- }
- }
-
- /**
- * Implementation of getNodeProperties
- *
- * @see QuizQuestion#getNodeProperties()
- */
- public function getNodeProperties() {
- if (isset($this->nodeProperties)) {
- return $this->nodeProperties;
- }
- $props = parent::getNodeProperties();
-
- //$sql = "SELECT match_id, question, answer, feedback FROM {quiz_matching_node} WHERE nid = %d AND vid = %d";
- $query = db_query('SELECT match_id, question, answer, feedback FROM {quiz_matching_node} WHERE nid = :nid AND vid = :vid', array(':nid' => $this->node->nid, ':vid' => $this->node->vid));
- while ($result = $query->fetch()) {
- $props['match'][] = array(
- 'match_id' => $result->match_id,
- 'question' => $result->question,
- 'answer' => $result->answer,
- 'feedback' => $result->feedback,
- );
- }
- $this->nodeProperties = $props;
- return $props;
- }
-
- /**
- * Implementation of getNodeView
- *
- * @see QuizQuestion#getNodeView()
- */
- public function getNodeView() {
- $content = parent::getNodeView();
-
- list($matches, $select_option) = $this->getSubquestions();
- $subquestions = array();
- if ($this->viewCanRevealCorrect()) {
- foreach ($matches as $match) {
- $subquestions[] = array(
- 'question' => $match['question'],
- 'correct' => $match['answer'],
- 'feedback' => $match['feedback']
- );
- }
- }
- else {
- // shuffle the answer column
- foreach ($matches as $match) {
- $sub_qs[] = $match['question'];
- $sub_as[] = $match['answer'];
- }
- shuffle($sub_as);
- foreach ($sub_qs as $i => $sub_q) {
- $subquestions[] = array(
- 'question' => $sub_q,
- 'random' => $sub_as[$i],
- );
- }
- }
- $content['answers'] = array(
- '#markup' => theme('matching_match_node_view', array('subquestions' => $subquestions)),
- '#weight' => 2,
- );
- return $content;
- }
-
- /**
- * Implementation of getAnsweringForm
- *
- * @see QuizQuestion#getAnsweringForm($form_state, $rid)
- */
- public function getAnsweringForm(array $form_state = NULL, $rid) {
- $form = parent::getAnsweringForm($form_state, $rid);
- //$form['#theme'] = 'matching_answering_form';
-
- if (isset($rid)) {
- // The question has already been answered. We load the answers
- $response = new MatchingResponse($rid, $this->node);
- $responses = $response->getResponse();
- }
- list($matches, $select_option) = $this->getSubquestions();
- $form['subquestions']['#theme'] = 'matching_subquestion_form';
- foreach ($matches as $match) {
- $form['subquestions'][$match['match_id']]['#question'] = check_markup($match['question']);
- $form['subquestions'][$match['match_id']]['tries[' . $match['match_id'] . ']'] = array(
- '#type' => 'select',
- '#options' => $this->customShuffle($select_option),
- );
- if (isset($rid)) {
- // If this question already has been answered
- $form['subquestions'][$match['match_id']]['tries[' . $match['match_id'] . ']']['#default_value'] = $responses[$match['match_id']];
- }
- }
- $form['scoring_info'] = array(
- '#markup' => '' . t('You lose points by selecting incorrect options. You may leave an option blank to avoid losing points.') . '
', - ); - if (variable_get('quiz_matching_shuffle_options', TRUE)) { - $form['subquestions'] = $this->customShuffle($form['subquestions']); - } - return $form; - } - - /** - * Shuffles an array, but keeps the keys, and makes sure the first element is the default element - * - * @param $array - * Array to be shuffled - * @return - * A shuffled version of the array with $array['def'] = '' as the first element - */ - private function customShuffle(array $array = array()) { - $new_array = array(); - $new_array['def'] = ''; - while (count($array)) { - $element = array_rand($array); - $new_array[$element] = $array[$element]; - unset($array[$element]); - } - return $new_array; - } - - /** - * Helper function to fetch subquestions - * - * @return - * Array with two arrays, matches and selected options - */ - private function getSubquestions() { - $matches = $select_option = array(); - //$sql = "SELECT match_id, question, answer, feedback FROM {quiz_matching_node} WHERE nid = %d AND vid = %d"; - $query = db_query('SELECT match_id, question, answer, feedback FROM {quiz_matching_node} WHERE nid = :nid AND vid = :vid', array(':nid' => $this->node->nid, ':vid' => $this->node->vid)); - while ($result = $query->fetch()) { - $matches[] = array( - 'match_id' => $result->match_id, - 'question' => $result->question, - 'answer' => $result->answer, - 'feedback' => $result->feedback, - ); - $select_option[$result->match_id] = $result->answer; - } - return array($matches, $select_option); - } - - /** - * Implementation of getBodyFieldTitle - * - * @see QuizQuestion#getBodyFieldTitle() - */ - public function getBodyFieldTitle() { - return t('Instruction'); - } - - /** - * Implementation of getCreationForm - * - * @see QuizQuestion#getCreationForm($form_state) - */ - public function getCreationForm(array $form_state = NULL) { - $form['match'] = array( - '#type' => 'fieldset', - '#title' => t('Answer'), - '#weight' => -4, - '#tree' => TRUE, - '#theme' => 'matching_question_form', - '#description' => t('Write your pairs in the question and answer columns. For the user the question will be fixed and the answers will be shown as alternatives in a dropdown box.'), - ); - for ($i = 1; $i <= variable_get('quiz_matching_form_size', 5); ++$i) { - $form['match'][$i] = array( - '#type' => 'fieldset', - '#title' => t('Question ' . $i), - ); - $form['match'][$i]['match_id'] = array( - '#type' => 'value', - '#default_value' => isset($this->node->match[$i -1]['match_id']) ? $this->node->match[$i -1]['match_id'] : '' - ); - $form['match'][$i]['question'] = array( - '#type' => 'textarea', - '#rows' => 2, - '#default_value' => isset($this->node->match[$i -1]['question']) ? $this->node->match[$i -1]['question'] : '', - '#required' => $i < 3, - ); - $form['match'][$i]['answer'] = array( - '#type' => 'textarea', - '#rows' => 2, - '#default_value' => isset($this->node->match[$i -1]['answer']) ? $this->node->match[$i -1]['answer'] : '', - '#required' => $i < 3, - ); - - $form['match'][$i]['feedback'] = array( - '#type' => 'textarea', - '#rows' => 2, - '#default_value' => isset($this->node->match[$i -1]['feedback']) ? $this->node->match[$i -1]['feedback'] : '' - ); - } - return $form; - } - - /** - * Implementation of getMaximumScore - * - * @see QuizQuestion#getMaximumScore() - */ - public function getMaximumScore() { - $to_return = 0; - foreach ($this->node->match as $match) { - if (empty($match['question']) || empty($match['answer'])) { - continue; - } - $to_return++; - } - // The maximum score = the number of sub-questions - return $to_return; - } - - /** - * Get the correct answers for this question - * - * @return - * Array of correct answers - */ - public function getCorrectAnswer() { - $correct_answers = array(); - $query = db_query('SELECT match_id, question, answer, feedback FROM {quiz_matching_node} WHERE nid = :nid AND vid = :vid', array(':nid' => $this->node->nid, ':vid' => $this->node->vid)); - while ($result = $query->fetch()) { - $correct_answers[$result->match_id] = array( - 'match_id' => $result->match_id, - 'question' => $result->question, - 'answer' => $result->answer, - 'feedback' => $result->feedback, - ); - } - return $correct_answers; - } -} - -/** - * Extension of QuizQuestionResponse - */ -class MatchingResponse extends QuizQuestionResponse { - - /** - * Constructor - */ - public function __construct($result_id, stdClass $question_node, $answer = NULL) { - parent::__construct($result_id, $question_node, $answer); - if (!isset($answer)) { - $res = db_query('SELECT ua.answer, score, ua.match_id FROM {quiz_matching_user_answers} ua - JOIN {quiz_matching_node} n ON n.match_id = ua.match_id - WHERE n.nid = :nid AND n.vid = :vid AND ua.result_id = :result_id', array(':nid' => $question_node->nid, ':vid' => $question_node->vid, ':result_id' => $result_id)); - $this->answer = array(); - while ($obj = $res->fetch()) { - $this->answer[$obj->match_id] = $obj->answer; - } - } - $this->is_correct = $this->isCorrect(); - } - - /** - * Implementation of isValid - * - * @see QuizQuestionResponse#isValid() - */ - public function isValid() { - foreach ($this->answer as $value) { - if ($value != 'def') { - return TRUE; - } - } - return t('You need to match at least one of the items.'); - } - - /** - * Implementation of save - * - * @see QuizQuestionResponse#save() - */ - public function save() { - if (!isset($this->answer) || !is_array($this->answer)) { - return; - } - $insert = db_insert('quiz_matching_user_answers')->fields(array('match_id', 'result_id', 'answer', 'score')); - foreach ($this->answer as $key => $value) { - $insert->values(array( - 'match_id' => $key, - 'result_id' => $this->rid, - 'answer' => (int) $value, - 'score' => ($key == $value) ? 1 : 0, - )); - } - $insert->execute(); - } - - /** - * Implementation of delete - * - * @see QuizQuestionResponse#delete() - */ - public function delete() { - $match_id = db_query('SELECT match_id FROM {quiz_matching_node} WHERE nid = :nid AND vid = :vid', array(':nid' => $this->question->nid, ':vid' => $this->question->vid))->fetchCol(); - db_delete('quiz_matching_user_answers') - ->condition('match_id', is_array($match_id) ? $match_id : array(0), 'IN') - ->condition('result_id', $this->rid) - ->execute(); - } - - /** - * Implementation of score - * - * @see QuizQuestionResponse#score() - */ - public function score() { - $wrong_answer = 0; - $correct_answer = 0; - $user_answers = isset($this->answer['answer']) ? $this->answer['answer'] : $this->answer; - foreach ((array) $user_answers as $key => $value) { - if ($key == $value) { - $correct_answer++; - } - elseif ($value == 0 || $value == 'def') { - } - else { - $wrong_answer++; - } - } - $score = $correct_answer - $wrong_answer; - return $score < 0 ? 0 : $score; - } - - /** - * Implementation of getResponse - * - * @see QuizQuestionResponse#getResponse() - */ - public function getResponse() { - return $this->answer; - } - - /** - * Implementation of getReportFormResponse - * - * @see QuizQuestionResponse#getReportFormResponse($showpoints, $showfeedback, $allow_scoring) - */ - public function getReportFormResponse($showpoints = TRUE, $showfeedback = TRUE, $allow_scoring = FALSE) { - $data = $metadata = array(); - // Build the question answers header (add blank space for IE). - $metadata[] = t('Match'); - if ($showpoints) { - $metadata[] = t('Correct Answer'); - } - $metadata[] = t('User answer'); - - $MatchingQuestion = new MatchingQuestion($this->question); - $correct_answers = $MatchingQuestion->getCorrectAnswer(); - $user_answers = isset($this->answer['answer']) ? $this->answer['answer'] : $this->answer; - - $has_feedback = TRUE; - foreach ($correct_answers as $correct_answer) { - $answer_data = array(); - $id = $user_answers[$correct_answer['match_id']]; - $correct = isset($correct_answers[$id]) && $correct_answer['answer'] == $correct_answers[$id]['answer']; - $answer_data['question'] = $correct_answer['question']; - if ($showpoints) { - $answer_data['correct_answer'] = $correct_answer['answer']; - } - $answer_data['user_answer'] = isset($correct_answers[$id]) ? $correct_answers[$id]['answer'] : NULL; - if ($showfeedback && !empty($correct_answer['feedback'])) { - $answer_data['feedback'] = check_markup($correct_answer['feedback']); - $answer_data['is_correct'] = $correct; - } - else { - $has_feedback = FALSE; - } - $data[] = $answer_data; - } - - if ($showfeedback && $has_feedback) { - $metadata[] = t('Feedback'); - } - - return array( - '#markup' => theme('matching_response', array('metadata' => $metadata, 'data' => $data)), - ); - } -} diff --git a/question_types/matching/matching.info b/question_types/matching/matching.info deleted file mode 100644 index 95edb04..0000000 --- a/question_types/matching/matching.info +++ /dev/null @@ -1,9 +0,0 @@ -name = Matching question -package = Quiz Question -description = Provide a way to create matching type of questions. -core = 7.x -dependencies[] = quiz -dependencies[] = quiz_question - -files[] = matching.classes.inc -files[] = matching.test diff --git a/question_types/matching/matching.install b/question_types/matching/matching.install deleted file mode 100644 index 416332b..0000000 --- a/question_types/matching/matching.install +++ /dev/null @@ -1,114 +0,0 @@ - array( - 'match_id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'vid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'question' => array( - 'type' => 'text', - 'not null' => TRUE, - ), - 'answer' => array( - 'type' => 'text', - 'not null' => TRUE, - ), - 'feedback' => array( - 'type' => 'text', - 'not null' => TRUE, - ), - ), - 'primary key' => array('match_id'), - 'indexes' => array( - 'question_id' => array('nid', 'vid'), - ), - ); - - // User answers go in here. - $schema['quiz_matching_user_answers'] = array( - 'fields' => array( - 'answer_id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - - 'match_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'result_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'score' => array( - 'type' => 'int', - 'size' => 'tiny', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'answer' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - ), - 'primary key' => array('answer_id'), - ); - - return $schema; -} diff --git a/question_types/matching/matching.module b/question_types/matching/matching.module deleted file mode 100644 index 1f55f6b..0000000 --- a/question_types/matching/matching.module +++ /dev/null @@ -1,104 +0,0 @@ - array( - 'name' => t('Matching'), - 'description' => t('Matching question type for quiz module. A question type for the quiz module: allows you to create matching type questions, which connect terms with one another.'), - 'question provider' => 'MatchingQuestion', - 'response provider' => 'MatchingResponse', - 'module' => 'quiz_question', // All wrapper functions are in that module. - ), - ); -} - -/** - * hook_config - * - * @return FAPI array - */ -function matching_config() { - $form['quiz_matching_form_size'] = array( - '#type' => 'textfield', - '#title' => t('Match Question Size'), - '#description' => t('Number of questions allowed to wrap under a matching type question.'), - '#default_value' => variable_get('quiz_matching_form_size', 5), - ); - $form['quiz_matching_shuffle_options'] = array( - '#type' => 'checkbox', - '#title' => t('Shuffle Matching Questions'), - '#default_value' => variable_get('quiz_matching_shuffle_options', TRUE), - '#description' => t('If checked matching questions will be shuffled'), - ); - $form['#validate'] = 'matching_config_validate'; - return $form; -} - -/** - * Validate the matching config form values - */ -function matching_config_validate($form, $form_state) { - if (!_quiz_is_int($form_state['values']['quiz_matching_form_size'], 2, 50)) { - form_set_error('quiz_matching_form_size', t('The number of questions must be between 2 and 50')); - } -} - -/** - * Implements hook_theme(). - */ -function matching_theme() { - return array( - 'matching_question_form' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'matching') . '/theme', - 'file' => 'matching.theme.inc', - ), - 'matching_response' => array( - 'variables' => array('metadata' => NULL, 'data' => NULL), - 'path' => drupal_get_path('module', 'matching') . '/theme', - 'file' => 'matching.theme.inc', - ), - 'matching_subquestion_form' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'matching') . '/theme', - 'file' => 'matching.theme.inc', - ), - 'matching_match_node_view' => array( - 'variables' => array('subquestions' => NULL), - 'path' => drupal_get_path('module', 'matching') . '/theme', - 'file' => 'matching.theme.inc', - ), - 'matching_answering_form' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'matching') . '/theme', - 'template' => 'matching-answering-form', - ), - - ); -} diff --git a/question_types/matching/matching.test b/question_types/matching/matching.test deleted file mode 100644 index f546891..0000000 --- a/question_types/matching/matching.test +++ /dev/null @@ -1,162 +0,0 @@ -min, $this->max); - } - - /* - * The getInfo() method provides information about the test. - * In order for the test to be run, the getInfo() method needs - * to be implemented. - */ - public static function getInfo() { - return array( - 'name' => t('Matching Questions unit test'), - 'description' => t('Unit test for matching question type.'), - 'group' => t('Quiz'), - ); - } - - /* - * Implementing setUp() to enable truefalse module testing - */ - function setUp() { - parent::setUp('taxonomy', 'quiz', 'views', 'autoload', 'multichoice', - 'quiz_directions', 'quiz_question', 'querypath', 'questions_import', - 'short_answer', 'truefalse', 'long_answer', 'matching', 'questions_export'); - - // Create and log in our test user. Should be cleaned up as I something - // was wrong with permissions and I basically kept adding potentially - // useful ones until it worked. - - // array of drupal permission - $permission = array('administer site configuration', 'access administration pages', - 'administer quiz', 'access quiz', 'administer blocks', 'import questions', 'create quiz', - 'administer quiz configuration', 'use PHP for block visibility', 'administer blocks', - 'create multichoice', 'edit any multichoice', 'administer taxonomy', 'allow multiple correct answers', - 'allow any number of answers', 'export questions'); - - // create a drupal test user with specified permissions - $user = $this->drupalCreateUser($permission); - $this->drupalLogin($user); - - // create one quiz, which will be the default in the import form - $quiz_settings = array( - 'title' => $this->randomName($this->getRandSize()), - 'comment' => $this->randomName($this->getRandSize()), - 'type' => 'quiz', - ); - // $this->drupalCreateNode($quiz_settings); - } - - /* - * checks whether hook_quiz_question for true/false questions type has been - * defined or not. - */ - public function testMatchingQuizQuestionInfo() { - $info = matching_quiz_question_info(); - $this->assertEqual(count($info), 1, t('Check that info was returned.')); - $this->assertTrue(isset($info['matching']), t('Check that matching question type exists.')); - } - - /* - * function to create true false type question node for testing. - */ - public function createMatchingQuestion() { - // matching question node attributes - $this->title = $this->randomName(mt_rand($this->min, $this->max)); - $this->body = $this->randomName($this->getRandSize()); - - // array of node attributes to create a test node - $settings = array( - 'type' => $this->question_node_type, - 'title' => $this->title, - 'body' => $this->body, - 'revisions' => TRUE, - ); - - $this->match = array(); - $form_size = variable_get('quiz_matching_form_size', 5); - // generate rand match question, answer and feedback - for ($i = 1; $i < $form_size; $i++) { - $settings['match'][$i]['question'] = $this->match[$i]['question'] = $this->randomName($this->getRandSize()); - $setting['match'][$i]['answer'] = $this->match[$i]['answer'] = $this->randomName($this->getRandSize()); - $settings['match'][$i]['feedback'] = $this->match[$i]['feedback'] = $this->randomName($this->getRandSize()); - } - - // create drupal node - return $this->drupalCreateNode($settings); - } - - /* - * function to test matching type question node creation. - */ - public function unitTestCreateMatchingQuestionNode() { - $node = $this->createMatchingQuestion(); - // raise an exception if node is not created. - if (!$node) { - throw new Exception('Expected to have a node to work with.'); - } - $this->node_id = $node->nid; - $this->assertEqual($node->title, $this->title, t('Title of stored node should equal the original title.')); - $this->assertEqual($node->body, $this->body, t('Body of stored node should be equal to original body.')); - $this->assertEqual($node->type, $this->question_node_type, t('Stored node type should be matching')); - } - - /* - * @function - * function tests whether node properties has been saved appropriately or not. - */ - public function unitTestCheckMatchingNodeProperties() { - //$node = node_load($this->node_id); - //$this->assertEqual(count($node->match), count($this->match), t('Test that all match questions was appropriately stored in database.')); - } - - /** - * Check that question exists in DB. - */ - public function unitTestCheckMatchingQuestionsList() { - $questions = matching_questions_list(); - $this->assertEqual(count($questions), 1, t('Verify that the question exists.')); - } - - - /** - * Run a bundle of Node API tests. - * - * This tests CRUD and all the functionality on a singlea node. - */ - public function testMatchingNodeOperations() { - $this->unitTestCreateMatchingQuestionNode(); - $this->unitTestCheckMatchingNodeProperties(); - $this->unitTestCheckMatchingQuestionsList(); - - } -} diff --git a/question_types/matching/theme/matching-answering-form.tpl.php b/question_types/matching/theme/matching-answering-form.tpl.php deleted file mode 100755 index 5410a07..0000000 --- a/question_types/matching/theme/matching-answering-form.tpl.php +++ /dev/null @@ -1,12 +0,0 @@ - \ No newline at end of file diff --git a/question_types/matching/theme/matching.theme.inc b/question_types/matching/theme/matching.theme.inc deleted file mode 100644 index 07ea822..0000000 --- a/question_types/matching/theme/matching.theme.inc +++ /dev/null @@ -1,91 +0,0 @@ - t('Question'), - 'answer' => t('Correct answer'), - 'feedback' => t('Feedback'), - ); - foreach (element_children($form) as $key) { - $rows[] = array( - 'question' => drupal_render($form[$key]['question']), - 'answer' => drupal_render($form[$key]['answer']), - 'feedback' => drupal_render($form[$key]['feedback']), - ); - } - // Theme output and display to screen. - return theme('table', array('header' => $header, 'rows' => $rows)); -} - -/** - * Theme the answering form - */ -function theme_matching_subquestion_form($variables) { - $form = $variables['form']; - $out = '' . $value['#question']; - $out .= ' | ' . drupal_render_children($value) . ' |
The module has three settings. - Multiple answers allows the quiz taker to select more than one alternative - (it also allows for the possibility that none of the alternatives are correct). - Alternatives are selected using checkboxes instead of radio buttons. - Random order displays the alternatives in random order when quiz is beeing taken. - Simple scoring gives max score if everything is correct. Zero points otherwise.
- -The scoring system in multichoice is a bit complex. With multiple answers each alternative adds a given number of points to - the total score if it is chosen, and another number of points is added if the alternative isn't chosen. Both score if chosen and - score if not chosen may be edited for each alternative by the question creator. - If multiple answers isn't allowed the score will be set to the score if chosen of the alternative that has been chosen. - The question is considered correct if the quiz taker gets the maximum amount of points possible for the question.
- "); - } -} -/** - * Implements hook_quiz_question_info(). - */ -function multichoice_quiz_question_info() { - return array( - 'multichoice' => array( - 'name' => t('Multiple choice question'), - 'description' => t('This provides multiple choice questions for use by the Quiz module.'), - 'question provider' => 'MultichoiceQuestion', - 'response provider' => 'MultichoiceResponse', - 'module' => 'quiz_question', // All wrapper functions are in that module. - ), - ); -} -/** - * Implements hook_config(). - */ -function multichoice_config() { - $form['multichoice_def_num_of_alts'] = array( - '#type' => 'textfield', - '#title' => t('Default number of alternatives'), - '#default_value' => variable_get('multichoice_def_num_of_alts', 2), - ); - $form['multichoice_def_scoring'] = array( - '#type' => 'radios', - '#title' => t('Default scoring method'), - '#description' => t('Choose the default scoring method for questions with multiple correct answers.'), - '#options' => array( - 0 => t('Give minus one point for incorrect answers'), - 1 => t("Give one point for each incorrect option that haven't been chosen"), - ), - '#default_value' => variable_get('multichoice_def_scoring', 0), - ); - $form['#validate'] = 'multichoice_config_validate'; - return $form; -} - -/** - * Validate the multichoice config form values - */ -function multichoice_config_validate($form, $form_state) { - if (!_quiz_is_int($form_state['values']['multichoice_def_num_of_alts'], 2, 50)) { - form_set_error('multichoice_def_num_of_alts', t('The default number of alternatives must be between 2 and 50')); - } -} - -/** - * Implements hook_theme(). - */ -function multichoice_theme($existing, $type, $theme, $path) { - return array( - 'multichoice_creation_form' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'multichoice') . '/theme', - 'file' => 'multichoice.theme.inc', - ), - 'multichoice_answer_node_view' => array( - 'variables' => array( - 'alternatives' => NULL, - 'show_correct' => NULL, - ), - 'path' => drupal_get_path('module', 'multichoice') . '/theme', - 'file' => 'multichoice.theme.inc', - ), - 'multichoice_response' => array( - 'variables' => array( - 'data' => array(), - ), - 'path' => drupal_get_path('module', 'multichoice') . '/theme', - 'file' => 'multichoice.theme.inc', - ), - 'multichoice_alternative_creation' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'multichoice') . '/theme', - 'template' => 'multichoice-alternative-creation', - ), - 'multichoice_answering_form' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'multichoice') . '/theme', - 'template' => 'multichoice-answering-form', - ), - 'multichoice_alternative' => array( - 'render element' => 'form', - 'path' => drupal_get_path('module', 'multichoice') . '/theme', - 'template' => 'multichoice-alternative', - ), - ); -} - -/** - * ajax callback function used when adding alternatives to the node-form - */ -function multichoice_add_alternative_ajax_callback($form, &$form_state) { - $i = 0; - while (isset($form['alternatives'][$i])) { - $i++; - } - return $form['alternatives'][$i - 1]; -} - -/** - * Submit handler used when adding more alternatives to the node-form - */ -function multichoice_more_choices_submit($form, &$form_state) { - // Set the form to rebuild and run submit handlers. - node_form_submit_build_node($form, $form_state); - - // Count the existing alternatives - $exists = 0; - while (isset($form['alternatives'][$exists])) { - $exists++; - } - - // Make the changes we want to the form state. - if ($form_state['values']['alternatives']['multichoice_add_alternative']) { - // We add 3 if js is disabled. 1 if the adding is done using ahah - $n = $_GET['q'] == 'system/ajax' ? 1 : 3; - $form_state['choice_count'] = $exists + $n; - } - $form_state['rebuild'] = TRUE; -} -/** - * Recursive helper function to set the validated property. (Taken from the skip validation module.) - * - * @param &$elements - * The elements that are currently being processed. - */ -function _multichoice_skip_validation(&$elements) { - $elements['#validated'] = TRUE; - foreach (element_children($elements) as $key) { - _multichoice_skip_validation($elements[$key]); - } -} - -/** - * Implements hook_user_cancel(). - */ -function multichoice_user_cancel($edit, $account, $method) { - db_delete('quiz_multichoice_user_settings') - ->condition('uid', $account->uid) - ->execute(); -} - -/** - * Implementation of hook user. - */ -function multichoice_user_OLD($op, &$edit, &$account, $category = NULL) { } - -/** - * Implements hook_field_extra_fields(). - */ -function multichoice_field_extra_fields() { - $extra['node']['multichoice'] = array( - 'form' => array( - 'alternatives' => array( - 'label' => t('Alternatives'), - 'description' => t('Alternatives for multichoice'), - 'weight' => -4, - ), - ), - ); - return $extra; -} diff --git a/question_types/multichoice/multichoice.test b/question_types/multichoice/multichoice.test deleted file mode 100755 index 256ca2a..0000000 --- a/question_types/multichoice/multichoice.test +++ /dev/null @@ -1,66 +0,0 @@ - t('Choice unit test'), - 'description' => t('Unit test for true or false question type.'), - 'group' => t('Quiz'), - ); - } - - /* - * @function - * generates a rand integer between the specified range - * - * @return - * random Integer value - */ - public function getRandSize() { - return mt_rand($this->min, $this->max); - } - - /* - * Implementing setUp() to enable truefalse module testing - */ - function setUp() { - parent::setUp('taxonomy', 'quiz', 'views', 'autoload', 'multichoice', - 'quiz_directions', 'quiz_question', 'querypath', 'questions_import', - 'short_answer', 'truefalse', 'long_answer', 'matching', 'questions_export'); - - // Create and log in our test user. Should be cleaned up as I something - // was wrong with permissions and I basically kept adding potentially - // useful ones until it worked. - - // array of drupal permission - $permission = array('administer site configuration', 'access administration pages', - 'administer quiz', 'access quiz', 'administer blocks', 'import questions', 'create quiz', - 'administer quiz configuration', 'use PHP for block visibility', 'administer blocks', - 'create multichoice', 'edit any multichoice', 'administer taxonomy', 'allow multiple correct answers', - 'allow any number of answers', 'export questions'); - - // create a drupal test user with specified permissions - $user = $this->drupalCreateUser($permission); - $this->drupalLogin($user); - - // create one quiz, which will be the default in the import form - $quiz_settings = array(); - $quiz_settings['title'] = $this->randomName($this->getRandSize()); - $quiz_settings['comment'] = $this->randomName($this->getRandSize()); - $quiz_settings['type'] = 'quiz'; - // $this->drupalCreateNode($quiz_settings); - // don't' create a questions node here. - } -} diff --git a/question_types/multichoice/multichoice_update_6400.inc b/question_types/multichoice/multichoice_update_6400.inc deleted file mode 100755 index 3b1757d..0000000 --- a/question_types/multichoice/multichoice_update_6400.inc +++ /dev/null @@ -1,404 +0,0 @@ - 'serial', - 'unsignet' => TRUE, - 'not_null' => TRUE, - ), array('primary key' => array('id'))); - db_add_field('quiz_multichoice_user_answers', 'choice_order', array('type' => 'text')); - db_create_table('quiz_multichoice_user_answer_multi', array( - 'fields' => array( - 'user_answer_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'answer_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - ), - 'primary key' => array( - 'user_answer_id', 'answer_id' - ), - )); -} - -/** - * Move/convert user answer data from the old data model to the new - * - * @param $to_return - * Array where progress can be reported - * @param $sandbox - * Array where persistent data can be stored, and progress can be found - */ -function _multichoice_move_old_answers(&$to_return, &$sandbox = NULL) { - // Store persistent data in the sandbox variable - if (!isset($sandbox['step_progress'])) { - $sandbox['step_progress'] = 0; - $sandbox['last_rid'] = -1; - $sandbox['last_nid'] = -1; - $sandbox['last_vid'] = -1; - $sandbox['last_id'] = -1; - $sandbox['step_max'] = db_query('SELECT COUNT(DISTINCT id) FROM {quiz_multichoice_user_answers}')->fetchField(); - } - // Fetch old user ans - $sql = 'SELECT id, answer_id, question_nid, question_vid, result_id - FROM {quiz_multichoice_user_answers} - WHERE result_id >= %d - ORDER BY result_id, question_nid, question_vid, answer_id'; - $res = db_query_range('SELECT id, answer_id, question_nid, question_vid, result_id - FROM {quiz_multichoice_user_answers} - WHERE result_id >= :result_id - ORDER BY result_id, question_nid, question_vid, answer_id', array(':result_id' => $sandbox['last_rid'])); - $progress_to_add = 0; - while ($res_o = db_fetch_object($res)) { - if ($res_o->question_nid == $sandbox['last_nid'] - && $res_o->question_vid == $sandbox['last_vid'] - && $res_o->result_id == $sandbox['last_rid']) { - // We only keep one row for each question result in quiz_multichoice_user_answers - if ($res_o->id > $sandbox['last_id']) { - $sql2 = 'DELETE FROM {quiz_multichoice_user_answers} - WHERE id = %d'; - // TODO Please review the conversion of this statement to the D7 database API syntax. - /* db_query($sql2, $res_o->id) */ - db_delete('quiz_multichoice_user_answers') - ->condition('id', $res_o->id) - ->execute(); - if ($progress_to_add + $sandbox['step_progress'] + 1 >= $sandbox['step_max']) { - $sandbox['step_progress'] = $sandbox['step_max']; - } - } - } - else { - // This is the first row for this question result - $sandbox['last_nid'] = $res_o->question_nid; - $sandbox['last_vid'] = $res_o->question_vid; - $sandbox['last_rid'] = $res_o->result_id; - $sandbox['last_id'] = $res_o->id; - $sandbox['step_progress'] += $progress_to_add; - $progress_to_add = 0; - } - // We insert ther answers in quiz_multichoice_user_answer_multi - $sql = 'INSERT IGNORE INTO {quiz_multichoice_user_answer_multi} - (user_answer_id, answer_id) - VALUES(%d, %d)'; - // TODO Please convert this statement to the D7 database API syntax. - /* db_query($sql, $sandbox['last_id'], $res_o->answer_id) */ - NULL; - $progress_to_add++; - } - // We move on if all user answers have been moved - if ($sandbox['step_progress'] >= $sandbox['step_max']) { - _multichoice_next_step($to_return, $sandbox); - } -} - -/** - * We delete fields not needed after the user_answer data was moved from the old data model - * - * @param $to_return - * Array where progress can be reported - * @param $sandbox - * Array where persistent data can be stored, and progress can be found - */ -function _multichoice_alter_user_answers2(&$to_return, &$sandbox = NULL) { - db_drop_field('quiz_multichoice_user_answers', 'answer_id'); -} - -/** - * We create a new table to store multichoice properties in - * - * @param $to_return - * Array where progress can be reported - * @param $sandbox - * Array where persistent data can be stored, and progress can be found - */ -function _multichoice_create_properties(&$to_return, &$sandbox = NULL) { - db_create_table('quiz_multichoice_properties', array( - 'fields' => array( - 'nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'vid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'choice_multi' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'choice_random' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - 'choice_boolean' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array( - 'nid', 'vid' - ), - )); -} - -/** - * We alter the quiz_multichoice_answers table to fit the new data model - * - * @param $to_return - * Array where progress can be reported - * @param $sandbox - * Array where persistent data can be stored, and progress can be found - */ -function _multichoice_alter_answers(&$to_return, &$sandbox = NULL) { - //db_drop_primary_key($to_return, 'quiz_multichoice_answers'); - db_change_field('quiz_multichoice_answers', 'answer_id', 'id', array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - )); - db_change_field('quiz_multichoice_answers', 'nid', 'question_nid', array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - )); - db_change_field('quiz_multichoice_answers', 'vid', 'question_vid', array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - )); - db_change_field('quiz_multichoice_answers', 'feedback', 'feedback_if_chosen', array( - 'type' => 'text', - )); - db_add_field('quiz_multichoice_answers', 'feedback_if_chosen_format', array( - 'type' => 'int', - )); - db_add_field('quiz_multichoice_answers', 'feedback_if_not_chosen', array( - 'type' => 'text', - )); - db_add_field('quiz_multichoice_answers', 'feedback_if_not_chosen_format', array( - 'type' => 'int', - )); - db_add_field('quiz_multichoice_answers', 'answer_format', array( - 'type' => 'int', - )); - db_change_field('quiz_multichoice_answers', 'is_correct', 'score_if_chosen', array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not_null' => TRUE, - 'default' => 0, - )); - db_add_field('quiz_multichoice_answers', 'score_if_not_chosen', array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not_null' => TRUE, - 'default' => 0, - )); - db_drop_field('quiz_multichoice_answers', 'result_option'); -} - -/** - * We go through the alternatives and update question properties and field formats - * - * @param $to_return - * Array where progress can be reported - * @param $sandbox - * Array where persistent data can be stored, and progress can be found - */ -function _multichoice_check_answers(&$to_return, &$sandbox) { - // Init persistent variables - if (!isset($sandbox['multichoice_check_answers'])) { - $sandbox['step_progress'] = 0; - $sandbox['multichoice_check_answers'] = TRUE; - $sandbox['last_nid'] = 0; - $sandbox['last_vid'] = 0; - $sandbox['step_max'] = db_query('SELECT COUNT(DISTINCT id) FROM {quiz_multichoice_answers}')->fetchField(); - } - $sandbox['num_corrects'] = 0; - $sql = 'SELECT id, question_nid, question_vid, score_if_chosen, score_if_not_chosen - FROM {quiz_multichoice_answers} - WHERE question_nid >= %d AND question_vid >= %d - ORDER BY question_nid, question_vid'; - $res = db_query_range('SELECT id, question_nid, question_vid, score_if_chosen, score_if_not_chosen - FROM {quiz_multichoice_answers} - WHERE question_nid >= :question_nid AND question_vid >= :question_vid - ORDER BY question_nid, question_vid', array(':question_nid' => $sandbox['last_nid'], ':question_vid' => $sandbox['last_vid'])); - $progress_to_add = 0; - while ($res_o = db_fetch_object($res)) { - if ($res_o->question_nid == $sandbox['last_nid'] - && $res_o->question_vid == $sandbox['last_vid']) { - // This is the same node as we processed in the last loop - if ($progress_to_add + $sandbox['step_progress'] + 1 >= $sandbox['step_max']) { - $sandbox['step_progress'] = $sandbox['step_max']; - } - } - else { - // New node - $sandbox['step_progress'] += $progress_to_add; - $progress_to_add = 0; - $sandbox['last_nid'] = $res_o->question_nid; - $sandbox['last_vid'] = $res_o->question_vid; - $sandbox['num_corrects'] = 0; - - // Store the format for this node - $sql = 'SELECT format - FROM {node_revision} - WHERE vid = %d'; - $res2 = db_query('SELECT format - FROM {node_revision} - WHERE vid = :vid', array(':vid' => $res_o->question_vid)); - $sandbox['last_format'] = $res2->fetchField(); - $sql = 'INSERT INTO {quiz_multichoice_properties} - (nid, vid, choice_boolean) - VALUES(%d, %d, 1)'; - // TODO Please convert this statement to the D7 database API syntax. - /* db_query($sql, $res_o->question_nid, $res_o->question_vid) */ - NULL; - } - - if ($res_o->score_if_chosen == 1) { - $sandbox['num_corrects']++; - if ($sandbox['num_corrects'] == 2) { - // If more than one answer is correct we set the choice_multi property to one. - $sql = 'UPDATE {quiz_multichoice_properties} - SET choice_multi = 1 - WHERE nid = %d AND vid = %d'; - // TODO Please review the conversion of this statement to the D7 database API syntax. - /* db_query($sql, $res_o->question_nid, $res_o->question_vid) */ - db_update('quiz_multichoice_properties') - ->fields(array( - 'choice_multi' => 1, - )) - ->condition('nid', $res_o->question_nid) - ->condition('vid', $res_o->question_vid) - ->execute(); - } - } - // Update all the formats - $sql = 'UPDATE {quiz_multichoice_answers} - SET answer_format = %d, feedback_if_chosen_format = %d, feedback_if_not_chosen_format = %d - WHERE question_vid = %d'; // This table haven't been indexed yet, making this query very slow... :/ - $lf = $sandbox['last_format']; - // TODO Please review the conversion of this statement to the D7 database API syntax. - /* db_query($sql, $lf, $lf, $lf, $res_o->question_vid) */ - db_update('quiz_multichoice_answers') - ->fields(array( - 'answer_format' => $lf, - 'feedback_if_chosen_format' => $lf, - 'feedback_if_not_chosen_format' => $lf, - )) - ->condition('question_vid', $res_o->question_vid) - ->execute(); - $progress_to_add++; - } - $sandbox['step_progress'] += $progress_to_add; - // Check if we are finished, and jump to the next step if we are - if ($sandbox['step_progress'] >= $sandbox['step_max']) { - _multichoice_next_step($to_return, $sandbox); - } -} - -/** - * We create the user settings table - * - * @param $to_return - * Array where progress can be reported - * @param $sandbox - * Array where persistent data can be stored, and progress can be found - */ -function _multichoice_create_user_settings(&$to_return, &$sandbox = NULL) { - db_create_table('quiz_multichoice_user_settings', array( - 'fields' => array( - 'uid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'last_nid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'last_vid' => array( - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - ), - 'primary key' => array( - 'uid' - ), - )); -} - -/** - * Helper function to switch steps in the update process - * - * @param $to_return - * Array where progress can be reported - * @param $sandbox - * Array where persistent data can be stored, and progress can be found - */ -function _multichoice_next_step(&$to_return, &$sandbox) { - $sandbox['current_step']++; -} diff --git a/question_types/multichoice/theme/images/almost.png b/question_types/multichoice/theme/images/almost.png deleted file mode 100755 index 7c88ba008df65e95afabca596efddf66f2ba3831..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49235 zcmcHAbx<6^A0T=H1cC>5ClG?WLvVMO#a)8CyF0<%-Q9h0cXxM}1&;iFclY1B_o}Ym z)b#f5_V#r5?9^^`f94x1CnJggivtS=28JLmCZzD06MQE8Z&06)9r4P09y=fn{`)s*JjMgzd9mw|~&n z@mF@N*xN?h%l>u6Ix?qhtnC#z_3wjI=z`u<@BpKYb;6)Z#>-z(mFzF8yGOrnFWzk& z{<^CE8_p()F??ulw;!!Nx*I3AIx0Ik)Uucz(vR$y`SXbLrPQCVE4080s3G ztOLcKqV7=$>!x2$uuUNLw!*(LKE~#-@o5m%{`vg&s08_7p6gkM_l`^$(40eYm$))E zfcj7v+gy3AN61}U)_!##sCOL*=T66!SfuTN4?3}f#K9n#%_@I)5t;YR?0Y1!59e!Z z@o=2JCmG)r4E4bT>Jg&GYZ_QlPgYQ?uJHR4;p3lPXUsEm@K+$s3I8|-^SgC*H09au zo=rrtf3$ggLsZQTS!+n*S^q*%C0(JRU86%NMXWULl|}b^C&tm0nUotn412D))LC37 zQQHP>p`ip;Q5fZq4zZL*>Rk8Ln!0DlM_^JpoZZOdZNyj4s)0B3NXtT)Mo2ti)`0R* z?$&02(S5=oY`t71n=QiHm(f(8tgDneLpHzC57-ewxBET5-zrDw9)%VLmqE3Ok1=#x zTEm{33ahkwr^4B|x#YYQ3&x#Ti=hI*iZa;+{u|D_8iScYn2aJ}H`IBY@~m!kw3DXT z2F*Qm* 6Ml}>=T!`yalHH_Qfq^9S%pCSX^C!4^P&v zfAg*d*e~$Ag7;#*q0N#7yq(!r$TlRG3T-1hzB6 _N;$V-q z7F`wZJSKP*)VqjwlW*-%#(R+(`(R3kvmh>1&18U6d~&%NEd#HT|VoLghGv0HL#p zjju8N!mObPxk%|(8S@OipxG#B1>Z=twcv2_g1^tA?5)S;Q5~3))Hh|(HO vTou|KjUR-P74dsB#Bu?iWBF7+Q3}mlw>>l~c) WMUn4=H8G6AkKyoT8P-SIiOXtZecs3P$2F%E+uDF3sqXoHkRl|o36@i z7TbG8kSx{f?^B#&;{vaF`=wyO=A&d){`F?pm$01>4X3@X?0Q$3(WEW43TBf=mqN}5 z`}=PD*`cbw&nfIai*B?kg)zP$$B)YNnE4aK(6Sh}R>-;^9|;$$S#N65cOMyhcoW@$ zc2bj5-y4X|;fKd!Rm^DU`J<$FW;w;5Ojfd (}J9_3-r5;_e 7@X!dNh z1@!(UTlHdEQd!k){F{RXd?TJzYs&>4h7$!pM5stVv9$3AE&igprl@VGMz^LUcnwwg zqJyc`aAh#ak=V1LewcI}sJ^nXyJBov-MDWijXb^@YQ6sZ6bSkq`JTqzxtJR(OG=Ey zS7!sb5d+qSko8xj`E?0uL?M42yEeT3+IT{Stz%k_?v2dPP-!oK>^Am#>*yUl*w>~f zZN3pN?cN#K`0S3cn?_h5>UB`i%t%DTW(%mzibF#mnZj2T8YX@|*?VPQqZsTWueiIm z_hM+IFZlNLMX0Q%gIPVS5x9Uq$khK< oxsLTb~hQuCMfF|b-j_2R6dF128wW>%cHOkeR#=;su{gH zD3_}F^4Baym=1_GBb%J#T@c}ZSG)DfA)03Ai4I$|h52^#7ttnWKj;jxG6Xf&$cati z1T}U3&-dL1u@lFz$|ijU!r3unDso5A lPId`G&ob+VwLe^*VaRAyO}b9`z?=K+5d#!7QM;y22`fDy{Q zfUQ^XGpSCJr3O{qA~iE+(litCte#LskCQjRPM;&KEah-WgqgOCkh0EN#Q@m_ZiQ(# z%ckCsKto9*x>u^+oiYnbtbm_s79llfu*>m;O8O>Bg*b~W$TpOl{H!Q}D#EKi3*od% zo-pRnQ~Ketaxn8e)F2$Uw&|!LUVGST@NQGy`&>xmY6pu#M}nP>y#P5_(ts{KBXtJ3 zGr{sk9M_Nuc9MUw8G6+ZXKYh5Fkxgnf)9uhGW8nyj`$$LcI#Bcm)3Tb*B!kW?rDJp z?rJ@))uY)=aO-RATmAuIPT%Oi!tHbJZF{kv=Q0A{$hg`->BV;)v_5-{H__qDnAe*+ zes4Bw;T&yjKW{-uCNLx?)~e%m^c}oXeh4 z%Q4u#7Z@X$E?-%_8kY3MJRmnFpHQ%1*XCI`AZQyUU{&$AcF^h2OLD?SR9`2iny9%F zIXq9}XuE>IqGe3&026j`W%B0n@k|YK9vY3k$4y5f RJNstd0JHS&^PQ(> z5MRx6ZJ}wR`lvS7Iu??1fq|fLv*yZJW<%|%!g{)?51DddJ7n4 hz%@z9uFDybqS%!JI5>{?K6c<34V#4iiwZ5rr#AT2|0S6KeaiJb z%b^ M>N7`B7 zIl||XVedD^#p^ptnB{PszOl6x5LDxzDxEP#>rfyHB#Y^h&q8lL^`5 nK9O-)G^bsc4ApC*rV#=&llx$0eP}+-7cG)#__3_n(X42ge@(~nvzULZx zN(pkq$}^BX0RDBhd~ii+%T+iE69q!|1l`UORLC0$dko-UmlZM?%s1Dt?lzh?!As3x zpHD}kb^Z+|HY8IVmM;FGcZ(Rbp7z{x%)$D$b%%hC;W2wE*Qxj8wDIH)Le!xrhy7@V z1*w&;Mnb+?)!#bUw0B+AB}Xy*=lvYX-v_95L@g&V{K>Ly2GRwYe5`lWHXM7)(x|+! zWCF*WAG#Z+Uo=K>FElUOb6f>aBU Zf5Fpww1pudw oO5bR_@HrS2wdT?$ld;GdB$qnRCrtfRb-Soy^AGO^!Sz;qf07$ zW}yj!&t`g!iF&&b^l$%(joPPReX!0gu%I%q NTx07x03UETfXsmt+X;c>R zUa-{UEpUd(aL&hlN1+V*uQAr>Dq_g21bxn7NYX{ndS6_u|F jc5S}DN2C?y{*x(`LqDvObPiNd@a|Ag^hg^VnfCNsh}8FDjv=~ z{(8E{88fBbKJ54&L&juFsQ7W+FeDWxb}4sVfisp^)%x$-0DAPn?q9~nOIP1`9sR!u z-M4_TM8fmt)&L-LhiQ07BZ>1F0^oyUdcHF*`5+D8;9hTAP1$^9P3V~-WZT^v(_wk| zcF3*0_ e1o`S@4VrZ9;`eae)YqWa;&%kI5&Zj0l3Aho1v@K;mcD_u zn(0dp5q4#V|0668B${(3C|&cafsn)b(exRk&{Om(!wbz3gW=k`{Z5p!>}dP^k?CR6 zF;3awNLV4c<_Z;`res$1SdBZsI{B&ZSOQhLBmaD^gM$A-C~C*X;qJ7w=b5D#thJ5* zuD%-0db?4?9S6}^P2mkC{d zP^=fbYHE~(UmHPDH$Ojq8Vm&bP#^G*!Hf%9ym@O{5ORSuR1_VHrse<{gK;z%S o3zy?qSmL%)5Hzq+)hw*l})pOBXawh2Y9O# zhdNt0CfvpvS}`J4nqgYo=+Lg3O*-hSEOIqagB^$SXo)9{bs^o1IfMsF=Y?E~+g{%D zVa3ixlx4AFFO*R{i@olf`iPNA#fBpEz_g>^>pX9`}Eh$1k`U5X0v;{hkK)?T1p zoqv+~?$qVVSq{fru<87|i^rH0AXg#6%bARH-J`5s+Wb^s82mEUaxHy*_*~3DZahBP8R~_aWV-6U4qmi3|w^b>kZa z4I*f;X>t|<#s=EauNdC>!(VBXXt%hY6~Dc&Wtq!bb3FoC7SW!FHcE2%%;hH>Ih#9o zFupEA%Q!+LWq_e+JyPIxEuP6EaknFT%8Oz@uJ5zA+TsD+#p~TP*(792QZ3p0@qA4W z(Ifo&Q&K$O3BP8V#fG}+y))a0gpbb~6c_EW|D<=?3DXP=&OZk7<9!lvy#J*)R;I|E zvfj%t!)Xj)^U@Z;LPF4n*<6F9_k9WQxNB`}>$C4wPOGRQ!6PFnlQ_T|l#W4XK?uoY zGOW4mP%>JLYK%}|@B=`(F&e)@QQ={^2iyU#MANq|J`5mu&8|Z#&w_T&y1#w$1x>Z~ za=M+k1$CA(CY53pzB8*wXG+rWqgj*Bw7j;f7dlUBS^UNt(NDj54>l+rrFTrV1C|Ux ztI!Z6TFj>qN?gPeGMWcp#1RUhh&&t )^~>>B%Fa9oMHs#;r)dsce KgZ=#!IYpPh95|2 zM?G$d9>lL8`g1%@Dy<@9Qq1o4hd1MoEO^2_PkzsPlbfD_81bryOuPSIJn_A5mk~Lh zlT&i4u^F37K@_;y;8IjxiB_ADed!4#=-`jO{LYjsv~&_KvM{$3d#8JpyErOK_ad0o z{x?3jlfpCkF8? p175*zX*`&!2W7Zu9 zuawBBm4Q_5*a;6NaU%7^ZIJK*wWb(>08;`NEwq+;?V~xIc_>cL+n!H6!R5AoShS|x zeR8v)W4OJUV7$FU&jQ}1Le7{V0$vW+6n9}r;3rG^?OoX541g1zG#JAeD6tm+&5rXd z^V|z}H+@R`8y0QQ1}9DlBDFam;T>8>6M9XjSJSW#2N_x-!W82^8~mo(9qCtW1JOQe z$9#k=SBrw`Z{B<2J#TiqzljK}1NZ0K0jQ4;c|C3imOTipQbArF=~er$-_IzgF5##t z06|E47+I*u9w934a@~^+$b-4+1Obbu&ejLGkJUxm4roA}eoW9#h_tCf{Q-4@aSgdy zxwsr2B#j$?^zJp`Z-#B&ZpS@W*et)Ok!{74%jbAoZRqH-Bm8#ot?M3PmmAV24Nr|OE47~VDQ#j7tuOEW c1u8Wj*=x#rX{(^=<)TPn ehfx~KTQ&XtgR6OQ|E7KQy-ZR$32$Z2iW zaL-wT&4{HY m|2^oX7}4 wXd4AWosNp?EL|4tdv>{c0s zuDk6`Tzi|>8Tqx9g_q-MOEI^6uD6}8Q0hG1xbsq)(G0L^QX0 R{{edSgMv~l&s0TlEOxIi6WwFB(I{94(`4{6415Lv& zLOS@DBFlDF1o4X;cTeVQw$s{it5y|7|+kq;3%3_>UbO}wLZL*LMGgjNnS3Ax2lB~OA*fxhyv6m z;T2FiDVqJtg~0#i)Isaig*-3_f%_*_zYeabh4rc`8}P^BcCq5y*tXb10A;6=n|pIO zk;ERxp*ow9`W^Kmu5WGruZ0=S&uKb36EjF!v8IM&0cai;_H2zAU$$fz)}`=Wt|Cp? zz4Bjg=eBIKp$J`#(Rg!T1$ouUgX=Zw8TncD{8_k2QHT+Pa%Mvnq)Xvw38~iW8tF)% z+BQ!z?DBy#(| 14 z1l;4to*v6wqWI1e4ZC(I=!8YJc~=lN$}p=Z5)@|QB4R-6Va(d;(kc2S(d-Xbzx%Ma zxTC-M>79VN$0}v#Gg?*tSGtlxXHr%R#18g!Kwmx&?iJ={LD>HT@=1B_KR5|RgJ(pV z#?UVHstEb_JDGyZ1n*&2`y@(flGLI^)td@$1~`Z~8Kzn`9*c3GpzVS9q(pkkwgEU6 z-Y8hx3|sdp2gNZ6SeSsK+&1*;#E^>S!}WF*kZnM)GpW?sfcBTH$z{#-x?r127oG&S ze;B3;|Hi`P(4l|i4xB=+`qZ-jEXJ2&YgAypY|!7anjCG4y>#`kcm*@a4NRkps^Yz| z*w@S3-gRu7ZiYwMd{WYX4x-Fdug!4Rp?4IVo^zd&X48evrTUM`RL3VsWYD>+dAWAY zm%~S#E0 7c}g+p`w`^2Znsq`%8&}; r z`p@3}k)86VHr_S$emdUzo8BkcRqh;x<#q8yeb%}_68$8*|MaAS;nVS)z5i7!``4It zXvTl|@008R|M5h)e~xE`!{w|;m!J$exz+Lzk v&po@v(8pz;YggO@7lVc YP(|{!=|Pxf z8}=_ #T-|HkM^i0yWp z_m$*S1^%sp@h(0_)*@ai$_qo#HmTXSWA@mS+E^F4lc_YE;2Q3ooc)!Le{*%}Ptrvg zL$9Xk2f7)a-B8xm8)vi0-rfg-#)r?HH|9rTeKAACC0emntdZ1kF1$YOX0FYgNnaJ5 z%3kz`?IpQ~b5A9Cwf9;s!JH-k?g!Lsaxbq8A~1yWshCy$6pKnWH{5hs#?tEOMPGv4 zL^8<*Wx4)K7u4CMg)d^(U_Nxb4{2(DKkYGG?diF`L}7q!X|lphc!Jp|Nox~6SDKEq zx5scRQIQAVq}tS@#+uIbka4Q6WVgJb;85yNUihlAimX}rZ@7Io)WvT6>T;MTm@IKw zC(`1Vs%-9jRoQn4!$9pGlUCM;KR+#8k)FXP{;oaJG@PR}YD2aXoA21`TOEYkX8d$m zYke`qC#w=9 }#*F z;ytEZ&=OX)O*fIWD#*NgftP6iVBR4lnaw}hR ^d}X0@CVx)Y+f(jn$~fc%h(dPlE!Pd`b^ zUpKn@xy*AB6G`t$XIDTG`xdVA<1MqWB(|p99X9%xm+Y~sL|f5W8TVhUACm%^;#Erh zoI=bXs=)^K(}^(rYo(Q556xvip4dL#ZXJ35xiOwRJ7jmiqfBwjfwiQgjU7NK)s_`@ zQ=-8kQ0^mzM5=AtUKzfFEJy_+i1pWhwck1-XZER~9BG}HV>1SimFgXz5Lep2Cf2L0 z#)(M1HF$I-Vvsni-T#Y^6Ft`Y*yJm|2jCJ{#bujyoOxLVv<36>Tci2TekTIRIQhJ^ zIA4%?U+EE(|0i+w;xcbicMYo}^LrjPnzQA#7v6mX?J@AL-`mr=7*UiC$5~S!joj$U zy;q4n1(Je^ni2ALuuq?8>YIpCCuD~tqfT}RBn26N+9-s{HuDHD0vHurakiwzHa@91 zc%a0%cs_Y;P;MNzV)04)YUEYTV}#w$*UN>ozegrr$$;Jy$F!HR4_imk?6WltD#49~ zeL4y0($@&k68;Brc`7Cf0bOcdz3XBmgtOcwmWtuDPnH~`JK|C83qN3z?0%!g1xpsU zF V1&!H%-M>w+_8d2`*}G=HkEMXIm#HCH)N P0UYUeovpU6(k{{?<3L3PG#gMW5 zp1b*JbPe25_;x}yvKrC--rw@WtbVSJz9+lg)w->~EVs%$D?qun;s9#I@xtv#$!)1e z)_x>r#LyYXcs1uEO@y(tN`7i$HTqeH >4B*HmWtOk0hxwyL5QC{s5>D$I)$WohdCS z_O#CUpbug$KsPGfYg!CQ;n>`1-F>+cR2Y?NpMp28zpY$uENQTP`K9QQIVuIr Cw3*yP1l7(24Mu`_7_Z;tvo6oYnnzIM@(8x{FI!? kX4)e* wT__kcQ-#Y*rdo|u+! z*T+^f-YqYBqrv&K=^iS0z8SU)-$Gb# Le5R;j8UlGmM%%rsBNOsAJZI^qsH1{)F_a!xi z&S}s4Ai# 1h=;H(!(H)N>zzL%>}dt*!oNjIx-2mi*-@D1t^VmLZ# zi&jK*eYt&G240ol5j*2C(VoQm!^4K@5 W9<(3Ma@2r_HefI1=>Yn;MMVJa(S(PMqfu(ouPXd$xT!ZWWLSI zn(%sd`c3w+Y@S^A6++++64zPx-d1yyvoCivtWy}}NDb(+ esXK4W3=_b z+s04^Raf%HkwZ5xeDqV|1Len-;{=Sr &3Do!h+IjbXe+mt5(e*AAJ zQBRev$`5IOUc9YSmbBKMEDKo23|d*=21=grY34Y_3k${4dVYUF3?&OcwCn-x4}_5F zu16@B%l$9f2bM$m! x^0PA{hCuP!qal53rt z!J~NigpjxsvhMZ2Bhat+^$l542g^lvRc0e_Bi9+}1&!;WD}Y(5qCUG+PL7k39e;zn zL9DLzs*sg? {SiuM0A_UQn&=&XJ&S zH;BP>d_WwieA(e(MWFhOwe0*1zx)i91jvjc`q6(s8{fP_1@qrR9_b6Jz$r#<_pO0Z zl}=3_e{Z89f<9!7Pstb``o*F)=OL7(v4z8PV%!=kW$^vt_-BF$A8hdiZesVUOT zOyeE6A0oWK47OC6UG;SXl&DRdwIME4xe4KeAzh;p@31i}#}djUAYOH1h#v>7Rx9#B zODfIyMbpLX+;TE65B1Go$)1qmK@8?qKGd>bjL>hXNH}cfHX?4b;CH|D2eD8I(l z*P=olb)s`fH83#PhR=~Gy-a)tl6tGQ{$E9r{?E#v0i+p`v8(LxB8p--;>46>k@+!b zaUu$0e`3;{Z!h4)hSjU^S6!Gg=TCz3zoF!U#k@S*V+HB`@7+bb)BEFn^%59O_<`R> zp&aF&w7vX%wcTgkRi)GQUi!GwY4WDZQ3~Jp>f1iK^Kg~;-qO|>HE&JfT`8)H;Lhs> zT3VQwc)$3t^nSTJUfi_a?0f^=?2mfC O#I+$LR|sh z$9*{4(_y@Msyf=6WY`n%Zh()qZ$N)k-vhF2@pf_s@qQ@GzV$4}X-u%RW0_!XLTr zX?vH^5G}4WQooI<4R5-E9lid|bnKh5mwqN)j8g`j@`~v^CU?6&smB4GT8URf?<4kg ztoqI1KNK8y-tFrN0pn`vR)j@5Ca;w0>Kl|uFIESDMOW&i+H$KEBDWxh;gi(=c`jXq zv4ETLqScE =+Q zR`lCYC-o}b`Q}PN(oGZLj{NHHkxegFLVG?+&(I0#79w2B&a=bxJGdniH6qe8r>}?d zhxJJD4<=3cEAWp3nJE}I7iY^U+9R8ecg4HDl^+m}rX5?IR(w&O?tq9XTCX9`DJPxB z4+*YQmqY6@6N!h@J9R+ZM%}6R@5W;Y+Osmslx=HUZ^1-RS3IBRChu?Ht6Em~#Afmh z1q}#nZ+7_OXNPJm-{iwfT5U~x_}#!N=Hbi6G1Oh?DopD@E0)vyU_#gA>7H!*NXnhU zd-~#Y-@DV9rX-&xzh?m-&71SX`F_Kk)%1Mqn GnlXrJC%=$P=0-DFpZ@TKR~l@WdXU0-yf_wZPXn!;W3<^dLob;%80tePC! zpEd2$0Zj>)Vz0#!fDUhKUYLvSu{Vb=e?ITK@NK-*ICw&8brs4yO}d>tYr17Q3OW8b zR2%j#_)vAax+^G>#mA*QCwZ+gpr61SbyDAoUmAde|2WjRx%BYbYSH;YH~E!%;?CPf z%V%`B#dV_kvxIJH_?U~%B?xn2d&2%6q;m|_#KHH0UnV!UR>iX3rgg5_vFqxwu?&vY zcX;0)$(}Jcx&D-80=D ?heHg@BAGiAio`t)*Hi-G; zwue(T`~ X!&g5-9z4FE=iS^ zzHjYKVs!JuO}jgishiewqWzP}ZR}?9;m8p-tEbR%m5R0PWz(ttB8@vGXz_brRWL%Q z1smXl&T`fOS5x;*cIDxD>W^o~;^9qdK-nc+tmw$*X;ckV>SDi}Lkx1Ww$uM$?Y)Dd z=(>Gjk^+)Jf|5j(ARq#g!w@7WNkAlnl5@^!K(gd4X%GQH$vH=XArBxh )w@>jwRW#mYwzD)-4f&m0wndE2HY=&^H9t#TNYQWb@`aL z{)d3u&Tomyn#Tx#UqoC$3&$Lo4H;q8;{Ou!3)P0sL;a Tj%Rknd*j{ mIXS~^I1x#Ms_X`nR8^p>0TGB338(Ely7>y~m? zI*O!fQ}qz1d^UGHU@TyMXw_;EPJ+x TDyg`mP+F6VYoOZ@2P4@WY+iiYveI2-pMW4#3KnWME `&*H1Wo8G4+JcjXwjhT6ZelwJ$|AG8)_% zmlMT~UY*~mz|QZq$u$0*S8~9{Ek1R_T>Dq!x>s~*?Ya<)5A?bJ Zo$P_caKrC z-@AsIQ-LxR68X+OLVL8Z2WCcCvlqYvC(mjNr^7!AGFxs%SRG7#yjtotC74L6767A` za{iGxS>?=z?Uj*6z?P-X+C{)X$(v1Z*@XuRv5Q&yZ+8{2<~tIXW!w*3`MQkUcV+g4 zH3D8iW83N3vBIF$wQK(!@ML0Em_?V#&$`+IUvksieaEP?_V4Kw6JRbHsP#<^Rw9@+ z|L4fx`9f1%$M1F&{mwAq$Kq9Wzvw}FU`TM2vE;)=fH%fDA@iv13YH>bHR@ixI*IN& z*EQD(_P!~>6q>dmbV)TuHM#ZzC3$XiAbZLesMmpjIqM_Vdh7(E@dW_vo{)*q3x)_n zT>3RI8T0wWch*1$BaVdzTPA!igMEXY9nVHB4XrvoREZV2I&|vR#~NXm-(e!qNx|bD zFE^ fKvvm!Bfo2FNp<*Sg>uzhRUiAU!9x(ZQNy)b zd W*CCsR|i%>ccS>;>t34(Lrx5791;+f~n09Dfj#nz(W81XYm zjTXG7(x4Km)FPH~0HmVO0Bi?KN=Uzx%7ibjVhZ>UJ#kK3`%?Tg x}WL6&ADf)inTEg-hc@P<%k>wP1kibt>58guq-*7Xtp8USC~y1M-g@Q zQNfW 5JwHI1g6s5f~q(HAcbU}cOv-V%TPtRye4#YTdD2Jc~)10$MhdHy7?L}*~dfrV?! z-9hOA@&h1t&-+z--=Mz7Nt-hj9*O!xT@F=oI_0b%k8)7_Gq5(VchWBVq{`^76RFsl zHZ@LcVePqE`I!X8)*F*rktO?b>%6Qp8?|o-1payaRXlSYMga*~tIVzLC(8;K>Ximi zx1Yi5yGCtH0f~OG?S++RV}6f`E$$8a%XO8kT(n&zuSotq_JN0&)r5e>Gs|iGhdliC zy3&v;mAU7K=O3d!`wC%kzJIxWvexyy5C?}G`tJZXCG>wUfMwz)jbM(ZaeG4E7b8YV zxbcQ*&~$HK2HIxxRu%%m=vUd>+qBtHQWH&tfig3DKD}Zfy&v|k+Zq6MdwH-8gX9AP zHzW|hVH+OB f@S0OJugOFE}@M=*jf(xXw z(loLjln&Co?7|b7#8h6HU25HWr~gca9 8&l R^!4c`qQm!Y9v45!8;K5 qV^>du2pQzF zOCto4ZKPVDMWP(=>O|Jh7 pufLNl)DAO4Kd}C` z;mbb_f?{q*Y0FW-kJhu;Jz!pY=qP62rBvsm&moil$DqlV8)5ttGNr+xQXk#DTV-X2 z*#HPy-8}S75@clc5f&~$-`tPcmAYlWJl-nCq@#nFH@X|=q%lfHPwrR@jx4{>T8NcD zFVM((dD8Tf=P0?w)Cy=h4eLOux$qbn_w!wqFgvutx5zB7^ng+Dv0JX7Wpv`c$g$fp z`dKG?9l(D=Bx6th@2@dC`oTL$0>GIXYWvZcXeHaG(4nT8R?tK~uGb}e-{sLNQ#MQ1 zp3$u|sF=djp?EMBd9B`!O&)-=aR%|@Uk*fBwobSU-VB+66>g)j@Y+(8MCPxHy;kOb z{U#D(h|Fc+*qKX@baOb57~lEpp#DWruf=v>u|CAoEi}Mf7qdT4n?LLJEwT64c;#;M zh7+8vY@o5s=EeB$Gge(=|0a~tO}(<*_N+9GX$g nbZ`Hjxvc+vAx%|C z(p}ek&UQYG={x#BmtObRpcuaa;BI|pgTW57WYZw^ax*< F!tr2sQ|j0zr?ttnju_xs&bDKl2f4E~P}k84gv6~@_8TnK}f z8@du6fv!FfmRxnw?z^l#PV=b>)Ul?oL@xFG%})dJ IrmL%$ zl<*}(9?T7a5gXk6(V9BrB!0CAy0@wS^j>gf+zNk~`=gZ*r%z=XEZIicCM!W2{ti=V z`ro?wpszFfCijci6NhiQyuVdxJQkTTFqXMw;SgsQgPlrV#C@2#zE1!t@ZJV%d6&;D z$MWrE7V?o$r4}?T^jf3*M9az>%1WGQLReZg(A0WWWn-aIbvpurJvz4Q$U}S$IL^En zmHR0K+PioQJQs@u+t!`=oDEx!PI*CnMg}jM!*rS6BDVBkZfRjBVim_TP}ti-8NxiC z%)(RI?ZY$**Q^UBt^WM=^Fd_`jA7<)RNk5K!0*xh+l2N$M%@9L^261 Ymy!b~Ca-?AIXpDm z1Hv4sZf;9P==~ ah1NW|ZGVO#hWlPT35YT$rc)m@m z;g~_7)TMxiUw?U^qA14U_CGm6R0A$6XR-eL4mnFunJuYo2v$0NJUK`se{8#rA#^;%^Sw37|Sv6(p&-5?ld9k}>4A2wnRxV5#fwHpVLN@RW+K?=xGZJ6W zQ;;{bHf2{9oi P%gz(Tzp3jZCO@uiYX%vH zZ%qca2??=LIAypSi|UHUIqk1<1@uqmx#necdRr}R 94xp jk8q=R67lus-4FJIUW?1t?wz_prFnmUa(S>T<}xo%=_E1;(FLkCka zoY}O*QhaYI+yg^%|JJJ7MM7iTg!PAyH4j~j6RPf+({(yO3#K~>D(@r*yX^X>3o)Ed zej{^I-6Yv9+w|5Q7{{2Hw%OHdv{I~WH|sIWUA3{-BYYDM(#rPml6UM^hdui)51B~Z zNaE`x2n)L)iqaEg{?BJ4R^4v66)a9?3~(D1`|lDhx2*cgVLxqUG+zOa7bzEosxbx9 zw*38HD$e4i7p8LswOyuFhgGL;3(`*1(td%e=V#a6{bg3Dm_=jsH}}D>d9QIB^1-aq z2|1?$di%|{%K}qKBtf2}^m5bonJ?4A0gWqC(nVJ0HYaWH+ur>T#?uLC9u|d~)ceve z;8{auf#156Pu?+WHY*Ho Ga4&(w zo7~=0T(Hv3@59MKki#w`?8pZdOtXL3iQzrRj+aUE(v1V*y53 zeYCX1YVrHykDIi(Ag}yFse14eDULE!*S?TynIDb9uVw~*`kU7og-q^XZae7)%nSa& z@dVQ(lM$^Jjfp$~w*=)DqWEGTnLjN7+v)2hQcAxs&(Gt>dp=_F-D?Fqoaa{@U9fZU zb0y%w8mC5=oJKs(4L9?{ilZNYV@HnN^FCPt5BIICYp}G5yCM%>5tcgor{sIct(eu` zEzzz%rkL^-t(iS+DtvLQ8x((Bp {1Hz_bC*ITO*yJ-(F{i1w2&YC))=jI1r4ro z0HW^u{erNYHDT>t2!r4F>26bWSDOC51$`367J^*kS_!x5kPa=!Q7#Pti6W+%kxKNC z0+_s^<=AI_h!9wb${#t90sK| )qz6Ut3XN!P0?Im)aZNHi?-`3D--lj9kAugIstX-7u|!C{ z8@oeg3) i0ULH?Yu%vo}X~JC(rg~R}nJvoi&p)R(#(F z092Kn@|8IAWjtxUCzWv+DBDP}Xi?V(>8PZQD!KW1yXLnsKug$Wac9J27c&xcSbWCB z )G7J$>gZ< cWsZ~3wPcCX#e&I4+F2DY)+8MjW9IBoN24RWdI8zizQP7Z%x0m>s zGUDj;*I`wcMQ 1J6EDQx<)q@wC*X1>@|J}%YL;=iHcAaia3fvjq_?|&HYn(2 zR&*6#juo4$%Gln^w|4;n+7=jV9e^t%Z*H$wS8EEcfKKg^XtWREw9LgH@ `(^YhuH!mVH*4z`9IlmhiFW6_LUbG(!+;f>uoTbS4+hN;pNp@f z{x9F07fY8e=>`5B3U-q=E9k`W``xMDwsZ=cpT`{oBQmPCz=;kRT+b4?xsJ5p4l=NK zss`d$ud!ejq*--r1-(hzN$yxoyjnw_c&e@C>WJjKeNw<4joLF=qw9q-*hlu3s_&A3 z837XP-jT{t6~+XSoeGOVUm8C>(i<6FG1)#ZAI6|GB|8N Aluh3!4z9_DfKrHQUmX^utd%Zl?k1$3-q)uhRw!aCa*gt>DgEbGGJhTWEn&>z2 zhS4P-|6W@5^_c=)EK4k8$}ISOy&Ls{Q;O(k_CF4MI_U23J^lWj;{Fi%UrKSaW1J3N zQQkB8|J7)s0V3BHQjRL~HI~=egruHH8#N;ePa9Twiqo{;asP8cLlFoB=HS5LUBqfw zElZ{vl6ucfM>l`jS# ?;=FsjB4(1WX~#PZvb{#d>gsRnyfhw70gCkA%M1&= z^C arm6q z=x6?iTmwdxwhx_mpCfO~xxpSC9i1dZ2l@U)fYsQ8 82je32fJ|&IjZ@5b#Rz-(Ge)h5Kns}lZ^&|lP+{Ko{vbD z!Ko8}RG1Po1tBKpH^)-OaZN4f6==bFJAhlz+hBsnlP~(LIqp%Pi*`$=#}jVQg`& zRwsUj6EYwGA$;@<-Hjx$oBZj!^AKwzHSp~0>6mHfbLo9l)qji>NGEdvHs%Sp;=h?q zJ9v^={W*wntkdBZF>Tgx<<|fx7oW}^avbxIpTY(?v3H#-ucA~P*vfM*&K &$%v<%h)=>{4xs+q=i}XA~?m1@AC8xX{jrEM5 Yc>0sB1Q zx?v8h7Y@j~7D8|(riTF?PT8C|orXd4gV;jB{PUNC!A7&(LCUM+t>%f7`ie_xRWK-e z9@&%a+MBVT{&_mAr3fhsJDU_`fFNK>ewVKnWLIO=7-<0Kp!60*gQ~4vhpcqxMc02R z MbfY44qojAE z{O6&jJ(Q&BCmZ%FF)UOeM-zFV?l`WK^6c&Zak5UjF~K_FnlSN^1|LLMt0uz51nR*A z_Q?JTh8!}Wc@fhcUUVb1Ql{Wnq`=N7Pk60GdQjbG?}{;`bOUfkv t;`H^V| =pi=9YSI4~3SVqQ-;N?+UFfc=TNWsy3?W-N z wtP3 z=;*pHA_;SO< qFt92COxX~=U3VVDy`q*yq1$hh&)n;(iY=HPvAa@F uIoi4;LB`KNb4Z(cj)#*&=lyK}2{9OH<(P5AlX zs#9uKII{cb=W(}kuvcRC$doCky%%b+<-3|gC&59`nUBtsOiz;$ D1Z#^}FiRN(p_T>M8QU_t92qb9qm-mP-cEJ?zJcx=BrX$faRRP}R++WVYaIL) z6~cf&ZNNxHH^tnUx_mN!rEh($cY=t?*1T5h1|1yE_+#s{t}ATXs%^RrhHNx3$I)5I zdH5f5Vz7Jjg#kchj^@zPzRa1A>S&zcg>k7YEHwUyJ|bEm_%A0mg40^bP6E6rQ!Rhc zA+^jp>UNmgCAqPa_S1l0JL^34PMj7pwa>5-u_&y|7RCQt8UM#`F~7R5#AER%yi{*+ za9+^=+bB$*+|j`OAJh52H2eqMou~PbYxXKdg_k;}?;m)1x<_|+_GJBM)Q@7~ A&ZI zvl o9R;{i3RLImQxp_D(+-PV z$4Y)0PsJ`XEZ-}Pnu*g5PRVHAf5qWg_PROotrHLC_`o^I=Xg<>KGD6u`Z_*q4OdF* zcbFS7#pFX`o~q2+2$j?z(%XW{9;<>TW}sVQOJ#8I7bEVMg~AqLF(SOPx6S?CJzt#2 zLCgeRn%}SCrQ)u}*MHGc{EQU*fOb? eEw4Yr^e7%5(Wne24aUCjqY}*Q1Lkm6b$c0iC3s@f1qG# (GZ%ufJCKi;vPtX&y1N @8^e zXp@uuDex_?)K+tP(3;XYxt6`jgv)cI_n#-xxX~hL27PLSt}&@~`riP`QUe!ToDd}t zJ?fQ+=z3@ctH?X0(@bTflZbmh-rU&`Q2;N~maI?0gZTT->TBSi%QQ}-_l6y00GGQ% zY76#oiaJ{9kY$R6=RRSE(RThyAAIkJQeK2VqYABZcz-39qu7+>baqW_FQ=JxIt{&D z2`K|H$pm9kBDa^;<|30@@9)Wr-!^OzN0Q7I;fd*1l&Lb2EBx`Uw>LkaP$SvhhAFh; z!9)D2?iG;0SmGs%oapUI-oKo__ayUr6aL2Gl(u)@2UDPnRqX8z5xQ6^?MX{cPZ9Gy z(Cv #q*5YDOeOqyi_^H2~6FyM- zphbXW A@tW ztm9}R(%re*=~-y$fGJdbJRI&XuM7cw*OI0kaJHu0atMF3e^qWJ`9P`^eNFfIFG_~D zVdDoV$9@3r5cNtv-^i8ga^qPSGfrz1_ 8k5(rnA81(sLVtX>$MU((jzGJ^C3feWQ>s^ z{UZq(DX_ZiyF&4EJTzYU# S6S8>$KbeG?aH zJcrNnOHl>(VEF)|FD6xgA3!%Ub*J_4YDq=2rUZcY4#8in(kztQy4>kTz&ETDn{Vb` zGEz%ljHuQx9Ul<&%P#9JgKN~O@sY2#Lz3_}kw?2*?XSj-?=PJB0+Y@wy&-O^8iqee zWY`G?$il+o*5nm4iL;8i)4#NG_|3@$d2R#bk^3YDhfZ9*dT(9VLx*b4LQYAV13LsW zd3Moj#)3^(PmWf&lN^`Cnr1y68SXLl$Qr(s?+7V`GZ3U(okqmQhiU7qUW5*WImuXl zdmFlkal~AlacYo#j @Pk@st}9l{>0YkpxCs!c-Es($k#_-c^S)hx6EER zOR9Y7xgAiXlOL#wWS+Hf> }Q^Mw&KADN+m97p3+A=qf z9^4a}E075Z7ku*QeJkay8x|@`NDE1*BSFuQqj)tuI_=w(vZUo3zM?*nlpclFr7M)2 zT@lzN=%#SvYVRc_%BuCR-k+)rkHy9?<28XxoXqdm^D3#NYP(g}GW}^$>R{R4yhi9V zwc5pRCDdMOI+ZKSp_eI}E~bJc8ecr_9HH?d?X$i&C&4T`T&As(^%DPV9{#mjr1!MF z=8s5%ebP2=iN%hxrmH3&8Rcz-2(t?P6#8~)7^s>z^loQSw|o4MPNMZ4IIJ`8D|*OV z%EmuoGj5mQ^vNlfNI>a@n^P8_WGdO)v-pQNqGSO46_yRnLoNe%vUy7@@B_$@(*0q2 zvh6J5soTk7Y=(RPoJs~I>qyV%Loj%;V7FL?VSo~cw;;W_N(dXv_~y`%D7HoMba Zl`$0195xuYqFR1G3DN(Ye6ZwMs3gTkJAehPRqoCZFH-0F9$)lR`{%64 z<~>vCaKdo(kT$H*j6U)*tB%{LFdQt?{YHqLrMHl@-HekA_7>C2JXGODF&3-PGt8$Q z?@Vd6SruMa3pr=?@JR1IR)dU64OZ~eG^{ada|fz avrz_5wjVs;9-{sp#|<@72lb=O7h2--yhW#M_)X~j!(}fa)u Sf=Jd} zh-;4|Om@pl7DY;wY#n`SBHLx-ru+v2L|WgQWdTTW8_5(87^jvQCT#0|T&=bghW+vW zv7=f>%8!)ad#XN#skN2thMu0@(!&TQY6@Js4!CW!@tmIgc~&mm#rq^pG0Ydm1^6vN z(`eJJd(aR;uhjk;l(paegm`z2j_AB$wdIfE>$uSSY#DWK07}QLZ-hyga+R0l(fM=+ zce5NlB%HKaesd`kVyg{T{T7tn!g+jNQE_YdBd=iVUeN{@eEl5hC5gM;Pk$It5=(Qw zjYN+#hl!mu?J@+kWjRGNs__Shwt(|vf|1yloAU6=wYkYrIq6c+HLShHAZvs) z(4XMqM8Z0Tk-fJZ=wjO W67%e5C&75!FJJGX<_dx zaiZNQSq{87M7hH6G2Q4&%HtKJI6m=LI`D-i+^-A$5v!hq714Sa7+`tfr1`<26& zWX>Fo)(i8|H2#8TiW=!HzaOaJ%R%i{kXj_tU%dEO`9JJv$-NOzEi9d~nK{%uI$3*e zU )Pw7_~xwqoWE<>X!)$SV{wI}G)Wj`A`9(M zv%%pm*e{=UsZ-b!A06>?ii!m4q!EL{JYc$H5QngDWMzKS_N;-I=+ z@amTp_E6Y;s4!FGlzYl5ros@mX=Qy1ZyE3;O&OPZ|IQG5kx(rgmIzO}8C=5dG8eSb zz0Ok&tW0G8L{1+>|2f}*N?T(C`<*j0_^mEmufv%hZb+zFT#h01^368_4b}9X;(LhE z0^QB~q=mVVJ_LGMvOe2plp3xU{F%#SyK03^-rjqcYts4*e92na)pB9nbS}SrFDOOM zO9i1_Q1qSZ{x}}ajLiY!+wo&{)?<=VDbXlW&DbW1!J}a#gp-KZk@r=?GFVb5VW&ZQ z5OwUsyjS8A=ghK;tioR3CT0>86$!bjV#d{f6g3uym6ipZzr^t*seD?1f9~v={TM*A zb`WoAcUo;esvuH#E?qsWNL=^mJ4*%240!}^NHODNrRLy++T6=mEw&i%sFAx2BSn%$ zx)pw@{_%M+%@rbN_;<8BssyKdtM_%D3IiFlnETfJ^nKOC_>dvr)+0Hl3yEPtvIQ!u z?w7yHaiPokuQa4=@(#AbuCq9!e-0myi4^WdJ@5IWJSs3d+vF5 3lTR@mB}^ zid4ZLHx_)PrXMMU&K=YZy9!sAG`+WhF{KUGx2Wz&2*Le~`*_zIUs>gz*VA zwe#M?IU3=2e?7u|zATxN{(h5>uq~zd5`PirfAyuUv2YbFhD~2%dVi2+lIfpEe{$X` z(8%apyb(-~gKBsu#%|I1JQUJNQ`uyp6kvIt$+*0V&)nMK!i c`NXSoKurDbfuoHVvxUQr*DriK;H^7bSb64%+ g9uP?q7LB9Ap>Ai(UhA1CZ{Z!a}Jaus>K)p|$LbOQj0M(jB#|hcJvt?Z31!F^Q zH*aGetF~rqM *ZddU-KiwDI?ctaUi94VT$Nhh jVhdo7enU*--Mc>daZob LOpyx;gOrgUq=s1bi&& T(DxQ7Di;@yp)xCfT>eURkKwY1;VV_+ zOqS?^3<&3gYk_c7Z1a45k>g{bVja}exx9U~ (g=2tT@t;d_0o9uS#Jasp+ZH8<~#F!>XWw8C)ltJ2ZIW0qSLH*P- z7J%^A^zTmhJ@>t$O`b$OPmoPOEJsL?zqS=y3~T;Y(B|oNNH5eadHY~IS+ht$j^91D zi=Z>>-UG$~Dv0(A;qE(^HgagU#9_})V89|M(fZ7gi0gBO<&NyC45$AC(8K%TP&LZ( zs=D$;Jo i;%&$2;|i>rg;4n4 @%I!NWjnY$TZ#l9FdEJVw zErj~ftR4Vp%s_hmdmY?ia^?zCmwT6*H*D_yUE!a96bb9pj5*I{7S=^mvG+cdLcbRB zOI#9p`kVIqls0;Q@?teiA@qwD!FCsiOtVp9JNpB99F;L}$7+Cgfz{eQsDtJo=!YRk zH?Blo75tO22HQjm(zfL^G0Ik6S TxuymWD(eu{o085t;=Vf2!gso~A6!V$e(Za&o(dyE}; zXU`Ffs)~W+7rdP e+PE9@u9Lbb&&rkuK6 zScvI$SFzd!^*6T~fncfwN`<6Vg;Z?XHAo> l}?mK2E(<86*&sr*p8~ zhn2nSNnkfD;`?;_G1R3bOMT??6LEJ2D*9(QG{-Zo5AhB8+HKDrY|mI9umTiPpEVI9 zO*2kC`w|gd;+9;=Vs!79BTN<=vkRd Znb61!mvPmy-FV9z zQkBO)SHij8+*U %n5mN*zgG8p$wKG+tNCf02js~QU^q#I!qOTwC^&U zVyPeg-t$};3@BE}Y{=;tRVc@^_|$Pc|7tkGn?;!UPp_}kR@k|*xB*_%GS*@p37yyq z5?FX8a)-ObHk){!e;~eZ=_tXOL5dVmdD _l~ZGo)Wq?Sx%Kc1SLK%m&ggBBv)mY1I$%52QdeFR^Z$Nxls z3+uM$Yk8tY{(H0+N7FKcZGn0E;4SgTYh~#=y=x#F zuDi%yj?biu(0L6*8y#}W_30@ybTO8m6t|_|OCh_B$P3?Man_T{aJWN&N>ZhiUQ+25 z(b1AToz`l+CajA>{UZU@O+ g0^SObjKH%6rgo@eSK@ksXz1xWgTWLv1C=b(tfuWVQ|ZP@$Bs>fuq&< z#JUGptdSg|31V6WpIxmk!Kd)Z-K%KfC*m$1Kh+ofGKnu$3HT-#y}lNC(TVRY5z1To zimF6?%<7z- UB&Fu*pvcfma+9h;f^*TXC2;ImDg6 z9W}4?kifNW<>a40ed#7=-fXXvWy6>}ZwXuSXNOAny({tne-g?B>OHaYmx_P?R{4xV zA@xfo@>Qwq?&~8kvWo 9q|(~$0qwMaPn(4~B_gw%a>!Cs|3&8VO-0PwW* z1v6blUaNhI8weFExjfjjwDI_`0iaM93F0NiGt0|PeZrby{Ka9uoM7b| 6QUXK5?I_w%ZnOG~WQU!dcAA63!#!@WB~$Hg%?rk{Cd zSj{kw=3LX=0#Sg|u7j}qN9W`ap;YGZ uy$IFy72Ca#!z!XDy<_X)r zzzpBoCY5tpGLc0Ce>*)^$8aAlnjTUrruCpNer^=>9%~hcb!?x?>R5yQ*njY8GAxtK zB>%o@DtP-tKOK?eG&@4|5ZI0(JS`pdgPev=UgqEJ4*cSki|mh3Mw7+V7*noK? ldH(;8SK%9Gnl{IlOW%j`>mbrrCEl5eXzoGv-%GT^b#FL%QyTKki0hm|?sSr_8gR z-k?tcJTxr+Amh(TC3>S373xNn6W)^0*)kM&gvYMBq5blVUm>-P@Z;=~4Ok00SYEol zw|UNLOtL|F_^G)GV7atNFW^^~w(X_9C~^miW2kQ;_UGEvHR89A)&ZSjCyb1P2p$(z zAwXyCPNSWT-=k`WJcUg>qf{=U8>jhuGQFIem$(SVok(Y3iK{xX72*u-uI#-|Yf8Nj z4rph3h+C7*!Xha-8?5yGO7&PsbiyI~VaBMJ6p`yygXM6-_HHzfLMiZ3b`en)X}tD2 zMKVI&zhxj1)+Nfw?Tj-OLiB5g;A;Abqar@JGBcy!m}5tj1odb#_HF#iQHl#|jr^gM zoN+>2wy{W}<;gBu>F1go>l&nBC_*d7Xrnun)3N};cdEmj9@riV}2kr;2 z0Y|Hz%M|l$xgfdbgPkKf&{#c2U=z*ws#`9Abx8Flb@&_O!@sTVp7xW3XW$7-I<>db z?jT|+iXZQ_ZO)P4b2BybX6?GdJ+gk#0i`3LY$GWR#LZScYg87a)Vo9EG7BT|vE#sV zFilhV$mJ>PiqaGOi>M_(53rr`4OPih)^@HX7>kLsq9RZ*zxBO09aA0pdrw5Ur|uVm zIk`MuD`M7CP}F#IuFNu5Cz)|b+PF;#uf+OA`>VZVX8grMoF#J2_xMS-U|}xzaKqIs z8egqsN<%ssqT*oX?{Zcw8hn?VH&}avizU&_sZM9fTW7%EmZ$HVs(hz{=%Sj^AJb7X z^RH=aT`}OS=t(Sp<*3huc1F%i_&z1wuPB}jqm+#7(}q1fN=R#=Ck?H%*vg5R%(|>O zU7|&!(DaIqPRhL{pQRH?=xvG4=z2@#mCbg%BI=+9&W49&6-ovkPcC%#8lKYnt21(p zNQ^8iJ2&G(ypCR8zR{0)95y7Z@BEM{yQkm^FGq4EsByaNxxOZ#BUq5*W_gb3xS3GN z-QNBO6~hna2(joK5 A&mlz4qH=e`Uz ztaD_LI3V>{ZzpegB34Tz+}-`|GFv7XYgZqd8=5q6{_U+)l0)7Rd2b_G^nB5#=?@*K zZ?;kFE@ADg2YE<^@*Dc*b-M0Nu1Fl7n9Qe&g)Ma|&a;tnW;yG|O4THWHy!hjo==T3 z#-{X50amcDKQ$1Pm;2;FS >ZFtI|Us%s||M{>nv4tu>J@H71 zf%0_QLJ}r2o_f-s!{RCF3osY&>LhRp+vInonD54w?ML{{=(4?*$?enj=kKEWeeKI@ z!Ian@Oi`zHW$^_6&QO4DifzQ{=+mDz*MA;UN&66eyUS?4BWE(BzlH;D|L$Q&8 pPYH^mQIIc2enDReUKUe2&hugp4@iG1l2#hsG{o+%TEy9vm={Ed3lG zIzEnZ7CM8Y&Ilo{;)*O~@#OLV3ux%*Y&rPOAM7kk7XGN%WAa`(xeaj+eVRE=gnMM( zz}&)3&> DA@oe*DeYba$*vCDb}KindpHsDgz6mLi|5KTrZeXSiocws@<(joEKchM(2 zuxJeAQ<3=`ZDp%%Q@uTRi@US5W7#DRt4EEd{~r0{4HD}1CVOL(6>kfZkpW=Og!W== z^9|lOB-toza^VlRQ1*uXo=%Uyfa(n4Hbs_C=LYVQQ3-1Gpu}JQ;yUdBXevKU*AjXi zL1ukmVNuuk-o+LFLx{oWunaw}LK#*tR<^=(Q~!1JrxP{hpYtHsbfVay@XgQg&Fuba zM;7BEBk?a5NI{YF4P9~|sk(?YZ|m2y_7NP;9Qq=vi2ERgiP_WF*LG5G5Y7a(@B;R0 zUsUEq9hr*1Fszfp==XzKvtN*&rd`H*EzjH@rx A zgiPD~j2 f6x?5N)pvFKD#E=yJ$(AaMbwaK zA V(njbl3XUW@1v$16gor#LNz}VqB@;p9@4rY#xW}VY(fiR>Quf zezgX?2BF*ABs$JULn%_t3a8V&pCA*Nk?L!b4j>8Fu~}d(EBOXfyxHu{x`+K-f}4-Y zGo`4)ASUb&+BUy3GAI ~vwok7Mj;s)`lzCyx#N8uE$=SWQ<+*<&GAs$ICRT=T<*HRsjZ6%8rg&kvcp z2am7M!aLdsV)J||p7Ou0TbWI~OI16M6*{#(8QlxetV< {@OQ)IQl09}NwUZAqaUA} z*ir1S0xZaS2D?w5H+a3#Q;^4{DK-p|+Z!_!Y2%$sgdu&=BwU8iqiP!FU_K#}-Roaf z% y0;9)%J_t3%v2PQ@L-SVQs`&@t8b3Smd5^rc% zdNWwg9V|h*xl7ZUpDC{Fv~-y2E0|Jz!w2xVpW*&l`Kh0`Ze6JBXGv`e3cNDdUEQxQ zESLVts9e4=2mNhJ>H{nwp1qIo(3yDg=+%OiReVQ%s%5ZCiraQ$MHL$k8@TqpsS3AD zr%S`ZVDih#zILMI=|*W4zqUuB^*Z%@>UbRw*6&>Ag_vKVMdLVC)@q3xO?coEJJE{p z(;LBwWRfVy1}FARv~JAgFnQY{+6pyZH2yJie9)X;J?UXs(b%_p2z$9v3(@ 9SOQS7s$nt87RFO;@gl+3*g36nlD zsccJ{cgH?;3wH5OR!7!fpPtrH&XYuDPX!})JQQ$DL^?iKi<-N}j;$HOf5;{o-F riGN+XSmK1>arsE98`~ z^vz_Ft8NACx>@BcV$7O{w9CycmV9?uju%6^g@|`DLtG- (XC0?QZfK94iG_W(uZcjA%0{?5b>TUT3%2LHjUOKhpau{^cSkk>FMN zBD*dLzwKle3i+h%rWW7x=5Zp`>)N1evfzgFwSl8o#r0`or4=petn^J*aia2!oiC}- zZC74h87*S@cDcVBejnX0J8?AtO`Ya5zH3_$>-ej1G5(W6s7yv*^li;6 ?xZaa=(rhi_DJGa4CisWr_mtRmR#OI5;n4 zLXynQ$b31iLl^k{4q2@=xP#EsE;NYl%#$gBKT03=lsP|JwF8S?`?Oi{V(~o&L^pr9 ze{`%Pk+M5%#(&S^g1WGKCV5YUQn*_= W%#=l~?0&dwjk8 zS-4BjT>pyuqM>-)Ii-u8rD5~cjU%|xq*O)SR)5|n8}+7?X^sA8PQ0s^+IPP3NqyJq zm|fkm*TzrX{Yv3*UaCTN%IM;!HmC5F4Y2Qq?)>c9)x+JmD6DO_cJO_l<0TTGh<`e4 zKJ#2zj3Vd?&b+kt9Xq|(_v}C Vo-j&SnJoO zto@5y1F~Oq28F$adv$l5YUC1SX7)gYIi yHzvDceQLFlJ3pvqp zv0=7Cv0gmA8256={+ULv>US9>G5`NVwVxLi`f{|gx!Ve-Wn@q)G dw~I4|#0Z5E z$&7;(7%w#J^WQ(QR=hAGDVV~B9HdZbbR2Z5>Mj&QBjcc6#x4k#Fk4C>ZC4bNLWtV! zPKpX9nUkSbmaDNmjI=>0g-wL;LPO{*3=aqW+!rGqFHOUtkk1G^7zedpS`gyx;tH{4 zFewlt7z&0mBq2=D5Mwk9ZHzQU8yi4S2!t6NVG2i~43TCSBoc!}L6#4wQ!J%c%ngw!LzKBY5`i&7 zV~kMy{#5@3`b)VBgG>vETrNi<3=t-VD6~7m9D_8$7$cU;{{$_UW8q68{v_tJ^`&e2 z%l!WmMkamr6~ )?SFaZo^D3NVR3nfx1@Gv?R3;#Cx zx99ietfjG#W)l82lz*K1*IW6I48D2)N7{dB3N8XjE~pFC1;_v(7)%$a3y=XoFqke- z7a#+GU@%>vE H=f{5DcaZ)CI@@ zAQ(&+s0)w*KromtP!}KrfM76Ppe{fL0Ks6oKwW?g0D{4Efw}-000e{S0(Aj000;)t z1?mE101ynO3)BV303aAl7pM!60YEUAE>IUB1AySaP1ovg+dL?A>D~^mbQ4G37Cucz zW`% *R(41Ul$&Jv@cLw(Ww~pAvLK3D*G1VKc|5$a#;EO z`%Q0N)a0H?*l;AOG(D(oyDa>A(0=zvL3R{^bD-pLAKA3~Ekfc{UX@#6lB#g*Tx38* zwIp_A#!}K`qZlE=M96XU<1Kvrx+7lcJf_dbXOA?5Qn58d1Gn>1;-16nQF}9uOIPa| z8CUe~R?yF>$h};3(!ckYed|{D$oDG0Z^}8XdGF4xb%)0srpjeZ54XxYQHyz7WGzoR zgsaSkuS=>}8-wBr-cD&uZoNa=r0aY@d|*Q&;;b^Ed7o1Kh#)GVHc?Yt7l(+3NK`(R zYD7OiC*P|gh?ZwnWOSyyap}(|&D@){SAE*w5y5XPBI@0=GpLY>tKq9=l$cfNj+t4# z|H)fxPh?;8#H!Lg%`blb@nGay0nIm9V?wQN<L>YHz3@i) z#tVfdBuixRQAim=ceGAuC3;j)shUI+yfz?=4YkL#{BYv8l@1S7rW~EO1U%W8m1*m` zR?o9ceUKVCJUQ4C6N5m1B(%W!4X?$ccW@$C%f)$N%5{sQ+%uXBLEq)pXm8i^u3UBb z+*wyM0_RFwY~>5|?|1iAWX-}l{nV6d74vE}lZPFDcS;-&(XTa0!Ix=N-uW~seY3BQ z{DF}Arc1D^YXT}I#&Nq#N)0dgI2SSxnx{NaFKl==Vb;XHSo*;uAug)tc$M>=EUoe@ z-7sWiF>(9RNIkg^5%+?}&2~Mruy}boLaDXIEXCr%kqOa_ELRDi!!dkh98VbObUpNz z&Y1J_qWepRf-O~E63X`duzeL pE_B+o9Vd{^Sk5iFN8}R(KgRtvqoFs8y*$?L*g>9Ma z&RKb}+otp^1=)SA@scgsBL9%7u*YzPzK^OcM!%nu8KdT-DEYU{C?8MG+Oppz>a|H# qTc%a) *Wi%=!9#F|;O_1rxCM6z?(TANcMAj$?(XjH?k*R(xO0)^dG`Olwfkv5 z?ACTo_e{^}(>B%B)#o=S{HMGGG6Eg~1Ox=Kl;jVkk2}#vB>W8fk#^*-BYoUHIenK> z`TP;QKO2XA dxGOSI$Lf%=B*#%|zJI8b*TSTA%!^BZ(>)IYs## zM<@ Zq#xOyyD8i#vY(6QO86H{gxdW}4RC(WT$)bf zDLDfN?q#oY)sMy1)_B} i}n4g93`5dd0^QT6*xVzwt=Xpn79Zf*?w3 zp_a4<@g=O%U#Dw~gxarP!+)E0;e<}kqjrgZe>ZwSI#hpF_XQFNFsNXPDsf1$RjLet zPE6Io?K48UG)u3u#4!?Xnr^D`Nk&q0RTzCgTo$6s!GqFet=&2XqgdA58cTG|$W$Y? zxF~ lz9ug`xpW~k`v|Z=Xtv&xF8_a;zv>>cvqtE>iUo5zr`+(^iy>V&tQIiX9$;P ze*FPB)z>H>PGyolhi@*iV=*SKu+1Ckp+5gk!eV>2ynwYE0;aJ0 Zhxk?WHDLbyT;4mOcJz4dgmDgI?Y!_7Ukk zxk^DHiPjIP@_q@7g43B{(--h@T?C?z4pf~N!Ek$b4!J}WRqef`%f6*}9qev_|CVQu zzXhVu3d47Xyjb_T@lsIgGYIyb?~TAo|L#J`HmKdobQCN;--ER0gQ@aX_d$Mg(|A!Y zwPFxVgXW{)Tij2vo!_D*t_LB`+YS9M6VfyY#{jnZXMXYzeT@pnd_p}QUe~G$Jt%#N zCFgwJ8bb ;3u$f+WpOgKl@n|;6*X}d2Mg*l=wTqu*&!PZBOe8j6m}pE#ha; zH;kO&`)|IGhY3e5xz2y%KGvV4jn#MY=grZ#kLOPx@&78& (^*^I-xv4} zC@sa;&Z|+xkXnhe_U~VPEIlbZIkDV+Gy$8wS(;BAHa=Lo^QV{T*)50DhrIKrPPjN( zm&W9I0$*eJ)Z4kNubzufCtU{k`0-ktDh`kz7~D3e+jCqv11HX)xdUZ{HvevQr8dD` z7S(Y*vsc+H4006RH*yxsSz%vVsCiuwW)k!z+blamYR|7uRlBHx5%1kEklTuk_zyPW zGd)o8$`#V}+Ps3It%*g)bTy^{^%a2Ck&_wD(>Sa%!3yiHd@*29_OC99tx&SnlBGhs z4pIjBWoaws^rjO%uYS&$ E-W3!Rnt=-{VM?*DB6>R;?c2*t|VaW!moH8)enPQ$1zI=i7QgEJL8bt}xBs j@VF&VScs#jcjWm^?pV=rEF278&GnIK3J_d zr#EkM)ohdNGY>U#{(M>`x$#_mw_6En3?ioXk9Oo*=9l^9B=f3STcjiOoev&STf^(e z8f~7CRU8Y+KMI-z;*-lv9j?F(N>e0S|h_Y%kx28qR{Q*W!~JSD@r>=Wad|VNnG{ zkxX^tDW+%5_@8bTEOw4ykvwK|4A|)2zlu2Q(jw+rQmWMC<=|rBlUdB85)6v-?NOt8 z*OctwxREccxZe*eZjZhsyPg%^ALzkGTekBVvHp>TO`P~uiz|CIIf@c$fhv21(C&r* z<|D4btr5$uF))sVPt;Rn%j{?F#V9OC78qM>hwX3fI5sS4^w|Q5G?3JoIS6TSN3>?p z)7u??Ue_Pfg!vXcLH4tz%WqDoE=Hd-LCI {VJy1_vWs2NnN^59{>+@Qw7obAZW z_t*Y{2Ij^q&h}ABk_I0=#AMFkr+iAkl{8kB1>& 43? zQoUxbJX-N5?p_rf+5SJDlhS|dX?&tl;_MhfM{ecS)M$BwcjfKtFdW9E43sBCoPJsn z3Kjtx )k&CCUl zcvTO!j?9e3XIjc1gqj@|{OlpIjjrC*T@q6Bt^v2iaPA#>!V8emHG%cVeeNi^UlJY4 zhmo8_*;Spmb&jAAOO*kmEl8;1-P8_R<91j-tUtRJ%N1SbWY+gtk9#A6ewO>|k?>Vx z+UKPwn5X(__tDsez)mr(=}g^**I2KU|5&`Qn^*@YNjM7k47$91PV>vR!mux_tnq_- ze)EHta#+81`0+?D6d)Sf{hjB%@9)`=<6EeKMHn`G+(Eu18O1R(pOyyzxz|Y9h?^~t z3AUb>K{gsPERF7L*mXG2n^YeUI6zwdPDT~DXl&kqqlKaMBT1>nCTsYY28;P6Q5wHw zjv#zMu#sk1a d6960>Lml0W|)D1ATc?ER^=(jzQ7A1TG!pC*ztU%@}fA zHr~vu*-Vajij`{Aedf`$D2X|=JKS1{;LqICtiAKizfO=AA`ehxF~{+#$7m$u`ZN`g zTAFww)cAP%vyj!;VLV~Ro3;54vkM!_zR_!n q=jBPA@tNh3Y zV^qbj2WoH=@M5hA VxtVb@b9fi}86hl++cnFk>XgrNym~Vy1BTYkk z;Dmw*kXn{-QC|JlrSFMMa+hPm48;MN?GBo4 7G_bwN6gh42HZ$)Gz81i z17VE_pcmr&wYc_2t*;$)6H!6EtXW{Qp6NJ4;xRnO?kBfTS}ustuiTOd6@GNLQlyn% zJfk30jZIsWTZLgSpm342Hw>0%m5L}z=-E7I7=A+dnJ40*rfQ`eKq3Ab_(DvVD~K7M z`9k{17o^Zo)I Oi3DB~|aa#w;ygl2*>w59MEcgWxH$J1i?05*sjFKP=qA?1T3yZ(#6* zWkI=?7z2mTVW47GboP3W1Qq*!hNk;L2uXqLxs(}zS!y0>q*H!V$x%SVeZs+jX3%v} z34Y?L _@o=4gFGWX; z|J2g#M;EL^aKe)_BzOdZzPb5w&L$K~OpQ`OH$Ibiiu+tT`n}Zf&vT!#08)W**zUxS zjJBzClXkB|9q8Vl$$Lp);+PV~zIlsL=pw8OjRtqEN>wul>R<_<(PF|786O0xqm`n- zBRCoEtL@yw-7vCA$M~L-G=eEl0)@69{8E`{?RZ?SF>^+x;k|TM?~{;0oAhK}hSc2! z9;%2KD#Ci^PtVv#S#ti~jdJOcF@`C>M6s$RT16>B2uYh$R@o~ruI>aH1*e6s*^$r9 z@y5#m0_0_a#ts0U5N}a;Q@HOA7fR*?6>%a%G>-HY)|jm#v%=I2X|nhgVX=~q-~xBn zf-Dfy-&PX+*HlA$7L!@%t!NHf6J^TledQcH0ilO{X|M=7Su$Hq0cjyG15t70j}ndp zLN`b5K9Z~WTPBYnULa3Ec_xFcamV#?pP`gMF2?f+IKPKfh?Dj{n4iXU4Xr=o{hxlr8?{CGvT>ZJwAZ znp73xcQHJwJ^OXwbuiVD(Y&r726glfJ9z;tyS)_M3u -bbRv@Amt&45!u+H*0;6R77VIiFfqN0r=K`kX}7o zG{#~{ I{>U#o7FOHNTJqiq}myKy{BSc4&@@=y-fGZ zL6l-m83neQ rBowqHDzsi6VvrZDx%i299xhfapBIfZYaADXT9)-B^#$S{Q+2s#S`R~ z>R%biX3o05vK&PC>&-*A#ygjHozWYu878p(*^iQTj9mVf*v)arTjkyUNjvGnN$pp* zc1>BS+BXKWS?0#t<6}L8n%Xhtm$&D-q`S!QG2>lUItzwx6_7CT^2Us PHk(TFo{G8f!i>S7NLfcoq z{T-|tQKF9tFh!@VlcJ~-YhSf0<;b9H*ZNP3X{;7 1bfi z-a5}sNQ!DYjtRB7U 1C3-0;aZ}}=9{!^$CvopUk(YHzH;iCC+ zHB89jWsgOe>6 G=MdUXjU1hrR9c2#qznTA35S?>8cGyxWs&8G+t^UuXy!M6 zE>o(}jKu2B>cQE~)jXXw6hx`;HwjGjwz``|5n{Ake|h)1@Akz=({lN8xjt_+^V)LE zJEY;O7lj~h?c287DiDgvIqi+0ml;MPh>_xNytvQ|sFBkT #ca t0II%4@Or&{}77=c+#k$k^r@tjV zd&0 1Li(k8SEp?<` zJbc?rQ&tKlqvah8Ye4q3JF>`>h4CRd5XOA`GOqUvj81pyW&=VkPxol5i}lq@h`5rH zza>XXzdyYOHZQB#FyFOis#gQM7XDhqlVyv5K(jj9DoteE0dhvER}a0_d7XQu+|h(! za%;d(LFa*XexY-_2Js1um=!qmN5Yt%6)?LdW-|L=)@(1;ZDZRO&DKuW7BC$xo;4HkGjOU ze3ms*8HSCDtW=)aK+ursS6jb%53GEJtT;kLP?o;Xbzqm@k-mTwxMwdz_1mSWnZ_)S zv|Pdk`Su=iUYGI5TwdA+g@-rG*xzFTL8R+^dZ-0@EY1Vdn4-QDNV-)x&zmgH %4B=SF1GxmGSe(V3am83K`w z%)&R+e!F3ssb_S!PO5PQ)s>C5soJ}mM>g$%O0O&b?FvUoCGEvIv8eh-K~Z(2*9vB5 zRS!2`oYvCzGVa#hBfHqV4=r%tjOu~zzr(RY(pBBuApS><_MgVI4S}WC=WjJmsvhb! zP6Lx$P<$VCr4LT(E+d*?_OQ8E3K#(|#qjNnyuz&k_>&V&IQZ;cs%+bCP(Ulj0mIq` z$$CfaVv*Gt!c6s3yp0qAV~%|#79jQpmxTlGdNhr_*=-m*>WW7A`O|Yf{@m}{R-gpz z`W(QiRSmn8Cb`IJ0`my(`HPKF{Ta cRk)@$x z1N&OVM;4}0I#x`Kq4fIG1KrmR_$Le;-rR|%Uq_@m2I9>sNV+Vy0OO;HDXg$^ZI;`m z(P@S6f)#SCJkOd)1G>Ag_wq##H5vY5kCW!ww_*C1Ts LEDdOH-#)51FxE1$;lAdYyfrKt)PERK$y8+Q*ytQi6)%T> zY1RGKiFI^oA?wKu&z&nJ3rgrDY1?EhGM2_E8l|DglT2--&-GfXse4*GSD@F#-cKS1 z;vx>}MGn#7t;nKV!*fSif7V3zaCT@Ly-pfLsQ+2X<$$~nIhH1zahZB+%oR}jjxZ|f zdABbltcHW>U1*i(CaO6mu#d>C?zq)7-zcNug?1ED96lA^ph-XIk~{5idOgA`sRu{Q zN2&W~+CwX!OR=Ko44pgjyAQr*o0#HNkar!IfDTAQOnlZ4gAJp_+1Rt%Z5z-s19> zo(Th1A0*t)=6D%`Ua(g8x(-xh1`&6f-Cl?iB;E+~4G`YWZR@4lQ>x|m@VrP3JtMC< z&|Xs^*K}t^vCK;CP1$CA?XEXN M3UG~8HcG5-iz1?E8zQVl zE4&b^yd|&oy(-J!nq%i+OLkG%Q5?9Sd6l+!NCNyWN+{dc9A-QI9ib5#7UDb5jAgc= zSwog3t^LQ2PP>}QaT)HB&N42o#Y}}Umn!T$wO_3q5Pr_ES UP0(}S)(K%T`k*R0*b5(>G@gS}?D zqR@V#8#BZ-3T$5blSfxz)hVD4ORrg;O|B73$UQ(X4vX #ihioqYQ_< zB>kWs3_fDR;S#T+>NkfyAk^%0s=yD7Af;)fWOdWR@-uO(O(MC3xp4YFj5e>zN^T$Tu=7N3oF$?V{#Mzs5(Q zN5&J4*2 KWynz+pj7qpc}A~ z;4A%b7VA?k@%%3Dh$6B5ou^RUJWMOjHN^NpvaBBK56KYal|?4`3)|V0A#tF_B86S4 z(m|vYCbRNv5RG-804NC9j?XZhY}!Z}w8a(bq`G{l `= zQmcUC?MEY4<1WL2k 8&RP`mVk3VAO|A#uRRg~H zs|IoLjBg_FL2LfTf_#5R9jj7?=vV_QKB(DM`O-~tj3v*HBa6PP+ccB>1~Ix}m+2ez zp^H4VXsnBG=lO-AuRGUzg4ToaT$Wvq&0E&M Z>8>QM|$ z-hQ07O9X&`2z^gK(e(L|qwA0MAEgi|quhmMy)=5UHKQApJ kyX1OG;Fp4&IX; zX50m$p^qz^dVlyZYa2ez9elJUSJ;&n`5mvxk~T|8tc7tMW+LyKo5j{blmnKCOGoP^ zw5p!>PK9AzL07j~;9f{W6<8$u@y~ghVIFdsiCAhG`4wgM25?qq*MXi&wdjN_Q{4QD z2^=#9U!gq_Cj50^z~pQ6cL(p#0W$p*Yc0CEC3<%3lo__1S%V)HJ+8jGj)olRWw^j$ zadyTs;*wfBH6vVi#8tMv?CPcfG%Xda*k0*+FPdyv$pR6wIb^(?!7i8U-;`n!WpN5$ z`PA!d{hb5IE84fKBgOlifai;0eZF|2_T{0G zl&>*}@+Ct%N?V8$MTO2mOcd#cv#={sy;jp4L_Lka5Em~hzDpn24`v%7{y2IS_l5Jy zOZRA-%bw7ERNNjqt+9&;c<;C;iF?3`#%vEht-&$J8h#e~g?Q-tGaat_jcKyOD@h@> zN6WO0^WcJNv)YR{tFPrj-n72j)CsV9ySkCLqI0)8G&BvN(QUbAr46o6T*$LoBxP6y zXs!52MNYyC_4vegqDs^&S507vYI*dkX$7vdt3IseaaIu4)Wy=f#v8c`Hgd1x_n}cO z(+-?wmDYKnW8q~LdX>)Q!qIVPm|N$3*ad?B2;?d@H0>m&1W=F5Mah0x26o) -*l$Biq?9HQg0Gk=M55$I;cpdiN&rEiQRdCe`%2y~)j>fOB zK QkD*2Eqn8q;iNBfs=Z z@<6nAp9t0!{M(h@-t^72)9I!TMh1HB35 clA#gHGemmG5X+)u5fXzk-Dli1vD5Dngx@`{7*iz@=vHp^#F&B7pePalKR-@0ozs z+D4w;#$LKd7ndV&l&O%5m#`YOkF2v0Fe>Dp=@c-{C+I&$ob7y^vAMk+)LIjmCMR!- zt#drX0&i?TZ-%&j*Ja_37n-4YTqeEI%j;cBqq?mW-aVzlUNKw5%d|7?zBHR-Fa|FP zsRbguOTFCmBL{og`Mv28ZAa9R9;;+)?^tmg%gS0R2e}{Ifj`aEh=B>+j@UQIzS n`k-4jcmor(g-GRYp+z}N=bW5q__CSLNQy+WJ7$s zfG8qj=dp`Mr-962 |yKW*%!HHFandaC0aq2R{ref-!=a+ zl##1;JR&y3vmaP}zPV*ALxm^YrD4a4*jiAB(O7>Q+IKn+Ch^m_>L-UY37r3=1al%? zb|SVc+N4_P7C@`o9t;15Auz{uUPm*H 9&l{}Dw17W@mVu WkQCpWz*>A&=) JzK0gBv93D_EcFOxLbyI%JO8$u=&FBw4(_X&>QRqRDWY?EDvG{6SM5 z`+!9SIOev2;nr0`>p5xHFqPfLK}<{g7cC+LiW^K*BjFX0uutXFVtic$sF1iEBiyP) ze08%wV9{zDoBBR%{5chg^hV9oc{tHr|J-D{c(9F)GH}G0c?F3wy$9o>9Ay9JzB_2y z>RXlhx8Am2)KUJb epX` t^F zz{wF-mG)Ebqp&Yz+Y25@;(s>$6DV>Jkz5pC@n_@sPPW7f!%Hg;R*xLpBHfJgww$8u zi;uJ- m$nE8@c-J@Vi9QKP=Qg6}zjJp-$}Kn-7w-a?pU9E}sr!uy=ve#K zzsP+cXzbSRfD@H$N5;NDG-l}xtebQgH2eTU%Q+fvbE-3De729{Y!9`A>o2-(aW0A( zR>-Ch HeLp$S2(buyoO3kP