=============
The Gradebook
=============

There are many tasks that are involved in setting up and using a
gradebook. The first task the administrator has to complete during the
SchoolTool setup is the configuration of the categories. So let's log in as a
manager:

   >>> from schooltool.app.browser.ftests import setup
   >>> manager = setup.logInManager()


Initial School Setup
--------------------

We will use the 'Manage' tab to find the 'Activity Categories' link to set up
the categories:

    >>> manager.getLink('Manage').click()
    >>> manager.getLink('Activity Categories').click()

As you can see, there are already several categories pre-defined. Oftentimes,
those categories do not work for a school. Either you do not need some and/or
others are missing. So let's start by deleting a couple of categories:

    >>> 'essay' in manager.contents
    True
    >>> 'journal' in manager.contents
    True

    >>> manager.getControl(name='field.categories:list').value = [
    ...     'essay', 'journal', 'homework', 'presentation']
    >>> manager.getControl('Remove').click()

    >>> 'essay' in manager.contents
    False
    >>> 'journal' in manager.contents
    False

Next, we add a new category:

    >>> 'Lab Report' in manager.contents
    False

    >>> manager.getControl('New Category').value = 'Lab Report'
    >>> manager.getControl('Add').click()

    >>> 'Lab Report' in manager.contents
    True

We can also change the default category:

    >>> manager.getControl('Default Category').value
    ['assignment']

    >>> manager.getControl('Default Category').value = ['exam']
    >>> manager.getControl('Change').click()

    >>> manager.getControl('Default Category').value
    ['exam']

Se up the school year and a couple of terms:

   >>> setup.addSchoolYear('2007', '2007-01-01', '2007-12-31')
   >>> setup.addTerm('Winter', '2007-01-01', '2007-06-01', schoolyear='2007')

Next the administrator defines the courses that are available in the school.

    >>> manager.reload()
    >>> manager.getLink('2007').click()
    >>> manager.getLink('Courses').click()
    >>> manager.getLink('New Course').click()
    >>> manager.getControl('Title').value = 'Physics I'
    >>> manager.getControl('Add').click()
    >>> manager.getLink('Physics I').click()

This completes the initial school setup.


Term Setup
----------

Every term, the administrators of a school are going to setup sections. So
let's add a section for our course:

    >>> from schooltool.app.browser.ftests import setup
    >>> setup.addSection('Physics I', '2007', 'Winter')

But what would a section be without some students and a teacher?

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

Now we can add those people to the section:

    >>> manager.getLink('2007').click()
    >>> manager.getLink('Courses').click()
    >>> manager.getLink('Physics I').click()
    >>> manager.getLink('(1)').click()

    >>> manager.getLink('edit individuals').click()
    >>> manager.getControl('Paul Cardune').click()
    >>> manager.getControl('Tom Hoffman').click()
    >>> manager.getControl('Claudia Richter').click()
    >>> manager.getControl('Add').click()
    >>> manager.getControl('OK').click()

    >>> 'Paul Cardune' in manager.contents
    True

    >>> manager.getLink('edit instructors').click()
    >>> manager.getControl('Stephan Richter').click()
    >>> manager.getControl('Add').click()
    >>> manager.getControl('OK').click()


Instructor should be automatically capable of manipulating activities
and other section data.

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

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.  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 teachers.  In this case, he only teachers the
one section.

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

    >>> stephan = setup.logIn('stephan', 'pwd')
    >>> stephan.getLink('Gradebook').click()
    >>> stephan.getLink('Worksheets').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Worksheets...
    ...Sheet1...

First, we will change the title of our default worksheet to 'Week 1'.

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

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()
    >>> print stephan.contents
    <BLANKLINE>
    ...Worksheets...
    ...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()
    >>> print stephan.contents
    <BLANKLINE>
    ...Worksheet...
    ...New Activity...
    ...Manage Activities...
    ...Physics I (1)...
    ...Claudia...
    ...Paul...
    ...Tom...
    >>> '<span style="font-weight: bold;">Week 1</span>' in stephan.contents
    True

