source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/students/tests/test_browser.py @ 9695

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

Fix test_student_expired_personal_data.

Mangle exam results for export.

  • Property svn:keywords set to Id
File size: 14.9 KB
Line 
1## $Id: test_browser.py 9582 2012-11-08 22:14:13Z 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##
18import os
19import shutil
20import tempfile
21from datetime import datetime, timedelta
22from StringIO import StringIO
23from hurry.workflow.interfaces import IWorkflowState, IWorkflowInfo
24from zope.component.hooks import setSite, clearSite
25from zope.component import getUtility, createObject
26from zope.interface import verify
27from waeup.kofa.app import University
28from waeup.kofa.students.tests.test_browser import (
29    StudentsFullSetup, SAMPLE_IMAGE)
30from waeup.kofa.testing import FunctionalTestCase
31from waeup.kofa.interfaces import (
32    IExtFileStore, IFileStoreNameChooser)
33from waeup.kofa.students.batching import StudentProcessor
34from waeup.kofa.students.interfaces import IStudentsUtils
35from kofacustom.nigeria.students.batching import NigeriaStudentProcessor
36from kofacustom.nigeria.testing import FunctionalLayer
37from kofacustom.nigeria.students.interfaces import (
38    INigeriaStudentStudyCourse, INigeriaStudent,
39    INigeriaStudentStudyLevel, INigeriaCourseTicket)
40
41
42STUDENT_SAMPLE_DATA = open(
43    os.path.join(os.path.dirname(__file__), 'sample_student_data.csv'),
44    'rb').read()
45
46STUDENT_HEADER_FIELDS = STUDENT_SAMPLE_DATA.split(
47    '\n')[0].split(',')
48
49class StudentProcessorTest(FunctionalTestCase):
50    """Perform some batching tests.
51    """
52
53    layer = FunctionalLayer
54
55    def setUp(self):
56        super(StudentProcessorTest, self).setUp()
57        # Setup a sample site for each test
58        app = University()
59        self.dc_root = tempfile.mkdtemp()
60        app['datacenter'].setStoragePath(self.dc_root)
61
62        # Prepopulate the ZODB...
63        self.getRootFolder()['app'] = app
64        # we add the site immediately after creation to the
65        # ZODB. Catalogs and other local utilities are not setup
66        # before that step.
67        self.app = self.getRootFolder()['app']
68        # Set site here. Some of the following setup code might need
69        # to access grok.getSite() and should get our new app then
70        setSite(app)
71
72        self.processor_base = StudentProcessor()
73        self.processor = NigeriaStudentProcessor()
74        self.workdir = tempfile.mkdtemp()
75        self.csv_file = os.path.join(self.workdir, 'sample_student_data.csv')
76        open(self.csv_file, 'wb').write(STUDENT_SAMPLE_DATA)
77
78    def tearDown(self):
79        super(StudentProcessorTest, self).tearDown()
80        shutil.rmtree(self.workdir)
81        shutil.rmtree(self.dc_root)
82        clearSite()
83        return
84
85    def test_import(self):
86        # We have an empty column 'date_of_birth' in  the import file.
87        # The original processor will fail because 'date_of_birth' is required
88        # in the base package.
89        num, num_warns, fin_file, fail_file = self.processor_base.doImport(
90            self.csv_file, STUDENT_HEADER_FIELDS)
91        self.assertEqual(num_warns,3)
92        assert len(self.app['students'].keys()) == 0
93        # The customized processor does not complain since 'date_of_birth' is
94        # not required in the custom package.
95        num, num_warns, fin_file, fail_file = self.processor.doImport(
96            self.csv_file, STUDENT_HEADER_FIELDS)
97        #print open(fail_file).read()
98        self.assertEqual(num_warns,0)
99        assert len(self.app['students'].keys()) == 3
100        shutil.rmtree(os.path.dirname(fin_file))
101
102
103class StudentUITests(StudentsFullSetup):
104    """Tests for customized student class views and pages
105    """
106
107    layer = FunctionalLayer
108
109    def setUp(self):
110        super(StudentUITests, self).setUp()
111
112    def test_classes(self):
113        # Let's see if objects created in the customized
114        # portal really implement the customized interfaces
115        verify.verifyObject(INigeriaStudent, self.student)
116        verify.verifyObject(
117            INigeriaStudentStudyCourse, self.student['studycourse'])
118        studylevel = createObject(u'waeup.StudentStudyLevel')
119        verify.verifyObject(INigeriaStudentStudyLevel, studylevel)
120        ticket = createObject(u'waeup.CourseTicket')
121        verify.verifyObject(INigeriaCourseTicket, ticket)
122        IWorkflowState(self.student).setState('returning')
123        # Let's see if next_session_allowed works as expected
124        # A, ug_ft, 100
125        self.assertTrue(self.student['studycourse'].next_session_allowed)
126        # Zero, ug_ft, 100
127        self.student['studycourse'].current_verdict = '0' # Zero!
128        self.assertTrue(self.student['studycourse'].next_session_allowed)
129        # Zero, ug_ft, 200
130        self.student['studycourse'].current_level = 200
131        self.assertFalse(self.student['studycourse'].next_session_allowed)
132        # Zero, de_ft, 200
133        self.student['studycourse'].certificate.study_mode = 'de_ft'
134        self.assertTrue(self.student['studycourse'].next_session_allowed)
135        # Zero, ph_ft, 300
136        self.student['studycourse'].certificate.study_mode = 'ph_ft'
137        self.student['studycourse'].current_level = 300
138        self.assertTrue(self.student['studycourse'].next_session_allowed)
139        # Zero, ph_ft, 400
140        self.student['studycourse'].current_level = 400
141        self.assertFalse(self.student['studycourse'].next_session_allowed)
142
143        # Now we convert the certificate into a postgraduate certificate
144        IWorkflowState(self.student).setState('school fee paid')
145        self.certificate.study_mode = 'pg_ft'
146        # ... and voila next session registration is allowed
147        self.assertTrue(self.student['studycourse'].next_session_allowed)
148
149    def test_manage_access(self):
150        self.student.nationality = u'DE'
151        # Managers can access the pages of students
152        # and can perform actions
153        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
154        # The student created in the base package is an ug student
155        self.browser.open(self.student_path)
156        self.browser.getLink("Clearance Data").click()
157        self.assertEqual(self.browser.headers['Status'], '200 Ok')
158        self.assertEqual(self.browser.url, self.clearance_path)
159        self.browser.getLink("Manage").click()
160        self.assertEqual(self.browser.headers['Status'], '200 Ok')
161        self.assertEqual(self.browser.url, self.manage_clearance_path)
162        self.browser.getControl(name="form.date_of_birth").value = '09/10/1961'
163        self.browser.getControl("Save").click()
164        self.assertMatches('...Form has been saved...',
165                           self.browser.contents)
166        self.assertMatches('...First Sitting Record...',
167                           self.browser.contents)
168        # Managers can open clearance slip of ug students
169        self.browser.open(self.student_path + '/clearance_slip.pdf')
170        self.assertEqual(self.browser.headers['Status'], '200 Ok')
171        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
172        # There is no pg field in the clearance form
173        self.assertFalse('Second Higher Education Record'
174            in self.browser.contents)
175        # Now we change the study mode ...
176        self.certificate.study_mode = 'pg_ft'
177        self.browser.open(self.clearance_path)
178        # ... and additional pg clearance fields appear
179        self.assertMatches('...Second Higher Education Record...',
180                           self.browser.contents)
181        # But also fields from the ug form are displayed
182        self.assertMatches('...First Sitting Record...',
183                           self.browser.contents)
184        # Managers can open clearance slip of pg students
185        self.browser.open(self.student_path + '/clearance_slip.pdf')
186        self.assertEqual(self.browser.headers['Status'], '200 Ok')
187        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
188        # Managers can edit personal data. No fields are required.
189        self.browser.open(self.manage_personal_path)
190        self.browser.getControl("Save").click()
191        self.assertMatches('...Form has been saved...',
192                           self.browser.contents)
193
194    def test_student_access(self):
195        # Students can edit clearance data
196        IWorkflowState(self.student).setState('cleared')
197        self.browser.open(self.login_path)
198        self.browser.getControl(name="form.login").value = self.student_id
199        self.browser.getControl(name="form.password").value = 'spwd'
200        self.browser.getControl("Login").click()
201        # Even in state admitted students can't change the portait if
202        # application slip exists.
203        IWorkflowState(self.student).setState('admitted')
204        self.browser.open(self.student_path)
205        self.assertTrue('Change portrait' in self.browser.contents)
206        file_store = getUtility(IExtFileStore)
207        applicant_slip = 'My application slip'
208        file_id = IFileStoreNameChooser(self.student).chooseName(
209            attr="application_slip.pdf")
210        file_store.createFile(file_id, StringIO(applicant_slip))
211        self.browser.open(self.student_path)
212        self.assertFalse('Change portrait' in self.browser.contents)
213        self.browser.open(self.student_path + '/change_portrait')
214        self.assertTrue('The requested form is locked' in self.browser.contents)
215
216        # Student can view and edit clearance data if clearance has started ...
217        IWorkflowInfo(self.student).fireTransition('start_clearance')
218        self.student.officer_comment = u'Fill properly'
219        self.browser.getLink("Clearance Data").click()
220        self.assertTrue("Officer's Comment" in self.browser.contents)
221        # Students can't edit officer's comment
222        self.browser.getLink("Edit").click()
223        self.assertFalse("Officer's Comment" in self.browser.contents)
224        self.assertTrue('Save' in self.browser.contents)
225        # ... and request clearance if nationality field has been filled.
226        self.browser.getControl("Save and request clearance").click()
227        self.assertMatches('...Required input is missing...',
228                           self.browser.contents)
229        self.student.nationality = u'DE'
230        self.browser.open(self.edit_clearance_path)
231        self.browser.getControl("Save and request clearance").click()
232        self.assertMatches('...Clearance has been requested...',
233                           self.browser.contents)
234
235        # Students can edit personal data. Some fields are required.
236        self.browser.open(self.personal_path)
237        self.assertTrue('Updated' in self.browser.contents)
238        self.browser.getLink("Edit").click()
239        self.assertEqual(self.browser.headers['Status'], '200 Ok')
240        self.assertEqual(self.browser.url, self.edit_personal_path)
241        self.browser.getControl("Save").click()
242        self.assertMatches('...Required input is missing...',
243                           self.browser.contents)
244        self.browser.getControl(name="form.perm_address").value = 'My address!'
245        self.browser.getControl("Save").click()
246        self.assertMatches('...Required input is missing...',
247                           self.browser.contents)
248        # Ok, let's give up and fill the rest.
249        self.browser.getControl(name="form.next_kin_name").value = 'My Mutti'
250        self.browser.getControl(name="form.next_kin_relation").value = 'mother'
251        self.browser.getControl(name="form.next_kin_address").value = 'sweet home'
252        self.browser.getControl(name="form.next_kin_phone.country").value = ['+234']
253        self.browser.getControl(name="form.next_kin_phone.ext").value = '45678'
254        self.browser.getControl("Save").click()
255        self.assertMatches('...Form has been saved...',
256                           self.browser.contents)
257
258    def test_manage_upload_file(self):
259        # Managers can upload a file via the StudentClearanceManageFormPage
260        # The image is stored even if form has errors
261        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
262        self.browser.open(self.manage_clearance_path)
263        # Managers can add and delete a file
264        self.browser.open(self.manage_clearance_path)
265        image = open(SAMPLE_IMAGE, 'rb')
266        ctrl = self.browser.getControl(name='birthcertificateupload')
267        file_ctrl = ctrl.mech_control
268        file_ctrl.add_file(image, filename='my_acceptance_letter.jpg')
269        self.browser.getControl(
270            name='upload_acceptanceletterupload').click()
271        # Uups, we used the wrong 'Browse' field
272        self.assertFalse(
273            '<a target="image" href="acc_let">'
274            in self.browser.contents)
275        ctrl = self.browser.getControl(name='acceptanceletterupload')
276        file_ctrl = ctrl.mech_control
277        image.seek(0)
278        file_ctrl.add_file(image, filename='my_acceptance_letter.jpg')
279        self.browser.getControl(
280            name='upload_acceptanceletterupload').click()
281        self.assertTrue(
282            '<a target="image" href="acc_let">'
283            in self.browser.contents)
284        self.browser.getControl(
285            name='delete_acceptanceletterupload').click()
286        self.assertTrue(
287            'acc_let deleted'
288            in self.browser.contents)
289
290    def test_student_expired_personal_data(self):
291        # Login
292        IWorkflowState(self.student).setState('school fee paid')
293        delta = timedelta(days=180)
294        self.student.personal_updated = datetime.utcnow() - delta
295        self.browser.open(self.login_path)
296        self.browser.getControl(name="form.login").value = self.student_id
297        self.browser.getControl(name="form.password").value = 'spwd'
298        self.browser.getControl("Login").click()
299        self.assertEqual(self.browser.url, self.student_path)
300        self.assertTrue(
301            'You logged in' in self.browser.contents)
302        # Students don't see personal_updated field in edit form
303        self.browser.open(self.edit_personal_path)
304        self.assertFalse('Updated' in self.browser.contents)
305        self.browser.open(self.personal_path)
306        self.assertTrue('Updated' in self.browser.contents)
307        self.browser.getLink("Logout").click()
308        delta = timedelta(days=181)
309        self.student.personal_updated = datetime.utcnow() - delta
310        self.browser.open(self.login_path)
311        self.browser.getControl(name="form.login").value = self.student_id
312        self.browser.getControl(name="form.password").value = 'spwd'
313        self.browser.getControl("Login").click()
314        self.assertEqual(self.browser.url, self.edit_personal_path)
315        self.assertTrue(
316            'Your personal data record is outdated.' in self.browser.contents)
Note: See TracBrowser for help on using the repository browser.