Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Adding question types to lesson: Difference between revisions

From MoodleDocs
m (category removed)
Line 320: Line 320:
[[Category:Moodle 2.0]]
[[Category:Moodle 2.0]]


[[Category:Modules]]
[[Category:Lesson]]
[[Category:Lesson]]

Revision as of 16:03, 10 September 2006

Please note that this page deals with a future version of Moodle 2.0.

A regularly discussed topic is that Lesson should use the same question classes as Quiz. This page is geared toward this goal by explaining the different aspects required to complete this project.--Mark Nielsen

I think this would be a really good idea, and am willing to help--Tim Hunt, Quiz module/question bank maintainer.

Project goals

  • (Primary) Implement the question type classes in Lesson.
  • (Primary) Reduce Moodle's code base. Instead of Lesson having its own question code, it can now use existing code.
  • Simplify Lesson's code. There is a high mix of presentation and logic and some rather confusing algorithms due to the nature of Lesson and how it is organized.

Primary coding tasks

Adding support for question class

Implement the question class code and the necessary interfaces for adding and editing questions within Lesson. The needed functionality is as follows:

  • Printing questions for attempts. Should be nothing to do if we are happy with the same presentation for both quiz and lesson--Tim Hunt
  • Grading the questions. Again, should be fine, as long as the question_(attempts|sessions|states) model is flexible enough for the quiz--Tim Hunt
  • Provide a method of adding new questions to a Lesson. Hopefully question/showbank.php will work for you, perhaps with a few changs.--Tim Hunt
    • When adding a question, display questions in course question bank to select from.
      • Provide links/tabs for creating new questions.
    • After adding a question, provide an interface to define possible jumps (This step may not be needed. Depends on the solution to the questions and jumps problem). This will be new work--Tim Hunt

Changing the attempts logic

Among other Lesson table changes, one that is the most significant is changing the lesson_attempts table. Instead of storing one attempt record for every user answer, lesson_attempts table should store one record per Lesson attempt. The question_states table will replace the functionality of the original lesson_attempts table. Since this is changing such a fundamental part of Lesson, almost all of Lesson's code will have to be adapted to the new logic of attempts.

Fixing code breaks

By switching to the question class, nearly all of the current Lesson code will be broken and must be replaced or fixed. Here is a quick breakdown of foreseeable code breaks:

Database migration

Lesson tables need to be removed or changed and their old data needs to be migrated to new tables. Some of this code will be used in the restore process as well. Here is a basic overview of the database migration process:

  • migrate lesson_pages and lesson_answer content to question tables. Note: this will only be the content, not the logic for ordering Lesson pages or any other Lesson specific data.
  • migrate lesson_attempts to question_states.
  • Re-organization of the lesson tables (see new database schema).

Unsolved problems

Here are some tricky issues that do not have a solid or obvious solution. Please advise.

Questions and jumps

Page jumps determine the flow from one page to another and are a unique feature to Lesson. So, how does one figure out all the necessary jump definitions needed for a question? In Lesson, every answer has a jump, but this solution is not ideal. A multiple choice question for example would have two cases:

  • Single answer: a jump must be defined for each answer.
  • Multiple answer: a jump would be defined for the correct answer and a jump for the wrong answer.

The follow two sections discuss possible solutions, but please feel free to suggest others. When thinking about this problem and perhaps a new solution, remember the primary goals of this project: reduce code size and simplify the Lesson code to make it easier to maintain.

Invasive solution

An invasive solution would be to modify the question class code. This is invasive because the introduced code may only be used by Lesson unless it was implemented in such a way that it would be viable for other uses. Some of the foreseeable changes would include the following:

  • Add methods to the default_questiontype class that would handle the default behavior for defining jumps. Default behavior would be two jump definitions: one for correct answers and one for incorrect answers.
  • Provide a place to store the defined jumps.
  • In each question type, override the default methods in the default_questiontype class to suite the behavior of the question (optional for each question type).


Pros for this implementation:

  • All question type code is in one place.
  • Clean implementation
  • Better work-flow for question authoring

Cons:

  • Potentially introduce code that is unusable by other modules, etc.

Noninvasive solution

