=====================
Student Interventions
=====================

This package supplies the school with a number of objects for tracking
interventions with students who are having either academic or behavioral
problems.  Objects include messages passed between concerned faculty members
and goals that are established for the student.

Let's import some zope stuff before we use it.

    >>> from zope.component import (provideHandler, provideUtility,
    ...     getMultiAdapter)
    >>> from zope.lifecycleevent.interfaces import (IObjectAddedEvent,
    ...     IObjectRemovedEvent)
    >>> from zope.interface.verify import verifyObject

We'll set up the app object, a schoolyear and a current term.

    >>> from schooltool.testing import setup as stsetup
    >>> app = stsetup.setUpSchoolToolSite()

    >>> from datetime import date
    >>> from schooltool.schoolyear.interfaces import ISchoolYearContainer
    >>> from schooltool.schoolyear.schoolyear import SchoolYear
    >>> schoolyear = SchoolYear("Sample", date(2004, 9, 1), date(2005, 12, 20))
    >>> ISchoolYearContainer(app)['2004-2005'] = schoolyear

    >>> from schooltool.term.interfaces import ITermContainer
    >>> from schooltool.term.term import Term
    >>> term = Term('Sample', date(2004, 9, 1), date(2004, 12, 20))
    >>> terms = ITermContainer(app)
    >>> terms['2004-fall'] = term

    >>> from schooltool.term.tests import setUpDateManagerStub
    >>> setUpDateManagerStub(date(2004, 9, 1), term)


Interventions
-------------

The intervention objects make up a hierarchy starting at the intervention root
which hangs off of the application root.  It has one InterventionSchoolYear
object per school year each containing whatever InterventionStudent objects
were created for the given year.  The InterventionStudent objects have two
containers under them for messages and goals.

The first adapter we have is to get the intervention root. 

    >>> from schooltool.intervention import intervention, interfaces
    >>> interventionRoot = interfaces.IInterventionRoot(app)
    >>> verifyObject(interfaces.IInterventionRoot, interventionRoot)
    True

It will have no InterventionSchoolYear objects  in it yet because they are not
created until needed.  They are auto-vivified when the adapter that seeks
them is called.

    >>> [key for key in interventionRoot]
    []

We have an adapter that adapts a SchoolYear to an InterventionSchoolYear.
It will create the missing InterventionSchoolYear and return it to us.

    >>> interventionSchoolYear = interfaces.IInterventionSchoolYear(schoolyear)
    >>> verifyObject(interfaces.IInterventionSchoolYear, interventionSchoolYear)
    True
    >>> [key for key in interventionRoot]
    [u'2004-2005']

If we adapt the app to IInterventionSchoolYear it will return the one for the
current school year which is the same year.

    >>> interventionSchoolYear is interfaces.IInterventionSchoolYear(app)
    True

Let's create a student and a contact for the parent and set both emails.

    >>> from schooltool.contact.interfaces import IContact, IContactable
    >>> from schooltool.contact.contact import Contact
    >>> def addEmail(person):
    ...     IContact(person).email = '%s@example.com' % person.__name__

    >>> from schooltool.basicperson.person import BasicPerson
    >>> jdoe = BasicPerson('jdoe', 'John', 'Doe')
    >>> app['persons']['jdoe'] = jdoe
    >>> addEmail(jdoe)

    >>> parent1 = Contact()
    >>> parent1.email = 'parent1@provider.com'
    >>> IContactable(jdoe).contacts.add(parent1)

We can adapt from a student, schoolyear pair to an InterventionStudent.
Since this is the first time, it will be created automatically.

    >>> [key for key in interventionSchoolYear]
    []
    >>> jdoeIntervention = getMultiAdapter((jdoe, schoolyear),
    ...     interfaces.IInterventionStudent)
    >>> [key for key in interventionSchoolYear]
    [u'jdoe']
    >>> verifyObject(interfaces.IInterventionStudent, jdoeIntervention)
    True

We can also adapt directly from the student, in which case the current school
year will be assumed.  Since that year is the same, we will get the same
InterventionStudent back.

    >>> jdoeIntervention is interfaces.IInterventionStudent(jdoe)
    True

We also have adapter from any intervention object, InterventionStudent and
below, to the student object itself.

    >>> from schooltool.person.interfaces import IPerson
    >>> IPerson(jdoeIntervention) is jdoe
    True

The new InterventionStudent will have messages and goals containers in it.

    >>> [key for key in jdoeIntervention]
    [u'goals', u'messages']
    >>> jdoeMessages = jdoeIntervention['messages']
    >>> verifyObject(interfaces.IInterventionMessages, jdoeMessages)
    True
    >>> len(jdoeMessages)
    0
    >>> jdoeGoals = jdoeIntervention['goals']
    >>> verifyObject(interfaces.IInterventionGoals, jdoeGoals)
    True
    >>> len(jdoeGoals)
    0