Now, let's add some activities to it.  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').value = ['assignment']
    >>> stephan.getControl('Maximum').value = '50'
    >>> stephan.getControl('Add').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Worksheet...
    ...New Activity...
    ...Manage Activities...
    ...HW 1...

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').value = ['exam']
    >>> stephan.getControl('Add').click()
    >>> 'Quiz' in stephan.contents
    True

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

    >>> stephan.getLink('Manage Activities').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Edit...
    ...New Activity...
    ...Return to Gradebook...
    ...HW 1...
    ...Quiz...
    ...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('Week 2').click()
    >>> '<span style="font-weight: bold;">Week 2</span>' in stephan.contents
    True

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').value = ['assignment']
    >>> stephan.getControl('Add').click()
    >>> 'HW 2' in stephan.contents
    True
    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'Final'
    >>> stephan.getControl('Description').value = 'Final Exam'
    >>> stephan.getControl('Category').value = ['exam']
    >>> stephan.getControl('Add').click()
    >>> 'Final' in stephan.contents
    True

Now that we have all our activities setup, we would like to rearrange their
order more logically. The final in week 2 should really be at the end of the
list. In the browser you should usually just select the new position and some
Javascript would submit the form. Since Javascript is not working in the
tests, we submit, the form manually:

    >>> stephan.getLink('Manage Activities').click()
    >>> stephan.open(stephan.url+'?form-submitted=&pos.Activity-3=3')
    >>> stephan.contents.find('HW 2') \
    ...     < stephan.contents.find('Final')
    True

You can also delete activities that you have created:

    >>> stephan.getLink('New Activity').click()
    >>> stephan.getControl('Title').value = 'HW 3'
    >>> stephan.getControl('Description').value = 'Homework 3'
    >>> stephan.getControl('Category').value = ['assignment']
    >>> stephan.getControl('Add').click()
    >>> stephan.getLink('Manage Activities').click()
    >>> 'HW 3' in stephan.contents
    True
    >>> stephan.getControl(name='delete:list').value = ['Activity-3']
    >>> stephan.getControl('Delete').click()
    >>> 'HW 3' in stephan.contents
    False

Fianlly, let's change the current workskeet back to 'Week 1'.  This setting
of current worksheet will be in effect for the gradebook as well.

    >>> stephan.open('http://localhost/schoolyears/2007/winter/sections/1/gradebook/')
    >>> stephan.getLink('Week 1').click()
    >>> '<span style="font-weight: bold;">Week 1</span>' in stephan.contents
    True


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.

    >>> 'HW 1' in stephan.contents and 'Quiz' in stephan.contents
    True
    >>> 'HW 2' in stephan.contents or 'Final</a>' in stephan.contents
    False

We can enter a score into any cell.  Let's enter one for Claudia's HW 1
activity.  We'll do some trickery to calculate the cell name, taking advantage
of the fact that it's the first cell.

    >>> index = stephan.contents.find('claudia')
    >>> contents = stephan.contents[index:]
    >>> search_text = 'name="'
    >>> index = contents.find(search_text) + len(search_text)
    >>> txt = contents[index:]
    >>> cell_name = txt[:txt.find('"')]
    >>> stephan.getControl(name=cell_name).value = '56'
    >>> stephan.getControl('Update').click()

We should see the score reflected in the spreadsheet.

    >>> stephan.getControl(name=cell_name).value
    '56'

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

    >>> stephan.getControl(name=cell_name).value = 'Bad'
    >>> stephan.getControl('Update').click()
    >>> 'The grade Bad for activity HW 1 is not valid.' in stephan.contents
    True

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

    >>> stephan.getControl(name=cell_name).value = '88'
    >>> stephan.getControl('Update').click()
    >>> stephan.getControl(name=cell_name).value
    '88'

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

    >>> stephan.getControl(name=cell_name).value = ''
    >>> stephan.getControl('Update').click()
    >>> stephan.getControl(name=cell_name).value
    ''

We need to put the score back for future tests to pass.

    >>> stephan.getControl(name=cell_name).value = '36'
    >>> stephan.getControl('Update').click()


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()

Now we just enter the grades. Since Claudia has already a grade, we only need
to grade Paul and Tom:

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

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

    >>> 'The grade -1 for Paul Cardune is not valid.' in stephan.contents
    True

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

    >>> 'value="-1"' in stephan.contents
    True
    >>> 'value="42"' in stephan.contents
    True
    >>> 'value="36"' in stephan.contents
    True
    >>> stephan.getControl('Paul Cardune').value = u'40'
    >>> stephan.getControl('Update').click()

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

    >>> 'value="40"' in stephan.contents
    True
    >>> 'value="42"' in stephan.contents
    True
    >>> 'value="36"' in stephan.contents
    True

