Gradebook Management
--------------------

Import helper to print the gradebook:

    >>> from schooltool.gradebook.browser.ftests import printGradebook

Log in as manager:

    >>> manager = Browser('manager', 'schooltool')

Now, set up a school year (2005-2006) with two terms (Fall and
Spring):

    >>> from schooltool.app.browser.ftests import setup
    >>> setup.setUpBasicSchool()

Set up three courses:

    >>> setup.addCourse('Physics I', '2005-2006')
    >>> setup.addCourse('English I', '2005-2006')
    >>> setup.addCourse('Math I', '2005-2006')

Set up persons:

    >>> from schooltool.basicperson.browser.ftests.setup import addPerson
    >>> addPerson('Paul', 'Carduner', 'paul', 'pwd', browser=manager)
    >>> addPerson('Tom', 'Hoffman', 'tom', 'pwd', browser=manager)
    >>> addPerson('Claudia', 'Richter', 'claudia', 'pwd', browser=manager)
    >>> addPerson('Stephan', 'Richter', 'stephan', 'pwd', browser=manager)

Set up three sections with instructor and students, two for the Fall
and one for the Spring:

    >>> setup.addSection('Physics I', '2005-2006', 'Fall',
    ...                  instructors=['Stephan'],
    ...                  members=['Tom', 'Claudia', 'Paul'])
    >>> setup.addSection('Math I', '2005-2006', 'Fall',
    ...                  instructors=['Stephan'],
    ...                  members=['Tom', 'Claudia', 'Paul'])
    >>> setup.addSection('English I', '2005-2006', 'Spring',
    ...                  instructors=['Stephan'],
    ...                  members=['Tom', 'Claudia', 'Paul'])

Log in as teacher:

    >>> stephan = Browser('stephan', 'pwd')

Once the term started, the instructor of the section will start by
creating two worksheets, one for each week in our two week section.
To set up the activities, we will start by clicking the 'Gradebook'
tab.

    >>> stephan.getLink('Gradebook').click()

As Stephan is only a teacher at this point and does not attend any
classes himself (we will change that later), he will be taken directly
to the gradebook view for the first section in the list of sections he
teaches.

Since his section has not yet been set up to have any worksheets, a default
worksheet called, 'Sheet1', will automatically be created.

    >>> stephan.getLink('Worksheets').click()
    >>> stephan.printQuery('//h1')
    <h1>Worksheets</h1>
    >>> stephan.printQuery('//form//table/tr/td[3]/a/text()')
    Sheet1

First, we will change the title of our default worksheet to 'Week
1'. We'll also change the Cancel button in the Edit form for
worksheets:

    >>> stephan.getLink('Sheet1').click()
    >>> stephan.printQuery('//h1/text()')
    Activities

    >>> stephan.getLink('Edit').click()
    >>> stephan.getControl('Cancel').click()

    >>> stephan.printQuery('//h1')
    <h1>Worksheets</h1>
    >>> stephan.printQuery('//form//table/tr/td[3]/a/text()')
    Sheet1

    >>> stephan.getLink('Sheet1').click()
    >>> stephan.getLink('Edit').click()
    >>> stephan.getControl('Title').value = 'Week 1'
    >>> stephan.getControl('Apply').click()

    >>> stephan.printQuery('//h1')
    <h1>Worksheets</h1>
    >>> stephan.printQuery('//form//table/tr/td[3]/a/text()')
    Week 1

We'll note the message that appears for empty worksheets.  Also, the fact that
there's no delete button.

    >>> stephan.getLink('Week 1').click()
    >>> stephan.printQuery("id('content-body')/form/div[1]")
    <div>This worksheet has no activities.</div>
    >>> stephan.printQuery("id('content-body')/form/div[3]")
    >>> stephan.getLink('Worksheets').click()

Then, we can use the 'New Worksheet' action link to create our second worksheet.

    >>> stephan.getLink('New Worksheet').click()
    >>> stephan.getControl('Title').value = 'Week 2'
    >>> stephan.getControl('Add').click()
    >>> stephan.printQuery('//h1')
    <h1>Worksheets</h1>
    >>> stephan.printQuery('//form//table/tr/td[3]/a/text()')
    Week 1
    Week 2

