diff --git a/classes/courseformat/overview.php b/classes/courseformat/overview.php new file mode 100644 index 00000000..68a31dcb --- /dev/null +++ b/classes/courseformat/overview.php @@ -0,0 +1,172 @@ +. + +namespace mod_questionnaire\courseformat; + +use cm_info; +use core\output\pix_icon; +use mod_questionnaire\manager; +use core\activity_dates; +use core\output\action_link; +use core_calendar\output\humandate; +use core\output\local\properties\button; +use core\output\local\properties\text_align; +use core_courseformat\local\overview\overviewitem; + +/** + * Questionnaire overview integration. + * + * @package mod_questionnaire + * @copyright 2025 Luca Bösch + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class overview extends \core_courseformat\activityoverviewbase { + /** + * @var manager the questionnaire manager. + */ + private manager $manager; + + /** + * Constructor. + * + * @param cm_info $cm the course module instance. + * @param \core\output\renderer_helper $rendererhelper the renderer helper. + */ + public function __construct( + cm_info $cm, + /** @var \core\output\renderer_helper $rendererhelper the renderer helper */ + protected readonly \core\output\renderer_helper $rendererhelper, + ) { + parent::__construct($cm); + $this->manager = manager::create_from_coursemodule($cm); + } + + #[\Override] + public function get_due_date_overview(): ?overviewitem { + global $USER; + + $dates = activity_dates::get_dates_for_module($this->cm, $USER->id); + $closedate = null; + foreach ($dates as $date) { + if ($date['dataid'] === 'timeclose') { + $closedate = $date['timestamp']; + break; + } + } + if (empty($closedate)) { + return new overviewitem( + name: get_string('duedate', 'questionnaire'), + value: null, + content: '-', + ); + } + + $content = humandate::create_from_timestamp($closedate); + + return new overviewitem( + name: get_string('duedate', 'questionnaire'), + value: $closedate, + content: $content, + ); + } + + #[\Override] + public function get_actions_overview(): ?overviewitem { + if (!has_capability('mod/questionnaire:viewsingleresponse', $this->context)) { + return null; + } + + $currentanswerscount = $this->manager->count_all_users_answered(); + + $content = new action_link( + url: new \moodle_url('/mod/questionnaire/report.php', ['instance' => $this->cm->instance]), + text: get_string('view', 'core'), + attributes: ['class' => button::SECONDARY_OUTLINE->classes()], + ); + + return new overviewitem( + name: get_string('actions'), + value: get_string('viewallxresponses', 'questionnaire', $currentanswerscount), + content: $content, + textalign: text_align::CENTER, + ); + } + + #[\Override] + public function get_extra_overview_items(): array { + return [ + 'studentwhoresponded' => $this->get_extra_students_who_responded_overview(), + 'responded' => $this->get_extra_status_for_user(), + ]; + } + + /** + * Get the response status overview item. + * + * @return overviewitem|null An overview item or null for teachers. + */ + private function get_extra_status_for_user(): ?overviewitem { + if (has_capability('mod/questionnaire:viewsingleresponse', $this->cm->context)) { + return null; + } + + $status = $this->manager->has_answered(); + $statustext = get_string('notanswered', 'questionnaire'); + if ($status) { + $statustext = get_string('answered', 'questionnaire'); + } + $submittedstatuscontent = "-"; + if ($status) { + $submittedstatuscontent = new pix_icon( + pix: 'i/checkedcircle', + alt: $statustext, + component: 'core', + attributes: ['class' => 'text-success'], + ); + } + return new overviewitem( + name: get_string('responded', 'questionnaire'), + value: $status, + content: $submittedstatuscontent, + textalign: text_align::CENTER, + ); + } + + /** + * Get the count of student who responded. + * + * @return overviewitem|null An overview item or null if for students. + */ + private function get_extra_students_who_responded_overview(): ?overviewitem { + if (!has_capability('mod/questionnaire:viewsingleresponse', $this->cm->context)) { + return null; + } + + if (is_callable([$this, 'get_groups_for_filtering'])) { + $groupids = array_keys($this->get_groups_for_filtering()); + } else { + $groupids = []; + } + $studentswhoresponded = $this->manager->count_all_users_answered($groupids); + + return new overviewitem( + name: get_string('studentwhoresponded', 'questionnaire'), + value: $studentswhoresponded, + content: $studentswhoresponded, + textalign: text_align::END, + ); + } +} diff --git a/classes/dates.php b/classes/dates.php new file mode 100644 index 00000000..1f966cfb --- /dev/null +++ b/classes/dates.php @@ -0,0 +1,68 @@ +. + +/** + * Contains the class for fetching the important dates in mod_questionnaire for a given module instance and a user. + * + * @package mod_questionnaire + * @copyright Luca Bösch + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +declare(strict_types=1); + +namespace mod_questionnaire; + +use core\activity_dates; + +/** + * Class for fetching the important dates in mod_questionnaire for a given module instance and a user. + * + * @copyright Luca Bösch + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class dates extends activity_dates { + /** + * Returns a list of important dates in mod_questionnaire + * + * @return array + */ + protected function get_dates(): array { + $timeopen = $this->cm->customdata['timeopen'] ?? null; + $timeclose = $this->cm->customdata['timeclose'] ?? null; + $now = time(); + $dates = []; + if ($timeopen) { + $openlabelid = $timeopen > $now ? 'activitydate:opens' : 'activitydate:opened'; + $dates[] = [ + 'dataid' => 'timeopen', + 'label' => get_string($openlabelid, 'course'), + 'timestamp' => (int) $timeopen, + ]; + } + + if ($timeclose) { + $closelabelid = $timeclose > $now ? 'activitydate:closes' : 'activitydate:closed'; + $dates[] = [ + 'dataid' => 'timeclose', + 'label' => get_string($closelabelid, 'course'), + 'timestamp' => (int) $timeclose, + ]; + } + + return $dates; + } +} diff --git a/classes/manager.php b/classes/manager.php new file mode 100644 index 00000000..c5468be2 --- /dev/null +++ b/classes/manager.php @@ -0,0 +1,179 @@ +. + +namespace mod_questionnaire; + +use cm_info; +use context_module; +use stdClass; + +/** + * Class manager for questionnaire activity + * + * @package mod_questionnaire + * @copyright 2025 Luca Bösch + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class manager { + /** Module name. */ + public const MODULE = 'questionnaire'; + + /** @var context_module the current context. */ + private $context; + + /** @var stdClass $course record. */ + private $course; + + /** @var \moodle_database the database instance. */ + private \moodle_database $db; + + /** + * Class constructor. + * + * @param cm_info $cm course module info object + * @param stdClass $instance activity instance object. + */ + public function __construct( + /** @var cm_info $cm the given course module info */ + private cm_info $cm, + /** @var stdClass $instance activity instance object */ + private stdClass $instance + ) { + $this->context = context_module::instance($cm->id); + $this->db = \core\di::get(\moodle_database::class); + $this->course = $cm->get_course(); + } + + /** + * Create a manager instance from an instance record. + * + * @param stdClass $instance an activity record + * @return manager + */ + public static function create_from_instance(stdClass $instance): self { + $cm = get_coursemodule_from_instance(self::MODULE, $instance->id); + // Ensure that $this->cm is a cm_info object. + $cm = cm_info::create($cm); + return new self($cm, $instance); + } + + /** + * Create a manager instance from a course_modules record. + * + * @param stdClass|cm_info $cm an activity record + * @return manager + */ + public static function create_from_coursemodule(stdClass|cm_info $cm): self { + // Ensure that $this->cm is a cm_info object. + $cm = cm_info::create($cm); + $db = \core\di::get(\moodle_database::class); + $instance = $db->get_record(self::MODULE, ['id' => $cm->instance], '*', MUST_EXIST); + return new self($cm, $instance); + } + + /** + * Return the current context. + * + * @return context_module + */ + public function get_context(): context_module { + return $this->context; + } + + /** + * Return the current instance. + * + * @return stdClass the instance record + */ + public function get_instance(): stdClass { + return $this->instance; + } + + /** + * Return the current cm_info. + * + * @return cm_info the course module + */ + public function get_coursemodule(): cm_info { + return $this->cm; + } + + /** + * Return the current count of users who have answered this questionnaire module, that the current user can see. + * + * @param int[] $groupids the group identifiers to filter by, empty array means no filtering + * @param int|null $optionid the option ID to filter by, or null to count all answers + * @return int the number of answers that the user can see + */ + public function count_all_users_answered( + array $groupids = [], + ?int $optionid = null, + ): int { + if (!has_capability('mod/questionnaire:viewsingleresponse', $this->context)) { + return 0; + } + + $tableprefix = empty($groupids) ? '' : 'qr.'; + $select = $tableprefix . 'questionnaireid = :questionnaireid'; + $params = [ + 'questionnaireid' => $this->instance->id, + ]; + if ($optionid) { + $select .= ' AND ' . $tableprefix . 'optionid = :optionid '; + $params['optionid'] = $optionid; + } + + if (empty($groupids)) { + // No groups filtering, count all users answered. + return $this->db->count_records_select('questionnaire_response', $select, $params, 'COUNT(DISTINCT userid)'); + } + + // Groups filtering is applied. + [$gsql, $gparams] = $this->db->get_in_or_equal($groupids, SQL_PARAMS_NAMED); + $query = "SELECT COUNT(DISTINCT qr.userid) + FROM {questionnaire_response} qr, {groups_members} gm + WHERE $select + AND (gm.groupid $gsql OR gm.groupid = 0) + AND qr.userid = gm.userid"; + return $this->db->count_records_sql($query, $params + $gparams); + } + + /** + * Check if the current user has answered the questionnaire. + * + * Note: this will count all answers, regardless of grouping. + * + * @return bool true if the user has answered, false otherwise + */ + public function has_answered(): bool { + global $USER; + $conditions = ['questionnaireid' => $this->instance->id, 'userid' => $USER->id]; + return $this->db->record_exists('questionnaire_response', $conditions); + } + + /** + * Get the options for this questionnaire activity. + * + * @return array of questionnaire options + */ + public function get_options(): array { + return $this->db->get_records( + 'questionnaire_options', + ['questionnaireid' => $this->instance->id], + 'id ASC', + ); + } +} diff --git a/index.php b/index.php index 9c5b421d..e34563d4 100644 --- a/index.php +++ b/index.php @@ -22,204 +22,11 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -require_once("../../config.php"); -require_once($CFG->dirroot . '/mod/questionnaire/locallib.php'); - -$id = required_param('id', PARAM_INT); -$PAGE->set_url('/mod/questionnaire/index.php', ['id' => $id]); -if (! $course = $DB->get_record('course', ['id' => $id])) { - throw new \moodle_exception('Filter has not been set.', 'mod_questionnaire'); -} -$coursecontext = context_course::instance($id); -require_login($course->id); -$PAGE->set_pagelayout('incourse'); - -$event = \mod_questionnaire\event\course_module_instance_list_viewed::create( - ['context' => context_course::instance($course->id)] -); -$event->trigger(); - -// Print the header. -$strquestionnaires = get_string("modulenameplural", "questionnaire"); -$PAGE->navbar->add($strquestionnaires); -$PAGE->set_title("$course->shortname: $strquestionnaires"); -$PAGE->set_heading(format_string($course->fullname)); -echo $OUTPUT->header(); - -// Get all the appropriate data. -if (!$questionnaires = get_all_instances_in_course("questionnaire", $course)) { - notice(get_string('thereareno', 'moodle', $strquestionnaires), "../../course/view.php?id=$course->id"); - die; -} - -// Check if we need the closing date header. -$showclosingheader = false; -foreach ($questionnaires as $questionnaire) { - if ($questionnaire->closedate != 0) { - $showclosingheader = true; - } - if ($showclosingheader) { - break; - } -} - -// Configure table for displaying the list of instances. -$headings = [get_string('name')]; -$align = ['left']; - -if ($showclosingheader) { - array_push($headings, get_string('questionnairecloses', 'questionnaire')); - array_push($align, 'left'); -} +// phpcs:disable moodle.Files.MoodleInternal.MoodleInternalGlobalState +// phpcs:disable moodle.Files.RequireLogin.Missing -array_unshift($headings, get_string('sectionname', 'format_' . $course->format)); -array_unshift($align, 'left'); - -$showing = ''; - -// Current user role == admin or teacher. -if (has_capability('mod/questionnaire:viewsingleresponse', $coursecontext)) { - array_push($headings, get_string('responses', 'questionnaire')); - array_push($align, 'center'); - $showing = 'stats'; - array_push($headings, get_string('realm', 'questionnaire')); - array_push($align, 'left'); - // Current user role == student. -} else if (has_capability('mod/questionnaire:submit', $coursecontext)) { - array_push($headings, get_string('status')); - array_push($align, 'left'); - $showing = 'responses'; -} - -$table = new html_table(); -$table->head = $headings; -$table->align = $align; - -// Populate the table with the list of instances. -$currentsection = ''; -foreach ($questionnaires as $questionnaire) { - $cmid = $questionnaire->coursemodule; - $data = []; - $realm = $DB->get_field('questionnaire_survey', 'realm', ['id' => $questionnaire->sid]); - // Template surveys should NOT be displayed as an activity to students. - if (!($realm == 'template' && !has_capability('mod/questionnaire:manage', context_module::instance($cmid)))) { - // Section number if necessary. - $strsection = ''; - if ($questionnaire->section != $currentsection) { - $strsection = get_section_name($course, $questionnaire->section); - $currentsection = $questionnaire->section; - } - $data[] = $strsection; - // Show normal if the mod is visible. - $class = ''; - if (!$questionnaire->visible) { - $class = ' class="dimmed"'; - } - $data[] = "$questionnaire->name"; - - // Close date. - if ($questionnaire->closedate) { - $data[] = userdate($questionnaire->closedate); - } else if ($showclosingheader) { - $data[] = ''; - } - - if ($showing == 'responses') { - $status = ''; - if ($responses = questionnaire_get_user_responses($questionnaire->id, $USER->id, $complete = false)) { - foreach ($responses as $response) { - if ($response->complete == 'y') { - $status .= get_string('submitted', 'questionnaire') . ' ' . userdate($response->submitted) . '
'; - } else { - $status .= get_string('attemptstillinprogress', 'questionnaire') . ' ' . - userdate($response->submitted) . '
'; - } - } - } - $data[] = $status; - } else if ($showing == 'stats') { - $data[] = $DB->count_records('questionnaire_response', ['questionnaireid' => $questionnaire->id, 'complete' => 'y']); - if ($survey = $DB->get_record('questionnaire_survey', ['id' => $questionnaire->sid])) { - // For a public questionnaire, look for the original public questionnaire that it is based on. - if ($survey->realm == 'public') { - $strpreview = get_string('preview_questionnaire', 'questionnaire'); - if ($survey->courseid != $course->id) { - $publicoriginal = ''; - $originalcourse = $DB->get_record('course', ['id' => $survey->courseid]); - $originalcoursecontext = context_course::instance($survey->courseid); - $originalquestionnaire = $DB->get_record( - 'questionnaire', - ['sid' => $survey->id, 'course' => $survey->courseid] - ); - $cm = get_coursemodule_from_instance("questionnaire", $originalquestionnaire->id, $survey->courseid); - $context = context_course::instance($survey->courseid, MUST_EXIST); - $canvieworiginal = has_capability('mod/questionnaire:preview', $context, $USER->id, true); - // If current user can view questionnaires in original course, - // provide a link to the original public questionnaire. - if ($canvieworiginal) { - $publicoriginal = '
' . get_string('publicoriginal', 'questionnaire') . ' ' . - '' . $originalquestionnaire->name . ' [' . - $originalcourse->fullname . ']'; - } else { - // If current user is not enrolled as teacher in original course, - // only display the original public questionnaire's name and course name. - $publicoriginal = '
' . get_string('publicoriginal', 'questionnaire') . ' ' . - $originalquestionnaire->name . ' [' . $originalcourse->fullname . ']'; - } - $data[] = get_string($realm, 'questionnaire') . ' ' . $publicoriginal; - } else { - // Original public questionnaire was created in current course. - // Find which courses it is used in. - $publiccopy = ''; - $select = 'course != ' . $course->id . ' AND sid = ' . $questionnaire->sid; - if ( - $copies = $DB->get_records_select( - 'questionnaire', - $select, - null, - $sort = 'course ASC', - $fields = 'id, course, name' - ) - ) { - foreach ($copies as $copy) { - $copycourse = $DB->get_record('course', ['id' => $copy->course]); - $select = 'course = ' . $copycourse->id . ' AND sid = ' . $questionnaire->sid; - $copyquestionnaire = $DB->get_record( - 'questionnaire', - ['id' => $copy->id, 'sid' => $survey->id, 'course' => $copycourse->id] - ); - $cm = get_coursemodule_from_instance("questionnaire", $copyquestionnaire->id, $copycourse->id); - $context = context_course::instance($copycourse->id, MUST_EXIST); - $canviewcopy = has_capability('mod/questionnaire:view', $context, $USER->id, true); - if ($canviewcopy) { - $publiccopy .= '
' . get_string('publiccopy', 'questionnaire') . ' : ' . - '' . - $copyquestionnaire->name . ' [' . $copycourse->fullname . ']'; - } else { - // If current user does not have "view" capability in copy course, - // only display the copied public questionnaire's name and course name. - $publiccopy .= '
' . get_string('publiccopy', 'questionnaire') . ' : ' . - $copyquestionnaire->name . ' [' . $copycourse->fullname . ']'; - } - } - } - $data[] = get_string($realm, 'questionnaire') . ' ' . $publiccopy; - } - } else { - $data[] = get_string($realm, 'questionnaire'); - } - } else { - // If a questionnaire is a copy of a public questionnaire which has been deleted. - $data[] = get_string('removenotinuse', 'questionnaire'); - } - } - } - $table->data[] = $data; -} // End of loop over questionnaire instances. +require_once("../../config.php"); -echo html_writer::table($table); +$courseid = required_param('id', PARAM_INT); -// Finish the page. -echo $OUTPUT->footer(); +\core_courseformat\activityoverviewbase::redirect_to_overview_page($courseid, 'questionnaire'); diff --git a/lang/en/questionnaire.php b/lang/en/questionnaire.php index 5c79e58e..22c2ae7e 100644 --- a/lang/en/questionnaire.php +++ b/lang/en/questionnaire.php @@ -48,6 +48,7 @@ $string['andaveragevalues'] = 'and average values'; $string['anonymous'] = 'Anonymous'; $string['answer'] = 'Answer'; +$string['answered'] = 'Answered'; $string['answergiven'] = 'This answer given'; $string['answernotgiven'] = 'This answer not given'; $string['answerquestions'] = 'Answer the questions...'; @@ -168,6 +169,7 @@ $string['dropdown_help'] = 'There is no real advantage to using the Dropdown Box over using the Radio Buttons except perhaps for longish lists of options, to save screen space.'; $string['dropdown_link'] = 'mod/questionnaire/questions#Dropdown_Box'; +$string['duedate'] = 'Due date'; $string['edit'] = 'Edit'; $string['editingfeedback'] = 'Editing feedback settings'; $string['editingquestionnaire'] = 'Editing Questionnaire Settings'; @@ -365,6 +367,7 @@ $string['noresponses'] = 'No responses'; $string['normal'] = 'Normal'; $string['not_started'] = 'not started'; +$string['notanswered'] = 'Not answered'; $string['notanumber'] = '{$a} is not an accepted number format.'; $string['notapplicable'] = 'N/A'; $string['notapplicablecolumn'] = 'N/A column'; @@ -569,6 +572,7 @@ $string['requiredparameter'] = 'A required parameter was missing.'; $string['reset'] = 'Reset'; $string['respeligiblerepl'] = '(replaced by role overrides)'; +$string['responded'] = 'Responded'; $string['respondent'] = 'Respondent'; $string['respondenteligibleall'] = 'all'; $string['respondenteligiblestudents'] = 'students only'; @@ -644,6 +648,7 @@ // Prior to release 3.6.0, you could specify an input date format in the above string. Now, the format must be as below. // This string is used now in case sites modified the above string. $string['strictdateformatting'] = 'Enter the date using the date picker below.'; +$string['studentwhoresponded'] = 'Students who responded'; $string['subject'] = 'Subject'; $string['submissionnotificationhtmlanon'] = 'There is a new submission to the "{$a->name}" questionnaire.'; $string['submissionnotificationhtmluser'] = '{$a->username} has a new submission to the "{$a->name}" questionnaire in the course "{$a->coursename}".'; @@ -710,6 +715,7 @@ If the setting is **Group Mode**: *Separate groups*, then users who do not have the *moodle/site:accessallgroups* capability (usually students, or non-editing teachers, etc.) will only be able to view the responses of the group(s) they belong to.'; $string['viewallresponses_link'] = 'Viewing_Questionnaire_responses#Group_filtering'; +$string['viewallxresponses'] = 'View all {$a} responses'; $string['viewbyresponse'] = 'List of responses'; $string['viewindividualresponse'] = 'Individual responses'; $string['viewindividualresponse_help'] = 'Click on the respondents\' names in the list below to view their individual responses.'; diff --git a/lib.php b/lib.php index 5d0203a2..6b6de8cf 100644 --- a/lib.php +++ b/lib.php @@ -264,26 +264,42 @@ function questionnaire_get_coursemodule_info($coursemodule) { $questionnaire = $DB->get_record( 'questionnaire', ['id' => $coursemodule->instance], - 'id, name, intro, introformat, completionsubmit' + 'id, + name, + intro, + introformat, + opendate, + closedate, + completionsubmit', ); + if (!$questionnaire) { return null; } - $info = new cached_cm_info(); - $info->customdata = (object)[]; + $result = new cached_cm_info(); + $result->name = $questionnaire->name; if ($coursemodule->showdescription) { // Convert intro to html. Do not filter cached version, filters run at display time. // Based on the function quiz_get_coursemodule_info() in the quiz module. - $info->content = format_module_intro('questionnaire', $questionnaire, $coursemodule->id, false); + $result->content = format_module_intro('questionnaire', $questionnaire, $coursemodule->id, false); } // Populate the custom completion rules as key => value pairs, but only if the completion mode is 'automatic'. if ($coursemodule->completion == COMPLETION_TRACKING_AUTOMATIC) { - $info->customdata->customcompletionrules['completionsubmit'] = $questionnaire->completionsubmit; + $result->customdata['customcompletionrules']['completionsubmit'] = $questionnaire->completionsubmit; + } + + // Populate some other values that can be used in calendar or on dashboard. + if ($questionnaire->opendate) { + $result->customdata['timeopen'] = $questionnaire->opendate; } - return $info; + if ($questionnaire->closedate) { + $result->customdata['timeclose'] = $questionnaire->closedate; + } + + return $result; } /** diff --git a/questionnaire.class.php b/questionnaire.class.php index a731c02f..c71e70f5 100644 --- a/questionnaire.class.php +++ b/questionnaire.class.php @@ -63,7 +63,7 @@ class questionnaire { /** * The constructor. * @param stdClass $course - * @param stdClass $cm + * @param cm_info $cm * @param int $id * @param null|stdClass $questionnaire * @param bool $addquestions diff --git a/tests/behat/overview_report.feature b/tests/behat/overview_report.feature new file mode 100644 index 00000000..a393a0b6 --- /dev/null +++ b/tests/behat/overview_report.feature @@ -0,0 +1,174 @@ +@mod @mod_questionnaire +Feature: Testing overview integration in mod_questionnaire + In order to analyze the questionnaires filled out by students + As a user + I need to be able to see the questionnaire overview + + Background: + Given the following "users" exist: + | username | firstname | lastname | + | student1 | Student | 1 | + | student2 | Student | 2 | + | student3 | Student | 3 | + | teacher1 | Teacher | T | + And the following "courses" exist: + | fullname | shortname | enablecompletion | + | Course 1 | C1 | 1 | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + | teacher1 | C1 | editingteacher | + And the following "activities" exist: + | activity | name | description | course | idnumber | completion | + | questionnaire | Questionnaire 1 | Questionnaire Description 1 | C1 | questionnaire1 | 1 | + | questionnaire | Questionnaire 2 | Questionnaire Description 2 | C1 | questionnaire2 | 0 | + | questionnaire | Questionnaire 3 | Questionnaire Description 3 | C1 | questionnaire3 | 0 | + | questionnaire | Questionnaire 4 | Questionnaire Description 4 | C1 | questionnaire4 | 0 | + And I log in as "teacher1" + And I am on "Course 1" course homepage + And I follow "Questionnaire 1" + And I navigate to "Questions" in current page administration + And I add a "Rate (scale 1..5)" question and I fill the form with: + | Question Name | Q1 | + | Yes | y | + | Nb of scale items | 3 | + | Type of rate scale | Normal | + | Question Text | What did you think of these movies? | + | Possible answers | Star Wars,Casablanca,Airplane | + | Named degrees | 1=I did not like,2=Ehhh,3=I liked | + And I navigate to "Settings" in current page administration + And I set the following fields to these values: + | closedate[enabled] | 1 | + | closedate[day] | 1 | + | closedate[month] | January | + | closedate[year] | 2040 | + | closedate[hour] | 08 | + | closedate[minute] | 00 | + And I press "Save and return to course" + And I follow "Questionnaire 2" + And I navigate to "Questions" in current page administration + And I add a "Rate (scale 1..5)" question and I fill the form with: + | Question Name | Q1 | + | Yes | y | + | Nb of scale items | 3 | + | Type of rate scale | Normal | + | Question Text | What did you think of these movies? | + | Possible answers | Star Wars,Casablanca,Airplane | + | Named degrees | 1=I did not like,2=Ehhh,3=I liked | + And I log out + + Scenario: The questionnaire overview report should generate log events + Given I am on the "Course 1" "course > activities > questionnaire" page logged in as "teacher1" + When I am on the "Course 1" "course" page logged in as "teacher1" + And I navigate to "Reports" in current page administration + And I click on "Logs" "link" + And I click on "Get these logs" "button" + Then I should see "Course activities overview page viewed" + And I should see "viewed the instance list for the module 'questionnaire'" + + @javascript + Scenario: Students can see relevant columns and content in the questionnaire overview for Moodle ≤ 5.0 + + Given the site is running Moodle version 5.0 or lower + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Questionnaire 1" + And I navigate to "Answer the questions..." in current page administration + And I should see "Questionnaire 1" + And I should see "What did you think of these movies?" + And I click on "Row 2, Star Wars: Column 5, I liked." "radio" + And I click on "Row 3, Casablanca: Column 5, I liked." "radio" + And I click on "Row 4, Airplane: Column 5, I liked." "radio" + And I press "Submit questionnaire" + And I am on the "Course 1" "course > activities > questionnaire" page + Then the following should exist in the "Table listing all Questionnaire activities" table: + | Name | Due date | Completion status | Responded | + | Questionnaire 1 | 1 January 2040 | Mark as done | | + | Questionnaire 2 | - | - | - | + | Questionnaire 3 | - | - | - | + | Questionnaire 4 | - | - | - | + And I should not see "Actions" in the "questionnaire_overview_collapsible" "region" + # Check Responded column. + And "Answered" "icon" should exist in the "Questionnaire 1" "table_row" + And "Answered" "icon" should not exist in the "Questionnaire 2" "table_row" + + @javascript + Scenario: Students can see relevant columns and content in the questionnaire overview for Moodle ≥ 5.1 + Given the site is running Moodle version 5.1 or higher + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Questionnaire 1" + And I navigate to "Answer the questions..." in current page administration + And I should see "Questionnaire 1" + And I should see "What did you think of these movies?" + And I click on "Row 2, Star Wars: Column 5, I liked." "radio" + And I click on "Row 3, Casablanca: Column 5, I liked." "radio" + And I click on "Row 4, Airplane: Column 5, I liked." "radio" + And I press "Submit questionnaire" + And I am on the "Course 1" "course > activities > questionnaire" page + Then the following should exist in the "Table listing all Questionnaire activities" table: + | Name | Due date | Status | Responded | + | Questionnaire 1 | 1 January 2040 | Mark as done | | + | Questionnaire 2 | - | - | - | + | Questionnaire 3 | - | - | - | + | Questionnaire 4 | - | - | - | + And I should not see "Actions" in the "questionnaire_overview_collapsible" "region" + # Check Responded column. + And "Answered" "icon" should exist in the "Questionnaire 1" "table_row" + And "Answered" "icon" should not exist in the "Questionnaire 2" "table_row" + + @javascript + Scenario: Teachers can see relevant columns and content in the questionnaire overview + Given I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Questionnaire 1" + And I navigate to "Answer the questions..." in current page administration + And I should see "Questionnaire 1" + And I should see "What did you think of these movies?" + And I click on "Row 2, Star Wars: Column 5, I liked." "radio" + And I click on "Row 3, Casablanca: Column 5, I liked." "radio" + And I click on "Row 4, Airplane: Column 5, I liked." "radio" + And I press "Submit questionnaire" + And I log out + And I log in as "student2" + And I am on "Course 1" course homepage + And I follow "Questionnaire 1" + And I navigate to "Answer the questions..." in current page administration + And I should see "Questionnaire 1" + And I should see "What did you think of these movies?" + And I click on "Row 2, Star Wars: Column 5, I liked." "radio" + And I click on "Row 3, Casablanca: Column 5, I liked." "radio" + And I click on "Row 4, Airplane: Column 5, I liked." "radio" + And I press "Submit questionnaire" + And I log out + And I log in as "student1" + And I am on "Course 1" course homepage + And I follow "Questionnaire 2" + And I navigate to "Answer the questions..." in current page administration + And I should see "Questionnaire 2" + And I should see "What did you think of these movies?" + And I click on "Row 2, Star Wars: Column 5, I liked." "radio" + And I click on "Row 3, Casablanca: Column 5, I liked." "radio" + And I click on "Row 4, Airplane: Column 5, I liked." "radio" + And I press "Submit questionnaire" + And I log out + And I am on the "Course 1" "course > activities > questionnaire" page logged in as "teacher1" + Then the following should exist in the "Table listing all Questionnaire activities" table: + | Name | Due date | Students who responded | Actions | + | Questionnaire 1 | 1 January 2040 | 2 | View | + | Questionnaire 2 | - | 1 | View | + | Questionnaire 3 | - | 0 | View | + | Questionnaire 4 | - | 0 | View | + And I should not see "Status" in the "questionnaire_overview_collapsible" "region" + And I should not see "Responded" in the "questionnaire_overview_collapsible" "region" + + Scenario: The questionnaire index redirect to the activities overview + When I log in as "admin" + And I am on "Course 1" course homepage with editing mode on + And I add the "Activities" block + And I click on "Questionnaires" "link" in the "Activities" "block" + Then I should see "An overview of all activities in the course" + And I should see "Name" in the "questionnaire_overview_collapsible" "region" + And I should see "Due date" in the "questionnaire_overview_collapsible" "region"