Lesson would extend all question types that to handle jumps. Lesson would have its own type folder (mod/lesson/type). This type folder would be organized the same as question/type directory. Each question type would have its own folder and in that folder a questiontype.php. So, mod/lesson/type/{questiontype}/questiontype.php where {questiontype} is replaced with each question type name. The questiontype.php would extend the original question type class and add the necessary methods for handling jumps. The following methods would be added:

  • print_jump_form: accepts the possible jump values from Lesson and then print (or return) the contents of a form. This form would then be presented to the user to define the possible jumps.
  • process_jump_form: accepts the form data from print_jump_form and organize it. It then returns the organized data so that Lesson can store it.
  • interpret_jump: accepts the user's answer to the question and the returned value from process_jump_form. This method would determine which jump Lesson should use.
  • restore_jumps: restore the returned value from process_jump_form during Lesson's restore routine. Returns the restored value so that Lesson can store it.

Example use:

Two jump definitions: correct or incorrect:

  • print_jump_form would return the following form:
 Correct answer jump: [drop-down-menu-with-lesson-jumps]
 Wrong answer jump: [drop-down-menu-with-lesson-jumps]
  • process_jump_form would accept the POST data from the above form and organize it in an array like this: array('correct' => [lesson-jump-code], 'wrong' => [lesson-jump-code]). Where [lesson-jump-code] is a Lesson page id or jump code from the [drop-down-menu-with-lesson-jumps]. This array would be returned so that Lesson could store it.
  • interpret_jump would accept the students answer and the above array. It would return the [lesson-jump-code] either associated with correct or wrong based on the student's answer.
  • restore_jumps would be called during the Lesson restore process and it would return the restored array so that Lesson could store it. Example: if each answer had a jump then the array would be defined like this: array(answerid => [lesson-jump-code] ... ). The answerid would have to be mapped to the new answerid during the restore process.


Pros for this implementation:

  • Potential Lesson specific code remains in Lesson.

Cons:

  • Work-flow would have an extra step (create question then define jumps).
  • Creating new question types is more difficult.

A middle way

I (Tim) don't like either of the above two solutions. I don't like excessively lesson specific code in the question types, but it would be really bad if the code for a particular question type was not all in one place.

I think that the solution is to add a new concept: question outcomes. It will take me a couple of paragraphs to explain what I mean with this.

Currently, after a student attempts a question, you get stuff that falls into two categories:

  1. generic stuff, like the grade (both before and after applying the penalty factor), the penalty factor itself, the classification of the before grade as Correct/Partially Correct/Incorrect, a possible manual comment added by the teacher, the state of the question (saved, graded, manual graded, closed)
  2. question-type specific stuff, like the data in question_states->answer column, or the feedback that is displayed on-screen.

I think that in the generic stuff category, we need to add the new concept of 'outcome'. This would be what the lesson module uses to select branches, and would probably also be useful to the quiz reports. It would categorise the student's answer into one of a number of categories, and the question type class would need to implement two new methods:

array of strings all_outcomes($question)

string outcome($question, $state)