Worksheets can be reordered:

    >>> stephan.getControl(name='pos.Worksheet-2').displayValue = ['1']
    >>> stephan.getForm().submit()
    >>> stephan.printQuery('//h1')
    <h1>Worksheets</h1>
    >>> stephan.printQuery('//form//table/tr/td[3]/a/text()')
    Week 2
    Week 1

    >>> stephan.getControl(name='pos.Worksheet').displayValue = ['1']
    >>> stephan.getForm().submit()
    >>> stephan.printQuery('//h1')
    <h1>Worksheets</h1>
    >>> stephan.printQuery('//form//table/tr/td[3]/a/text()')
    Week 1
    Week 2

To return to the gradebook for this section, we will click on the
'Return to Gradebook' link.  Since this is the first time Stephan has
entered the gradebook for this section, the current worksheet will default
to the first one.

    >>> stephan.getLink('Return to Gradebook').click()
    >>> stephan.printQuery('id("content-nav-group")/div/a/text()')
    Worksheets
    New Activity
    New External Activity
    New Linked Column
    Manage Worksheet
    Weight Categories
    Preferences
    Export XLS
    Download PDF
    >>> stephan.printQuery('//select[@name="currentTerm"]/option/text()')
    2005-2006 / Fall
    2005-2006 / Spring
    >>> stephan.printQuery('//select[@name="currentTerm"]/option[@selected="selected"]/text()')
    2005-2006 / Fall
    >>> stephan.printQuery('//select[@name="currentSection"]/option/text()')
    Physics I - Physics I (1)
    Math I - Math I (2)
    >>> stephan.printQuery('//select[@name="currentSection"]/option[@selected="selected"]/text()')
    Physics I - Physics I (1)
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+------+
    | Name            | Total | Ave. |
    +-----------------+-------+------+
    | Paul Carduner   | 0.0   | N/A  |
    | Tom Hoffman     | 0.0   | N/A  |
    | Claudia Richter | 0.0   | N/A  |
    +-----------------+-------+------+

Now, we'll test the Term and Section dropdowns which allow the
teachers to navigate through their sections. Lets change to Stephan's
Math section of the Fall:

    >>> stephan.getControl(name='currentSection').displayValue = ['Math I - Math I (2)']
    >>> stephan.getForm().submit()
    >>> stephan.printQuery('//select[@name="currentTerm"]/option/text()')
    2005-2006 / Fall
    2005-2006 / Spring
    >>> stephan.printQuery('//select[@name="currentTerm"]/option[@selected="selected"]/text()')
    2005-2006 / Fall
    >>> stephan.printQuery('//select[@name="currentSection"]/option/text()')
    Physics I - Physics I (1)
    Math I - Math I (2)
    >>> stephan.printQuery('//select[@name="currentSection"]/option[@selected="selected"]/text()')
    Math I - Math I (2)

Now, to the Spring term:

    >>> stephan.getControl(name='currentTerm').displayValue = ['2005-2006 / Spring']
    >>> stephan.getForm().submit()
    >>> stephan.printQuery('//select[@name="currentTerm"]/option/text()')
    2005-2006 / Fall
    2005-2006 / Spring
    >>> stephan.printQuery('//select[@name="currentTerm"]/option[@selected="selected"]/text()')
    2005-2006 / Spring
    >>> stephan.printQuery('//select[@name="currentSection"]/option/text()')
    English I - English I (1)
    >>> stephan.printQuery('//select[@name="currentSection"]/option[@selected="selected"]/text()')
    English I - English I (1)

If we change back to the Fall term, we get to the first section of the
Section dropdown, in this case the Physics I section:

    >>> stephan.getControl(name='currentTerm').displayValue = ['2005-2006 / Fall']
    >>> stephan.getForm().submit()
    >>> stephan.printQuery('//select[@name="currentTerm"]/option[@selected="selected"]/text()')
    2005-2006 / Fall
    >>> stephan.printQuery('//select[@name="currentSection"]/option[@selected="selected"]/text()')
    Physics I - Physics I (1)

Let's add some activities to the Physics I section worksheet.  After
adding the first activity we'll note that the new activity appears in
the gradebook.

    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'HW 1'
    >>> stephan.getControl('Description').value = 'Homework 1'
    >>> stephan.getControl('Category').displayValue = ['Assignment']
    >>> stephan.getControl('Maximum').value = '50'
    >>> stephan.getControl('Add').click()
    >>> stephan.printQuery('id("content-nav-group")/div/a/text()')
    Worksheets
    New Activity
    New External Activity
    New Linked Column
    Manage Worksheet
    Weight Categories
    Preferences
    Export XLS
    Download PDF
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+------+---------+
    | Name            | Total | Ave. | HW1     |
    +-----------------+-------+------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] |
    +-----------------+-------+------+---------+