We'll note that the student adapter returns the correct student for both
containers.

    >>> IPerson(jdoeMessages) is jdoe
    True
    >>> IPerson(jdoeGoals) is jdoe
    True

Intervention Messagess
----------------------

Now we will create some InterventionMessage objects for the student and put
them in the student's InterventionMessages container.

First, we'll need to register the object added handler that sends the message
to the recipients as an email as well as the dummy mail sender that we need
for testing.

    >>> from schooltool.email.interfaces import IEmailUtility
    >>> from schooltool.intervention import sendmail
    >>> provideHandler(sendmail.sendInterventionMessageEmail, 
    ...                adapts=(interfaces.IInterventionMessage, IObjectAddedEvent))
    >>> provideUtility(sendmail.TestMailDelivery(), provides=IEmailUtility)

    >>> manager_user = BasicPerson('manager', 'SchoolTool', 'Manager')
    >>> app['persons']['manager'] = manager_user
    >>> addEmail(manager_user)

We will create person objects for some teachers and advisors.

    >>> teacher1 = BasicPerson('teacher1', '1', 'Teacher')
    >>> app['persons']['teacher1'] = teacher1
    >>> addEmail(teacher1)
    >>> teacher2 = BasicPerson('teacher2', '2', 'Teacher')
    >>> app['persons']['teacher2'] = teacher2
    >>> addEmail(teacher2)
    >>> advisor1 = BasicPerson('advisor1', '1', 'Advisor')
    >>> app['persons']['advisor1'] = advisor1
    >>> addEmail(advisor1)
    >>> advisor2 = BasicPerson('advisor2', '2', 'Advisor')
    >>> app['persons']['advisor2'] = advisor2
    >>> addEmail(advisor2)

Now we'll create a message and add it to the container.  Upon adding the message
the dummy mail sender will print out the email that would otherwise be sent to
a real SMTP server.

    >>> body = "John has been a bad student."
    >>> message1 = intervention.InterventionMessage(teacher1, 
    ...     [teacher2, advisor1], body)
    >>> verifyObject(interfaces.IInterventionMessage, message1)
    True
    >>> jdoeMessages['1'] = message1
    From: teacher1@example.com
    To: advisor1@example.com, teacher2@example.com
    Subject: INTERVENTION MESSAGE: John Doe
    1 Teacher writes:
    <BLANKLINE>
    John has been a bad student.

We'll note that the student adapter returns the correct student.

    >>> IPerson(message1) is jdoe
    True

We'll create another message with different sender and recipients and add it
to the container.  The difference will be reflected in the email message.

    >>> body = "John still needs to learn to behave."
    >>> message2 = intervention.InterventionMessage(teacher2, 
    ...     [advisor1, advisor2], body)
    >>> jdoeMessages['2'] = message2
    From: teacher2@example.com
    To: advisor1@example.com, advisor2@example.com
    Subject: INTERVENTION MESSAGE: John Doe
    2 Teacher writes:
    <BLANKLINE>
    John still needs to learn to behave.

Intervention Goals
------------------

First, we'll need to register the object added handler that sends an email
to the persons responsible for a goal when the goal is added.

    >>> provideHandler(sendmail.sendInterventionGoalAddEmail, 
    ...                adapts=(interfaces.IInterventionGoal, IObjectAddedEvent))

Let's create some InterventionGoal objects for the student and put them in
the student's InterventionGoals container.  We'll note that emails will
be sent when a goal is added to the goals container.

    >>> from datetime import date
    >>> goal1 = intervention.InterventionGoal('bad behaviour', 'be nicer', 
    ...     'smart', 'nicer to clasmates', 'teach manners', date(2004, 9, 1),
    ...     [advisor1, advisor2], creator=teacher1)
    >>> verifyObject(interfaces.IInterventionGoal, goal1)
    True
    >>> jdoeGoals['1'] = goal1
    From: teacher1@example.com
    To: advisor1@example.com, advisor2@example.com
    Subject: INTERVENTION GOAL ADDED: John Doe
    The following goal was added for John Doe:
    <BLANKLINE>
    Presenting concerns
    -------------------
    <BLANKLINE>
    bad behaviour
    <BLANKLINE>
    Goal
    ----
    <BLANKLINE>
    be nicer
    <BLANKLINE>
    Strengths
    ---------
    <BLANKLINE>
    smart
    <BLANKLINE>
    Indicators
    ----------
    <BLANKLINE>
    nicer to clasmates
    <BLANKLINE>
    Intervention
    ------------
    <BLANKLINE>
    teach manners
    <BLANKLINE>
    Timeline
    --------
    <BLANKLINE>
    ...
    <BLANKLINE>
    Persons responsible
    -------------------
    <BLANKLINE>
    1 Advisor
    2 Advisor
    <BLANKLINE>
    Intervention Center
    -------------------
    <BLANKLINE>
    http://127.0.0.1/schooltool.interventions/2004-2005/jdoe
    <BLANKLINE>