For multiple-choice (single response) this would return the answer selected. For shortanswer, numerical and calculated, this would return the answer that was matched. The generic implementation in the base class would probably have to just return 'incorrect'/'partiallycorrect'/'correct' (that is the ids of the strings that are looked up in the language file.

The lesson would then need to do branching be specifying where to go for each outcome. It would requre a multi-stage UI (create the question using the standard questionbank code, add the question to the lesson, link up the outcomes to jump targets). However, done well, this could be a very natural UI.

The quiz UI is not perfect, but I think the way it is now: create a question, then add the question to the quiz, then adjust the grade for that question within the quiz, is very natural to use.

Migration of jumps

Regardless of implementation, one problem remains: if each question potentially does not have a jump defined for each answer, then how are the currently defined jumps supposed to be migrated to the new question implementation (remember, Lesson questions have one jump defined for each answer regardless if it makes sense at all)? The only solution that comes to mind is to have strict definitions for migration for the following question types that exist in Lesson:

  • Multichoice (single and multianswer)
  • Matching
  • Numerical
  • Short Answer
  • True/False
  • Essay

Note: the use of migration here refers to upgrading old Lessons and restoring older Lessons.

Branch tables

Once a solution for the above problem has been found, I would like to discuss the possibility of replacing the functionality of branch tables with the description question type. Reason for discussing this after a solution has been found is because the solution may influence the outcome to this problem.

New database schema

 ------------------      ------------------
 |                |      |                |
 | course_modules |      | lesson_default |
 |                |      |                |
 ------------------      ------------------
         |                        |
         |                        |
  --------------           --------------
  |            |           |            |
  |   lesson   |-----------|   course   |
  |            |           |            |
  --------------           --------------
         |
         |      ----------------------      ----------
         |      |                    |      |        |
         |------| lesson_high_scores |------|  user  |
         |      |                    |      |        |
         |      ----------------------      ----------
         |                                       |
         |      -----------------                |
         |      |               |                |
         |------| lesson_grades |----------------|
         |      |               |                |
         |      -----------------                |
         |                                       |
         |      -------------------              |
         |      |                 |--------------|    ---------------------
         |------| lesson_attempts |                   |                   |
         |      |                 |-------------------| question_attempts |
         |      -------------------                   |                   |
         |                                            ---------------------
         |      ----------------
         |      |              |
         |------| lesson_pages |
                |              |
                ----------------
                       |      -----------------------------      ------------
                       |      |                           |      |          |
                       |------| lesson_question_instances |------| question |
                       |      |                           |      |          |
                       |      -----------------------------      ------------
                       |      
                       |      -------------------
                       |      |                 |
                       |------| lesson_branches |
                              |                 |
                              -------------------
  • Note: tables in bold are standard in Moodle and are there only to show relations.

Table Descriptions

Key:

  • PK = primary key
  • FK = foreign key

lesson

Primary module table

Fields:

  • id: PK
  • course: FK to course
  • name: Name of the lesson
  • practice: Flag for practice lessons
  • modattempts
  • password: Stores password for password protected lessons
  • dependency: FK to lesson table. The lesson that this lesson is dependent upon
  • conditions: conditions for the dependency
  • grade: Max grade for the lesson
  • cusom: custom scoring flag
  • ongoing: display on going score flag
  • usemaxgrade: use max grade or mean flag
  • maxanswers: maximum answers for a question (would only be used by branch tables, remove?)
  • maxattempts: number of attempts on a question
  • review
  • nextpagedefault: default flow control
  • minquestions: minimum questions to answer
  • maxpages
  • time: allowed time in the lesson per attempt
  • retake: allow student to retake
  • activitylink: FK to course_modules table
  • mediafile
  • mediaheight: height of pop-up
  • mediawidth: width of pop-up
  • mediaclose: display close button for media
  • slideshow: display branches in slide show mode flag
  • width: width of slide show
  • height: height of slide show
  • bgcolor: background color of slide show
  • displayleft: display left menu
  • displayleftif: display left menu if student has already completed lesson flag
  • progressbar: display progress bar flag
  • highscores: high scores
  • available: when lesson is available to student
  • deadline: when lesson closes to student
  • timemodified: last updated

lesson_default

Lesson default settings for a course.

Fields:

  • same as the lesson table minus the following fields: name, dependency, activity, mediafile, available, deadline, and modified

lesson_high_scores

Keep track of the lesson top scores.

Fields:

  • id: PK
  • lessonid: FK to lesson table
  • userid: FK to user table
  • gradeid: FK to lesson_grades table
  • nickname: user's nickname

lesson_grades

User attempt grades.

Fields:

  • id: PK
  • lessonid: FK to lesson table
  • userid: FK to user table
  • grade: attempt grade
  • completed: time of completion

lesson_attempts

Lesson attempts by users.

Fields:

  • id: PK
  • uniqueid: FK to question_sessions table
  • lessonid: FK to lesson table
  • userid: FK to user table
  • path: users path through the lesson (comma separated list of page ids from the lesson_pages table)
  • attempt: user's attempt count
  • sumgrade: current total for grades
  • timestart: starting time of attempt
  • timefinish: ending time of attempt
  • timemodified: last time updated

lesson_pages

All content pages in lesson.

Fields:

  • id: PK
  • lessonid: FK to lesson table
  • prevpageid: FK to lesson_pages table
  • nextpageid: FK to lesson_pages table
  • type: values include
    • branch
    • endofbranch
    • cluster
    • endofcluster
    • question
  • display: display in left menu flag
  • jumps: jumps related to this page
  • timemodified: time last updated

lesson_question_instances

Store relation between lesson pages that are questions and the question table. (does not exist in moodle 1.6.1 standard)

Fields:

  • id: PK
  • lessonid: FK to lesson table
  • pageid: FK to lesson_pages table
  • questionid: FK to question table
  • grade: point value of the question

lesson_branches

Lesson pages that are branch tables. (does not exist in moodle 1.6.1 but there is different set of fields in lesson_branch)

Fields:

  • id: PK
  • lessonid: FK to lesson table
  • pageid: FK to lesson_pages table
  • boilerplates: text of branch buttons
  • layout: layout of branch buttons

Discussion

If you would like to discuss topics on this page, please make a post in the lesson forum.

Sorry, I did not see this until I had added my comments inline. I hope that is not too much of a problem.--Tim Hunt

See also