We'll add a second activity.

    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'Quiz'
    >>> stephan.getControl('Description').value = 'Week 1 Pop Quiz'
    >>> stephan.getControl('Category').displayValue = ['Exam']
    >>> stephan.getControl('Add').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+------+---------+---------+
    | Name            | Total | Ave. | HW1     | Quiz    |
    +-----------------+-------+------+---------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] | [_____] |
    +-----------------+-------+------+---------+---------+

We'll test editing an activity's description.  To do this, we need to go to use
the 'Manage Worksheet' link.  This presents us with a view of the current
worksheet's activities with links to edit them.

    >>> stephan.getLink('Manage Worksheet').click()
    >>> stephan.printQuery('id("content-nav-group")/div/a/text()')
    Edit
    New Activity
    New External Activity
    New Linked Column
    Return to Gradebook
    Worksheets
    >>> stephan.printQuery('//h1')
    <h1>Activities</h1>
    >>> stephan.printQuery('//form/div/a/text()')
    HW 1
    Quiz
    >>> stephan.printQuery('//form/div[@class="controls"]/input/@value')
    Delete

Now let's click on 'HW 1' to change its description.

    >>> stephan.getLink('HW 1').click()
    >>> stephan.getControl('Description').value = 'Homework One'
    >>> stephan.getControl('Apply').click()

Now let's change the current worksheet to 'Week 2'.

    >>> stephan.getLink('Return to Gradebook').click()
    >>> stephan.getLink('Week 2').click()
    >>> printGradebook(stephan.contents)
    +--------+----------+
    | Week 1 | *Week 2* |
    +--------+----------+
    +-----------------+-------+------+
    | Name            | Total | Ave. |
    +-----------------+-------+------+
    | Paul Carduner   | 0.0   | N/A  |
    | Tom Hoffman     | 0.0   | N/A  |
    | Claudia Richter | 0.0   | N/A  |
    +-----------------+-------+------+

Now we'll add some activities to it.

    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'HW 2'
    >>> stephan.getControl('Description').value = 'Homework 2'
    >>> stephan.getControl('Category').displayValue = ['Assignment']
    >>> stephan.getControl('Add').click()
    >>> printGradebook(stephan.contents)
    +--------+----------+
    | Week 1 | *Week 2* |
    +--------+----------+
    +-----------------+-------+------+---------+
    | Name            | Total | Ave. | HW2     |
    +-----------------+-------+------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] |
    +-----------------+-------+------+---------+

    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'Final'
    >>> stephan.getControl('Description').value = 'Final Exam'
    >>> stephan.getControl('Category').displayValue = ['Exam']
    >>> stephan.getControl('Add').click()
    >>> printGradebook(stephan.contents)
    +--------+----------+
    | Week 1 | *Week 2* |
    +--------+----------+
    +-----------------+-------+------+---------+---------+
    | Name            | Total | Ave. | HW2     | Final   |
    +-----------------+-------+------+---------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] | [_____] |
    +-----------------+-------+------+---------+---------+

The 'Manage Worksheet' view allows for reordering the columns of the gradebook.
We'll switch the order or our two activities.  Since Javascript is not working
in the tests, we submit the form manually:

    >>> stephan.getLink('Manage Worksheet').click()
    >>> stephan.printQuery("id('content-body')//a")
    <a href="http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet-2/Activity">HW 2</a>
    <a href="http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet-2/Activity-2">Final</a>
    >>> stephan.getControl(name='pos.Activity-2').displayValue = ['1']
    >>> stephan.getForm().submit()
    >>> stephan.printQuery("id('content-body')//a")
    <a href="http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet-2/Activity-2">Final</a>
    <a href="http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet-2/Activity">HW 2</a>

The gradebook shows the new positions:

    >>> stephan.getLink('Return to Gradebook').click()
    >>> printGradebook(stephan.contents)
    +--------+----------+
    | Week 1 | *Week 2* |
    +--------+----------+
    +-----------------+-------+------+---------+---------+
    | Name            | Total | Ave. | Final   | HW2     |
    +-----------------+-------+------+---------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] | [_____] |
    +-----------------+-------+------+---------+---------+

We'll switch them back.

    >>> stephan.getLink('Manage Worksheet').click()
    >>> stephan.getControl(name='pos.Activity').displayValue = ['1']
    >>> stephan.getForm().submit()
    >>> stephan.printQuery("id('content-body')//a")
    <a href="http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet-2/Activity">HW 2</a>
    <a href="http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet-2/Activity-2">Final</a>

