{}[https://codeclimate.com/github/ucberkeley/moocchat]
{}[https://codeclimate.com/github/ucberkeley/moocchat]
MOOCchat is a SaaS app for integrating peer learning/peer discussion into MOOCs and similar settings.
This {short screencast}[https://www.youtube.com/watch?v=FIc36pfeyqA] shows the previous version of this software in action. In this new version, the flow and appearance are basically the same, but the code has been completely rewritten: a new back end (Rails instead of Node) and new front end (jQuery and Websockets).
This document gives an overview of how to set up experiments/activities
with MOOCchat and an overview of the structure of the backend server.
Partial URLs are given for all paths, so that if the
app is hosted on http://moocchat.herokuapp.com, the path
/tasks/welcome refers to http://moocchat.herokuapp.com/tasks/welcome.
= Data Model and Terminology
== User Roles
Single-table inheritance is used to distinguish three types of Users:
In addition, a Cohort is a group of learners, e.g., the enrollees in a course. A learner can belong to multiple cohorts.
== Describing an Experiment
As {this video}[https://www.youtube.com/watch?v=FIc36pfeyqA] shows, a typical "experiment" involves a cohort of students who as a group go through an activity in which they answer a question individually, discuss the answer in small groups using text chat, and then possibly get to answer again.
An experimental condition is a collection of parameters defining such an activity, as follows:
A Question consists of some question text accompanied by a set of multiple-choice answers and an indicator of which answer is correct.
An ActivitySchema is a list of Questions that may feature some peer instruction, for example, a quiz review session. Besides the list of questions, the ActivitySchema has a name, a start and end time, and a "starts every" interval -- how often the activity starts. For example, if "starts every" is 10 minutes, learners can only do the activity between the start and end times, and when they arrive, they must wait until the next 10-minute boundary before they begin.
A Template is an HTML page processed through Erb that can contain elements such as questions, radiobuttons for learners to select an answer, text fields for learners to provide textual explanations, a chat box, and so on. The next section describes how instructors can create templates.
A Condition describes how a learner or group of learners interacts with an Activity Schema (set of questions). The Condition consists of a set of page views based on Templates. Specifically, a condition specifies:
The rationale is that the same ActivitySchema, that is, the same list of questions to be used during the same review session, can be instantiated under different experimental conditions. For example, one group of learners assigned to Condition A would be given a followup ("transfer") question after the initial question, while those in Condition B would not; or the learners in conditions A and B would be allowed differing amounts of time to discuss their answers.
== The Task model
All of the above data structures (Condition, ActivitySchema, Learner, Question, Template) are created to support the run of an experiment.
A Task is a runtime-only data structure that captures a single learner's session interacting with an experiment. Specifically, a Task associates the triplet (Learner, Condition, ActivitySchema), and is created only when such a triplet is supplied. The Task#static view presents a form that can be submitted to create a task, or an HTTP POST to Task#create_from_params can do it (for example, by embedding a form in the online course).
At Task creation time, each learner is assigned to a chat group with other learners; the preferred and minimum allowed sizes of a chat group are determined by the associated Condition.
Thereafter, the Task object is responsible for the "session state" that tracks each learner through the activity:
The Task captures the Learner answers and explanations, and makes them available to the Templates shown to other learners in the same chat group.
Each template can specify whether to advance the question counter or not, to consume successive questions from the ActivitySchema. For example, suppose the body contains two pages, each page consumes a question, and the body is to be repeated five times. Then the Activity Schema should contain at least ten questions to avoid repetition. (The question counter will wrap around to the beginning of the question list if it runs out of questions.)
The Task is responsible for receiving all interaction from a Learner (choosing an answer, voting to continue, etc) and logging an appropriate event; see Logging below.
= Creating Templates
Templates should be complete legal HTML 5 pages. At the moment, we store the raw HTML of the pages verbatim, so you can author your templates in whatever environment you want and then cut-and-paste the HTML. You can incorporate inline CSS styles in the template, or use a element to point to an external stylesheet.
There are two rules you must follow when creating a template:
Inside the head element, you must include the following line, to ensure the page loads the JavaScript functions that allow the timer and other interactions to work:
<%= javascript_include_tag "application" %>
The +body+ element must not have the CSS class "admin". The presence of this class indicates that the rendered page is not a template that's part of a flow, and therefore inhibits some JavaScript behaviors from loading (so they won't affect the administrative interface).
Templates are processed through erb, which allows the values of Ruby variables and expressions to be interpolated into the HTML. The notation <%= expr %> evaluates the Ruby expression expr and inserts the result into the HTML template.
Non-Rubyists may find it helpful to know that Ruby instance variable names begin with tt>@</tt.
== Templates and Timers
Because the activities require each chat group to move through the activity more or less together, most pages have a "Continue" button, but that button just registers (via AJAX) the learner's intent to continue; it does not actually do the form-submit that causes the next-page action to happen.
Instead, most templates will have a timer element that automatically advances to the next page when the timer runs out. The timer value is determined by the Condition.
When a template is first displayed to the learner, the HTML5 element with id interstitial (if one exists) is immediately hidden; if the learner tries to advance to the next page before the timer runs out, this element is revealed, so it should display a message to the effect that "You'll proceed to the next step as soon as all learners in your group are ready." You can use CSS to float the element in a lightbox, overlay it on top of the main HTML, or whatever.
Exception: if ALL learners vote to continue before the timer runs out, they should be allowed to do so.
== Displaying a question and answer choices
Below is a line-numbered example of the of a template keyed to the following explanation. Line numbers are in parentheses.
(1) tt>@start_form_tag</tt:: A complete
== Showing a chat room and/or showing other learners' responses
(3) tt>@data</tt::
An array of hashes, where each element is a hash of
one learner's user data (collected by form fields named
u[...]). Here it's used to display what answers and explanations the
other learners gave; its keys are the same as those you specified as
in lines 7 and 11 in the previous example.
(4) tt>@me</tt:: The index of the array corresponding to this learner. tt>@u['foo']</tt is therefore a synonym for tt>@data[@me]['foo']</tt. BUT (important) to persist collected data, you must use form fields named u[...].
(10) chat:: A macro that shows a chat window that is "listening" to the chat channel tt>@chat_group</tt.
If learners all click Request to End Chat, this overrides the timer-based form submission and allows everyone to proceed.
1 <%= @start_form_tag %> 2
Here are the answers your peers gave:
3 <% @data.each_with_index do |other, index| %> 4 <% next if index == @me %> 5Student <%= index %> selected choice <%= other['response'] %> because:
6<%= other['explanation'] %> 7 <% end %> 8 9
Discuss with others until time runs out or you're ready to go on :
10 <%= chat %> 11 <%= timer %>== Revealing the correct answer and going on to next question
(3) tt>@question.correct_answer_index</tt:: Numerical index into the array of answers.
(5) next_question:: If this hidden field is present and has any nonblank value, the question counter will be advanced to cause the next question to be consumed. You would use this if a single pass through the activity involves answering more than one question, for example, a basic question followed by a transfer question.
1 <%= @start_form_tag %> 2
The correct answer was:
3 <%= @question.answers[@question.correct_answer_index] %> 4 5 6= Server's view of task flow
The relevant controller actions in TasksController are:
+static+ (GET):: Serve a static page that lets learner choose activity and condition from menus. Used primarily for testing.
+create+ (POST):: Create a new +Task+ from a learner name, +Condition+ (experimental setup specifying page flow, group sizes, time spent on each page, and so on) and +ActivitySchema+ (list of questions to be used in the task). Also adds the task to a +WaitingRoom+ with other learners assigned to the same condition and activity. The +ActivitySchema+ specifies how often the waiting room is "emptied".
+welcome+ (GET):: A successful +POST+ to +create+ results in a redirect to the welcome action, which shows a timer that counts down to when this waiting room is to be emptied. On expiration, a POST to +first_page+ occurs.
+join_group+ (POST):: processes the waiting room, so that every +Task+ in the waiting room gets assigned to a +chat_group+. If the value of +chat_group+ is the same as the value of the constant WaitingRoom::CHAT_GROUP_NONE, then there weren't enough waiting learners to fill out the minimum group size specified by the +Condition+, and this learner is redirected to +sorry+ and asked to retry the activity later. Otherwise, the learner is redirected to +page+, a generic endpoint that will be called to render the next template in the flow.
+page+:: sets up all the variables described under Templates, above, and renders the page. Most templates will have a timer. Advancing past the current page, whether manually by clicking the Submit button or automatically on timer expiration, results in a POST to +next_page+, which advances the page counter and then redirects back to +page+. As described above, the presence of the next_question hidden form element determines whether the next usage of tt>@question</tt (and its corresponding tt>@answers</tt) will use the same question or a new one. The body portion of the condition will be repeated while questions remain. This action is idempotent: reloading a page while in this state has no effect.
+sorry+:: A dead-end page that asks the learner to come back later, since they couldn't be placed into a chat group.
== Additional template elements
These will be less frequently used but are listed here for completeness.
tt>@task_id</tt:: an identifier for a data structure that associates a specific Learner, Condition, and Activity Schema, and stores all state related to that learner. Useful to display for debugging purposes.
tt>@counter</tt:: an integer tracking the page number in the overall activity, starting from 1 for the first prologue page. For example, a task with 1 prologue page, 2 epilogue pages, and a 4-page sequence for each of three questions has 12(3*4)=15 total pages, so counter values will range from 1 to 15.
tt>@subcounter</tt:: a zero-based counter tracking the page number within the current sequence (prologue, body, epilogue). E.g., for the preceding example, its successive values would be 0, 0,1,2,3, 0,1,2,3, 0,1,2,3, 0,1.
tt>@where</tt:: one of the three strings prologue, body, or epilogue, indicating where we are in the current condition. Can be used, e.g., in conjunction with tt>@subcounter</tt and/or tt>@task_id</tt to style page elements accordingly by giving them CSS classes based on these variables' values, as in <body class=<%= @where "-" @subcounter %>>', giving class names such as prologue-0, body-2, etc.
tt>@chat_group</tt:: a string that identifies the chat group this learner is in; think of it as a channel. After the Welcome page of the task, this is guaranteed to be nonblank (the learner could be in a chat group of size 1, but she will definitely be in some group).
= Logging
Every interesting "event" is logged in a table from which log information can be easily extracted as JSON or CSV. Some events are logged at the server side; for events detected at the client side, TasksController#log_event can receive a POST (from whose URL the ID of the task can be determined) and will look for fields +name+ (required) and +value+ (optional since not all events use it) to create and log the event, and return a 200 OK if all goes well.
Each row of the table has the following attributes:
created_at:: When this event was logged, to the nearest second in UTC.
task_id, learner_id, activity_schema_id, condition_id:: Foreign keys to the corresponding entities associated with the event.
counter, subcounter, question_counter:: Current values of the counter (overall page in the task, 0-based), subcounter (subpage within prologue/body/epilogue), question counter (how many questions have been consumed so far)
question_id:: The ID of the current question pointed to by the question counter (whether or not the question is displayed on the current page)
chat_group:: For all events where the learner is already associated with a chat group (typically, any except +start+ and +reject+), the chat group channel name.
name, value:: +name+ is a lower_snake_case string indicating what event happened. A few events also have a string +value+, as indicated below; for events with no value, the +value+ column is blank: