This file is indexed.

/usr/lib/python2.7/dist-packages/schooltool/intervention/README.txt is in python-schooltool.intervention 2.7.1-0ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
=====================
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

    >>> 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 = app['persons']['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 dummy mail sender for testing.

    >>> from schooltool.email.interfaces import IEmailUtility
    >>> from schooltool.intervention import sendmail
    >>> 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.

    >>> body = "John has been a bad student."
    >>> message1 = app['messages']['message1'] = intervention.InterventionMessage(body)
    >>> message1.sender.add(IContact(teacher1))
    >>> message1.recipients.add(IContact(teacher2))
    >>> message1.recipients.add(IContact(advisor1))
    >>> verifyObject(interfaces.IInterventionMessage, message1)
    True
    >>> jdoeMessages['1'] = message1

We'll call the method in sendmail for emailing the message which causes the
dummy mail sender to print out the email that would otherwise be sent to
a real SMTP server.

    >>> email = sendmail.sendInterventionMessageEmail(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 = app['messages']['message2'] = intervention.InterventionMessage(body)
    >>> message2.sender.add(IContact(teacher2))
    >>> message2.recipients.add(IContact(advisor1))
    >>> message2.recipients.add(IContact(advisor2))
    >>> jdoeMessages['2'] = message2
    >>> email = sendmail.sendInterventionMessageEmail(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
------------------

Let's create some InterventionGoal objects for the student and put them in
the student's InterventionGoals container.

    >>> from datetime import date
    >>> goal1 = app['goals']['goal1'] = intervention.InterventionGoal('be nicer', date(2004, 9, 1),
    ...     'bad behaviour', 'smart',
    ...     'nicer to clasmates', 'teach manners')
    >>> goal1.creator.add(IContact(teacher1))
    >>> goal1.persons_responsible = [advisor1, advisor2]
    >>> verifyObject(interfaces.IInterventionGoal, goal1)
    True
    >>> jdoeGoals['1'] = goal1

We'll call the method in sendmail for emailing the goal which causes the
dummy mail sender to print out the email that would otherwise be sent to
a real SMTP server.

    >>> emails = sendmail.sendInterventionGoalAddEmail(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 = app['goals']['goal2'] = intervention.InterventionGoal('passing grades', date.today(),
    ...     'bad grades', 'friendly',
    ...     'grades are passing', 'tutor student')
    >>> goal2.creator.add(IContact(teacher1))
    >>> goal2.persons_responsible = [teacher1, advisor2]
    >>> jdoeGoals['2'] = goal2

Send the goal email.

    >>> emails = sendmail.sendInterventionGoalAddEmail(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']