We'll switch to the Spring term and add some activities to the English I section:

    >>> stephan.getLink('Return to Gradebook').click()
    >>> stephan.getControl(name='currentTerm').displayValue = ['2005-2006 / Spring']
    >>> stephan.getForm().submit()

    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'Lab 1'
    >>> stephan.getControl('Description').value = 'Laboratory 1'
    >>> stephan.getControl('Category').displayValue = ['Assignment']
    >>> stephan.getControl('Maximum').value = '99'
    >>> stephan.getControl('Add').click()
    >>> printGradebook(stephan.contents)
    +----------+
    | *Sheet1* |
    +----------+
    +-----------------+-------+------+---------+
    | Name            | Total | Ave. | Lab1    |
    +-----------------+-------+------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] |
    +-----------------+-------+------+---------+

    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'Final'
    >>> stephan.getControl('Description').value = 'Final Exam'
    >>> stephan.getControl('Category').displayValue = ['Exam']
    >>> stephan.getControl('Add').click()
    >>> printGradebook(stephan.contents)
    +----------+
    | *Sheet1* |
    +----------+
    +-----------------+-------+------+---------+---------+
    | Name            | Total | Ave. | Lab1    | Final   |
    +-----------------+-------+------+---------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] | [_____] |
    +-----------------+-------+------+---------+---------+

Finally, we'll change the section back to the Physics section of the
Fall and the current worksheet back to 'Week 1'.

    >>> stephan.getControl(name='currentTerm').displayValue = ['2005-2006 / Fall']
    >>> stephan.getForm().submit()
    >>> stephan.getLink('Week 1').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+------+---------+---------+
    | Name            | Total | Ave. | HW1     | Quiz    |
    +-----------------+-------+------+---------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] | [_____] |
    +-----------------+-------+------+---------+---------+


Grading
~~~~~~~

The initial gradebook screen is a simple spreadsheet.  Since we just loaded up
the gradebook for the first time, the current worksheet will be the first one,
Week 1.  Only the activities for that worksheet should appear.

    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+------+---------+---------+
    | Name            | Total | Ave. | HW1     | Quiz    |
    +-----------------+-------+------+---------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] | [_____] |
    +-----------------+-------+------+---------+---------+

We can enter a score into any cell.  Let's enter one for Claudia's HW 1
activity:

    >>> stephan.getControl(name='Activity_claudia').value = '36'
    >>> stephan.getControl('Save').click()

We should see the score reflected in the spreadsheet.

    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 0.0   | N/A   | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A   | [_____] | [_____] |
    | Claudia Richter | 36.0  | 72.0% | [36___] | [_____] |
    +-----------------+-------+-------+---------+---------+

If we enter an invalid score, we will get an error message.

    >>> stephan.getControl(name='Activity_claudia').value = 'Bad'
    >>> stephan.getControl('Save').click()
    >>> stephan.printQuery('//div[@class="message"]/text()')
    Invalid scores (highlighted in red) were not saved.

We can change the score and see the change reflected in the spreadsheet.

    >>> stephan.getControl(name='Activity_claudia').value = '58'
    >>> stephan.getControl('Save').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+--------+---------+---------+
    | Name            | Total | Ave.   | HW1     | Quiz    |
    +-----------------+-------+--------+---------+---------+
    | Paul Carduner   | 0.0   | N/A    | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A    | [_____] | [_____] |
    | Claudia Richter | 58.0  | 116.0% | [58___] | [_____] |
    +-----------------+-------+--------+---------+---------+

Note how the average shows a value greater than 100%. The gradebook
allows extra credits for ranged score systems (e.g. 0-50).

Finally, we can remove the score by clearing out the cell.

    >>> stephan.getControl(name='Activity_claudia').value = ''
    >>> stephan.getControl('Save').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+------+---------+---------+
    | Name            | Total | Ave. | HW1     | Quiz    |
    +-----------------+-------+------+---------+---------+
    | Paul Carduner   | 0.0   | N/A  | [_____] | [_____] |
    | Tom Hoffman     | 0.0   | N/A  | [_____] | [_____] |
    | Claudia Richter | 0.0   | N/A  | [_____] | [_____] |
    +-----------------+-------+------+---------+---------+


Entering Scores for a Column (Activity)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Let's say we want to enter the grades for Homework 1. All we do is to simply
click on the activity's name:

    >>> stephan.getLink('HW1').click()
    >>> stephan.printQuery('//h3')
    <h3>
      Grade HW 1
    </h3>

