<nop>WeBWorK Gateways or Quizzes
<nop>WeBWorK's philosophy as a homework generation and delivery system is one of creating a unique problem set for each user of the system. For many testing situations this is completely appropriate, but in the case where a user can retake a test multiple times (as is the case for Gateway tests as implemented at the University of Michigan) we want the system to generate a unique problem set for each login instance: that is, each time a user logs in he or she should get a new problem set. We would like to be able to generate the variations on the problems in the set by drawing them from a database of some sort.
To support this, we need the ability to record each version of the test/quiz that is taken by a user, and what their submitted solution is. In addition, there needs to be the ability to include proctor authorization for a test/quiz, and there needs to be a timing mechanism to constrain the length of time that is allowed on the test/quiz.
Note that the grading of a test/quiz is slightly different from that in a homework set: <nop>WeBWorK's homework philosophy is that any submission or preview is recorded, and seeing if a problem is correct or not by submitting is equivalent in many respects to previewing it. For a test/quiz, there is a difference between the actual submission and the previewing cycle (not least because we don't want to show the correct solution until the submission).
That is, we need
- to generate a new version of a problem set for each login,
- to be able to generate problem versions by drawing them from a database,
- to restrict the amount of time allowed a student to complete a problem set version,
- to record each version of the problem set taken by the user, and
- to require a proctor authorization before students are allowed to take or grade a problem set version.
An outline the following:
- Discussion of database tables
- Overview of problem/set management
- Preliminary thoughts about code changes
<nop>WeBWorK database tables
We propose to only add this functionality to <nop>WeBWorK2, using mySQL as its database. Current database tables relating to the generation of problem sets that we need to consider are the set, problem, set_user and problem_user tables. The set_user and problem_user table entries are created when the problem set is built using the build set instructor tool, and provide the user-specific versions of the problem set and contained problems. Entries in the set table are identified by a
set_id which is the same as the setName. Entries in the set_user table are identified by their
With gateways/quizzes, we need to have multiple problem set versions per user. This is best (?) implemented by having multiple set_user table entries per user. To do this we need an index that allows identification of these multiple versions. Two possible solutions are to add a
set_user_id index to the table entries, or to implement a workaround with the current
user_id indices. The latter requires less changes elsewhere in the system. In particular, we can change the
set_id value from setName to setName.vN, where the version number N gives the user's set version number.
In addition, we will need to add a proctor category of user to the user table. This should be a smaller change, requiring the addition of a new authorization level between "student" and "professor".
A new table descriptions for sets may therefore look like the following:
||auto-increment serial number|
|| id of set this entry is derived from, appending |
||id of user this entry is generated for|
||set header, if different from that in set|
||problem header, if different from that in set|
||open date, if...|
||due date, if...|
||answer date, if...|
||type of assignment|
||whether problems should be arranged in a random order|
||time limit for each attempt|
||number of attempts per version|
||time interval for versioning|
||how many set versions students can get in time_interval|
|| when the version was created
This allows having some number of tests per time interval (e.g., 2
versions_per_interval with a
time_interval of 1 day), and a restriction on the number of times each version may be attempted.
Note that having the
version_time_limit field here allows us to set it for users needing, for example, time and a half on the midterm. In that this is a property of the user, not the set, it may make better sense to have an
assignment_length_multiplier field in the user table of the database.
version_creation_time is conceptually different than the other fields here (it doesn't have an inherited value from the global set entry). But I think we need it to be able to keep track of the number of versions a student grabs in a time interval. I suppose the other option is to make
remaining_versions_per_interval? This would require getting earlier versions'
open_date (which is the same as the creation time) to figure out when the time interval started. This seems like it's getting way too complicated. More on this to come.
Gateway/Quiz problem set creation and management
When the professor for the class runs the build set utility, a set_user entry is created for each user, as it is currently. This has a
set_id which is the setName for the given set.
Then, the <nop>GatewayQuiz <nop>ContentGenerator wants to go through the following steps:
- authenticate the user as usual
- look for a valid set_user entry with a
setName.v#(valid means that the number of attempts allowed for the set_version has not been exceeded)
- if such exists, send that to the user
- otherwise generate a new set_version as described below, if allowed by the number of attempts per time interval
- create a new set_version with a new routine
assignSetVersionToUser, which will create set_user and problem_user table entries for the new version of the set and set the
close_dateof the created set to the current time and the current time plus the assignment time limit, respectively
- we noted that the <nop>ContentGenerator could also reset the
answer_datewhen the set is graded, but I think this may be unnecessary with the
assignSetVersionToUserroutine doing it
Other changes in code:
getMergedProblem will have to look for the versioned
set_user_id s. The
assignSetVersionToUser routine will want access to a modified set of these,
Preliminary thoughts about code changes
We're storing versions of problems with a
N= equal to 1, 2, etc. I'm assuming that the user will still have an assignment called =setName as well, but that this won't be actually used as a problem set that the user works when versioning is in place. This leads to the modifications/additions to subroutines:
- User Set Management %BR%
In general, it seems logical to have routines actionSetTo/FromUser and actionProblemTo/FromUser continue to operate as normal, but where a set is versioned to perform the action on the latest version as appropriate. Then the new routines actionSetVersionTo/FromUser and actionProblemVersionTo/FromUser should do the action on a specific version. Really I think we only need the Version routines for the <nop>GatewayQuiz module, assuming that those are referred to directly there. I think needed code changes boil down to the following:
DB->getUserSetVersionNumber($userID,$setID).: input userID and setID are the userID and nonversioned setID; output is 0 (no versioned set exists) or an integer value giving the latest version number.
DB->getUserSets(@userSetIDs).: input is $userSetIDs[i] = [ userID, setID ], where setID is a non-versioned setID; retrieves the user sets, substituting the lastest version for versioned sets.
DB->getUserSetVersions(@userSetIDs).: as getUserSets, but return all versions of the set(s) specified by @userSetIDs (in which setID is a non-versioned ID).
DB->getMergedSet($userID,$setID).: return merged set, or merged form of latest version.
Instructor->assignSetToUser(userID,GlobalSet).: as existing routine, but check $db->getUserSetVersionNumber(userID,GlobalSet->set_id) to see if the set is versioned, and if so add the next version of the set instead of adding the set.
Instructor->assignSetVersionToUser(userID,GlobalSet).: as assignSetToUser, but if no set version(s) exist, assign the first set version to the user. if there is no existing base set assigned to the user, assign this too.
Instructor->unassignSetFromUser(userID,setID).: as existing routine, but if set is versioned removes all versions of the set.
Instructor->unassignSetVersionFromUser(userID,setID).: deletes the last set version.
Instructor->assignProblemToUser(userID,setID[,versionNum]).: as current routine, with an added call to DB->getUserSetVersionNumber(userID,setID) to see if the set is versioned. If so, we redefine the setID to which the problems are assigned to be the latest version of the set. The additional (optional) argument allows specification of a version to assign to.
Instructor->unassignProblemFromUser(userID,setID,problemID).: as with assignProblemToUser, unassign problem from the latest version of the set, if any.
Instructor->unassignProblemVersionFromUser(userID,setID,problemID[,versionNum]).: unassign the indicated problem from the given version, or, if no version is specified, revert to behavior for unassignProblemFromUser.
- <nop>ContentGenerator %BR%
This is going to be easier explained with pasted code, I think.
-- Main.GavinLaRose - 13 Apr 2004