Now let's enter again and change a grade:

    >>> stephan.getLink('HW1').click()
    >>> stephan.getControl('Claudia Richter').value = u'48'
    >>> stephan.getControl('Update').click()
    >>> 'value="48"' in stephan.contents
    True

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

    >>> stephan.getLink('HW1').click()
    >>> stephan.getControl('Claudia Richter').value = u''
    >>> stephan.getControl('Update').click()
    >>> 'value="98"' in stephan.contents
    False

Of course, you can also abort the grading.

    >>> stephan.getLink('HW1').click()
    >>> stephan.getControl('Cancel').click()
    >>> stephan.url
    'http://localhost/schoolyears/2007/winter/sections/1/activities/Worksheet/gradebook/index.html'

Let's enter some grades for the second worksheet, 'Week 2'.

    >>> stephan.getLink('Week 2').click()
    >>> stephan.getLink('HW2').click()
    >>> stephan.getControl('Paul Cardune').value = u'90'
    >>> stephan.getControl('Tom Hoffman').value = u'72'
    >>> stephan.getControl('Claudia Richter').value = u'42'
    >>> stephan.getControl('Update').click()

We'll set the current worksheet back to week 1 for the rest of the tests.

    >>> stephan.getLink('Week 1').click()

We need to set Claudia's Quiz score to 86 to replace tests that we deleted.

    >>> stephan.getLink('Quiz').click()
    >>> stephan.getControl('Claudia Richter').value = u'86'
    >>> stephan.getControl('Update').click()
    >>> stephan.url
    'http://localhost/schoolyears/2007/winter/sections/1/activities/Worksheet/gradebook/index.html'


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:

    >>> stephan.contents.find('Claudia') \
    ...     < stephan.contents.find('Paul') \
    ...     < stephan.contents.find('Tom')
    True

Then we want to sort by grade in Homework 1, so we should have:

    >>> import re
    >>> url = re.compile('.*sort_by=-?[0-9]+')
    >>> stephan.getLink(url=url).click()
    >>> stephan.contents.find('Paul') \
    ...     < stephan.contents.find('Tom') \
    ...     < stephan.contents.find('Claudia')
    True

Clicking it again, reverses the order:

    >>> stephan.getLink(url=url).click()
    >>> stephan.contents.find('Claudia') \
    ...     < stephan.contents.find('Tom') \
    ...     < stephan.contents.find('Paul')
    True


Category Weighting
------------------

Let's create some category weights for the current worksheet.

    >>> stephan.getLink('Weight Categories').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Category weights for worksheet Week 1...
    ...Assignment...
    ...Exam...
    ...Lab...
    ...Lab Report...
    ...Project...

First we'll show what happens when a value is invalid.  The only valid weights
will be numbers between 0 and 100.

    >>> stephan.getControl('Assignment').value = u'bad value'
    >>> stephan.getControl('Update').click()
    >>> 'bad value is not a valid weight.' in stephan.contents
    True
    >>> stephan.getControl('Assignment').value = u'-1'
    >>> stephan.getControl('Update').click()
    >>> '-1 is not a valid weight.' in stephan.contents
    True
    >>> stephan.getControl('Assignment').value = u'101'
    >>> stephan.getControl('Update').click()
    >>> '101 is not a valid weight.' in stephan.contents
    True

We'll fill in valid values for Assignment and Exam, but they will not add up
to 100.  We should get an error message to that effect.

    >>> stephan.getControl('Assignment').value = u'35'
    >>> stephan.getControl('Exam').value = u'64'
    >>> stephan.getControl('Update').click()
    >>> 'Category weights must add up to 100.' in stephan.contents
    True

If we get the weights to add up to 100, hitting 'Update' will succeed and return
us to the gradebook.  There we will note the effect of the weighting.

    >>> stephan.getControl('Assignment').value = u'38'
    >>> stephan.getControl('Exam').value = u'62'
    >>> stephan.getControl('Update').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Claudia Richter...
    ...86%</b>...
    ...Tom Hoffman...
    ...84%</b>...
    ...Paul Cardune...
    ...80%</b>...