Now we just enter the grades. Let's only enter grades for Paul and
Tom:

    >>> stephan.getControl('Paul Carduner').value = u'-1'
    >>> stephan.getControl('Tom Hoffman').value = u'42'
    >>> stephan.getControl('Save').click()

Again, we entered an invalid value, this time for Paul:

    >>> stephan.printQuery('//div[@class="message"]/text()')
    The grade -1 for Paul Carduner is not valid.

Also note that all the other entered values should be retained:

    >>> stephan.printQuery('id("paul")/@value')
    -1
    >>> stephan.printQuery('id("tom")/@value')
    42
    >>> stephan.getControl('Paul Carduner').value = u'32'
    >>> stephan.getControl('Save').click()

The screen will return to the grade overview, where the grades are now
visible:

    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 32.0  | 64.0% | [32___] | [_____] |
    | Tom Hoffman     | 42.0  | 84.0% | [42___] | [_____] |
    | Claudia Richter | 0.0   | N/A   | [_____] | [_____] |
    +-----------------+-------+-------+---------+---------+

Now let's modify some grades:

    >>> stephan.getLink('HW1').click()
    >>> stephan.printQuery('//h3')
    <h3>
      Grade HW 1
    </h3>
    >>> stephan.getControl('Tom Hoffman').value = u'40'
    >>> stephan.getControl('Claudia Richter').value = u'48'
    >>> stephan.getControl('Save').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 32.0  | 64.0% | [32___] | [_____] |
    | Tom Hoffman     | 40.0  | 80.0% | [40___] | [_____] |
    | Claudia Richter | 48.0  | 96.0% | [48___] | [_____] |
    +-----------------+-------+-------+---------+---------+

When you want to delete an evaluation altogether, simply blank the value:

    >>> stephan.getLink('HW1').click()
    >>> stephan.printQuery('//h3')
    <h3>
      Grade HW 1
    </h3>
    >>> stephan.getControl('Claudia Richter').value = u''
    >>> stephan.getControl('Save').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 32.0  | 64.0% | [32___] | [_____] |
    | Tom Hoffman     | 40.0  | 80.0% | [40___] | [_____] |
    | Claudia Richter | 0.0   | N/A   | [_____] | [_____] |
    +-----------------+-------+-------+---------+---------+

Of course, you can also abort the grading.

    >>> stephan.getLink('HW1').click()
    >>> stephan.printQuery('//h3')
    <h3>
      Grade HW 1
    </h3>
    >>> stephan.url
    'http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet/gradebook/gradeActivity.html?activity=Activity'
    >>> stephan.getControl('Cancel').click()
    >>> stephan.url
    'http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet/gradebook'

Let's enter some grades for the second worksheet, 'Week 2', so that we have
some interesting numbers for the summary view.

    >>> stephan.getLink('Week 2').click()
    >>> stephan.getLink('HW2').click()
    >>> stephan.printQuery('//h3')
    <h3>
      Grade HW 2
    </h3>
    >>> stephan.getControl('Paul Carduner').value = u'90'
    >>> stephan.getControl('Tom Hoffman').value = u'72'
    >>> stephan.getControl('Claudia Richter').value = u'42'
    >>> stephan.getControl('Save').click()
    >>> printGradebook(stephan.contents)
    +--------+----------+
    | Week 1 | *Week 2* |
    +--------+----------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW2     | Final   |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 90.0  | 90.0% | [90___] | [_____] |
    | Tom Hoffman     | 72.0  | 72.0% | [72___] | [_____] |
    | Claudia Richter | 42.0  | 42.0% | [42___] | [_____] |
    +-----------------+-------+-------+---------+---------+


Entering Scores for a Row (Student)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

With the introduction of the comment score system, we need to provide the user
with a way of entering comments into the gradebook.  Since comments have
arbitrary length, we need a special view for entering them.  We will provide
a row view (by student) for this purpose.  When the user clicks on a student,
they will see a form with one field for each activity, comments having the
CKEditor widget, the rest of the score system types just having a TextLine
widget.

Additionally, the user will have two special buttons, 'Previous' and 'Next',
which allows them to go from student to student without having to return to
the gradebook spreadsheet each time.  'Apply' and 'Cancel' return to the
spreadsheet as one would expect.