We'll note that the student adapter returns the correct student.

    >>> IPerson(goal1) is jdoe
    True

As it turns out, goals have an at_one_time_responsible attribute that basically
is a union of every different value persons_responsible has had for the life
of the goal object.  Presently it's the same as persons_responsible.

    >>> sorted([IPerson(contact).username for contact in goal1.at_one_time_responsible])
    ['advisor1', 'advisor2']

If we change the persons_responsible to have a new user, we'll see that the
at_one_time_responsible attribute will have a record of all the historical
values.

    >>> goal1.persons_responsible = [teacher1]
    >>> sorted([IPerson(contact).username for contact in goal1.at_one_time_responsible])
    ['advisor1', 'advisor2', 'teacher1']

We'll restore persons_responsible for later tests.

    >>> goal1.persons_responsible = [advisor1, advisor2]

Note that at_one_time_responsible was not effected.

    >>> sorted([IPerson(contact).username for contact in goal1.at_one_time_responsible])
    ['advisor1', 'advisor2', 'teacher1']

Let's add a second one.

    >>> goal2 = intervention.InterventionGoal('bad grades', 'passing grades', 
    ...     'friendly', 'grades are passing', 'tutor student', date.today(),
    ...     [teacher1, advisor2], creator=teacher1)
    >>> jdoeGoals['2'] = goal2
    From: teacher1@example.com
    To: advisor2@example.com, teacher1@example.com
    Subject: INTERVENTION GOAL ADDED: John Doe
    The following goal was added for John Doe:
    <BLANKLINE>
    Presenting concerns
    -------------------
    <BLANKLINE>
    bad grades
    <BLANKLINE>
    Goal
    ----
    <BLANKLINE>
    passing grades
    <BLANKLINE>
    Strengths
    ---------
    <BLANKLINE>
    friendly
    <BLANKLINE>
    Indicators
    ----------
    <BLANKLINE>
    grades are passing
    <BLANKLINE>
    Intervention
    ------------
    <BLANKLINE>
    tutor student
    <BLANKLINE>
    Timeline
    --------
    <BLANKLINE>
    ...
    <BLANKLINE>
    Persons responsible
    -------------------
    <BLANKLINE>
    1 Teacher
    2 Advisor
    <BLANKLINE>
    Intervention Center
    -------------------
    <BLANKLINE>
    http://127.0.0.1/schooltool.interventions/2004-2005/jdoe
    <BLANKLINE>

We chose date.today() as the timeline for our goals because we wanted to test
right away the method in our sendmail module that notifies the persons
responsible via email when the timeline has been reached for a goal.  We'll
call this method and see the email messages that get generated.   Also, we'll
need to add the schooltool manager user that's expected by the routine to be
present.

    >>> from schooltool.intervention import sendmail
    >>> notified = sendmail.sendInterventionGoalNotifyEmails()
    From: teacher1@example.com
    To: advisor1@example.com, advisor2@example.com
    Subject: INTERVENTION GOAL DUE: John Doe
    Please follow the link below to update the follow up notes and, if
    appropriate, the goal met status of the intervention goal for John Doe.
    <BLANKLINE>
    http://127.0.0.1/schooltool.interventions/.../jdoe/goals/1/@@editGoal.html
    <BLANKLINE>
    From: teacher1@example.com
    To: advisor2@example.com, teacher1@example.com
    Subject: INTERVENTION GOAL DUE: John Doe
    Please follow the link below to update the follow up notes and, if
    appropriate, the goal met status of the intervention goal for John Doe.
    <BLANKLINE>
    http://127.0.0.1/schooltool.interventions/.../jdoe/goals/2/@@editGoal.html
    <BLANKLINE>
    >>> len(notified)
    2

If we call the same routine again, we will get nothing because the notified
flags have been set on the goals.

    >>> notified = sendmail.sendInterventionGoalNotifyEmails()
    >>> len(notified)
    0

Convenience functions
---------------------

We have a couple of convenience functions for converting contacts to sorted
lists of names (by last name) or email.

    >>> from schooltool.contact.interfaces import IContact
    >>> intervention.contactName(IContact(teacher1))
    '1 Teacher'
    >>> intervention.contactsName([IContact(teacher1), IContact(advisor1)])
    ['1 Advisor', '1 Teacher']
    >>> intervention.contactsEmail([IContact(teacher1), IContact(advisor1)])
    [u'advisor1@example.com', u'teacher1@example.com']