Finally, we'll test hitting the 'Cancel' button.  It should return to the
gradebook without changing the weights.

    >>> stephan.getLink('Weight Categories').click()
    >>> stephan.getControl('Exam').value
    '62'
    >>> stephan.getControl('Exam').value = u'85'
    >>> stephan.getControl('Cancel').click()
    >>> stephan.getLink('Weight Categories').click()
    >>> stephan.getControl('Exam').value
    '62'


My Grades
---------

Students should also be able to view their grades (not change them), so there's
a view for the student to see them.  Let's log in as Claudia and go to her grades
for the section.  It will come up with Week 1 as the current worksheet,  As
Claudia is only a student and only attends the one section, the 'Gradebook' tab
will take her directly to her grades for that section.

    >>> claudia = setup.logIn('claudia', 'pwd')

    >>> claudia.getLink('Gradebook').click()
    >>> claudia.url
    'http://localhost/schoolyears/2007/winter/sections/1/activities/Worksheet/mygrades'
    >>> 'HW 1' in claudia.contents and 'Quiz' in claudia.contents
    True
    >>> 'HW 2' in claudia.contents or 'Final' in claudia.contents
    False
    >>> claudia.contents.find('Current Grade: 86%') \
    ...     < claudia.contents.find('HW 1') \
    ...     < claudia.contents.find('Quiz') \
    ...     < claudia.contents.find('86 / 100')
    True


Gradebook Startup View
----------------------

Now that we've tested both the teacher's gradebook and the student's mygrades
views, we'll want to more thoroughly test the view that get's launched when
the user clicks on the 'Gradebook' tab.  Up until now, the startup view has
automatically redirected both the teacher and the student to the gradebook and
mygrades views respectively.  But what if the user neither attends or teachers
any classes, like a site manager, or if the user both teachers AND attends
classes?  We will test both of these scenarios.

First, the manager doesn't participate in any classes, so we'll give him a
simple message when he clicks on the 'Gradebook' tab.

    >>> manager.getLink('Gradebook').click()
    >>> print manager.contents
    <BLANKLINE>
    ...You do not teach or attend any classes...

In order to test the second scenario, we will have to create a second section
that has Stephan, teacher of the first Physics I section (1), attending a
second section rather than teaching.

    >>> setup.addSection('Physics I', '2007', 'Winter')
    >>> manager.getLink('2007').click()
    >>> manager.getLink('Courses').click()
    >>> manager.getLink('Physics I').click()
    >>> manager.getLink('(2)').click()

    >>> manager.getLink('edit individuals').click()
    >>> manager.getControl('Stephan Richter').click()
    >>> manager.getControl('Add').click()
    >>> manager.getControl('OK').click()

    >>> manager.getLink('edit instructors').click()
    >>> manager.getControl('Tom Hoffman').click()
    >>> manager.getControl('Add').click()
    >>> manager.getControl('OK').click()

    >>> print manager.contents
    <BLANKLINE>
    ...Instructors...
    ...Tom Hoffman...
    ...Students...
    ...Stephan Richter...

We'll have Tom set up a worksheet.

    >>> tom = setup.logIn('tom', 'pwd')
    >>> tom.getLink('Gradebook').click()
    >>> tom.getLink('Classes you teach').click()
    >>> tom.getLink('Worksheets').click()
    >>> tom.getLink('Sheet1').click()
    >>> tom.getLink('Edit').click()
    >>> tom.getControl('Title').value = 'Week 1'
    >>> tom.getControl('Apply').click()

Now, when Stephan clicks on the 'Gradebook' tab, he will get a startup view
that allows him to go to either his gradebook or his mygrades views.

    >>> stephan.getLink('Gradebook').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Classes you teach...
    ...Classes you attend...

    >>> stephan.getLink('Classes you teach').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Physics I (1)...

    >>> stephan.getLink('Gradebook').click()
    >>> stephan.getLink('Classes you attend').click()
    >>> print stephan.contents
    <BLANKLINE>
    ...Physics I (2)...
    ...Nothing Graded...


Gradebook Security
------------------

It was desirable to move the security tests out of schooltool and into the
schooltool.gradebook package where they belong, so here is where they will
be.