We'll start on the Week 1 worksheet by clicking on Paul and testing
the contents of the form.  Since Paul is the first student in the list
of students, there will be no 'Previous' button.

    >>> stephan.getLink('Week 1').click()
    >>> stephan.getLink('>', index=0).click()
    >>> stephan.printQuery("id('form')/div[1]/h3")
    <h3>Enter grades for Paul Carduner</h3>
    >>> stephan.printQuery('id("form")//fieldset//label/span/text()')
    HW 1 (50)
    Quiz (100)
    >>> stephan.printQuery("id('form')/div[2]//input")
    <input id="form-buttons-apply" name="form.buttons.apply" class="submit-widget button-field button-ok" value="Apply" type="submit" />
    <input id="form-buttons-next" name="form.buttons.next" class="submit-widget button-field button-neutral" value="Next" type="submit" />
    <input id="form-buttons-cancel" name="form.buttons.cancel" class="submit-widget button-field button-cancel" value="Cancel" type="submit" />

When we click on the 'Next' button it takes us to the middle student, Tom.
Here we will see both a 'Previous' and a 'Next' button.

    >>> stephan.getControl('Next').click()
    >>> stephan.printQuery("id('form')/div[1]/h3")
    <h3>Enter grades for Tom Hoffman</h3>
    >>> stephan.printQuery('id("form")//fieldset//label/span/text()')
    HW 1 (50)
    Quiz (100)
    >>> stephan.printQuery("id('form')/div[2]//input")
    <input id="form-buttons-apply" name="form.buttons.apply" class="submit-widget button-field button-ok" value="Apply" type="submit" />
    <input id="form-buttons-previous" name="form.buttons.previous" class="submit-widget button-field button-neutral" value="Previous" type="submit" />
    <input id="form-buttons-next" name="form.buttons.next" class="submit-widget button-field button-neutral" value="Next" type="submit" />
    <input id="form-buttons-cancel" name="form.buttons.cancel" class="submit-widget button-field button-cancel" value="Cancel" type="submit" />

When we click on the 'Next' button it takes us to the last student, Claudia.
Here we will see no 'Next' button.

    >>> stephan.getControl('Next').click()
    >>> stephan.printQuery("id('form')/div[1]/h3")
    <h3>Enter grades for Claudia Richter</h3>
    >>> stephan.printQuery('id("form")//fieldset//label/span/text()')
    HW 1 (50)
    Quiz (100)
    >>> stephan.printQuery("id('form')/div[2]//input")
    <input id="form-buttons-apply" name="form.buttons.apply" class="submit-widget button-field button-ok" value="Apply" type="submit" />
    <input id="form-buttons-previous" name="form.buttons.previous" class="submit-widget button-field button-neutral" value="Previous" type="submit" />
    <input id="form-buttons-cancel" name="form.buttons.cancel" class="submit-widget button-field button-cancel" value="Cancel" type="submit" />

Hitting the 'Cancel' button takes the user back to the gradebook.

    >>> stephan.getControl('Cancel').click()
    >>> stephan.url
    'http://localhost/schoolyears/2005-2006/fall/sections/1/activities/Worksheet/gradebook'
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 32.0  | 64.0% | [32___] | [_____] |
    | Tom Hoffman     | 40.0  | 80.0% | [40___] | [_____] |
    | Claudia Richter | 0.0   | N/A   | [_____] | [_____] |
    +-----------------+-------+-------+---------+---------+

Now we'll go change a cell for Paul and come back.

    >>> stephan.getLink('>', index=0).click()
    >>> stephan.getControl(name='form.widgets.Activity-2').value = '85'
    >>> stephan.getControl('Apply').click()

We see the new value where it wasn't before.

    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 117.0 | 78.0% | [32___] | [85___] |
    | Tom Hoffman     | 40.0  | 80.0% | [40___] | [_____] |
    | Claudia Richter | 0.0   | N/A   | [_____] | [_____] |
    +-----------------+-------+-------+---------+---------+

Let's change that new value to something else.

    >>> stephan.getLink('>', index=0).click()
    >>> stephan.getControl(name='form.widgets.Activity-2').value = '35'
    >>> stephan.getControl('Apply').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 67.0  | 44.7% | [32___] | [35___] |
    | Tom Hoffman     | 40.0  | 80.0% | [40___] | [_____] |
    | Claudia Richter | 0.0   | N/A   | [_____] | [_____] |
    +-----------------+-------+-------+---------+---------+

Finally, we'll change it back to the way it was, demonstrating that we can
remove scores in the student gradebook.

    >>> stephan.getLink('>', index=0).click()
    >>> stephan.getControl(name='form.widgets.Activity-2').value = ''
    >>> stephan.getControl('Apply').click()

