source: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_browser.py @ 9687

Last change on this file since 9687 was 9687, checked in by Henrik Bettermann, 12 years ago

Remove total_credits method from view. This is already done in the content class.

Calculate momentary gpa and show on level page.

  • Property svn:keywords set to Id
File size: 135.8 KB
Line 
1## $Id: test_browser.py 9687 2012-11-19 18:08:39Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""
19Test the student-related UI components.
20"""
21import shutil
22import tempfile
23import pytz
24import base64
25from datetime import datetime, timedelta
26from StringIO import StringIO
27import os
28import grok
29from zope.event import notify
30from zope.component import createObject, queryUtility
31from zope.component.hooks import setSite, clearSite
32from zope.catalog.interfaces import ICatalog
33from zope.security.interfaces import Unauthorized
34from zope.securitypolicy.interfaces import IPrincipalRoleManager
35from zope.testbrowser.testing import Browser
36from hurry.workflow.interfaces import IWorkflowInfo, IWorkflowState
37from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase
38from waeup.kofa.app import University
39from waeup.kofa.payments.interfaces import IPaymentWebservice
40from waeup.kofa.students.student import Student
41from waeup.kofa.students.studylevel import StudentStudyLevel
42from waeup.kofa.university.faculty import Faculty
43from waeup.kofa.university.department import Department
44from waeup.kofa.interfaces import IUserAccount
45from waeup.kofa.authentication import LocalRoleSetEvent
46from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED
47
48PH_LEN = 2059  # Length of placeholder file
49
50SAMPLE_IMAGE = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
51SAMPLE_IMAGE_BMP = os.path.join(os.path.dirname(__file__), 'test_image.bmp')
52
53def lookup_submit_value(name, value, browser):
54    """Find a button with a certain value."""
55    for num in range(0, 100):
56        try:
57            button = browser.getControl(name=name, index=num)
58            if button.value.endswith(value):
59                return button
60        except IndexError:
61            break
62    return None
63
64class StudentsFullSetup(FunctionalTestCase):
65    # A test case that only contains a setup and teardown
66    #
67    # Complete setup for students handlings is rather complex and
68    # requires lots of things created before we can start. This is a
69    # setup that does all this, creates a university, creates PINs,
70    # etc.  so that we do not have to bother with that in different
71    # test cases.
72
73    layer = FunctionalLayer
74
75    def setUp(self):
76        super(StudentsFullSetup, self).setUp()
77
78        # Setup a sample site for each test
79        app = University()
80        self.dc_root = tempfile.mkdtemp()
81        app['datacenter'].setStoragePath(self.dc_root)
82
83        # Prepopulate the ZODB...
84        self.getRootFolder()['app'] = app
85        # we add the site immediately after creation to the
86        # ZODB. Catalogs and other local utilities are not setup
87        # before that step.
88        self.app = self.getRootFolder()['app']
89        # Set site here. Some of the following setup code might need
90        # to access grok.getSite() and should get our new app then
91        setSite(app)
92
93        # Add student with subobjects
94        student = createObject('waeup.Student')
95        student.firstname = u'Anna'
96        student.lastname = u'Tester'
97        student.reg_number = u'123'
98        student.matric_number = u'234'
99        student.sex = u'm'
100        student.email = 'aa@aa.ng'
101        student.phone = u'1234'
102        self.app['students'].addStudent(student)
103        self.student_id = student.student_id
104        self.student = self.app['students'][self.student_id]
105
106        # Set password
107        IUserAccount(
108            self.app['students'][self.student_id]).setPassword('spwd')
109
110        self.login_path = 'http://localhost/app/login'
111        self.container_path = 'http://localhost/app/students'
112        self.manage_container_path = self.container_path + '/@@manage'
113        self.add_student_path = self.container_path + '/addstudent'
114        self.student_path = self.container_path + '/' + self.student_id
115        self.manage_student_path = self.student_path + '/manage_base'
116        self.trigtrans_path = self.student_path + '/trigtrans'
117        self.clearance_path = self.student_path + '/view_clearance'
118        self.personal_path = self.student_path + '/view_personal'
119        self.edit_clearance_path = self.student_path + '/cedit'
120        self.manage_clearance_path = self.student_path + '/manage_clearance'
121        self.edit_personal_path = self.student_path + '/edit_personal'
122        self.manage_personal_path = self.student_path + '/manage_personal'
123        self.studycourse_path = self.student_path + '/studycourse'
124        self.payments_path = self.student_path + '/payments'
125        self.acco_path = self.student_path + '/accommodation'
126        self.history_path = self.student_path + '/history'
127
128        # Create 5 access codes with prefix'PWD'
129        pin_container = self.app['accesscodes']
130        pin_container.createBatch(
131            datetime.utcnow(), 'some_userid', 'PWD', 9.99, 5)
132        pins = pin_container['PWD-1'].values()
133        self.pwdpins = [x.representation for x in pins]
134        self.existing_pwdpin = self.pwdpins[0]
135        parts = self.existing_pwdpin.split('-')[1:]
136        self.existing_pwdseries, self.existing_pwdnumber = parts
137        # Create 5 access codes with prefix 'CLR'
138        pin_container.createBatch(
139            datetime.now(), 'some_userid', 'CLR', 9.99, 5)
140        pins = pin_container['CLR-1'].values()
141        pins[0].owner = u'Hans Wurst'
142        self.existing_clrac = pins[0]
143        self.existing_clrpin = pins[0].representation
144        parts = self.existing_clrpin.split('-')[1:]
145        self.existing_clrseries, self.existing_clrnumber = parts
146        # Create 2 access codes with prefix 'HOS'
147        pin_container.createBatch(
148            datetime.now(), 'some_userid', 'HOS', 9.99, 2)
149        pins = pin_container['HOS-1'].values()
150        self.existing_hosac = pins[0]
151        self.existing_hospin = pins[0].representation
152        parts = self.existing_hospin.split('-')[1:]
153        self.existing_hosseries, self.existing_hosnumber = parts
154
155        # Populate university
156        self.certificate = createObject('waeup.Certificate')
157        self.certificate.code = u'CERT1'
158        self.certificate.application_category = 'basic'
159        self.certificate.study_mode = 'ug_ft'
160        self.certificate.start_level = 100
161        self.certificate.end_level = 500
162        self.certificate.school_fee_1 = 40000.0
163        self.certificate.school_fee_2 = 20000.0
164        self.app['faculties']['fac1'] = Faculty(code=u'fac1')
165        self.app['faculties']['fac1']['dep1'] = Department(code=u'dep1')
166        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
167            self.certificate)
168        self.course = createObject('waeup.Course')
169        self.course.code = 'COURSE1'
170        self.course.semester = 1
171        self.course.credits = 10
172        self.course.passmark = 40
173        self.app['faculties']['fac1']['dep1'].courses.addCourse(
174            self.course)
175        self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse(
176            self.course, level=100)
177
178        # Configure university and hostels
179        self.app['hostels'].accommodation_states = ['admitted']
180        self.app['hostels'].accommodation_session = 2004
181        delta = timedelta(days=10)
182        self.app['hostels'].startdate = datetime.now(pytz.utc) - delta
183        self.app['hostels'].enddate = datetime.now(pytz.utc) + delta
184        self.app['configuration'].carry_over = True
185        configuration = createObject('waeup.SessionConfiguration')
186        configuration.academic_session = 2004
187        configuration.clearance_fee = 3456.0
188        configuration.booking_fee = 123.4
189        configuration.maint_fee = 987.0
190        self.app['configuration'].addSessionConfiguration(configuration)
191
192        # Create a hostel with two beds
193        hostel = Hostel()
194        hostel.hostel_id = u'hall-1'
195        hostel.hostel_name = u'Hall 1'
196        self.app['hostels'].addHostel(hostel)
197        bed = Bed()
198        bed.bed_id = u'hall-1_A_101_A'
199        bed.bed_number = 1
200        bed.owner = NOT_OCCUPIED
201        bed.bed_type = u'regular_male_fr'
202        self.app['hostels'][hostel.hostel_id].addBed(bed)
203        bed = Bed()
204        bed.bed_id = u'hall-1_A_101_B'
205        bed.bed_number = 2
206        bed.owner = NOT_OCCUPIED
207        bed.bed_type = u'regular_female_fr'
208        self.app['hostels'][hostel.hostel_id].addBed(bed)
209
210        # Set study course attributes of test student
211        self.student['studycourse'].certificate = self.certificate
212        self.student['studycourse'].current_session = 2004
213        self.student['studycourse'].entry_session = 2004
214        self.student['studycourse'].current_verdict = 'A'
215        self.student['studycourse'].current_level = 100
216        # Update the catalog
217        notify(grok.ObjectModifiedEvent(self.student))
218
219        # Put the prepopulated site into test ZODB and prepare test
220        # browser
221        self.browser = Browser()
222        self.browser.handleErrors = False
223
224    def tearDown(self):
225        super(StudentsFullSetup, self).tearDown()
226        clearSite()
227        shutil.rmtree(self.dc_root)
228
229
230
231class StudentsContainerUITests(StudentsFullSetup):
232    # Tests for StudentsContainer class views and pages
233
234    layer = FunctionalLayer
235
236    def test_anonymous_access(self):
237        # Anonymous users can't access students containers
238        self.assertRaises(
239            Unauthorized, self.browser.open, self.container_path)
240        self.assertRaises(
241            Unauthorized, self.browser.open, self.manage_container_path)
242        return
243
244    def test_manage_access(self):
245        # Managers can access the view page of students
246        # containers and can perform actions
247        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
248        self.browser.open(self.container_path)
249        self.assertEqual(self.browser.headers['Status'], '200 Ok')
250        self.assertEqual(self.browser.url, self.container_path)
251        self.browser.getLink("Manage student section").click()
252        self.assertEqual(self.browser.headers['Status'], '200 Ok')
253        self.assertEqual(self.browser.url, self.manage_container_path)
254        return
255
256    def test_add_search_delete_students(self):
257        # Managers can add search and remove students
258        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
259        self.browser.open(self.manage_container_path)
260        self.browser.getLink("Add student").click()
261        self.assertEqual(self.browser.headers['Status'], '200 Ok')
262        self.assertEqual(self.browser.url, self.add_student_path)
263        self.browser.getControl(name="form.firstname").value = 'Bob'
264        self.browser.getControl(name="form.lastname").value = 'Tester'
265        self.browser.getControl(name="form.reg_number").value = '123'
266        self.browser.getControl("Create student record").click()
267        self.assertTrue('Registration number exists already'
268            in self.browser.contents)
269        self.browser.getControl(name="form.reg_number").value = '1234'
270        self.browser.getControl("Create student record").click()
271        self.assertTrue('Student record created' in self.browser.contents)
272
273        # Registration and matric numbers must be unique
274        self.browser.getLink("Manage").click()
275        self.browser.getControl(name="form.reg_number").value = '123'
276        self.browser.getControl("Save").click()
277        self.assertMatches('...Registration number exists...',
278                           self.browser.contents)
279        self.browser.getControl(name="form.reg_number").value = '789'
280        self.browser.getControl(name="form.matric_number").value = '234'
281        self.browser.getControl("Save").click()
282        self.assertMatches('...Matriculation number exists...',
283                           self.browser.contents)
284
285        # We can find a student with a certain student_id
286        self.browser.open(self.container_path)
287        self.browser.getControl("Search").click()
288        self.assertTrue('Empty search string' in self.browser.contents)
289        self.browser.getControl(name="searchtype").value = ['student_id']
290        self.browser.getControl(name="searchterm").value = self.student_id
291        self.browser.getControl("Search").click()
292        self.assertTrue('Anna Tester' in self.browser.contents)
293
294        # We can find a student in a certain session
295        self.browser.open(self.container_path)
296        self.browser.getControl(name="searchtype").value = ['current_session']
297        self.browser.getControl(name="searchterm").value = '2004'
298        self.browser.getControl("Search").click()
299        self.assertTrue('Anna Tester' in self.browser.contents)
300        # Session fileds require integer values
301        self.browser.open(self.container_path)
302        self.browser.getControl(name="searchtype").value = ['current_session']
303        self.browser.getControl(name="searchterm").value = '2004/2005'
304        self.browser.getControl("Search").click()
305        self.assertTrue('Only year dates allowed' in self.browser.contents)
306        self.browser.open(self.manage_container_path)
307        self.browser.getControl(name="searchtype").value = ['current_session']
308        self.browser.getControl(name="searchterm").value = '2004/2005'
309        self.browser.getControl("Search").click()
310        self.assertTrue('Only year dates allowed' in self.browser.contents)
311
312        # We can find a student in a certain study_mode
313        self.browser.open(self.container_path)
314        self.browser.getControl(name="searchtype").value = ['current_mode']
315        self.browser.getControl(name="searchterm").value = 'ug_ft'
316        self.browser.getControl("Search").click()
317        self.assertTrue('Anna Tester' in self.browser.contents)
318
319        # We can find a student in a certain department
320        self.browser.open(self.container_path)
321        self.browser.getControl(name="searchtype").value = ['depcode']
322        self.browser.getControl(name="searchterm").value = 'dep1'
323        self.browser.getControl("Search").click()
324        self.assertTrue('Anna Tester' in self.browser.contents)
325
326        # We can find a student by searching for all kind of name parts
327        self.browser.open(self.manage_container_path)
328        self.browser.getControl("Search").click()
329        self.assertTrue('Empty search string' in self.browser.contents)
330        self.browser.getControl(name="searchtype").value = ['fullname']
331        self.browser.getControl(name="searchterm").value = 'Anna Tester'
332        self.browser.getControl("Search").click()
333        self.assertTrue('Anna Tester' in self.browser.contents)
334        self.browser.open(self.manage_container_path)
335        self.browser.getControl(name="searchtype").value = ['fullname']
336        self.browser.getControl(name="searchterm").value = 'Anna'
337        self.browser.getControl("Search").click()
338        self.assertTrue('Anna Tester' in self.browser.contents)
339        self.browser.open(self.manage_container_path)
340        self.browser.getControl(name="searchtype").value = ['fullname']
341        self.browser.getControl(name="searchterm").value = 'Tester'
342        self.browser.getControl("Search").click()
343        self.assertTrue('Anna Tester' in self.browser.contents)
344        self.browser.open(self.manage_container_path)
345        self.browser.getControl(name="searchtype").value = ['fullname']
346        self.browser.getControl(name="searchterm").value = 'An'
347        self.browser.getControl("Search").click()
348        self.assertFalse('Anna Tester' in self.browser.contents)
349        self.browser.open(self.manage_container_path)
350        self.browser.getControl(name="searchtype").value = ['fullname']
351        self.browser.getControl(name="searchterm").value = 'An*'
352        self.browser.getControl("Search").click()
353        self.assertTrue('Anna Tester' in self.browser.contents)
354        self.browser.open(self.manage_container_path)
355        self.browser.getControl(name="searchtype").value = ['fullname']
356        self.browser.getControl(name="searchterm").value = 'tester'
357        self.browser.getControl("Search").click()
358        self.assertTrue('Anna Tester' in self.browser.contents)
359        self.browser.open(self.manage_container_path)
360        self.browser.getControl(name="searchtype").value = ['fullname']
361        self.browser.getControl(name="searchterm").value = 'Tester Ana'
362        self.browser.getControl("Search").click()
363        self.assertFalse('Anna Tester' in self.browser.contents)
364        self.browser.open(self.manage_container_path)
365        self.browser.getControl(name="searchtype").value = ['fullname']
366        self.browser.getControl(name="searchterm").value = 'Tester Anna'
367        self.browser.getControl("Search").click()
368        self.assertTrue('Anna Tester' in self.browser.contents)
369        # The old searchterm will be used again
370        self.browser.getControl("Search").click()
371        self.assertTrue('Anna Tester' in self.browser.contents)
372
373        # The catalog is informed when studycourse objects have been
374        # edited
375        self.browser.open(self.studycourse_path + '/manage')
376        self.browser.getControl(name="form.current_session").value = ['2010']
377        self.browser.getControl(name="form.entry_session").value = ['2010']
378        self.browser.getControl(name="form.entry_mode").value = ['ug_ft']
379        self.browser.getControl("Save").click()
380
381        # We can find the student in the new session
382        self.browser.open(self.manage_container_path)
383        self.browser.getControl(name="searchtype").value = ['current_session']
384        self.browser.getControl(name="searchterm").value = '2010'
385        self.browser.getControl("Search").click()
386        self.assertTrue('Anna Tester' in self.browser.contents)
387
388        ctrl = self.browser.getControl(name='entries')
389        ctrl.getControl(value=self.student_id).selected = True
390        self.browser.getControl("Remove selected", index=0).click()
391        self.assertTrue('Successfully removed' in self.browser.contents)
392        self.browser.getControl(name="searchtype").value = ['student_id']
393        self.browser.getControl(name="searchterm").value = self.student_id
394        self.browser.getControl("Search").click()
395        self.assertTrue('No student found' in self.browser.contents)
396
397        self.browser.open(self.container_path)
398        self.browser.getControl(name="searchtype").value = ['student_id']
399        self.browser.getControl(name="searchterm").value = self.student_id
400        self.browser.getControl("Search").click()
401        self.assertTrue('No student found' in self.browser.contents)
402        return
403
404class OfficerUITests(StudentsFullSetup):
405    # Tests for Student class views and pages
406
407    def test_student_properties(self):
408        self.student['studycourse'].current_level = 100
409        self.assertEqual(self.student.current_level, 100)
410        self.student['studycourse'].current_session = 2011
411        self.assertEqual(self.student.current_session, 2011)
412        self.student['studycourse'].current_verdict = 'A'
413        self.assertEqual(self.student.current_verdict, 'A')
414        return
415
416    def test_studylevelmanagepage(self):
417        studylevel = StudentStudyLevel()
418        studylevel.level = 100
419        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
420        self.student['studycourse'].addStudentStudyLevel(
421            cert,studylevel)
422        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
423        self.browser.open(self.studycourse_path + '/100/manage')
424        self.assertEqual(self.browser.url, self.studycourse_path + '/100/manage')
425        self.assertEqual(self.browser.headers['Status'], '200 Ok')
426
427    def test_basic_auth(self):
428        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
429        self.browser.open('http://localhost/app')
430        self.browser.getLink("Logout").click()
431        self.assertTrue('You have been logged out' in self.browser.contents)
432        # But we are still logged in since we've used basic authentication here.
433        # Wikipedia says: Existing browsers retain authentication information
434        # until the tab or browser is closed or the user clears the history.
435        # HTTP does not provide a method for a server to direct clients to
436        # discard these cached credentials. This means that there is no
437        # effective way for a server to "log out" the user without closing
438        # the browser. This is a significant defect that requires browser
439        # manufacturers to support a "logout" user interface element ...
440        self.assertTrue('Manager' in self.browser.contents)
441
442    def test_basic_auth_base64(self):
443        auth_token = base64.b64encode('mgr:mgrpw')
444        self.browser.addHeader('Authorization', 'Basic %s' % auth_token)
445        self.browser.open(self.manage_container_path)
446        self.assertEqual(self.browser.headers['Status'], '200 Ok')
447
448    def test_manage_access(self):
449        # Managers can access the pages of students
450        # and can perform actions
451        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
452        self.browser.open(self.student_path)
453        self.assertEqual(self.browser.headers['Status'], '200 Ok')
454        self.assertEqual(self.browser.url, self.student_path)
455        self.browser.getLink("Trigger").click()
456        self.assertEqual(self.browser.headers['Status'], '200 Ok')
457        # Managers can trigger transitions
458        self.browser.getControl(name="transition").value = ['admit']
459        self.browser.getControl("Save").click()
460        # Managers can edit base
461        self.browser.open(self.student_path)
462        self.browser.getLink("Manage").click()
463        self.assertEqual(self.browser.url, self.manage_student_path)
464        self.assertEqual(self.browser.headers['Status'], '200 Ok')
465        self.browser.getControl(name="form.firstname").value = 'John'
466        self.browser.getControl(name="form.lastname").value = 'Tester'
467        self.browser.getControl(name="form.reg_number").value = '345'
468        self.browser.getControl(name="password").value = 'secret'
469        self.browser.getControl(name="control_password").value = 'secret'
470        self.browser.getControl("Save").click()
471        self.assertMatches('...Form has been saved...',
472                           self.browser.contents)
473        self.browser.open(self.student_path)
474        self.browser.getLink("Clearance Data").click()
475        self.assertEqual(self.browser.headers['Status'], '200 Ok')
476        self.assertEqual(self.browser.url, self.clearance_path)
477        self.browser.getLink("Manage").click()
478        self.assertEqual(self.browser.headers['Status'], '200 Ok')
479        self.assertEqual(self.browser.url, self.manage_clearance_path)
480        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
481        self.browser.getControl("Save").click()
482        self.assertMatches('...Form has been saved...',
483                           self.browser.contents)
484
485        self.browser.open(self.student_path)
486        self.browser.getLink("Personal Data").click()
487        self.assertEqual(self.browser.headers['Status'], '200 Ok')
488        self.assertEqual(self.browser.url, self.personal_path)
489        self.browser.getLink("Manage").click()
490        self.assertEqual(self.browser.headers['Status'], '200 Ok')
491        self.assertEqual(self.browser.url, self.manage_personal_path)
492        self.browser.open(self.personal_path)
493        self.assertTrue('Updated' in self.browser.contents)
494        self.browser.getLink("Edit").click()
495        self.assertEqual(self.browser.headers['Status'], '200 Ok')
496        self.assertEqual(self.browser.url, self.edit_personal_path)
497        self.browser.getControl("Save").click()
498        # perm_address is required in IStudentPersonalEdit
499        self.assertMatches('...Required input is missing...',
500                           self.browser.contents)
501        self.browser.getControl(name="form.perm_address").value = 'My address!'
502        self.browser.getControl("Save").click()
503        self.assertMatches('...Form has been saved...',
504                           self.browser.contents)
505
506        # Managers can browse all subobjects
507        self.browser.open(self.student_path)
508        self.browser.getLink("Payments").click()
509        self.assertEqual(self.browser.headers['Status'], '200 Ok')
510        self.assertEqual(self.browser.url, self.payments_path)
511        self.browser.open(self.student_path)
512        self.browser.getLink("Accommodation").click()
513        self.assertEqual(self.browser.headers['Status'], '200 Ok')
514        self.assertEqual(self.browser.url, self.acco_path)
515        self.browser.open(self.student_path)
516        self.browser.getLink("History").click()
517        self.assertEqual(self.browser.headers['Status'], '200 Ok')
518        self.assertEqual(self.browser.url, self.history_path)
519        self.assertMatches('...Admitted by Manager...',
520                           self.browser.contents)
521        # Only the Application Slip does not exist
522        self.assertFalse('Application Slip' in self.browser.contents)
523        return
524
525    def test_manage_contact_student(self):
526        # Managers can contact student
527        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
528        self.student.email = None
529        self.browser.open(self.student_path)
530        self.browser.getLink("Send email").click()
531        self.browser.getControl(name="form.subject").value = 'Important subject'
532        self.browser.getControl(name="form.body").value = 'Hello!'
533        self.browser.getControl("Send message now").click()
534        self.assertTrue('An smtp server error occurred' in self.browser.contents)
535        self.student.email = 'xx@yy.zz'
536        self.browser.getControl("Send message now").click()
537        self.assertTrue('Your message has been sent' in self.browser.contents)
538        return
539
540    def test_manage_remove_department(self):
541        # Lazy student is studying CERT1
542        lazystudent = Student()
543        lazystudent.firstname = u'Lazy'
544        lazystudent.lastname = u'Student'
545        self.app['students'].addStudent(lazystudent)
546        student_id = lazystudent.student_id
547        student_path = self.container_path + '/' + student_id
548        lazystudent['studycourse'].certificate = self.certificate
549        notify(grok.ObjectModifiedEvent(lazystudent))
550        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
551        self.browser.open(student_path + '/studycourse')
552        self.assertTrue('CERT1' in self.browser.contents)
553        # After some years the department is removed
554        del self.app['faculties']['fac1']['dep1']
555        # So CERT1 does no longer exist and lazy student's
556        # certificate reference is removed too
557        self.browser.open(student_path + '/studycourse')
558        self.assertEqual(self.browser.headers['Status'], '200 Ok')
559        self.assertEqual(self.browser.url, student_path + '/studycourse')
560        self.assertFalse('CERT1' in self.browser.contents)
561        self.assertMatches('...<div>--</div>...',
562                           self.browser.contents)
563
564    def test_manage_upload_file(self):
565        # Managers can upload a file via the StudentClearanceManageFormPage
566        # The image is stored even if form has errors
567        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
568        self.browser.open(self.manage_clearance_path)
569        # No birth certificate has been uploaded yet
570        # Browsing the link shows a placerholder image
571        self.browser.open('birth_certificate')
572        self.assertEqual(
573            self.browser.headers['content-type'], 'image/jpeg')
574        self.assertEqual(len(self.browser.contents), PH_LEN)
575        # Create a pseudo image file and select it to be uploaded in form
576        # as birth certificate
577        self.browser.open(self.manage_clearance_path)
578        image = open(SAMPLE_IMAGE, 'rb')
579        ctrl = self.browser.getControl(name='birthcertificateupload')
580        file_ctrl = ctrl.mech_control
581        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
582        # The Save action does not upload files
583        self.browser.getControl("Save").click() # submit form
584        self.assertFalse(
585            '<a target="image" href="birth_certificate">'
586            in self.browser.contents)
587        # ... but the correct upload submit button does
588        image = open(SAMPLE_IMAGE)
589        ctrl = self.browser.getControl(name='birthcertificateupload')
590        file_ctrl = ctrl.mech_control
591        file_ctrl.add_file(image, filename='my_birth_certificate.jpg')
592        self.browser.getControl(
593            name='upload_birthcertificateupload').click()
594        # There is a correct <img> link included
595        self.assertTrue(
596            '<a target="image" href="birth_certificate">'
597            in self.browser.contents)
598        # Browsing the link shows a real image
599        self.browser.open('birth_certificate')
600        self.assertEqual(
601            self.browser.headers['content-type'], 'image/jpeg')
602        self.assertEqual(len(self.browser.contents), 2787)
603        # Reuploading a file which is bigger than 150k will raise an error
604        self.browser.open(self.manage_clearance_path)
605        # An image > 150K
606        big_image = StringIO(open(SAMPLE_IMAGE, 'rb').read() * 75)
607        ctrl = self.browser.getControl(name='birthcertificateupload')
608        file_ctrl = ctrl.mech_control
609        file_ctrl.add_file(big_image, filename='my_birth_certificate.jpg')
610        self.browser.getControl(
611            name='upload_birthcertificateupload').click()
612        self.assertTrue(
613            'Uploaded file is too big' in self.browser.contents)
614        # we do not rely on filename extensions given by uploaders
615        image = open(SAMPLE_IMAGE, 'rb') # a jpg-file
616        ctrl = self.browser.getControl(name='birthcertificateupload')
617        file_ctrl = ctrl.mech_control
618        # tell uploaded file is bmp
619        file_ctrl.add_file(image, filename='my_birth_certificate.bmp')
620        self.browser.getControl(
621            name='upload_birthcertificateupload').click()
622        self.assertTrue(
623            # jpg file was recognized
624            'File birth_certificate.jpg uploaded.' in self.browser.contents)
625        # File names must meet several conditions
626        bmp_image = open(SAMPLE_IMAGE_BMP, 'rb')
627        ctrl = self.browser.getControl(name='birthcertificateupload')
628        file_ctrl = ctrl.mech_control
629        file_ctrl.add_file(bmp_image, filename='my_birth_certificate.bmp')
630        self.browser.getControl(
631            name='upload_birthcertificateupload').click()
632        self.assertTrue('Only the following extensions are allowed'
633            in self.browser.contents)
634        # Managers can delete files
635        self.browser.getControl(name='delete_birthcertificateupload').click()
636        self.assertTrue(
637            'birth_certificate deleted' in self.browser.contents)
638
639        # Managers can upload a file via the StudentBaseManageFormPage
640        self.browser.open(self.manage_student_path)
641        image = open(SAMPLE_IMAGE_BMP, 'rb')
642        ctrl = self.browser.getControl(name='passportuploadmanage')
643        file_ctrl = ctrl.mech_control
644        file_ctrl.add_file(image, filename='my_photo.bmp')
645        self.browser.getControl(
646            name='upload_passportuploadmanage').click()
647        self.assertTrue('jpg file extension expected'
648            in self.browser.contents)
649        ctrl = self.browser.getControl(name='passportuploadmanage')
650        file_ctrl = ctrl.mech_control
651        image = open(SAMPLE_IMAGE, 'rb')
652        file_ctrl.add_file(image, filename='my_photo.jpg')
653        self.browser.getControl(
654            name='upload_passportuploadmanage').click()
655        self.assertTrue(
656            '<img align="middle" height="125px" src="passport.jpg" />'
657            in self.browser.contents)
658        # We remove the passport file again
659        self.browser.open(self.manage_student_path)
660        self.browser.getControl('Delete').click()
661        self.browser.open(self.student_path + '/clearance_slip.pdf')
662        self.assertEqual(self.browser.headers['Status'], '200 Ok')
663        self.assertEqual(self.browser.headers['Content-Type'],
664                         'application/pdf')
665
666    def test_manage_course_lists(self):
667        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
668        self.browser.open(self.student_path)
669        self.browser.getLink("Study Course").click()
670        self.assertEqual(self.browser.headers['Status'], '200 Ok')
671        self.assertEqual(self.browser.url, self.studycourse_path)
672        self.assertTrue('Undergraduate Full-Time' in self.browser.contents)
673        self.browser.getLink("Manage").click()
674        self.assertTrue('Manage study course' in self.browser.contents)
675        # Before we can select a level, the certificate must
676        # be selected and saved
677        self.browser.getControl(name="form.certificate").value = ['CERT1']
678        self.browser.getControl(name="form.current_session").value = ['2004']
679        self.browser.getControl(name="form.current_verdict").value = ['A']
680        self.browser.getControl(name="form.entry_mode").value = ['ug_ft']
681        self.browser.getControl("Save").click()
682        # Now we can save also the current level which depends on start and end
683        # level of the certificate
684        self.browser.getControl(name="form.current_level").value = ['100']
685        self.browser.getControl("Save").click()
686        # Managers can add and remove any study level (course list)
687        self.browser.getControl(name="addlevel").value = ['100']
688        self.browser.getControl("Add study level").click()
689        self.assertMatches(
690            '...You must select a session...', self.browser.contents)
691        self.browser.getControl(name="addlevel").value = ['100']
692        self.browser.getControl(name="level_session").value = ['2004']
693        self.browser.getControl("Add study level").click()
694        self.assertMatches('...<span>100</span>...', self.browser.contents)
695        self.assertEqual(self.student['studycourse']['100'].level, 100)
696        self.assertEqual(self.student['studycourse']['100'].level_session, 2004)
697        self.browser.getControl(name="addlevel").value = ['100']
698        self.browser.getControl(name="level_session").value = ['2004']
699        self.browser.getControl("Add study level").click()
700        self.assertMatches('...This level exists...', self.browser.contents)
701        self.browser.getControl("Remove selected").click()
702        self.assertMatches(
703            '...No study level selected...', self.browser.contents)
704        self.browser.getControl(name="val_id").value = ['100']
705        self.browser.getControl(name="level_session").value = ['2004']
706        self.browser.getControl("Remove selected").click()
707        self.assertMatches('...Successfully removed...', self.browser.contents)
708        # Removing levels is properly logged
709        logfile = os.path.join(
710            self.app['datacenter'].storage, 'logs', 'students.log')
711        logcontent = open(logfile).read()
712        self.assertTrue('zope.mgr - students.browser.StudyCourseManageFormPage '
713                        '- K1000000 - removed: 100' in logcontent)
714        # Add level again
715        self.browser.getControl(name="addlevel").value = ['100']
716        self.browser.getControl(name="level_session").value = ['2004']
717        self.browser.getControl("Add study level").click()
718
719        # Managers can view and manage course lists
720        self.browser.getLink("100").click()
721        self.assertMatches(
722            '...: Study Level 100 (Year 1)...', self.browser.contents)
723        self.browser.getLink("Manage").click()
724        self.browser.getControl(name="form.level_session").value = ['2002']
725        self.browser.getControl("Save").click()
726        self.browser.getControl("Remove selected").click()
727        self.assertMatches('...No ticket selected...', self.browser.contents)
728        ctrl = self.browser.getControl(name='val_id')
729        ctrl.getControl(value='COURSE1').selected = True
730        self.browser.getControl("Remove selected", index=0).click()
731        self.assertTrue('Successfully removed' in self.browser.contents)
732        # Removing course tickets is properly logged
733        logfile = os.path.join(
734            self.app['datacenter'].storage, 'logs', 'students.log')
735        logcontent = open(logfile).read()
736        self.assertTrue('zope.mgr - students.browser.StudyLevelManageFormPage '
737        '- K1000000 - removed: COURSE1' in logcontent)
738        self.browser.getControl("Add course ticket").click()
739        self.browser.getControl(name="form.course").value = ['COURSE1']
740        self.course.credits = 100
741        self.browser.getControl("Add course ticket").click()
742        self.assertMatches(
743            '...Total credits exceed 58...', self.browser.contents)
744        self.course.credits = 10
745        self.browser.getControl("Add course ticket").click()
746        self.assertTrue('Successfully added' in self.browser.contents)
747        self.browser.getControl("Add course ticket").click()
748        self.browser.getControl(name="form.course").value = ['COURSE1']
749        self.browser.getControl("Add course ticket").click()
750        self.assertTrue('The ticket exists' in self.browser.contents)
751        self.browser.getControl("Cancel").click()
752        self.browser.getLink("COURSE1").click()
753        self.browser.getLink("Manage").click()
754        #self.browser.getControl(name="form.score").value = '10'
755        self.browser.getControl("Save").click()
756        self.assertTrue('Form has been saved' in self.browser.contents)
757        # Grade and weight have been determined
758        self.browser.open(self.studycourse_path + '/100/COURSE1')
759        self.assertFalse('Grade' in self.browser.contents)
760        self.assertFalse('Weight' in self.browser.contents)
761        self.student['studycourse']['100']['COURSE1'].score = 55
762        self.browser.open(self.studycourse_path + '/100/COURSE1')
763        self.assertTrue('Grade' in self.browser.contents)
764        self.assertTrue('Weight' in self.browser.contents)
765        self.assertEqual(self.student['studycourse']['100']['COURSE1'].grade, 'C')
766        self.assertEqual(self.student['studycourse']['100']['COURSE1'].weight, 3)
767        # GPA has been properly calculated and is shown on the page
768        self.browser.open(self.student_path + '/studycourse/100')
769        self.assertTrue('3.00' in self.browser.contents)
770        self.assertEqual(self.student['studycourse']['100'].gpa, 3.0)
771        # Carry-over courses will be collected when next level is created
772        self.browser.open(self.student_path + '/studycourse/manage')
773        # Add next level
774        self.student['studycourse']['100']['COURSE1'].score = 10
775        self.browser.getControl(name="addlevel").value = ['200']
776        self.browser.getControl(name="level_session").value = ['2005']
777        self.browser.getControl("Add study level").click()
778        self.browser.getLink("200").click()
779        self.assertMatches(
780            '...: Study Level 200 (Year 2)...', self.browser.contents)
781        # If COURSE1 has score 10 it becomes a carry-over course
782        # in level 200
783        self.assertEqual(
784            sorted(self.student['studycourse']['200'].keys()), [u'COURSE1'])
785        self.assertTrue(
786            self.student['studycourse']['200']['COURSE1'].carry_over)
787        return
788
789    def test_manage_payments(self):
790        # Managers can add online school fee payment tickets
791        # if certain requirements are met
792        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
793        self.browser.open(self.payments_path)
794        IWorkflowState(self.student).setState('cleared')
795        self.browser.getLink("Add current session payment ticket").click()
796        self.browser.getControl(name="form.p_category").value = ['schoolfee']
797        self.browser.getControl("Create ticket").click()
798        self.assertMatches('...ticket created...',
799                           self.browser.contents)
800        ctrl = self.browser.getControl(name='val_id')
801        value = ctrl.options[0]
802        self.browser.getLink(value).click()
803        self.assertMatches('...Amount Authorized...',
804                           self.browser.contents)
805        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
806        payment_url = self.browser.url
807
808        # The pdf payment slip can't yet be opened
809        #self.browser.open(payment_url + '/payment_slip.pdf')
810        #self.assertMatches('...Ticket not yet paid...',
811        #                   self.browser.contents)
812
813        # The same payment (with same p_item, p_session and p_category)
814        # can be initialized a second time if the former ticket is not yet paid.
815        self.browser.open(self.payments_path)
816        self.browser.getLink("Add current session payment ticket").click()
817        self.browser.getControl(name="form.p_category").value = ['schoolfee']
818        self.browser.getControl("Create ticket").click()
819        self.assertMatches('...Payment ticket created...',
820                           self.browser.contents)
821
822        # The ticket can be found in the payments_catalog
823        cat = queryUtility(ICatalog, name='payments_catalog')
824        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
825        self.assertTrue(len(results), 1)
826        self.assertTrue(results[0] is self.student['payments'][value])
827
828        # Managers can approve the payment
829        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
830        self.browser.open(payment_url)
831        self.browser.getLink("Approve payment").click()
832        self.assertMatches('...Payment approved...',
833                          self.browser.contents)
834        # Approval is logged
835        logfile = os.path.join(
836            self.app['datacenter'].storage, 'logs', 'students.log')
837        logcontent = open(logfile).read()
838        self.assertTrue(
839            'zope.mgr - students.browser.OnlinePaymentApprovePage '
840            '- K1000000 - schoolfee payment approved'
841            in logcontent)
842
843        # The authorized amount has been stored in the access code
844        self.assertEqual(
845            self.app['accesscodes']['SFE-0'].values()[0].cost,40000.0)
846
847        # The catalog has been updated
848        results = list(cat.searchResults(p_state=('unpaid', 'unpaid')))
849        self.assertTrue(len(results), 0)
850        results = list(cat.searchResults(p_state=('paid', 'paid')))
851        self.assertTrue(len(results), 1)
852        self.assertTrue(results[0] is self.student['payments'][value])
853
854        # Payments can't be approved twice
855        self.browser.open(payment_url + '/approve')
856        self.assertMatches('...This ticket has already been paid...',
857                          self.browser.contents)
858
859        # Now the first ticket is paid and no more ticket of same type
860        # (with same p_item, p_session and p_category) can be added
861        self.browser.open(self.payments_path)
862        self.browser.getLink("Add current session payment ticket").click()
863        self.browser.getControl(name="form.p_category").value = ['schoolfee']
864        self.browser.getControl("Create ticket").click()
865        self.assertMatches(
866            '...This type of payment has already been made...',
867            self.browser.contents)
868
869        # Managers can open the pdf payment slip
870        self.browser.open(payment_url)
871        self.browser.getLink("Download payment slip").click()
872        self.assertEqual(self.browser.headers['Status'], '200 Ok')
873        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
874
875        # Managers can remove online school fee payment tickets
876        self.browser.open(self.payments_path)
877        self.browser.getControl("Remove selected").click()
878        self.assertMatches('...No payment selected...', self.browser.contents)
879        ctrl = self.browser.getControl(name='val_id')
880        value = ctrl.options[0]
881        ctrl.getControl(value=value).selected = True
882        self.browser.getControl("Remove selected", index=0).click()
883        self.assertTrue('Successfully removed' in self.browser.contents)
884
885        # Managers can add online clearance payment tickets
886        self.browser.open(self.payments_path + '/addop')
887        self.browser.getControl(name="form.p_category").value = ['clearance']
888        self.browser.getControl("Create ticket").click()
889        self.assertMatches('...ticket created...',
890                           self.browser.contents)
891
892        # Managers can approve the payment
893        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
894        ctrl = self.browser.getControl(name='val_id')
895        value = ctrl.options[1] # The clearance payment is the second in the table
896        self.browser.getLink(value).click()
897        self.browser.open(self.browser.url + '/approve')
898        self.assertMatches('...Payment approved...',
899                          self.browser.contents)
900        expected = '''...
901        <td>
902          <span>Paid</span>
903        </td>...'''
904        self.assertMatches(expected,self.browser.contents)
905        # The new CLR-0 pin has been created
906        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
907        pin = self.app['accesscodes']['CLR-0'].keys()[0]
908        ac = self.app['accesscodes']['CLR-0'][pin]
909        self.assertEqual(ac.owner, self.student_id)
910        self.assertEqual(ac.cost, 3456.0)
911        return
912
913    def test_manage_accommodation(self):
914        logfile = os.path.join(
915            self.app['datacenter'].storage, 'logs', 'students.log')
916        # Managers can add online booking fee payment tickets and open the
917        # callback view (see test_manage_payments)
918        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
919        self.browser.open(self.payments_path)
920        self.browser.getLink("Add current session payment ticket").click()
921        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
922        # If student is not in accommodation session, payment cannot be processed
923        self.app['hostels'].accommodation_session = 2011
924        self.browser.getControl("Create ticket").click()
925        self.assertMatches('...Your current session does not match...',
926                           self.browser.contents)
927        self.app['hostels'].accommodation_session = 2004
928        self.browser.getLink("Add current session payment ticket").click()
929        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
930        self.browser.getControl("Create ticket").click()
931        ctrl = self.browser.getControl(name='val_id')
932        value = ctrl.options[0]
933        self.browser.getLink(value).click()
934        self.browser.open(self.browser.url + '/approve')
935        # The new HOS-0 pin has been created
936        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
937        pin = self.app['accesscodes']['HOS-0'].keys()[0]
938        ac = self.app['accesscodes']['HOS-0'][pin]
939        self.assertEqual(ac.owner, self.student_id)
940        parts = pin.split('-')[1:]
941        sfeseries, sfenumber = parts
942        # Managers can use HOS code and book a bed space with it
943        self.browser.open(self.acco_path)
944        self.browser.getLink("Book accommodation").click()
945        self.assertMatches('...You are in the wrong...',
946                           self.browser.contents)
947        IWorkflowInfo(self.student).fireTransition('admit')
948        # An existing HOS code can only be used if students
949        # are in accommodation session
950        self.student['studycourse'].current_session = 2003
951        self.browser.getLink("Book accommodation").click()
952        self.assertMatches('...Your current session does not match...',
953                           self.browser.contents)
954        self.student['studycourse'].current_session = 2004
955        # All requirements are met and ticket can be created
956        self.browser.getLink("Book accommodation").click()
957        self.assertMatches('...Activation Code:...',
958                           self.browser.contents)
959        self.browser.getControl(name="ac_series").value = sfeseries
960        self.browser.getControl(name="ac_number").value = sfenumber
961        self.browser.getControl("Create bed ticket").click()
962        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
963                           self.browser.contents)
964        # Bed has been allocated
965        bed1 = self.app['hostels']['hall-1']['hall-1_A_101_A']
966        self.assertTrue(bed1.owner == self.student_id)
967        # BedTicketAddPage is now blocked
968        self.browser.getLink("Book accommodation").click()
969        self.assertMatches('...You already booked a bed space...',
970            self.browser.contents)
971        # The bed ticket displays the data correctly
972        self.browser.open(self.acco_path + '/2004')
973        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
974                           self.browser.contents)
975        self.assertMatches('...2004/2005...', self.browser.contents)
976        self.assertMatches('...regular_male_fr...', self.browser.contents)
977        self.assertMatches('...%s...' % pin, self.browser.contents)
978        # Booking is properly logged
979        logcontent = open(logfile).read()
980        self.assertTrue('zope.mgr - students.browser.BedTicketAddPage '
981            '- K1000000 - booked: hall-1_A_101_A' in logcontent)
982        # Managers can relocate students if the student's bed_type has changed
983        self.browser.getLink("Relocate student").click()
984        self.assertMatches(
985            "...Student can't be relocated...", self.browser.contents)
986        self.student.sex = u'f'
987        self.browser.getLink("Relocate student").click()
988        self.assertMatches(
989            "...Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
990        self.assertTrue(bed1.owner == NOT_OCCUPIED)
991        bed2 = self.app['hostels']['hall-1']['hall-1_A_101_B']
992        self.assertTrue(bed2.owner == self.student_id)
993        self.assertTrue(self.student['accommodation'][
994            '2004'].bed_type == u'regular_female_fr')
995        # Relocation is properly logged
996        logcontent = open(logfile).read()
997        self.assertTrue('zope.mgr - students.browser.BedTicketRelocationPage '
998            '- K1000000 - relocated: hall-1_A_101_B' in logcontent)
999        # The payment object still shows the original payment item
1000        payment_id = self.student['payments'].keys()[0]
1001        payment = self.student['payments'][payment_id]
1002        self.assertTrue(payment.p_item == u'regular_male_fr')
1003        # Managers can relocate students if the bed's bed_type has changed
1004        bed1.bed_type = u'regular_female_fr'
1005        bed2.bed_type = u'regular_male_fr'
1006        notify(grok.ObjectModifiedEvent(bed1))
1007        notify(grok.ObjectModifiedEvent(bed2))
1008        self.browser.getLink("Relocate student").click()
1009        self.assertMatches(
1010            "...Student relocated...", self.browser.contents)
1011        self.assertMatches(
1012            "... Hall 1, Block A, Room 101, Bed A...", self.browser.contents)
1013        self.assertMatches(bed1.owner, self.student_id)
1014        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1015        # Managers can't relocate students if bed is reserved
1016        self.student.sex = u'm'
1017        bed1.bed_type = u'regular_female_reserved'
1018        notify(grok.ObjectModifiedEvent(bed1))
1019        self.browser.getLink("Relocate student").click()
1020        self.assertMatches(
1021            "...Students in reserved beds can't be relocated...",
1022            self.browser.contents)
1023        # Managers can relocate students if booking has been cancelled but
1024        # other bed space has been manually allocated after cancellation
1025        old_owner = bed1.releaseBed()
1026        self.assertMatches(old_owner, self.student_id)
1027        bed2.owner = self.student_id
1028        self.browser.open(self.acco_path + '/2004')
1029        self.assertMatches(
1030            "...booking cancelled...", self.browser.contents)
1031        self.browser.getLink("Relocate student").click()
1032        # We didn't informed the catalog therefore the new owner is not found
1033        self.assertMatches(
1034            "...There is no free bed in your category regular_male_fr...",
1035            self.browser.contents)
1036        # Now we fire the event properly
1037        notify(grok.ObjectModifiedEvent(bed2))
1038        self.browser.getLink("Relocate student").click()
1039        self.assertMatches(
1040            "...Student relocated...", self.browser.contents)
1041        self.assertMatches(
1042            "... Hall 1, Block A, Room 101, Bed B...", self.browser.contents)
1043          # Managers can delete bed tickets
1044        self.browser.open(self.acco_path)
1045        ctrl = self.browser.getControl(name='val_id')
1046        value = ctrl.options[0]
1047        ctrl.getControl(value=value).selected = True
1048        self.browser.getControl("Remove selected", index=0).click()
1049        self.assertMatches('...Successfully removed...', self.browser.contents)
1050        # The bed has been properly released by the event handler
1051        self.assertMatches(bed1.owner, NOT_OCCUPIED)
1052        self.assertMatches(bed2.owner, NOT_OCCUPIED)
1053        return
1054
1055    def test_manage_workflow(self):
1056        # Managers can pass through the whole workflow
1057        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1058        student = self.app['students'][self.student_id]
1059        self.browser.open(self.trigtrans_path)
1060        self.assertTrue(student.clearance_locked)
1061        self.browser.getControl(name="transition").value = ['admit']
1062        self.browser.getControl("Save").click()
1063        self.assertTrue(student.clearance_locked)
1064        self.browser.getControl(name="transition").value = ['start_clearance']
1065        self.browser.getControl("Save").click()
1066        self.assertFalse(student.clearance_locked)
1067        self.browser.getControl(name="transition").value = ['request_clearance']
1068        self.browser.getControl("Save").click()
1069        self.assertTrue(student.clearance_locked)
1070        self.browser.getControl(name="transition").value = ['clear']
1071        self.browser.getControl("Save").click()
1072        # Managers approve payment, they do not pay
1073        self.assertFalse('pay_first_school_fee' in self.browser.contents)
1074        self.browser.getControl(
1075            name="transition").value = ['approve_first_school_fee']
1076        self.browser.getControl("Save").click()
1077        self.browser.getControl(name="transition").value = ['reset6']
1078        self.browser.getControl("Save").click()
1079        # In state returning the pay_school_fee transition triggers some
1080        # changes of attributes
1081        self.browser.getControl(name="transition").value = ['approve_school_fee']
1082        self.browser.getControl("Save").click()
1083        self.assertEqual(student['studycourse'].current_session, 2005) # +1
1084        self.assertEqual(student['studycourse'].current_level, 200) # +100
1085        self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = Zero = not set
1086        self.assertEqual(student['studycourse'].previous_verdict, 'A')
1087        self.browser.getControl(name="transition").value = ['register_courses']
1088        self.browser.getControl("Save").click()
1089        self.browser.getControl(name="transition").value = ['validate_courses']
1090        self.browser.getControl("Save").click()
1091        self.browser.getControl(name="transition").value = ['return']
1092        self.browser.getControl("Save").click()
1093        return
1094
1095    def test_manage_pg_workflow(self):
1096        # Managers can pass through the whole workflow
1097        IWorkflowState(self.student).setState('school fee paid')
1098        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1099        student = self.app['students'][self.student_id]
1100        self.browser.open(self.trigtrans_path)
1101        self.assertTrue('<option value="reset6">' in self.browser.contents)
1102        self.assertTrue('<option value="register_courses">' in self.browser.contents)
1103        self.assertTrue('<option value="reset5">' in self.browser.contents)
1104        self.certificate.study_mode = 'pg_ft'
1105        self.browser.open(self.trigtrans_path)
1106        self.assertFalse('<option value="reset6">' in self.browser.contents)
1107        self.assertFalse('<option value="register_courses">' in self.browser.contents)
1108        self.assertTrue('<option value="reset5">' in self.browser.contents)
1109        return
1110
1111    def test_manage_import(self):
1112        # Managers can import student data files
1113        datacenter_path = 'http://localhost/app/datacenter'
1114        # Prepare a csv file for students
1115        open('students.csv', 'wb').write(
1116"""firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,password
1117Aaren,Pieri,1,1990-01-02,100000,aa@aa.ng,1234,m,mypwd1
1118Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,m,mypwd1
1119Brit,Berson,3,1990-01-04,100001,aa@aa.ng,1234,m,mypwd1
1120""")
1121        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1122        self.browser.open(datacenter_path)
1123        self.browser.getLink('Upload data').click()
1124        filecontents = StringIO(open('students.csv', 'rb').read())
1125        filewidget = self.browser.getControl(name='uploadfile:file')
1126        filewidget.add_file(filecontents, 'text/plain', 'students.csv')
1127        self.browser.getControl(name='SUBMIT').click()
1128        self.browser.getLink('Process data').click()
1129        button = lookup_submit_value(
1130            'select', 'students_zope.mgr.csv', self.browser)
1131        button.click()
1132        importerselect = self.browser.getControl(name='importer')
1133        modeselect = self.browser.getControl(name='mode')
1134        importerselect.getControl('Student Processor').selected = True
1135        modeselect.getControl(value='create').selected = True
1136        self.browser.getControl('Proceed to step 3').click()
1137        self.assertTrue('Header fields OK' in self.browser.contents)
1138        self.browser.getControl('Perform import').click()
1139        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1140        self.assertTrue('Successfully processed 2 rows' in self.browser.contents)
1141        self.assertTrue('Batch processing finished' in self.browser.contents)
1142        open('studycourses.csv', 'wb').write(
1143"""reg_number,matric_number,certificate,current_session,current_level
11441,,CERT1,2008,100
1145,100001,CERT1,2008,100
1146,100002,CERT1,2008,100
1147""")
1148        self.browser.open(datacenter_path)
1149        self.browser.getLink('Upload data').click()
1150        filecontents = StringIO(open('studycourses.csv', 'rb').read())
1151        filewidget = self.browser.getControl(name='uploadfile:file')
1152        filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv')
1153        self.browser.getControl(name='SUBMIT').click()
1154        self.browser.getLink('Process data').click()
1155        button = lookup_submit_value(
1156            'select', 'studycourses_zope.mgr.csv', self.browser)
1157        button.click()
1158        importerselect = self.browser.getControl(name='importer')
1159        modeselect = self.browser.getControl(name='mode')
1160        importerselect.getControl(
1161            'StudentStudyCourse Processor (update only)').selected = True
1162        modeselect.getControl(value='create').selected = True
1163        self.browser.getControl('Proceed to step 3').click()
1164        self.assertTrue('Update mode only' in self.browser.contents)
1165        self.browser.getControl('Proceed to step 3').click()
1166        self.assertTrue('Header fields OK' in self.browser.contents)
1167        self.browser.getControl('Perform import').click()
1168        self.assertTrue('Processing of 1 rows failed' in self.browser.contents)
1169        self.assertTrue('Successfully processed 2 rows'
1170                        in self.browser.contents)
1171        # The students are properly indexed and we can
1172        # thus find a student in  the department
1173        self.browser.open(self.manage_container_path)
1174        self.browser.getControl(name="searchtype").value = ['depcode']
1175        self.browser.getControl(name="searchterm").value = 'dep1'
1176        self.browser.getControl("Search").click()
1177        self.assertTrue('Aaren Pieri' in self.browser.contents)
1178        # We can search for a new student by name ...
1179        self.browser.getControl(name="searchtype").value = ['fullname']
1180        self.browser.getControl(name="searchterm").value = 'Claus'
1181        self.browser.getControl("Search").click()
1182        self.assertTrue('Claus Finau' in self.browser.contents)
1183        # ... and check if the imported password has been properly set
1184        ctrl = self.browser.getControl(name='entries')
1185        value = ctrl.options[0]
1186        claus = self.app['students'][value]
1187        self.assertTrue(IUserAccount(claus).checkPassword('mypwd1'))
1188        return
1189
1190    def test_handle_clearance_by_co(self):
1191        # Create clearance officer
1192        self.app['users'].addUser('mrclear', 'mrclearsecret')
1193        self.app['users']['mrclear'].email = 'mrclear@foo.ng'
1194        self.app['users']['mrclear'].title = 'Carlo Pitter'
1195        # Clearance officers need not necessarily to get
1196        # the StudentsOfficer site role
1197        #prmglobal = IPrincipalRoleManager(self.app)
1198        #prmglobal.assignRoleToPrincipal('waeup.StudentsOfficer', 'mrclear')
1199        # Assign local ClearanceOfficer role
1200        department = self.app['faculties']['fac1']['dep1']
1201        prmlocal = IPrincipalRoleManager(department)
1202        prmlocal.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'mrclear')
1203        IWorkflowState(self.student).setState('clearance started')
1204        # Login as clearance officer
1205        self.browser.open(self.login_path)
1206        self.browser.getControl(name="form.login").value = 'mrclear'
1207        self.browser.getControl(name="form.password").value = 'mrclearsecret'
1208        self.browser.getControl("Login").click()
1209        self.assertMatches('...You logged in...', self.browser.contents)
1210        # CO can see his roles
1211        self.browser.getLink("My Roles").click()
1212        self.assertMatches(
1213            '...<div>Academics Officer (view only)</div>...',
1214            self.browser.contents)
1215        #self.assertMatches(
1216        #    '...<div>Students Officer (view only)</div>...',
1217        #    self.browser.contents)
1218        # But not his local role ...
1219        self.assertFalse('Clearance Officer' in self.browser.contents)
1220        # ... because we forgot to notify the department that the local role
1221        # has changed
1222        notify(LocalRoleSetEvent(
1223            department, 'waeup.local.ClearanceOfficer', 'mrclear', granted=True))
1224        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1225        self.assertTrue('Clearance Officer' in self.browser.contents)
1226        self.assertMatches(
1227            '...<a href="http://localhost/app/faculties/fac1/dep1">...',
1228            self.browser.contents)
1229        # CO can view the student ...
1230        self.browser.open(self.clearance_path)
1231        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1232        self.assertEqual(self.browser.url, self.clearance_path)
1233        # ... but not other students
1234        other_student = Student()
1235        other_student.firstname = u'Dep2'
1236        other_student.lastname = u'Student'
1237        self.app['students'].addStudent(other_student)
1238        other_student_path = (
1239            'http://localhost/app/students/%s' % other_student.student_id)
1240        self.assertRaises(
1241            Unauthorized, self.browser.open, other_student_path)
1242        # Only in state clearance requested the CO does see the 'Clear' button
1243        self.browser.open(self.clearance_path)
1244        self.assertFalse('Clear student' in self.browser.contents)
1245        IWorkflowInfo(self.student).fireTransition('request_clearance')
1246        self.browser.open(self.clearance_path)
1247        self.assertTrue('Clear student' in self.browser.contents)
1248        self.browser.getLink("Clear student").click()
1249        self.assertTrue('Student has been cleared' in self.browser.contents)
1250        self.assertTrue('cleared' in self.browser.contents)
1251        self.browser.open(self.history_path)
1252        self.assertTrue('Cleared by Carlo Pitter' in self.browser.contents)
1253        # Hide real name.
1254        self.app['users']['mrclear'].public_name = 'My Public Name'
1255        self.browser.open(self.clearance_path)
1256        self.browser.getLink("Reject clearance").click()
1257        self.assertEqual(
1258            self.browser.url, self.student_path + '/reject_clearance')
1259        # Type comment why
1260        self.browser.getControl(name="form.officer_comment").value = """Dear Student,
1261You did not fill properly.
1262"""
1263        self.browser.getControl("Save comment").click()
1264        self.assertTrue('Clearance has been annulled' in self.browser.contents)
1265        url = ('http://localhost/app/students/K1000000/'
1266              'contactstudent?body=Dear+Student%2C%0AYou+did+not+fill+properly.'
1267              '%0A&subject=Clearance+has+been+annulled.')
1268        # CO does now see the prefilled contact form and can send a message
1269        self.assertEqual(self.browser.url, url)
1270        self.assertTrue('clearance started' in self.browser.contents)
1271        self.assertTrue('name="form.subject" size="20" type="text" '
1272            'value="Clearance has been annulled."'
1273            in self.browser.contents)
1274        self.assertTrue('name="form.body" rows="10" >Dear Student,'
1275            in self.browser.contents)
1276        self.browser.getControl("Send message now").click()
1277        self.assertTrue('Your message has been sent' in self.browser.contents)
1278        # The comment has been stored ...
1279        self.assertEqual(self.student.officer_comment,
1280            u'Dear Student,\nYou did not fill properly.\n')
1281        # ... and logged
1282        logfile = os.path.join(
1283            self.app['datacenter'].storage, 'logs', 'students.log')
1284        logcontent = open(logfile).read()
1285        self.assertTrue(
1286            'INFO - mrclear - students.browser.StudentRejectClearancePage - '
1287            'K1000000 - comment: Dear Student,<br>You did not fill '
1288            'properly.<br>\n' in logcontent)
1289        self.browser.open(self.history_path)
1290        self.assertTrue("Reset to 'clearance started' by My Public Name" in
1291            self.browser.contents)
1292        IWorkflowInfo(self.student).fireTransition('request_clearance')
1293        self.browser.open(self.clearance_path)
1294        self.browser.getLink("Reject clearance").click()
1295        self.browser.getControl("Save comment").click()
1296        self.assertTrue('Clearance request has been rejected'
1297            in self.browser.contents)
1298        self.assertTrue('clearance started' in self.browser.contents)
1299        # The CO can't clear students if not in state
1300        # clearance requested
1301        self.browser.open(self.student_path + '/clear')
1302        self.assertTrue('Student is in wrong state'
1303            in self.browser.contents)
1304        # The CO can go to his department throug the my_roles page ...
1305        self.browser.open('http://localhost/app/users/mrclear/my_roles')
1306        self.browser.getLink("http://localhost/app/faculties/fac1/dep1").click()
1307        # ... and view the list of students
1308        self.browser.getLink("Show students").click()
1309        self.assertTrue(self.student_id in self.browser.contents)
1310        # The comment is indicated by 'yes'
1311        self.assertTrue('<td><span>yes</span></td>' in self.browser.contents)
1312        # When a student is cleared the comment is automatically deleted
1313        IWorkflowInfo(self.student).fireTransition('request_clearance')
1314        IWorkflowInfo(self.student).fireTransition('clear')
1315        self.assertEqual(self.student.officer_comment, None)
1316
1317    def test_handle_courses_by_ca(self):
1318        # Create course adviser
1319        self.app['users'].addUser('mrsadvise', 'mrsadvisesecret')
1320        self.app['users']['mrsadvise'].email = 'mradvise@foo.ng'
1321        self.app['users']['mrsadvise'].title = u'Helen Procter'
1322        # Assign local CourseAdviser100 role for a certificate
1323        cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1']
1324        prmlocal = IPrincipalRoleManager(cert)
1325        prmlocal.assignRoleToPrincipal('waeup.local.CourseAdviser100', 'mrsadvise')
1326        IWorkflowState(self.student).setState('school fee paid')
1327        # Login as course adviser
1328        self.browser.open(self.login_path)
1329        self.browser.getControl(name="form.login").value = 'mrsadvise'
1330        self.browser.getControl(name="form.password").value = 'mrsadvisesecret'
1331        self.browser.getControl("Login").click()
1332        self.assertMatches('...You logged in...', self.browser.contents)
1333        # CO can see his roles
1334        self.browser.getLink("My Roles").click()
1335        self.assertMatches(
1336            '...<div>Academics Officer (view only)</div>...',
1337            self.browser.contents)
1338        # But not his local role ...
1339        self.assertFalse('Course Adviser' in self.browser.contents)
1340        # ... because we forgot to notify the certificate that the local role
1341        # has changed
1342        notify(LocalRoleSetEvent(
1343            cert, 'waeup.local.CourseAdviser100', 'mrsadvise', granted=True))
1344        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1345        self.assertTrue('Course Adviser 100L' in self.browser.contents)
1346        self.assertMatches(
1347            '...<a href="http://localhost/app/faculties/fac1/dep1/certificates/CERT1">...',
1348            self.browser.contents)
1349        # CA can view the student ...
1350        self.browser.open(self.student_path)
1351        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1352        self.assertEqual(self.browser.url, self.student_path)
1353        # ... but not other students
1354        other_student = Student()
1355        other_student.firstname = u'Dep2'
1356        other_student.lastname = u'Student'
1357        self.app['students'].addStudent(other_student)
1358        other_student_path = (
1359            'http://localhost/app/students/%s' % other_student.student_id)
1360        self.assertRaises(
1361            Unauthorized, self.browser.open, other_student_path)
1362        # We add study level 110 to the student's studycourse
1363        studylevel = StudentStudyLevel()
1364        studylevel.level = 110
1365        self.student['studycourse'].addStudentStudyLevel(
1366            cert,studylevel)
1367        L110_student_path = self.studycourse_path + '/110'
1368        # Only in state courses registered and only if the current level
1369        # corresponds with the name of the study level object
1370        # the 100L CA does see the 'Validate' button
1371        self.browser.open(L110_student_path)
1372        self.assertFalse('Validate courses' in self.browser.contents)
1373        IWorkflowInfo(self.student).fireTransition('register_courses')
1374        self.browser.open(L110_student_path)
1375        self.assertFalse('Validate courses' in self.browser.contents)
1376        self.student['studycourse'].current_level = 110
1377        self.browser.open(L110_student_path)
1378        self.assertTrue('Validate courses' in self.browser.contents)
1379        # ... but a 100L CA does not see the button on other levels
1380        studylevel2 = StudentStudyLevel()
1381        studylevel2.level = 200
1382        self.student['studycourse'].addStudentStudyLevel(
1383            cert,studylevel2)
1384        L200_student_path = self.studycourse_path + '/200'
1385        self.browser.open(L200_student_path)
1386        self.assertFalse('Validate courses' in self.browser.contents)
1387        self.browser.open(L110_student_path)
1388        self.browser.getLink("Validate courses").click()
1389        self.assertTrue('Course list has been validated' in self.browser.contents)
1390        self.assertTrue('courses validated' in self.browser.contents)
1391        self.assertEqual(self.student['studycourse']['110'].validated_by,
1392            'Helen Procter')
1393        self.assertMatches(
1394            '<YYYY-MM-DD hh:mm:ss>',
1395            self.student['studycourse']['110'].validation_date.strftime(
1396                "%Y-%m-%d %H:%M:%S"))
1397        self.browser.getLink("Reject courses").click()
1398        self.assertTrue('Course list request has been annulled.'
1399            in self.browser.contents)
1400        urlmessage = 'Course+list+request+has+been+annulled.'
1401        self.assertEqual(self.browser.url, self.student_path +
1402            '/contactstudent?subject=%s' % urlmessage)
1403        self.assertTrue('school fee paid' in self.browser.contents)
1404        self.assertTrue(self.student['studycourse']['110'].validated_by is None)
1405        self.assertTrue(self.student['studycourse']['110'].validation_date is None)
1406        IWorkflowInfo(self.student).fireTransition('register_courses')
1407        self.browser.open(L110_student_path)
1408        self.browser.getLink("Reject courses").click()
1409        self.assertTrue('Course list request has been rejected'
1410            in self.browser.contents)
1411        self.assertTrue('school fee paid' in self.browser.contents)
1412        # CA does now see the contact form and can send a message
1413        self.browser.getControl(name="form.subject").value = 'Important subject'
1414        self.browser.getControl(name="form.body").value = 'Course list rejected'
1415        self.browser.getControl("Send message now").click()
1416        self.assertTrue('Your message has been sent' in self.browser.contents)
1417        # The CA can't validate courses if not in state
1418        # courses registered
1419        self.browser.open(L110_student_path + '/validate_courses')
1420        self.assertTrue('Student is in the wrong state'
1421            in self.browser.contents)
1422        # The CA can go to his certificate through the my_roles page
1423        self.browser.open('http://localhost/app/users/mrsadvise/my_roles')
1424        self.browser.getLink(
1425            "http://localhost/app/faculties/fac1/dep1/certificates/CERT1").click()
1426        # and view the list of students
1427        self.browser.getLink("Show students").click()
1428        self.assertTrue(self.student_id in self.browser.contents)
1429
1430    def test_handle_courses_by_lecturer(self):
1431        # Create course lecturer
1432        self.app['users'].addUser('mrslecturer', 'mrslecturersecret')
1433        self.app['users']['mrslecturer'].email = 'mrslecturer@foo.ng'
1434        self.app['users']['mrslecturer'].title = u'Mercedes Benz'
1435        # Assign local Courselecturer100 role for a certificate
1436        course = self.app['faculties']['fac1']['dep1'].courses['COURSE1']
1437        prmlocal = IPrincipalRoleManager(course)
1438        prmlocal.assignRoleToPrincipal('waeup.local.Lecturer', 'mrslecturer')
1439        # Login as lecturer
1440        self.browser.open(self.login_path)
1441        self.browser.getControl(name="form.login").value = 'mrslecturer'
1442        self.browser.getControl(name="form.password").value = 'mrslecturersecret'
1443        self.browser.getControl("Login").click()
1444        self.assertMatches('...You logged in...', self.browser.contents)
1445        # CO can see her roles
1446        self.browser.getLink("My Roles").click()
1447        self.assertMatches(
1448            '...<div>Academics Officer (view only)</div>...',
1449            self.browser.contents)
1450        # But not her local role ...
1451        self.assertFalse('Lecturer' in self.browser.contents)
1452        # ... because we forgot to notify the course that the local role
1453        # has changed
1454        notify(LocalRoleSetEvent(
1455            course, 'waeup.local.Lecturer', 'mrslecturer', granted=True))
1456        self.browser.open('http://localhost/app/users/mrslecturer/my_roles')
1457        self.assertTrue('Lecturer' in self.browser.contents)
1458        self.assertMatches(
1459            '...<a href="http://localhost/app/faculties/fac1/dep1/courses/COURSE1">...',
1460            self.browser.contents)
1461        # The lecturer can go to her course
1462        self.browser.getLink(
1463            "http://localhost/app/faculties/fac1/dep1/courses/COURSE1").click()
1464        # and view the list of students
1465        self.browser.getLink("Show students").click()
1466        self.assertTrue('<th>Fullname</th>' in self.browser.contents)
1467        # No student in course so far
1468        self.assertFalse(self.student_id in self.browser.contents)
1469        studylevel = createObject(u'waeup.StudentStudyLevel')
1470        studylevel.level = 100
1471        self.student['studycourse'].addStudentStudyLevel(
1472            self.certificate, studylevel)
1473        # Now the student has registered the course and can
1474        # be seen by the lecturer.
1475        self.browser.open("http://localhost/app/faculties/fac1/dep1/courses/COURSE1/showcoursestudents")
1476        self.assertTrue(self.student_id in self.browser.contents)
1477        # XXX: So far the lecturer can neither access ths student ...
1478        self.assertRaises(
1479            Unauthorized, self.browser.open, self.student_path)
1480        # ... nor the respective course ticket since a
1481        # CourseTicketPrincipalRoleManager does not yet exist.
1482        self.assertTrue('COURSE1' in self.student['studycourse']['100'].keys())
1483        course_ticket_path = self.student_path + '/studycourse/100/COURSE1'
1484        self.assertRaises(
1485            Unauthorized, self.browser.open, course_ticket_path)
1486
1487    def test_change_current_mode(self):
1488        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1489        self.browser.open(self.clearance_path)
1490        self.assertFalse('Employer' in self.browser.contents)
1491        self.browser.open(self.manage_clearance_path)
1492        self.assertFalse('Employer' in self.browser.contents)
1493        self.student.clearance_locked = False
1494        self.browser.open(self.edit_clearance_path)
1495        self.assertFalse('Employer' in self.browser.contents)
1496        # Now we change the study mode of the certificate and a different
1497        # interface is used by clearance views.
1498        self.certificate.study_mode = 'pg_ft'
1499        # Invariants are not being checked here?!
1500        self.certificate.end_level = 100
1501        self.browser.open(self.clearance_path)
1502        self.assertTrue('Employer' in self.browser.contents)
1503        self.browser.open(self.manage_clearance_path)
1504        self.assertTrue('Employer' in self.browser.contents)
1505        self.browser.open(self.edit_clearance_path)
1506        self.assertTrue('Employer' in self.browser.contents)
1507
1508    def test_activate_deactivate_buttons(self):
1509        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1510        self.browser.open(self.student_path)
1511        self.browser.getLink("Deactivate").click()
1512        self.assertTrue(
1513            'Student account has been deactivated.' in self.browser.contents)
1514        self.assertTrue(
1515            'Base Data (account deactivated)' in self.browser.contents)
1516        self.assertTrue(self.student.suspended)
1517        self.browser.getLink("Activate").click()
1518        self.assertTrue(
1519            'Student account has been activated.' in self.browser.contents)
1520        self.assertFalse(
1521            'Base Data (account deactivated)' in self.browser.contents)
1522        self.assertFalse(self.student.suspended)
1523        # History messages have been added ...
1524        self.browser.getLink("History").click()
1525        self.assertTrue(
1526            'Student account deactivated by Manager<br />' in self.browser.contents)
1527        self.assertTrue(
1528            'Student account activated by Manager<br />' in self.browser.contents)
1529        # ... and actions have been logged.
1530        logfile = os.path.join(
1531            self.app['datacenter'].storage, 'logs', 'students.log')
1532        logcontent = open(logfile).read()
1533        self.assertTrue('zope.mgr - students.browser.StudentDeactivatePage - '
1534                        'K1000000 - account deactivated' in logcontent)
1535        self.assertTrue('zope.mgr - students.browser.StudentActivatePage - '
1536                        'K1000000 - account activated' in logcontent)
1537
1538    def test_manage_student_transfer(self):
1539        # Add second certificate
1540        self.certificate2 = createObject('waeup.Certificate')
1541        self.certificate2.code = u'CERT2'
1542        self.certificate2.study_mode = 'ug_ft'
1543        self.certificate2.start_level = 999
1544        self.certificate2.end_level = 999
1545        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
1546            self.certificate2)
1547
1548        # Add study level to old study course
1549        studylevel = createObject(u'waeup.StudentStudyLevel')
1550        studylevel.level = 200
1551        self.student['studycourse'].addStudentStudyLevel(
1552            self.certificate, studylevel)
1553        studylevel = createObject(u'waeup.StudentStudyLevel')
1554        studylevel.level = 999
1555        self.student['studycourse'].addStudentStudyLevel(
1556            self.certificate, studylevel)
1557
1558        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
1559        self.browser.open(self.student_path)
1560        self.browser.getLink("Transfer").click()
1561        self.browser.getControl(name="form.certificate").value = ['CERT2']
1562        self.browser.getControl(name="form.current_session").value = ['2011']
1563        self.browser.getControl(name="form.current_level").value = ['200']
1564        self.browser.getControl("Transfer").click()
1565        self.assertTrue(
1566            'Current level does not match certificate levels'
1567            in self.browser.contents)
1568        self.browser.getControl(name="form.current_level").value = ['999']
1569        self.browser.getControl("Transfer").click()
1570        self.assertTrue('Successfully transferred' in self.browser.contents)
1571        # The catalog has been updated
1572        cat = queryUtility(ICatalog, name='students_catalog')
1573        results = list(
1574            cat.searchResults(
1575            certcode=('CERT2', 'CERT2')))
1576        self.assertTrue(results[0] is self.student)
1577        results = list(
1578            cat.searchResults(
1579            current_session=(2011, 2011)))
1580        self.assertTrue(results[0] is self.student)
1581        # Add study level to new study course
1582        studylevel = createObject(u'waeup.StudentStudyLevel')
1583        studylevel.level = 999
1584        self.student['studycourse'].addStudentStudyLevel(
1585            self.certificate, studylevel)
1586
1587        # Edit and add pages are locked for old study courses
1588        self.browser.open(self.student_path + '/studycourse/manage')
1589        self.assertFalse('The requested form is locked' in self.browser.contents)
1590        self.browser.open(self.student_path + '/studycourse_1/manage')
1591        self.assertTrue('The requested form is locked' in self.browser.contents)
1592
1593        self.browser.open(self.student_path + '/studycourse/start_session')
1594        self.assertFalse('The requested form is locked' in self.browser.contents)
1595        self.browser.open(self.student_path + '/studycourse_1/start_session')
1596        self.assertTrue('The requested form is locked' in self.browser.contents)
1597
1598        IWorkflowState(self.student).setState('school fee paid')
1599        self.browser.open(self.student_path + '/studycourse/add')
1600        self.assertFalse('The requested form is locked' in self.browser.contents)
1601        self.browser.open(self.student_path + '/studycourse_1/add')
1602        self.assertTrue('The requested form is locked' in self.browser.contents)
1603
1604        self.browser.open(self.student_path + '/studycourse/999/manage')
1605        self.assertFalse('The requested form is locked' in self.browser.contents)
1606        self.browser.open(self.student_path + '/studycourse_1/999/manage')
1607        self.assertTrue('The requested form is locked' in self.browser.contents)
1608
1609        self.browser.open(self.student_path + '/studycourse/999/validate_courses')
1610        self.assertFalse('The requested form is locked' in self.browser.contents)
1611        self.browser.open(self.student_path + '/studycourse_1/999/validate_courses')
1612        self.assertTrue('The requested form is locked' in self.browser.contents)
1613
1614        self.browser.open(self.student_path + '/studycourse/999/reject_courses')
1615        self.assertFalse('The requested form is locked' in self.browser.contents)
1616        self.browser.open(self.student_path + '/studycourse_1/999/reject_courses')
1617        self.assertTrue('The requested form is locked' in self.browser.contents)
1618
1619        self.browser.open(self.student_path + '/studycourse/999/add')
1620        self.assertFalse('The requested form is locked' in self.browser.contents)
1621        self.browser.open(self.student_path + '/studycourse_1/999/add')
1622        self.assertTrue('The requested form is locked' in self.browser.contents)
1623
1624        self.browser.open(self.student_path + '/studycourse/999/edit')
1625        self.assertFalse('The requested form is locked' in self.browser.contents)
1626        self.browser.open(self.student_path + '/studycourse_1/999/edit')
1627        self.assertTrue('The requested form is locked' in self.browser.contents)
1628
1629    def test_login_as_student(self):
1630        # StudentImpersonators can login as student
1631        # Create clearance officer
1632        self.app['users'].addUser('mrofficer', 'mrofficersecret')
1633        self.app['users']['mrofficer'].email = 'mrofficer@foo.ng'
1634        self.app['users']['mrofficer'].title = 'Harry Actor'
1635        prmglobal = IPrincipalRoleManager(self.app)
1636        prmglobal.assignRoleToPrincipal('waeup.StudentImpersonator', 'mrofficer')
1637        prmglobal.assignRoleToPrincipal('waeup.StudentsManager', 'mrofficer')
1638        # Login as student impersonator
1639        self.browser.open(self.login_path)
1640        self.browser.getControl(name="form.login").value = 'mrofficer'
1641        self.browser.getControl(name="form.password").value = 'mrofficersecret'
1642        self.browser.getControl("Login").click()
1643        self.assertMatches('...You logged in...', self.browser.contents)
1644        self.browser.open(self.student_path)
1645        self.browser.getLink("Login as").click()
1646        self.browser.getControl("Set password now").click()
1647        temp_password = self.browser.getControl(name='form.password').value
1648        self.browser.getControl("Login now").click()
1649        self.assertMatches(
1650            '...You successfully logged in as...', self.browser.contents)
1651        # We are logged in as student and can see the 'My Data' tab
1652        self.assertMatches(
1653            '...<a href="#" class="dropdown-toggle">My Data</a>...',
1654            self.browser.contents)
1655        self.browser.getLink("Logout").click()
1656        # The student can't login with the original password ...
1657        self.browser.open(self.login_path)
1658        self.browser.getControl(name="form.login").value = self.student_id
1659        self.browser.getControl(name="form.password").value = 'spwd'
1660        self.browser.getControl("Login").click()
1661        self.assertMatches(
1662            '...Your account has been temporarily deactivated...',
1663            self.browser.contents)
1664        # ... but with the temporary password
1665        self.browser.open(self.login_path)
1666        self.browser.getControl(name="form.login").value = self.student_id
1667        self.browser.getControl(name="form.password").value = temp_password
1668        self.browser.getControl("Login").click()
1669        self.assertMatches('...You logged in...', self.browser.contents)
1670        # Creation of temp_password is properly logged
1671        logfile = os.path.join(
1672            self.app['datacenter'].storage, 'logs', 'students.log')
1673        logcontent = open(logfile).read()
1674        self.assertTrue(
1675            'mrofficer - students.browser.LoginAsStudentStep1 - K1000000 - '
1676            'temp_password generated: %s' % temp_password in logcontent)
1677
1678class StudentUITests(StudentsFullSetup):
1679    # Tests for Student class views and pages
1680
1681    def test_student_change_password(self):
1682        # Students can change the password
1683        self.student.personal_updated = datetime.utcnow()
1684        self.browser.open(self.login_path)
1685        self.browser.getControl(name="form.login").value = self.student_id
1686        self.browser.getControl(name="form.password").value = 'spwd'
1687        self.browser.getControl("Login").click()
1688        self.assertEqual(self.browser.url, self.student_path)
1689        self.assertTrue('You logged in' in self.browser.contents)
1690        # Change password
1691        self.browser.getLink("Change password").click()
1692        self.browser.getControl(name="change_password").value = 'pw'
1693        self.browser.getControl(
1694            name="change_password_repeat").value = 'pw'
1695        self.browser.getControl("Save").click()
1696        self.assertTrue('Password must have at least' in self.browser.contents)
1697        self.browser.getControl(name="change_password").value = 'new_password'
1698        self.browser.getControl(
1699            name="change_password_repeat").value = 'new_passssword'
1700        self.browser.getControl("Save").click()
1701        self.assertTrue('Passwords do not match' in self.browser.contents)
1702        self.browser.getControl(name="change_password").value = 'new_password'
1703        self.browser.getControl(
1704            name="change_password_repeat").value = 'new_password'
1705        self.browser.getControl("Save").click()
1706        self.assertTrue('Password changed' in self.browser.contents)
1707        # We are still logged in. Changing the password hasn't thrown us out.
1708        self.browser.getLink("Base Data").click()
1709        self.assertEqual(self.browser.url, self.student_path)
1710        # We can logout
1711        self.browser.getLink("Logout").click()
1712        self.assertTrue('You have been logged out' in self.browser.contents)
1713        self.assertEqual(self.browser.url, 'http://localhost/app')
1714        # We can login again with the new password
1715        self.browser.getLink("Login").click()
1716        self.browser.open(self.login_path)
1717        self.browser.getControl(name="form.login").value = self.student_id
1718        self.browser.getControl(name="form.password").value = 'new_password'
1719        self.browser.getControl("Login").click()
1720        self.assertEqual(self.browser.url, self.student_path)
1721        self.assertTrue('You logged in' in self.browser.contents)
1722        return
1723
1724    def test_setpassword(self):
1725        # Set password for first-time access
1726        student = Student()
1727        student.reg_number = u'123456'
1728        student.firstname = u'Klaus'
1729        student.lastname = u'Tester'
1730        self.app['students'].addStudent(student)
1731        setpassword_path = 'http://localhost/app/setpassword'
1732        student_path = 'http://localhost/app/students/%s' % student.student_id
1733        self.browser.open(setpassword_path)
1734        self.browser.getControl(name="ac_series").value = self.existing_pwdseries
1735        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1736        self.browser.getControl(name="reg_number").value = '223456'
1737        self.browser.getControl("Set").click()
1738        self.assertMatches('...No student found...',
1739                           self.browser.contents)
1740        self.browser.getControl(name="reg_number").value = '123456'
1741        self.browser.getControl(name="ac_number").value = '999999'
1742        self.browser.getControl("Set").click()
1743        self.assertMatches('...Access code is invalid...',
1744                           self.browser.contents)
1745        self.browser.getControl(name="ac_number").value = self.existing_pwdnumber
1746        self.browser.getControl("Set").click()
1747        self.assertMatches('...Password has been set. Your Student Id is...',
1748                           self.browser.contents)
1749        self.browser.getControl("Set").click()
1750        self.assertMatches(
1751            '...Password has already been set. Your Student Id is...',
1752            self.browser.contents)
1753        existing_pwdpin = self.pwdpins[1]
1754        parts = existing_pwdpin.split('-')[1:]
1755        existing_pwdseries, existing_pwdnumber = parts
1756        self.browser.getControl(name="ac_series").value = existing_pwdseries
1757        self.browser.getControl(name="ac_number").value = existing_pwdnumber
1758        self.browser.getControl(name="reg_number").value = '123456'
1759        self.browser.getControl("Set").click()
1760        self.assertMatches(
1761            '...You are using the wrong Access Code...',
1762            self.browser.contents)
1763        # The student can login with the new credentials
1764        self.browser.open(self.login_path)
1765        self.browser.getControl(name="form.login").value = student.student_id
1766        self.browser.getControl(
1767            name="form.password").value = self.existing_pwdnumber
1768        self.browser.getControl("Login").click()
1769        self.assertEqual(self.browser.url, student_path)
1770        self.assertTrue('You logged in' in self.browser.contents)
1771        return
1772
1773    def test_student_login(self):
1774        # Student cant login if their password is not set
1775        self.student.password = None
1776        self.browser.open(self.login_path)
1777        self.browser.getControl(name="form.login").value = self.student_id
1778        self.browser.getControl(name="form.password").value = 'spwd'
1779        self.browser.getControl("Login").click()
1780        self.assertTrue(
1781            'You entered invalid credentials.' in self.browser.contents)
1782        # We set the password again
1783        IUserAccount(
1784            self.app['students'][self.student_id]).setPassword('spwd')
1785        # Students can't login if their account is suspended/deactivated
1786        self.student.suspended = True
1787        self.browser.open(self.login_path)
1788        self.browser.getControl(name="form.login").value = self.student_id
1789        self.browser.getControl(name="form.password").value = 'spwd'
1790        self.browser.getControl("Login").click()
1791        self.assertMatches(
1792            '...Your account has been deactivated...', self.browser.contents)
1793        self.student.suspended = False
1794        # Students can't login if a temporary password has been set and
1795        # is not expired
1796        self.app['students'][self.student_id].setTempPassword(
1797            'anybody', 'temp_spwd')
1798        self.browser.open(self.login_path)
1799        self.browser.getControl(name="form.login").value = self.student_id
1800        self.browser.getControl(name="form.password").value = 'spwd'
1801        self.browser.getControl("Login").click()
1802        self.assertMatches(
1803            '...Your account has been temporarily deactivated...',
1804            self.browser.contents)
1805        # The student can login with the temporary password
1806        self.browser.open(self.login_path)
1807        self.browser.getControl(name="form.login").value = self.student_id
1808        self.browser.getControl(name="form.password").value = 'temp_spwd'
1809        self.browser.getControl("Login").click()
1810        self.assertMatches(
1811            '...You logged in...', self.browser.contents)
1812        # Student can view the base data
1813        self.browser.open(self.student_path)
1814        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1815        self.assertEqual(self.browser.url, self.student_path)
1816        # When the password expires ...
1817        delta = timedelta(minutes=11)
1818        self.app['students'][self.student_id].temp_password[
1819            'timestamp'] = datetime.utcnow() - delta
1820        self.app['students'][self.student_id]._p_changed = True
1821        # ... the student will be automatically logged out
1822        self.assertRaises(
1823            Unauthorized, self.browser.open, self.student_path)
1824        # Then the student can login with the original password
1825        self.browser.open(self.login_path)
1826        self.browser.getControl(name="form.login").value = self.student_id
1827        self.browser.getControl(name="form.password").value = 'spwd'
1828        self.browser.getControl("Login").click()
1829        self.assertMatches(
1830            '...You logged in...', self.browser.contents)
1831
1832    def test_student_clearance(self):
1833        # Student cant login if their password is not set
1834        IWorkflowInfo(self.student).fireTransition('admit')
1835        self.browser.open(self.login_path)
1836        self.browser.getControl(name="form.login").value = self.student_id
1837        self.browser.getControl(name="form.password").value = 'spwd'
1838        self.browser.getControl("Login").click()
1839        self.assertMatches(
1840            '...You logged in...', self.browser.contents)
1841        # Admitted student can upload a passport picture
1842        self.browser.open(self.student_path + '/change_portrait')
1843        ctrl = self.browser.getControl(name='passportuploadedit')
1844        file_obj = open(SAMPLE_IMAGE, 'rb')
1845        file_ctrl = ctrl.mech_control
1846        file_ctrl.add_file(file_obj, filename='my_photo.jpg')
1847        self.browser.getControl(
1848            name='upload_passportuploadedit').click()
1849        self.assertTrue(
1850            '<img align="middle" height="125px" src="passport.jpg" />'
1851            in self.browser.contents)
1852        # Students can open admission letter
1853        self.browser.getLink("Base Data").click()
1854        self.browser.getLink("Download admission letter").click()
1855        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1856        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1857        # Student can view the clearance data
1858        self.browser.open(self.student_path)
1859        self.browser.getLink("Clearance Data").click()
1860        # Student can't open clearance edit form before starting clearance
1861        self.browser.open(self.student_path + '/cedit')
1862        self.assertMatches('...The requested form is locked...',
1863                           self.browser.contents)
1864        self.browser.getLink("Clearance Data").click()
1865        self.browser.getLink("Start clearance").click()
1866        self.student.email = None
1867        # Uups, we forgot to fill the email fields
1868        self.browser.getControl("Start clearance").click()
1869        self.assertMatches('...Not all required fields filled...',
1870                           self.browser.contents)
1871        self.browser.open(self.student_path + '/edit_base')
1872        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
1873        self.browser.getControl("Save").click()
1874        self.browser.open(self.student_path + '/start_clearance')
1875        self.browser.getControl(name="ac_series").value = '3'
1876        self.browser.getControl(name="ac_number").value = '4444444'
1877        self.browser.getControl("Start clearance now").click()
1878        self.assertMatches('...Activation code is invalid...',
1879                           self.browser.contents)
1880        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1881        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1882        # Owner is Hans Wurst, AC can't be invalidated
1883        self.browser.getControl("Start clearance now").click()
1884        self.assertMatches('...You are not the owner of this access code...',
1885                           self.browser.contents)
1886        # Set the correct owner
1887        self.existing_clrac.owner = self.student_id
1888        # clr_code might be set (and thus returns None) due importing
1889        # an empty clr_code column.
1890        self.student.clr_code = None
1891        self.browser.getControl("Start clearance now").click()
1892        self.assertMatches('...Clearance process has been started...',
1893                           self.browser.contents)
1894        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
1895        self.browser.getControl("Save", index=0).click()
1896        # Student can view the clearance data
1897        self.browser.getLink("Clearance Data").click()
1898        # and go back to the edit form
1899        self.browser.getLink("Edit").click()
1900        # Students can upload documents
1901        ctrl = self.browser.getControl(name='birthcertificateupload')
1902        file_obj = open(SAMPLE_IMAGE, 'rb')
1903        file_ctrl = ctrl.mech_control
1904        file_ctrl.add_file(file_obj, filename='my_birth_certificate.jpg')
1905        self.browser.getControl(
1906            name='upload_birthcertificateupload').click()
1907        self.assertTrue(
1908            '<a target="image" href="birth_certificate">Birth Certificate Scan</a>'
1909            in self.browser.contents)
1910        # Students can open clearance slip
1911        self.browser.getLink("View").click()
1912        self.browser.getLink("Download clearance slip").click()
1913        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1914        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1915        # Students can request clearance
1916        self.browser.open(self.edit_clearance_path)
1917        self.browser.getControl("Save and request clearance").click()
1918        self.browser.getControl(name="ac_series").value = self.existing_clrseries
1919        self.browser.getControl(name="ac_number").value = self.existing_clrnumber
1920        self.browser.getControl("Request clearance now").click()
1921        self.assertMatches('...Clearance has been requested...',
1922                           self.browser.contents)
1923        # Student can't reopen clearance form after requesting clearance
1924        self.browser.open(self.student_path + '/cedit')
1925        self.assertMatches('...The requested form is locked...',
1926                           self.browser.contents)
1927
1928    def test_student_course_registration(self):
1929        # Student cant login if their password is not set
1930        IWorkflowInfo(self.student).fireTransition('admit')
1931        self.browser.open(self.login_path)
1932        self.browser.getControl(name="form.login").value = self.student_id
1933        self.browser.getControl(name="form.password").value = 'spwd'
1934        self.browser.getControl("Login").click()
1935        # Student can't add study level if not in state 'school fee paid'
1936        self.browser.open(self.student_path + '/studycourse/add')
1937        self.assertMatches('...The requested form is locked...',
1938                           self.browser.contents)
1939        # ... and must be transferred first
1940        IWorkflowState(self.student).setState('school fee paid')
1941        # Now students can add the current study level
1942        self.browser.getLink("Study Course").click()
1943        self.browser.getLink("Add course list").click()
1944        self.assertMatches('...Add current level 100 (Year 1)...',
1945                           self.browser.contents)
1946        self.browser.getControl("Create course list now").click()
1947        # A level with one course ticket was created
1948        self.assertEqual(self.student['studycourse']['100'].number_of_tickets, 1)
1949        self.browser.getLink("100").click()
1950        self.browser.getLink("Edit course list").click()
1951        self.browser.getControl("Add course ticket").click()
1952        self.browser.getControl(name="form.course").value = ['COURSE1']
1953        self.browser.getControl("Add course ticket").click()
1954        self.assertMatches('...The ticket exists...',
1955                           self.browser.contents)
1956        self.student['studycourse'].current_level = 200
1957        self.browser.getLink("Study Course").click()
1958        self.browser.getLink("Add course list").click()
1959        self.assertMatches('...Add current level 200 (Year 2)...',
1960                           self.browser.contents)
1961        self.browser.getControl("Create course list now").click()
1962        self.browser.getLink("200").click()
1963        self.browser.getLink("Edit course list").click()
1964        self.browser.getControl("Add course ticket").click()
1965        self.browser.getControl(name="form.course").value = ['COURSE1']
1966        self.course.credits = 100
1967        self.browser.getControl("Add course ticket").click()
1968        self.assertMatches(
1969            '...Your total credits exceed 58...', self.browser.contents)
1970        self.course.credits = 10
1971        self.browser.getControl("Add course ticket").click()
1972        self.assertMatches('...The ticket exists...',
1973                           self.browser.contents)
1974        # Indeed the ticket exists as carry-over course from level 100
1975        # since its score was 0
1976        self.assertTrue(
1977            self.student['studycourse']['200']['COURSE1'].carry_over is True)
1978        # Students can open the pdf course registration slip
1979        self.browser.open(self.student_path + '/studycourse/200')
1980        self.browser.getLink("Download course registration slip").click()
1981        self.assertEqual(self.browser.headers['Status'], '200 Ok')
1982        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
1983        # Students can remove course tickets
1984        self.browser.open(self.student_path + '/studycourse/200/edit')
1985        self.browser.getControl("Remove selected", index=0).click()
1986        self.assertTrue('No ticket selected' in self.browser.contents)
1987        # No ticket can be selected since the carry-over course is a core course
1988        self.assertRaises(
1989            LookupError, self.browser.getControl, name='val_id')
1990        self.student['studycourse']['200']['COURSE1'].mandatory = False
1991        self.browser.open(self.student_path + '/studycourse/200/edit')
1992        # Course list can't be registered if total_credits exceeds max_credits
1993        self.student['studycourse']['200']['COURSE1'].credits = 60
1994        self.browser.getControl("Register course list").click()
1995        self.assertTrue('Maximum credits of 50 exceeded' in self.browser.contents)
1996        # Student can now remove the ticket
1997        ctrl = self.browser.getControl(name='val_id')
1998        ctrl.getControl(value='COURSE1').selected = True
1999        self.browser.getControl("Remove selected", index=0).click()
2000        self.assertTrue('Successfully removed' in self.browser.contents)
2001        # Course list can be registered, even if it's empty
2002        self.browser.getControl("Register course list").click()
2003        self.assertTrue('Course list has been registered' in self.browser.contents)
2004        self.assertEqual(self.student.state, 'courses registered')
2005        return
2006
2007    def test_postgraduate_student_access(self):
2008        self.certificate.study_mode = 'pg_ft'
2009        self.certificate.start_level = 999
2010        self.certificate.end_level = 999
2011        self.student['studycourse'].current_level = 999
2012        IWorkflowState(self.student).setState('school fee paid')
2013        self.browser.open(self.login_path)
2014        self.browser.getControl(name="form.login").value = self.student_id
2015        self.browser.getControl(name="form.password").value = 'spwd'
2016        self.browser.getControl("Login").click()
2017        self.assertTrue(
2018            'You logged in.' in self.browser.contents)
2019        # Now students can add the current study level
2020        self.browser.getLink("Study Course").click()
2021        self.browser.getLink("Add course list").click()
2022        self.assertMatches('...Add current level Postgraduate Level...',
2023                           self.browser.contents)
2024        self.browser.getControl("Create course list now").click()
2025        # A level with one course ticket was created
2026        self.assertEqual(self.student['studycourse']['999'].number_of_tickets, 0)
2027        self.browser.getLink("999").click()
2028        self.browser.getLink("Edit course list").click()
2029        self.browser.getControl("Add course ticket").click()
2030        self.browser.getControl(name="form.course").value = ['COURSE1']
2031        self.browser.getControl("Add course ticket").click()
2032        self.assertMatches('...Successfully added COURSE1...',
2033                           self.browser.contents)
2034        # Postgraduate students can't register course lists
2035        self.browser.getControl("Register course list").click()
2036        self.assertTrue("your course list can't bee registered"
2037            in self.browser.contents)
2038        self.assertEqual(self.student.state, 'school fee paid')
2039        return
2040
2041    def test_student_clearance_wo_clrcode(self):
2042        IWorkflowState(self.student).setState('clearance started')
2043        self.browser.open(self.login_path)
2044        self.browser.getControl(name="form.login").value = self.student_id
2045        self.browser.getControl(name="form.password").value = 'spwd'
2046        self.browser.getControl("Login").click()
2047        self.student.clearance_locked = False
2048        self.browser.open(self.edit_clearance_path)
2049        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
2050        self.browser.getControl("Save and request clearance").click()
2051        self.assertMatches('...Clearance has been requested...',
2052                           self.browser.contents)
2053
2054    def test_student_clearance_payment(self):
2055        # Login
2056        self.browser.open(self.login_path)
2057        self.browser.getControl(name="form.login").value = self.student_id
2058        self.browser.getControl(name="form.password").value = 'spwd'
2059        self.browser.getControl("Login").click()
2060
2061        # Students can add online clearance payment tickets
2062        self.browser.open(self.payments_path + '/addop')
2063        self.browser.getControl(name="form.p_category").value = ['clearance']
2064        self.browser.getControl("Create ticket").click()
2065        self.assertMatches('...ticket created...',
2066                           self.browser.contents)
2067
2068        # Students can't approve the payment
2069        self.assertEqual(len(self.app['accesscodes']['CLR-0']),0)
2070        ctrl = self.browser.getControl(name='val_id')
2071        value = ctrl.options[0]
2072        self.browser.getLink(value).click()
2073        payment_url = self.browser.url
2074        self.assertRaises(
2075            Unauthorized, self.browser.open, payment_url + '/approve')
2076        # In the base package they can 'use' a fake approval view
2077        self.browser.open(payment_url + '/fake_approve')
2078        self.assertMatches('...Payment approved...',
2079                          self.browser.contents)
2080        expected = '''...
2081        <td>
2082          <span>Paid</span>
2083        </td>...'''
2084        expected = '''...
2085        <td>
2086          <span>Paid</span>
2087        </td>...'''
2088        self.assertMatches(expected,self.browser.contents)
2089        payment_id = self.student['payments'].keys()[0]
2090        payment = self.student['payments'][payment_id]
2091        self.assertEqual(payment.p_state, 'paid')
2092        self.assertEqual(payment.r_amount_approved, 3456.0)
2093        self.assertEqual(payment.r_code, 'AP')
2094        self.assertEqual(payment.r_desc, u'Payment approved by Anna Tester')
2095        # The new CLR-0 pin has been created
2096        self.assertEqual(len(self.app['accesscodes']['CLR-0']),1)
2097        pin = self.app['accesscodes']['CLR-0'].keys()[0]
2098        ac = self.app['accesscodes']['CLR-0'][pin]
2099        self.assertEqual(ac.owner, self.student_id)
2100        self.assertEqual(ac.cost, 3456.0)
2101
2102        # Students can open the pdf payment slip
2103        self.browser.open(payment_url + '/payment_slip.pdf')
2104        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2105        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2106
2107        # The new CLR-0 pin can be used for starting clearance
2108        # but they have to upload a passport picture first
2109        # which is only possible in state admitted
2110        self.browser.open(self.student_path + '/change_portrait')
2111        self.assertMatches('...form is locked...',
2112                          self.browser.contents)
2113        IWorkflowInfo(self.student).fireTransition('admit')
2114        self.browser.open(self.student_path + '/change_portrait')
2115        image = open(SAMPLE_IMAGE, 'rb')
2116        ctrl = self.browser.getControl(name='passportuploadedit')
2117        file_ctrl = ctrl.mech_control
2118        file_ctrl.add_file(image, filename='my_photo.jpg')
2119        self.browser.getControl(
2120            name='upload_passportuploadedit').click()
2121        self.browser.open(self.student_path + '/start_clearance')
2122        parts = pin.split('-')[1:]
2123        clrseries, clrnumber = parts
2124        self.browser.getControl(name="ac_series").value = clrseries
2125        self.browser.getControl(name="ac_number").value = clrnumber
2126        self.browser.getControl("Start clearance now").click()
2127        self.assertMatches('...Clearance process has been started...',
2128                           self.browser.contents)
2129
2130    def test_student_schoolfee_payment(self):
2131        configuration = createObject('waeup.SessionConfiguration')
2132        configuration.academic_session = 2005
2133        self.app['configuration'].addSessionConfiguration(configuration)
2134        # Login
2135        self.browser.open(self.login_path)
2136        self.browser.getControl(name="form.login").value = self.student_id
2137        self.browser.getControl(name="form.password").value = 'spwd'
2138        self.browser.getControl("Login").click()
2139
2140        # Students can add online school fee payment tickets.
2141        IWorkflowState(self.student).setState('returning')
2142        self.browser.open(self.payments_path)
2143        self.assertRaises(
2144            LookupError, self.browser.getControl, name='val_id')
2145        self.browser.getLink("Add current session payment ticket").click()
2146        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2147        self.browser.getControl("Create ticket").click()
2148        self.assertMatches('...ticket created...',
2149                           self.browser.contents)
2150        ctrl = self.browser.getControl(name='val_id')
2151        value = ctrl.options[0]
2152        self.browser.getLink(value).click()
2153        self.assertMatches('...Amount Authorized...',
2154                           self.browser.contents)
2155        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2156        # Payment session and will be calculated as defined
2157        # in w.k.students.utils because we set changed the state
2158        # to returning
2159        self.assertEqual(self.student['payments'][value].p_session, 2005)
2160        self.assertEqual(self.student['payments'][value].p_level, 200)
2161
2162        # Student is the payee of the payment ticket.
2163        webservice = IPaymentWebservice(self.student['payments'][value])
2164        self.assertEqual(webservice.display_fullname, 'Anna Tester')
2165        self.assertEqual(webservice.id, self.student_id)
2166        self.assertEqual(webservice.faculty, 'fac1')
2167        self.assertEqual(webservice.department, 'dep1')
2168
2169        # We simulate the approval
2170        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2171        self.browser.open(self.browser.url + '/fake_approve')
2172        self.assertMatches('...Payment approved...',
2173                          self.browser.contents)
2174
2175        # The new SFE-0 pin can be used for starting new session
2176        self.browser.open(self.studycourse_path)
2177        self.browser.getLink('Start new session').click()
2178        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2179        parts = pin.split('-')[1:]
2180        sfeseries, sfenumber = parts
2181        self.browser.getControl(name="ac_series").value = sfeseries
2182        self.browser.getControl(name="ac_number").value = sfenumber
2183        self.browser.getControl("Start now").click()
2184        self.assertMatches('...Session started...',
2185                           self.browser.contents)
2186        self.assertTrue(self.student.state == 'school fee paid')
2187        return
2188
2189    def test_student_bedallocation_payment(self):
2190        # Login
2191        self.browser.open(self.login_path)
2192        self.browser.getControl(name="form.login").value = self.student_id
2193        self.browser.getControl(name="form.password").value = 'spwd'
2194        self.browser.getControl("Login").click()
2195        self.browser.open(self.payments_path)
2196        self.browser.open(self.payments_path + '/addop')
2197        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2198        self.browser.getControl("Create ticket").click()
2199        self.assertMatches('...ticket created...',
2200                           self.browser.contents)
2201        # Students can remove only online payment tickets which have
2202        # not received a valid callback
2203        self.browser.open(self.payments_path)
2204        ctrl = self.browser.getControl(name='val_id')
2205        value = ctrl.options[0]
2206        ctrl.getControl(value=value).selected = True
2207        self.browser.getControl("Remove selected", index=0).click()
2208        self.assertTrue('Successfully removed' in self.browser.contents)
2209
2210    def test_student_maintenance_payment(self):
2211        # Login
2212        self.browser.open(self.login_path)
2213        self.browser.getControl(name="form.login").value = self.student_id
2214        self.browser.getControl(name="form.password").value = 'spwd'
2215        self.browser.getControl("Login").click()
2216        self.browser.open(self.payments_path)
2217        self.browser.open(self.payments_path + '/addop')
2218        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2219        self.browser.getControl("Create ticket").click()
2220        self.assertMatches('...You have not yet booked accommodation...',
2221                           self.browser.contents)
2222        # We continue this test in test_student_accommodation
2223
2224    def test_student_previous_payments(self):
2225        configuration = createObject('waeup.SessionConfiguration')
2226        configuration.academic_session = 2000
2227        configuration.clearance_fee = 3456.0
2228        configuration.booking_fee = 123.4
2229        self.app['configuration'].addSessionConfiguration(configuration)
2230        configuration2 = createObject('waeup.SessionConfiguration')
2231        configuration2.academic_session = 2003
2232        configuration2.clearance_fee = 3456.0
2233        configuration2.booking_fee = 123.4
2234        self.app['configuration'].addSessionConfiguration(configuration2)
2235        configuration3 = createObject('waeup.SessionConfiguration')
2236        configuration3.academic_session = 2005
2237        configuration3.clearance_fee = 3456.0
2238        configuration3.booking_fee = 123.4
2239        self.app['configuration'].addSessionConfiguration(configuration3)
2240        self.student['studycourse'].entry_session = 2002
2241
2242        # Login
2243        self.browser.open(self.login_path)
2244        self.browser.getControl(name="form.login").value = self.student_id
2245        self.browser.getControl(name="form.password").value = 'spwd'
2246        self.browser.getControl("Login").click()
2247
2248        # Students can add previous school fee payment tickets in any state.
2249        IWorkflowState(self.student).setState('courses registered')
2250        self.browser.open(self.payments_path)
2251        self.browser.getLink("Add previous session payment ticket").click()
2252
2253        # Previous session payment form is provided
2254        self.assertEqual(self.student.current_session, 2004)
2255        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2256        self.browser.getControl(name="form.p_session").value = ['2000']
2257        self.browser.getControl(name="form.p_level").value = ['300']
2258        self.browser.getControl("Create ticket").click()
2259        self.assertMatches('...The previous session must not fall below...',
2260                           self.browser.contents)
2261        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2262        self.browser.getControl(name="form.p_session").value = ['2005']
2263        self.browser.getControl(name="form.p_level").value = ['300']
2264        self.browser.getControl("Create ticket").click()
2265        self.assertMatches('...This is not a previous session...',
2266                           self.browser.contents)
2267        # Students can pay current session school fee.
2268        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2269        self.browser.getControl(name="form.p_session").value = ['2004']
2270        self.browser.getControl(name="form.p_level").value = ['300']
2271        self.browser.getControl("Create ticket").click()
2272        self.assertMatches('...ticket created...',
2273                           self.browser.contents)
2274        ctrl = self.browser.getControl(name='val_id')
2275        value = ctrl.options[0]
2276        self.browser.getLink(value).click()
2277        self.assertMatches('...Amount Authorized...',
2278                           self.browser.contents)
2279        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2280
2281        # Payment session is properly set
2282        self.assertEqual(self.student['payments'][value].p_session, 2004)
2283        self.assertEqual(self.student['payments'][value].p_level, 300)
2284
2285        # We simulate the approval
2286        self.browser.open(self.browser.url + '/fake_approve')
2287        self.assertMatches('...Payment approved...',
2288                          self.browser.contents)
2289
2290        # No AC has been created
2291        self.assertEqual(len(self.app['accesscodes']['SFE-0'].keys()), 0)
2292        self.assertTrue(self.student['payments'][value].ac is None)
2293
2294        # Current payment flag is set False
2295        self.assertFalse(self.student['payments'][value].p_current)
2296
2297        # Button and form are not available for students who are in
2298        # states up to cleared
2299        self.student['studycourse'].entry_session = 2004
2300        IWorkflowState(self.student).setState('cleared')
2301        self.browser.open(self.payments_path)
2302        self.assertFalse(
2303            "Add previous session payment ticket" in self.browser.contents)
2304        self.browser.open(self.payments_path + '/addpp')
2305        self.assertTrue(
2306            "No previous payment to be made" in self.browser.contents)
2307        return
2308
2309    def test_postgraduate_student_payments(self):
2310        configuration = createObject('waeup.SessionConfiguration')
2311        configuration.academic_session = 2005
2312        self.app['configuration'].addSessionConfiguration(configuration)
2313        self.certificate.study_mode = 'pg_ft'
2314        self.certificate.start_level = 999
2315        self.certificate.end_level = 999
2316        self.student['studycourse'].current_level = 999
2317        # Login
2318        self.browser.open(self.login_path)
2319        self.browser.getControl(name="form.login").value = self.student_id
2320        self.browser.getControl(name="form.password").value = 'spwd'
2321        self.browser.getControl("Login").click()
2322        # Students can add online school fee payment tickets.
2323        IWorkflowState(self.student).setState('cleared')
2324        self.browser.open(self.payments_path)
2325        self.browser.getLink("Add current session payment ticket").click()
2326        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2327        self.browser.getControl("Create ticket").click()
2328        self.assertMatches('...ticket created...',
2329                           self.browser.contents)
2330        ctrl = self.browser.getControl(name='val_id')
2331        value = ctrl.options[0]
2332        self.browser.getLink(value).click()
2333        self.assertMatches('...Amount Authorized...',
2334                           self.browser.contents)
2335        # Payment session and level are current ones.
2336        # Postgrads have to pay school_fee_1.
2337        self.assertEqual(self.student['payments'][value].amount_auth, 40000.0)
2338        self.assertEqual(self.student['payments'][value].p_session, 2004)
2339        self.assertEqual(self.student['payments'][value].p_level, 999)
2340
2341        # We simulate the approval
2342        self.assertEqual(len(self.app['accesscodes']['SFE-0']),0)
2343        self.browser.open(self.browser.url + '/fake_approve')
2344        self.assertMatches('...Payment approved...',
2345                          self.browser.contents)
2346
2347        # The new SFE-0 pin can be used for starting session
2348        self.browser.open(self.studycourse_path)
2349        self.browser.getLink('Start new session').click()
2350        pin = self.app['accesscodes']['SFE-0'].keys()[0]
2351        parts = pin.split('-')[1:]
2352        sfeseries, sfenumber = parts
2353        self.browser.getControl(name="ac_series").value = sfeseries
2354        self.browser.getControl(name="ac_number").value = sfenumber
2355        self.browser.getControl("Start now").click()
2356        self.assertMatches('...Session started...',
2357                           self.browser.contents)
2358        self.assertTrue(self.student.state == 'school fee paid')
2359
2360        # Postgrad students do not need to register courses the
2361        # can just pay for the next session.
2362        self.browser.open(self.payments_path)
2363        # Remove first payment to be sure that we access the right ticket
2364        del self.student['payments'][value]
2365        self.browser.getLink("Add current session payment ticket").click()
2366        self.browser.getControl(name="form.p_category").value = ['schoolfee']
2367        self.browser.getControl("Create ticket").click()
2368        ctrl = self.browser.getControl(name='val_id')
2369        value = ctrl.options[0]
2370        self.browser.getLink(value).click()
2371        # Payment session has increased by one, payment level remains the same.
2372        # Returning Postgraduates have to pay school_fee_2.
2373        self.assertEqual(self.student['payments'][value].amount_auth, 20000.0)
2374        self.assertEqual(self.student['payments'][value].p_session, 2005)
2375        self.assertEqual(self.student['payments'][value].p_level, 999)
2376
2377        # Student is still in old session
2378        self.assertEqual(self.student.current_session, 2004)
2379
2380        # We do not need to pay the ticket if any other
2381        # SFE pin is provided
2382        pin_container = self.app['accesscodes']
2383        pin_container.createBatch(
2384            datetime.utcnow(), 'some_userid', 'SFE', 9.99, 1)
2385        pin = pin_container['SFE-1'].values()[0].representation
2386        sfeseries, sfenumber = pin.split('-')[1:]
2387        # The new SFE-1 pin can be used for starting new session
2388        self.browser.open(self.studycourse_path)
2389        self.browser.getLink('Start new session').click()
2390        self.browser.getControl(name="ac_series").value = sfeseries
2391        self.browser.getControl(name="ac_number").value = sfenumber
2392        self.browser.getControl("Start now").click()
2393        self.assertMatches('...Session started...',
2394                           self.browser.contents)
2395        self.assertTrue(self.student.state == 'school fee paid')
2396        # Student is in new session
2397        self.assertEqual(self.student.current_session, 2005)
2398        self.assertEqual(self.student['studycourse'].current_level, 999)
2399        return
2400
2401    def test_student_accommodation(self):
2402        # Login
2403        self.browser.open(self.login_path)
2404        self.browser.getControl(name="form.login").value = self.student_id
2405        self.browser.getControl(name="form.password").value = 'spwd'
2406        self.browser.getControl("Login").click()
2407
2408        # Students can add online booking fee payment tickets and open the
2409        # callback view (see test_manage_payments)
2410        self.browser.getLink("Payments").click()
2411        self.browser.getLink("Add current session payment ticket").click()
2412        self.browser.getControl(name="form.p_category").value = ['bed_allocation']
2413        self.browser.getControl("Create ticket").click()
2414        ctrl = self.browser.getControl(name='val_id')
2415        value = ctrl.options[0]
2416        self.browser.getLink(value).click()
2417        self.browser.open(self.browser.url + '/fake_approve')
2418        # The new HOS-0 pin has been created
2419        self.assertEqual(len(self.app['accesscodes']['HOS-0']),1)
2420        pin = self.app['accesscodes']['HOS-0'].keys()[0]
2421        ac = self.app['accesscodes']['HOS-0'][pin]
2422        parts = pin.split('-')[1:]
2423        sfeseries, sfenumber = parts
2424
2425        # Students can use HOS code and book a bed space with it ...
2426        self.browser.open(self.acco_path)
2427        # ... but not if booking period has expired ...
2428        self.app['hostels'].enddate = datetime.now(pytz.utc)
2429        self.browser.getLink("Book accommodation").click()
2430        self.assertMatches('...Outside booking period: ...',
2431                           self.browser.contents)
2432        self.app['hostels'].enddate = datetime.now(pytz.utc) + timedelta(days=10)
2433        # ... or student is not the an allowed state ...
2434        self.browser.getLink("Book accommodation").click()
2435        self.assertMatches('...You are in the wrong...',
2436                           self.browser.contents)
2437        IWorkflowInfo(self.student).fireTransition('admit')
2438        self.browser.getLink("Book accommodation").click()
2439        self.assertMatches('...Activation Code:...',
2440                           self.browser.contents)
2441        # Student can't used faked ACs ...
2442        self.browser.getControl(name="ac_series").value = u'nonsense'
2443        self.browser.getControl(name="ac_number").value = sfenumber
2444        self.browser.getControl("Create bed ticket").click()
2445        self.assertMatches('...Activation code is invalid...',
2446                           self.browser.contents)
2447        # ... or ACs owned by somebody else.
2448        ac.owner = u'Anybody'
2449        self.browser.getControl(name="ac_series").value = sfeseries
2450        self.browser.getControl(name="ac_number").value = sfenumber
2451        self.browser.getControl("Create bed ticket").click()
2452        self.assertMatches('...You are not the owner of this access code...',
2453                           self.browser.contents)
2454        ac.owner = self.student_id
2455        self.browser.getControl(name="ac_series").value = sfeseries
2456        self.browser.getControl(name="ac_number").value = sfenumber
2457        self.browser.getControl("Create bed ticket").click()
2458        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2459                           self.browser.contents)
2460
2461        # Bed has been allocated
2462        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
2463        self.assertTrue(bed.owner == self.student_id)
2464
2465        # BedTicketAddPage is now blocked
2466        self.browser.getLink("Book accommodation").click()
2467        self.assertMatches('...You already booked a bed space...',
2468            self.browser.contents)
2469
2470        # The bed ticket displays the data correctly
2471        self.browser.open(self.acco_path + '/2004')
2472        self.assertMatches('...Hall 1, Block A, Room 101, Bed A...',
2473                           self.browser.contents)
2474        self.assertMatches('...2004/2005...', self.browser.contents)
2475        self.assertMatches('...regular_male_fr...', self.browser.contents)
2476        self.assertMatches('...%s...' % pin, self.browser.contents)
2477
2478        # Students can open the pdf slip
2479        self.browser.open(self.browser.url + '/bed_allocation_slip.pdf')
2480        self.assertEqual(self.browser.headers['Status'], '200 Ok')
2481        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
2482
2483        # Students can't relocate themselves
2484        self.assertFalse('Relocate' in self.browser.contents)
2485        relocate_path = self.acco_path + '/2004/relocate'
2486        self.assertRaises(
2487            Unauthorized, self.browser.open, relocate_path)
2488
2489        # Students can't the Remove button and check boxes
2490        self.browser.open(self.acco_path)
2491        self.assertFalse('Remove' in self.browser.contents)
2492        self.assertFalse('val_id' in self.browser.contents)
2493
2494        # Students can pay maintenance fee now
2495        self.browser.open(self.payments_path)
2496        self.browser.open(self.payments_path + '/addop')
2497        self.browser.getControl(name="form.p_category").value = ['hostel_maintenance']
2498        self.browser.getControl("Create ticket").click()
2499        self.assertMatches('...Payment ticket created...',
2500                           self.browser.contents)
2501        return
2502
2503    def test_change_password_request(self):
2504        self.browser.open('http://localhost/app/changepw')
2505        self.browser.getControl(name="form.identifier").value = '123'
2506        self.browser.getControl(name="form.email").value = 'aa@aa.ng'
2507        self.browser.getControl("Send login credentials").click()
2508        self.assertTrue('An email with' in self.browser.contents)
2509
2510    def test_student_expired_personal_data(self):
2511        # Login
2512        IWorkflowState(self.student).setState('school fee paid')
2513        delta = timedelta(days=180)
2514        self.student.personal_updated = datetime.utcnow() - delta
2515        self.browser.open(self.login_path)
2516        self.browser.getControl(name="form.login").value = self.student_id
2517        self.browser.getControl(name="form.password").value = 'spwd'
2518        self.browser.getControl("Login").click()
2519        self.assertEqual(self.browser.url, self.student_path)
2520        self.assertTrue(
2521            'You logged in' in self.browser.contents)
2522        # Students don't see personal_updated field in edit form
2523        self.browser.open(self.edit_personal_path)
2524        self.assertFalse('Updated' in self.browser.contents)
2525        self.browser.open(self.personal_path)
2526        self.assertTrue('Updated' in self.browser.contents)
2527        self.browser.getLink("Logout").click()
2528        delta = timedelta(days=181)
2529        self.student.personal_updated = datetime.utcnow() - delta
2530        self.browser.open(self.login_path)
2531        self.browser.getControl(name="form.login").value = self.student_id
2532        self.browser.getControl(name="form.password").value = 'spwd'
2533        self.browser.getControl("Login").click()
2534        self.assertEqual(self.browser.url, self.edit_personal_path)
2535        self.assertTrue(
2536            'Your personal data record is outdated.' in self.browser.contents)
2537
2538class StudentRequestPWTests(StudentsFullSetup):
2539    # Tests for student registration
2540
2541    layer = FunctionalLayer
2542
2543    def test_request_pw(self):
2544        # Student with wrong number can't be found.
2545        self.browser.open('http://localhost/app/requestpw')
2546        self.browser.getControl(name="form.firstname").value = 'Anna'
2547        self.browser.getControl(name="form.number").value = 'anynumber'
2548        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2549        self.browser.getControl("Send login credentials").click()
2550        self.assertTrue('No student record found.'
2551            in self.browser.contents)
2552        # Anonymous is not informed that firstname verification failed.
2553        # It seems that the record doesn't exist.
2554        self.browser.open('http://localhost/app/requestpw')
2555        self.browser.getControl(name="form.firstname").value = 'Johnny'
2556        self.browser.getControl(name="form.number").value = '123'
2557        self.browser.getControl(name="form.email").value = 'xx@yy.zz'
2558        self.browser.getControl("Send login credentials").click()
2559        self.assertTrue('No student record found.'
2560            in self.browser.contents)
2561        # Even with the correct firstname we can't register if a
2562        # password has been set and used.
2563        self.browser.getControl(name="form.firstname").value = 'Anna'
2564        self.browser.getControl(name="form.number").value = '123'
2565        self.browser.getControl("Send login credentials").click()
2566        self.assertTrue('Your password has already been set and used.'
2567            in self.browser.contents)
2568        self.browser.open('http://localhost/app/requestpw')
2569        self.app['students'][self.student_id].password = None
2570        # The firstname field, used for verification, is not case-sensitive.
2571        self.browser.getControl(name="form.firstname").value = 'aNNa'
2572        self.browser.getControl(name="form.number").value = '123'
2573        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2574        self.browser.getControl("Send login credentials").click()
2575        # Yeah, we succeded ...
2576        self.assertTrue('Your password request was successful.'
2577            in self.browser.contents)
2578        # We can also use the matric_number instead.
2579        self.browser.open('http://localhost/app/requestpw')
2580        self.browser.getControl(name="form.firstname").value = 'aNNa'
2581        self.browser.getControl(name="form.number").value = '234'
2582        self.browser.getControl(name="form.email").value = 'new@yy.zz'
2583        self.browser.getControl("Send login credentials").click()
2584        self.assertTrue('Your password request was successful.'
2585            in self.browser.contents)
2586        # ... and  student can be found in the catalog via the email address
2587        cat = queryUtility(ICatalog, name='students_catalog')
2588        results = list(
2589            cat.searchResults(
2590            email=('new@yy.zz', 'new@yy.zz')))
2591        self.assertEqual(self.student,results[0])
2592        logfile = os.path.join(
2593            self.app['datacenter'].storage, 'logs', 'main.log')
2594        logcontent = open(logfile).read()
2595        self.assertTrue('zope.anybody - students.browser.StudentRequestPasswordPage - '
2596                        '234 (K1000000) - new@yy.zz' in logcontent)
2597        return
2598
2599    def test_student_locked_level_forms(self):
2600
2601        # Add two study levels, one current and one previous
2602        studylevel = createObject(u'waeup.StudentStudyLevel')
2603        studylevel.level = 100
2604        self.student['studycourse'].addStudentStudyLevel(
2605            self.certificate, studylevel)
2606        studylevel = createObject(u'waeup.StudentStudyLevel')
2607        studylevel.level = 200
2608        self.student['studycourse'].addStudentStudyLevel(
2609            self.certificate, studylevel)
2610        IWorkflowState(self.student).setState('school fee paid')
2611        self.student['studycourse'].current_level = 200
2612
2613        self.browser.open(self.login_path)
2614        self.browser.getControl(name="form.login").value = self.student_id
2615        self.browser.getControl(name="form.password").value = 'spwd'
2616        self.browser.getControl("Login").click()
2617
2618        self.browser.open(self.student_path + '/studycourse/200/edit')
2619        self.assertFalse('The requested form is locked' in self.browser.contents)
2620        self.browser.open(self.student_path + '/studycourse/100/edit')
2621        self.assertTrue('The requested form is locked' in self.browser.contents)
2622
2623        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2624        self.assertFalse('The requested form is locked' in self.browser.contents)
2625        self.browser.open(self.student_path + '/studycourse/100/ctadd')
2626        self.assertTrue('The requested form is locked' in self.browser.contents)
2627
2628        IWorkflowState(self.student).setState('courses registered')
2629        self.browser.open(self.student_path + '/studycourse/200/edit')
2630        self.assertTrue('The requested form is locked' in self.browser.contents)
2631        self.browser.open(self.student_path + '/studycourse/200/ctadd')
2632        self.assertTrue('The requested form is locked' in self.browser.contents)
2633
2634
2635class PublicPagesTests(StudentsFullSetup):
2636    # Tests for swebservices
2637
2638    layer = FunctionalLayer
2639
2640    def test_paymentrequest(self):
2641        payment = createObject('waeup.StudentOnlinePayment')
2642        payment.p_category = u'schoolfee'
2643        payment.p_session = self.student.current_session
2644        payment.p_item = u'My Certificate'
2645        payment.p_id = u'anyid'
2646        self.student['payments']['anykey'] = payment
2647        # Request information about unpaid payment ticket
2648        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2649        self.assertEqual(self.browser.contents, '-1')
2650        # Request information about paid payment ticket
2651        payment.p_state = u'paid'
2652        notify(grok.ObjectModifiedEvent(payment))
2653        self.browser.open('http://localhost/app/paymentrequest?P_ID=anyid')
2654        self.assertEqual(self.browser.contents,
2655            'FULL_NAME=Anna Tester&FACULTY=fac1&DEPARTMENT=dep1'
2656            '&PAYMENT_ITEM=My Certificate&PAYMENT_CATEGORY=School Fee'
2657            '&ACADEMIC_SESSION=2004/2005&MATRIC_NUMBER=234&FEE_AMOUNT=0.0')
2658        self.browser.open('http://localhost/app/paymentrequest?NONSENSE=nonsense')
2659        self.assertEqual(self.browser.contents, '-1')
2660        self.browser.open('http://localhost/app/paymentrequest?P_ID=nonsense')
2661        self.assertEqual(self.browser.contents, '-1')
Note: See TracBrowser for help on using the repository browser.