The first test will be for the unauthenticated user.  They should not be able
to see a gradebook and certainly don't have a mygrades view.

    >>> from zope.testbrowser.testing import Browser
    >>> unauth = Browser()
    >>> unauth.handleErrors = False
    >>> unauth.open('http://localhost/schoolyears/2007/winter/sections/1/gradebook')
    Traceback (most recent call last):
    ...
    Unauthorized: ...

    >>> unauth.open('http://localhost/schoolyears/2007/winter/sections/1/mygrades')
    Traceback (most recent call last):
    ...
    Unauthorized: ...

For managers, the default is to allow them to view. but not edit.

    >>> manager.open('http://localhost/schoolyears/2007/winter/sections/1/gradebook')
    >>> print manager.contents
    <BLANKLINE>
    ...Physics I (1)...
    >>> manager.getLink('HW1').click()
    Traceback (most recent call last):
    ...
    Unauthorized: ...

Administration can't grade students by default but can give itself
the permission to do it:

    >>> manager.open('http://localhost')
    >>> manager.getLink('Manage').click()
    >>> manager.getLink('Access Control').click()
    >>> manager.getControl('Administration can grade students').click()
    >>> manager.getControl('Apply').click()

And try again:

    >>> manager.open('http://localhost/schoolyears/2007/winter/sections/1/gradebook')
    >>> manager.getLink('HW1').click()
    >>> manager.getControl(name='tom').value = '45'
    >>> manager.getControl('Update').click()

Let's set the setting back to cover our tracks:

    >>> manager.getLink('Manage').click()
    >>> manager.getLink('Access Control').click()
    >>> manager.getControl('Administration can grade students').click()
    >>> manager.getControl('Apply').click()

A teacher should be able to view and edit his own gradebook.

    >>> stephan.open('http://localhost/schoolyears/2007/winter/sections/1/gradebook')
    >>> print stephan.contents
    <BLANKLINE>
    ...Physics I (1)...
    >>> stephan.getLink('HW1').click()
    >>> stephan.getControl(name='tom').value = '44'
    >>> stephan.getControl('Update').click()

Students won't be able to see each other's grade's because the mygrades view
uses the request's principal to determine which grades to display.

    >>> claudia.open('http://localhost/schoolyears/2007/winter/sections/1/mygrades')
    >>> print claudia.contents
    <BLANKLINE>
    ... Current Grade: 86%...
    >>> tom = setup.logIn('tom', 'pwd')
    >>> tom.open('http://localhost/schoolyears/2007/winter/sections/1/mygrades')
    >>> print tom.contents
    <BLANKLINE>
    ...Current Grade: 88%...

Students should not be able to view a teacher's gradebook.

    >>> claudia.open('http://localhost/schoolyears/2007/winter/sections/1/gradebook')
    Traceback (most recent call last):
    ...
    Unauthorized: ...


Export Worksheets as XLS
------------------------

Gradebook's worksheets can be exported to a XLS file:

    >>> stephan.getLink('Worksheets').click()
    >>> stephan.getLink('Export XLS').click()
    >>> stephan.headers.get('Content-Type')
    'application/excel'


External Activities
-------------------

External activities allow to get grades for a worksheet activity from
another schooltool module.

Before we test external activities, we are going to record the current
state of the gradebook:

    >>> stephan.open('http://localhost/gradebook.html')
    >>> stephan.getLink('Classes you teach').click()
    >>> '<span style="font-weight: bold;">Week 1</span>' in stephan.contents
    True
    >>> stephan.getLink('Manage Activities').click()

We have two regular activities. One assignment:

    >>> stephan.getLink('HW 1').click()
    >>> stephan.getControl('Category').displayValue
    ['Assignment']
    >>> stephan.getControl('Cancel').click()

# XXX Cancel does not return you back where you came from!

    >>> stephan.getLink('Gradebook').click()
    >>> stephan.getLink('Classes you teach').click()
    >>> stephan.getLink('Manage Activities').click()

And one exam:

    >>> stephan.getLink('Quiz').click()
    >>> stephan.getControl('Category').displayValue
    ['Exam']
    >>> stephan.getControl('Cancel').click()