The data cells are set as before.

    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 32.0  | 64.0% | [32___] | [_____] |
    | Tom Hoffman     | 40.0  | 80.0% | [40___] | [_____] |
    | Claudia Richter | 0.0   | N/A   | [_____] | [_____] |
    +-----------------+-------+-------+---------+---------+


Sorting
~~~~~~~

Another feature of the gradebook is the ability to sort each column in a
descending and ascending fashion.

By default the student's name is sorted alphabetically:

    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-----------------+-------+-------+---------+---------+
    | Name            | Total | Ave.  | HW1     | Quiz    |
    +-----------------+-------+-------+---------+---------+
    | Paul Carduner   | 32.0  | 64.0% | [32___] | [_____] |
    | Tom Hoffman     | 40.0  | 80.0% | [40___] | [_____] |
    | Claudia Richter | 0.0   | N/A   | [_____] | [_____] |
    +-----------------+-------+-------+---------+---------+

Let's add some fake students to our section:

    >>> addPerson('Fake1', 'Čardune', 'fake1', 'pwd', browser=manager)
    >>> addPerson('Fake2', 'Álvarez', 'fake2', 'pwd', browser=manager)
    >>> addPerson('Fake3', 'Alvarado', 'fake3', 'pwd', browser=manager)
    >>> addPerson('Fake4', 'Sandoval', 'fake4', 'pwd', browser=manager)
    >>> addPerson('Fake5', 'Šandoval', 'fake5', 'pwd', browser=manager)
    >>> addPerson('Fake6', 'Navarro', 'fake6', 'pwd', browser=manager)
    >>> addPerson('Fake7', 'Ñoño', 'fake7', 'pwd', browser=manager)
    >>> manager.getLink('2005-2006').click()
    >>> manager.getLink('Courses').click()
    >>> manager.getLink('Physics I').click()
    >>> manager.getLink('Physics I (1)').click()
    >>> manager.getLink('edit individuals').click()
    >>> manager.getControl('Fake1').click()
    >>> manager.getControl('Fake2').click()
    >>> manager.getControl('Fake3').click()
    >>> manager.getControl('Fake4').click()
    >>> manager.getControl('Fake5').click()
    >>> manager.getControl('Fake6').click()
    >>> manager.getControl('Fake7').click()
    >>> manager.getControl('Add').click()

And check the gradebook again to see the new sorting:

    >>> stephan.reload()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-------------------+-------+-------+---------+---------+
    | Name              | Total | Ave.  | HW1     | Quiz    |
    +-------------------+-------+-------+---------+---------+
    | Fake3 Alvarado    | 0.0   | N/A   | [_____] | [_____] |
    | Fake2 ...lvarez   | 0.0   | N/A   | [_____] | [_____] |
    | Fake1 ...ardune   | 0.0   | N/A   | [_____] | [_____] |
    | Paul Carduner     | 32.0  | 64.0% | [32___] | [_____] |
    | Tom Hoffman       | 40.0  | 80.0% | [40___] | [_____] |
    | Fake6 Navarro     | 0.0   | N/A   | [_____] | [_____] |
    | Fake7 ...o...o    | 0.0   | N/A   | [_____] | [_____] |
    | Claudia Richter   | 0.0   | N/A   | [_____] | [_____] |
    | Fake4 Sandoval    | 0.0   | N/A   | [_____] | [_____] |
    | Fake5 ...andoval  | 0.0   | N/A   | [_____] | [_____] |
    +-------------------+-------+-------+---------+---------+

We can sort by student name in reverse order:

    >>> url = stephan.url
    >>> stephan.open(url+'?sort_by=student')
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-------------------+-------+-------+---------+---------+
    | Name              | Total | Ave.  | HW1     | Quiz    |
    +-------------------+-------+-------+---------+---------+
    | Fake5 ...andoval  | 0.0   | N/A   | [_____] | [_____] |
    | Fake4 Sandoval    | 0.0   | N/A   | [_____] | [_____] |
    | Claudia Richter   | 0.0   | N/A   | [_____] | [_____] |
    | Fake7 ...o...o    | 0.0   | N/A   | [_____] | [_____] |
    | Fake6 Navarro     | 0.0   | N/A   | [_____] | [_____] |
    | Tom Hoffman       | 40.0  | 80.0% | [40___] | [_____] |
    | Paul Carduner     | 32.0  | 64.0% | [32___] | [_____] |
    | Fake1 ...ardune   | 0.0   | N/A   | [_____] | [_____] |
    | Fake2 ...lvarez   | 0.0   | N/A   | [_____] | [_____] |
    | Fake3 Alvarado    | 0.0   | N/A   | [_____] | [_____] |
    +-------------------+-------+-------+---------+---------+