# XXX Cancel does not return you back where you came from!

    >>> stephan.getLink('Gradebook').click()
    >>> stephan.getLink('Classes you teach').click()
    >>> stephan.getLink('Manage Activities').click()

And our grades are:

    >>> stephan.getLink('Return to Gradebook').click()
    >>> stephan.contents
    <BLANKLINE>
    ...Name...Total...Ave...HW 1...Quiz...
    ...Claudia Richter...<b>86</b>...<b>86%</b>...86...
    ...Tom Hoffman...<b>44</b>...<b>88%</b>...44...
    ...Paul Cardune...<b>40</b>...<b>80%</b>...40...

This state should change after we update the grades from external
activities. Remember that the gradebook has weighting defined?:

    >>> stephan.getLink('Weight Categories').click()
    >>> stephan.contents
    <BLANKLINE>
    ...Category weights for worksheet Week 1...
    ...Assignment...38...
    ...Exam...62...
    ...Lab...

This is important since external activities should be weightable. Now,
let's go back to the 'Activities' view of Stephan's teaching
gradebook:

    >>> stephan.getControl('Cancel').click()
    >>> stephan.getLink('Manage Activities').click()

We should have a 'New External Activity' button to add external
activities:

    >>> stephan.contents
    <BLANKLINE>
    ...Edit...
    ...New Activity...New External Activity...
    ...Return to Gradebook...

Let's add a new external activity as an assignment. For this test we
have registered two external utilities stubs titled "Hardware" and
"HTML":

    >>> stephan.getLink('New External Activity').click()
    >>> hardware_token = "samplesource-hardware"
    >>> stephan.contents
    <BLANKLINE>
    ...Add an External Activity...
    >>> stephan.getControl('External Activity').value = [hardware_token]
    >>> stephan.getControl('Category').value = ['assignment']
    >>> stephan.getControl('Points').value = '15'
    >>> stephan.getControl('Add').click()

Adding an external activity gets us back to the gradebook index view
where we can see the external activity which by default has been
loaded with the latest grades:

    >>> stephan.contents
    <BLANKLINE>
    ...Name...Total...Ave...HW 1...Quiz...Hardware...
    ...Claudia Richter...92.00...69%...86...6.00...
    ...Tom Hoffman...53.00...82%...44...9.00...
    ...Paul Cardune...40...80%...40...

Let's edit the external activity. The form doesn't allow to edit the
score system. The edit view also shows an 'Update Grades' button to
recalculate the activity grades from the external activity:

    >>> stephan.getLink('Manage Activities').click()
    >>> stephan.getLink('Hardware').click()
    >>> 'score system' not in stephan.contents
    True
    >>> 'Maximum' not in stephan.contents
    True
    >>> stephan.contents
    <BLANKLINE>
    ...Update Grades...
    ...<h3>Edit External Activity</h3>...
    ...External Activity...Sample Source - Hardware...
    ...Title...Hardware...
    ...Description...Hardware description...
    ...Category...
    ...<option...selected="selected"...value="assignment"...>Assignment...
    ...Points...15...
    >>> stephan.getControl('Title').value = u"Hardware Assignment"
    >>> stephan.getControl('Description').value = "The Hardware assignment"
    >>> stephan.getControl('Points').value = '25'
    >>> stephan.getControl('Apply').click()

Let's go back to the edit form to update the activity's grades using
the 'Update Grades' button:

    >>> stephan.getLink('Manage Activities').click()
    >>> stephan.getLink('Hardware Assignment').click()
    >>> stephan.contents
    <BLANKLINE>
    ...Update Grades...
    ...<h3>Edit External Activity</h3>...
    ...External Activity...Sample Source - Hardware...
    ...Title...Hardware Assignment...
    ...Description...The Hardware assignment...
    ...Category...
    ...<option...selected="selected"...value="assignment"...>Assignment...
    ...Points...25...
    >>> stephan.getLink('Update Grades').click()

This takes us to the gradebook where the averages should have
changed taking into account the weighting:

    >>> stephan.contents
    <BLANKLINE>
    ...Name...Total...Ave...HW 1...Quiz...Hardware As...
    ...Claudia Richter...96.00...69%...86...10.00...
    ...Tom Hoffman...59.00...79%...44...15.00...
    ...Paul Cardune...40...80%...40...