Let's add more grades for Homework 1:

    >>> stephan.getLink('HW1').click()
    >>> stephan.getControl('Fake3 Alvarado').value = '44'
    >>> stephan.getControl('Fake2').value = '32'
    >>> stephan.getControl('Fake1').value = '50'
    >>> stephan.getControl('Fake6 Navarro').value = '22'
    >>> stephan.getControl('Fake5').value = '39'
    >>> stephan.getControl('Claudia Richter').value = '32'
    >>> stephan.getControl('Save').click()
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-------------------+-------+--------+---------+---------+
    | Name              | Total | Ave.   | HW1     | Quiz    |
    +-------------------+-------+--------+---------+---------+
    | Fake5 ...andoval  | 39.0  | 78.0%  | [39___] | [_____] |
    | Fake4 Sandoval    | 0.0   | N/A    | [_____] | [_____] |
    | Claudia Richter   | 32.0  | 64.0%  | [32___] | [_____] |
    | Fake7 ...o...o    | 0.0   | N/A    | [_____] | [_____] |
    | Fake6 Navarro     | 22.0  | 44.0%  | [22___] | [_____] |
    | Tom Hoffman       | 40.0  | 80.0%  | [40___] | [_____] |
    | Paul Carduner     | 32.0  | 64.0%  | [32___] | [_____] |
    | Fake1 ...ardune   | 50.0  | 100.0% | [50___] | [_____] |
    | Fake2 ...lvarez   | 32.0  | 64.0%  | [32___] | [_____] |
    | Fake3 Alvarado    | 44.0  | 88.0%  | [44___] | [_____] |
    +-------------------+-------+--------+---------+---------+

Then we want to sort by grade in Homework 1 (default is reverse order):

    >>> stephan.open(url + '?sort_by=Activity')
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-------------------+-------+--------+---------+---------+
    | Name              | Total | Ave.   | HW1     | Quiz    |
    +-------------------+-------+--------+---------+---------+
    | Fake1 ...ardune   | 50.0  | 100.0% | [50___] | [_____] |
    | Fake3 Alvarado    | 44.0  | 88.0%  | [44___] | [_____] |
    | Tom Hoffman       | 40.0  | 80.0%  | [40___] | [_____] |
    | Fake5 ...andoval  | 39.0  | 78.0%  | [39___] | [_____] |
    | Claudia Richter   | 32.0  | 64.0%  | [32___] | [_____] |
    | Paul Carduner     | 32.0  | 64.0%  | [32___] | [_____] |
    | Fake2 ...lvarez   | 32.0  | 64.0%  | [32___] | [_____] |
    | Fake6 Navarro     | 22.0  | 44.0%  | [22___] | [_____] |
    | Fake4 Sandoval    | 0.0   | N/A    | [_____] | [_____] |
    | Fake7 ...o...o    | 0.0   | N/A    | [_____] | [_____] |
    +-------------------+-------+--------+---------+---------+

Note that after sorting by the selected activity, rows are also sorted
by student.

Clicking it again, reverses the order:

    >>> stephan.open(url + '?sort_by=Activity')
    >>> printGradebook(stephan.contents)
    +----------+--------+
    | *Week 1* | Week 2 |
    +----------+--------+
    +-------------------+-------+--------+---------+---------+
    | Name              | Total | Ave.   | HW1     | Quiz    |
    +-------------------+-------+--------+---------+---------+
    | Fake7 ...o...o    | 0.0   | N/A    | [_____] | [_____] |
    | Fake4 Sandoval    | 0.0   | N/A    | [_____] | [_____] |
    | Fake6 Navarro     | 22.0  | 44.0%  | [22___] | [_____] |
    | Fake2 ...lvarez   | 32.0  | 64.0%  | [32___] | [_____] |
    | Paul Carduner     | 32.0  | 64.0%  | [32___] | [_____] |
    | Claudia Richter   | 32.0  | 64.0%  | [32___] | [_____] |
    | Fake5 ...andoval  | 39.0  | 78.0%  | [39___] | [_____] |
    | Tom Hoffman       | 40.0  | 80.0%  | [40___] | [_____] |
    | Fake3 Alvarado    | 44.0  | 88.0%  | [44___] | [_____] |
    | Fake1 ...ardune   | 50.0  | 100.0% | [50___] | [_____] |
    +-------------------+-------+--------+---------+---------+

