source: main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_export.py @ 13003

Last change on this file since 13003 was 12971, checked in by Henrik Bettermann, 10 years ago

Add StudentUnpaidPaymentExporter? to export only unpaid tickets. This exporter is designed for finding and finally purging outdated payment ticket.

  • Property svn:keywords set to Id
File size: 48.5 KB
Line 
1## $Id: test_export.py 12971 2015-05-21 07:38:15Z 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
19import os
20import grok
21import datetime
22from cStringIO import StringIO
23from zope.component import queryUtility, getUtility
24from zope.event import notify
25from zope.interface.verify import verifyObject, verifyClass
26from waeup.kofa.interfaces import (
27    ICSVExporter, IExtFileStore, IFileStoreNameChooser)
28from waeup.kofa.students.catalog import StudentsQuery
29from waeup.kofa.students.export import (
30    StudentExporter, StudentStudyCourseExporter, StudentStudyLevelExporter,
31    CourseTicketExporter, StudentPaymentExporter, BedTicketExporter,
32    StudentPaymentsOverviewExporter, StudentStudyLevelsOverviewExporter,
33    ComboCardDataExporter, DataForBursaryExporter,
34    StudentUnpaidPaymentExporter,
35    get_students,)
36from waeup.kofa.students.accommodation import BedTicket
37from waeup.kofa.students.interfaces import ICSVStudentExporter
38from waeup.kofa.students.payments import StudentOnlinePayment
39from waeup.kofa.students.student import Student
40from waeup.kofa.students.studycourse import StudentStudyCourse
41from waeup.kofa.students.studylevel import StudentStudyLevel, CourseTicket
42from waeup.kofa.students.tests.test_batching import StudentImportExportSetup
43from waeup.kofa.testing import FunctionalLayer
44
45curr_year = datetime.datetime.now().year
46year_range = range(curr_year - 9, curr_year + 1)
47year_range_str = ','.join([str(i) for i in year_range])
48
49class ExportHelperTests(StudentImportExportSetup):
50    layer = FunctionalLayer
51    def setUp(self):
52        super(ExportHelperTests, self).setUp()
53        student = Student()
54        self.app['students'].addStudent(student)
55        student = self.setup_student(student)
56        notify(grok.ObjectModifiedEvent(student))
57        self.student = self.app['students'][student.student_id]
58        return
59
60    def test_get_students_plain(self):
61        # without a filter we get all students
62        result = get_students(self.app)
63        self.assertEqual(len(list(result)), 1)
64        return
65
66    def test_get_students_by_session(self):
67        # we can filter out students of a certain session
68        my_filter1 = StudentsQuery(current_session=2012)
69        result = get_students(self.app, stud_filter=my_filter1)
70        self.assertEqual(len(list(result)), 1)
71
72        my_filter2 = StudentsQuery(current_session=1964)
73        result = get_students(self.app, stud_filter=my_filter2)
74        self.assertEqual(len(list(result)), 0)
75        return
76
77    def test_get_students_by_level(self):
78        # we can filter out students of a certain level
79        my_filter1 = StudentsQuery(current_level=200)
80        result = get_students(self.app, stud_filter=my_filter1)
81        self.assertEqual(len(list(result)), 1)
82
83        my_filter2 = StudentsQuery(current_level=300)
84        result = get_students(self.app, stud_filter=my_filter2)
85        self.assertEqual(len(list(result)), 0)
86        return
87
88    def test_get_students_by_deptcode(self):
89        # we can filter out students of a certain dept.
90        my_filter1 = StudentsQuery(depcode='NA')
91        result = get_students(self.app, stud_filter=my_filter1)
92        self.assertEqual(len(list(result)), 1)
93
94        my_filter2 = StudentsQuery(depcode='NOTEXISTING')
95        result = get_students(self.app, stud_filter=my_filter2)
96        self.assertEqual(len(list(result)), 0)
97        return
98
99    def test_get_students_by_faccode(self):
100        # we can filter out students of a certain faculty.
101        my_filter1 = StudentsQuery(faccode='NA')
102        result = get_students(self.app, stud_filter=my_filter1)
103        self.assertEqual(len(list(result)), 1)
104
105        my_filter2 = StudentsQuery(faccode='NOTEXISTING')
106        result = get_students(self.app, stud_filter=my_filter2)
107        self.assertEqual(len(list(result)), 0)
108        return
109
110    def test_get_students_by_current_mode(self):
111        # we can filter out students in a certain mode.
112        my_filter1 = StudentsQuery(current_mode='ug_ft')
113        result = get_students(self.app, stud_filter=my_filter1)
114        self.assertEqual(len(list(result)), 1)
115
116        my_filter2 = StudentsQuery(current_mode='NOTEXISTING')
117        result = get_students(self.app, stud_filter=my_filter2)
118        self.assertEqual(len(list(result)), 0)
119        return
120
121
122class StudentExporterTest(StudentImportExportSetup):
123
124    layer = FunctionalLayer
125
126    std_csv_entry = (
127        'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,'
128        'Anna,Tester,234,M.,NG,,"Studentroad 21\nLagos 123456\n",,'
129        '+234-123-12345#,123,f,A111111,0,,,,created'
130        )
131
132    def setUp(self):
133        super(StudentExporterTest, self).setUp()
134        self.setup_for_export()
135        return
136
137    def test_ifaces(self):
138        # make sure we fullfill interface contracts
139        obj = StudentExporter()
140        verifyObject(ICSVStudentExporter, obj)
141        verifyClass(ICSVStudentExporter, StudentExporter)
142        return
143
144    def test_get_as_utility(self):
145        # we can get an student exporter as utility
146        result = queryUtility(ICSVExporter, name="students")
147        self.assertTrue(result is not None)
148        return
149
150    def test_export(self):
151        # we can really export students
152        # set values we can expect in export file
153        self.setup_student(self.student)
154        exporter = StudentExporter()
155        exporter.export([self.student], self.outfile)
156        result = open(self.outfile, 'rb').read()
157        self.assertTrue(
158            'adm_code,clearance_locked,clr_code,date_of_birth,email,'
159            'employer,firstname,lastname,matric_number,middlename,'
160            'nationality,officer_comment,perm_address,personal_updated,'
161            'phone,reg_number,sex,student_id,suspended,suspended_comment,'
162            'transcript_comment,password,state,history,certcode,is_postgrad,'
163            'current_level,current_session\r\n'
164            'my adm code,0,my clr code,'
165            '1981-02-04#,anna@sample.com,,Anna,Tester,234,M.,NG,,'
166            '"Studentroad 21\nLagos 123456\n",,+234-123-12345#,123,f,'
167            'A111111,0,,,,created'
168            in result
169            )
170        return
171
172    def test_export_all(self):
173        # we can really export students
174        # set values we can expect in export file
175        self.setup_student(self.student)
176        exporter = StudentExporter()
177        exporter.export_all(self.app, self.outfile)
178        result = open(self.outfile, 'rb').read()
179        self.assertTrue(
180            'adm_code,clearance_locked,clr_code,date_of_birth,email,'
181            'employer,firstname,lastname,matric_number,middlename,'
182            'nationality,officer_comment,perm_address,personal_updated,'
183            'phone,reg_number,sex,student_id,suspended,suspended_comment,'
184            'transcript_comment,password,state,history,certcode,'
185            'is_postgrad,current_level,current_session\r\n'
186            'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,'
187            'Anna,Tester,234,M.,NG,,"Studentroad 21\nLagos 123456\n"'
188            ',,+234-123-12345#,123,f,A111111,0,,,,created'
189            in result
190            )
191        return
192
193    def test_export_student(self):
194        # we can export a single student
195        self.setup_student(self.student)
196        exporter = StudentExporter()
197        exporter.export_student(self.student, self.outfile)
198        result = open(self.outfile, 'rb').read()
199        self.assertTrue(
200            'adm_code,clearance_locked,clr_code,date_of_birth,email,'
201            'employer,firstname,lastname,matric_number,middlename,'
202            'nationality,officer_comment,perm_address,personal_updated,'
203            'phone,reg_number,sex,student_id,suspended,suspended_comment,'
204            'transcript_comment,password,state,history,certcode,'
205            'is_postgrad,current_level,current_session\r\n'
206            'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,'
207            'Anna,Tester,234,M.,NG,,"Studentroad 21\nLagos 123456\n"'
208            ',,+234-123-12345#,123,f,A111111,0,,,,created'
209            in result
210            )
211        return
212
213    def test_export_filtered(self):
214        # we can export a filtered set of students (filtered by session/level)
215        self.setup_student(self.student)
216        self.app['students'].addStudent(self.student)
217        notify(grok.ObjectModifiedEvent(self.student))
218        exporter = StudentExporter()
219
220        exporter.export_filtered(
221            self.app, self.outfile,
222            current_session=None, current_level=None)
223        result1 = open(self.outfile, 'rb').read()
224        exporter.export_filtered(
225            self.app, self.outfile,
226            current_session=2012, current_level=None)
227        result2 = open(self.outfile, 'rb').read()
228        # current_level and current_session can be both a string ...
229        exporter.export_filtered(
230            self.app, self.outfile,
231            current_session='2012', current_level=u'200')
232        result3 = open(self.outfile, 'rb').read()
233        exporter.export_filtered(
234            self.app, self.outfile,
235            current_session=2011, current_level=None)
236        result4 = open(self.outfile, 'rb').read()
237        # ... and an integer
238        exporter.export_filtered(
239            self.app, self.outfile,
240            current_session=None, current_level=100)
241        result5 = open(self.outfile, 'rb').read()
242        # Also students at probating levels are being exported ...
243        self.student['studycourse'].current_level = 210
244        notify(grok.ObjectModifiedEvent(self.student))
245        exporter.export_filtered(
246            self.app, self.outfile,
247            current_session=None, current_level=200)
248        result6 = open(self.outfile, 'rb').read()
249        # ... but not in the wrong level range.
250        self.student['studycourse'].current_level = 310
251        notify(grok.ObjectModifiedEvent(self.student))
252        exporter.export_filtered(
253            self.app, self.outfile,
254            current_session=None, current_level=200)
255        result7 = open(self.outfile, 'rb').read()
256        self.assertTrue(self.std_csv_entry in result1)
257        self.assertTrue(self.std_csv_entry in result2)
258        self.assertTrue(self.std_csv_entry in result3)
259        self.assertFalse(self.std_csv_entry in result4)
260        self.assertFalse(self.std_csv_entry in result5)
261        self.assertTrue(self.std_csv_entry in result6)
262        self.assertFalse(self.std_csv_entry in result7)
263        return
264
265    def test_export_selected(self):
266        # we can export a filtered set of students (filtered by session/level)
267        self.setup_student(self.student)
268        self.app['students'].addStudent(self.student)
269        notify(grok.ObjectModifiedEvent(self.student))
270        exporter = StudentExporter()
271        exporter.export_selected(
272            self.app, self.outfile, selected=['A111111'])
273        result1 = open(self.outfile, 'rb').read()
274        exporter.export_selected(
275            self.app, self.outfile, selected=[])
276        result2 = open(self.outfile, 'rb').read()
277        self.assertTrue(self.std_csv_entry in result1)
278        self.assertFalse(self.std_csv_entry in result2)
279        return
280
281    def test_export_filtered_by_dept(self):
282        # we can export a set of students filtered by department
283        self.setup_student(self.student)
284        self.app['students'].addStudent(self.student)
285        notify(grok.ObjectModifiedEvent(self.student))
286        exporter = StudentExporter()
287        # current_session can be both a string ...
288        exporter.export_filtered(
289            self.app, self.outfile,
290            current_session='2012', current_level=u'200', depcode='NA')
291        result1 = open(self.outfile, 'rb').read()
292        # ... and an integer
293        exporter.export_filtered(
294            self.app, self.outfile,
295            current_session=2012, current_level=200, depcode='NODEPT')
296        result2 = open(self.outfile, 'rb').read()
297        self.assertTrue(self.std_csv_entry in result1)
298        self.assertTrue(self.std_csv_entry not in result2)
299        return
300
301    def test_export_filtered_by_faculty(self):
302        # we can export a set of students filtered by faculty
303        self.setup_student(self.student)
304        self.app['students'].addStudent(self.student)
305        notify(grok.ObjectModifiedEvent(self.student))
306        exporter = StudentExporter()
307
308        exporter.export_filtered(
309            self.app, self.outfile,
310            current_session=2012, current_level='200', faccode='NA')
311        result1 = open(self.outfile, 'rb').read()
312        exporter.export_filtered(
313            self.app, self.outfile,
314            current_session=2012, current_level=200, faccode='NOFAC')
315        result2 = open(self.outfile, 'rb').read()
316        self.assertTrue(self.std_csv_entry in result1)
317        self.assertTrue(self.std_csv_entry not in result2)
318        return
319
320class StudentStudyCourseExporterTest(StudentImportExportSetup):
321
322    layer = FunctionalLayer
323
324    def setUp(self):
325        super(StudentStudyCourseExporterTest, self).setUp()
326        self.setup_for_export()
327        return
328
329    def test_ifaces(self):
330        # make sure we fullfill interface contracts
331        obj = StudentStudyCourseExporter()
332        verifyObject(ICSVStudentExporter, obj)
333        verifyClass(ICSVStudentExporter, StudentStudyCourseExporter)
334        return
335
336    def test_get_as_utility(self):
337        # we can get an student exporter as utility
338        result = queryUtility(ICSVExporter, name="studentstudycourses")
339        self.assertTrue(result is not None)
340        return
341
342    def test_export_empty(self):
343        # we can export a nearly empty study course
344        study_course = StudentStudyCourse()
345        exporter = StudentStudyCourseExporter()
346        exporter.export([study_course], self.outfile)
347        result = open(self.outfile, 'rb').read()
348        self.assertEqual(
349            result,
350            'certificate,current_level,current_session,current_verdict,'
351            'entry_mode,entry_session,previous_verdict,student_id\r\n'
352
353            ',,,0,,,0,\r\n'
354            )
355        return
356
357    def test_export(self):
358        # we can really export study courses.
359        # set values we can expect in export file
360        self.setup_student(self.student)
361        study_course = self.student.get('studycourse')
362        exporter = StudentStudyCourseExporter()
363        exporter.export([study_course], self.outfile)
364        result = open(self.outfile, 'rb').read()
365        self.assertEqual(
366            result,
367            'certificate,current_level,current_session,current_verdict,'
368            'entry_mode,entry_session,previous_verdict,student_id\r\n'
369
370            'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n'
371            )
372        return
373
374    def test_export_all(self):
375        # we can really export students
376        # set values we can expect in export file
377        self.setup_student(self.student)
378        exporter = StudentStudyCourseExporter()
379        exporter.export_all(self.app, self.outfile)
380        result = open(self.outfile, 'rb').read()
381        self.assertEqual(
382            result,
383            'certificate,current_level,current_session,current_verdict,'
384            'entry_mode,entry_session,previous_verdict,student_id\r\n'
385
386            'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n'
387            )
388        return
389
390    def test_export_student(self):
391        # we can export studycourse of a certain student
392        self.setup_student(self.student)
393        exporter = StudentStudyCourseExporter()
394        exporter.export_student(self.student, self.outfile)
395        result = open(self.outfile, 'rb').read()
396        self.assertEqual(
397            result,
398            'certificate,current_level,current_session,current_verdict,'
399            'entry_mode,entry_session,previous_verdict,student_id\r\n'
400
401            'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n'
402            )
403        return
404
405    def test_export_filtered(self):
406        # we can export studycourses of a filtered set of students
407        self.setup_student(self.student)
408        self.app['students'].addStudent(self.student)
409        notify(grok.ObjectModifiedEvent(self.student))
410
411        exporter = StudentStudyCourseExporter()
412        exporter.export_filtered(
413            self.student, self.outfile, current_session=2012)
414        result = open(self.outfile, 'rb').read()
415        self.assertEqual(
416            result,
417            'certificate,current_level,current_session,current_verdict,'
418            'entry_mode,entry_session,previous_verdict,student_id\r\n'
419
420            'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n'
421            )
422        return
423
424    def test_export_selected(self):
425        # we can export a filtered set of students (filtered by session/level)
426        self.setup_student(self.student)
427        self.app['students'].addStudent(self.student)
428        notify(grok.ObjectModifiedEvent(self.student))
429        exporter = StudentStudyCourseExporter()
430        exporter.export_selected(
431            self.app, self.outfile, selected=['A111111'])
432        result = open(self.outfile, 'rb').read()
433        self.assertEqual(
434            result,
435            'certificate,current_level,current_session,current_verdict,'
436            'entry_mode,entry_session,previous_verdict,student_id\r\n'
437
438            'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n'
439            )
440        return
441
442
443class StudentStudyLevelExporterTest(StudentImportExportSetup):
444
445    layer = FunctionalLayer
446
447    def setUp(self):
448        super(StudentStudyLevelExporterTest, self).setUp()
449        self.setup_for_export()
450        return
451
452    def test_ifaces(self):
453        # make sure we fullfill interface contracts
454        obj = StudentStudyLevelExporter()
455        verifyObject(ICSVStudentExporter, obj)
456        verifyClass(ICSVStudentExporter, StudentStudyLevelExporter)
457        return
458
459    def test_get_as_utility(self):
460        # we can get an student exporter as utility
461        result = queryUtility(ICSVExporter, name="studentstudylevels")
462        self.assertTrue(result is not None)
463        return
464
465    def test_export_empty(self):
466        # we can export a nearly empty study level
467        study_level = StudentStudyLevel()
468        exporter = StudentStudyLevelExporter()
469        exporter.export([study_level], self.outfile)
470        result = open(self.outfile, 'rb').read()
471        self.assertEqual(
472            result,
473            'gpa,level,level_session,level_verdict,total_credits,'
474            'validated_by,validation_date,'
475            'student_id,number_of_tickets,certcode\r\n'
476            '0.0,,,0,0,,,,0,\r\n'
477            )
478        return
479
480    def test_export(self):
481        # we can really export study levels.
482        # set values we can expect in export file
483        self.setup_student(self.student)
484        study_course = self.student.get('studycourse')
485        study_level = study_course[study_course.keys()[0]]
486        exporter = StudentStudyLevelExporter()
487        exporter.export([study_level], self.outfile)
488        result = open(self.outfile, 'rb').read()
489        self.assertEqual(
490            result,
491            'gpa,level,level_session,level_verdict,total_credits,'
492            'validated_by,validation_date,'
493            'student_id,number_of_tickets,certcode\r\n'
494            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
495            )
496        return
497
498    def test_export_all(self):
499        # we can really export study levels
500        # set values we can expect in export file
501        self.setup_student(self.student)
502        exporter = StudentStudyLevelExporter()
503        exporter.export_all(self.app, self.outfile)
504        result = open(self.outfile, 'rb').read()
505        self.assertEqual(
506            result,
507            'gpa,level,level_session,level_verdict,total_credits,'
508            'validated_by,validation_date,'
509            'student_id,number_of_tickets,certcode\r\n'
510            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
511            )
512        return
513
514    def test_export_student(self):
515        # we can really export study levels of a certain student
516        self.setup_student(self.student)
517        exporter = StudentStudyLevelExporter()
518        exporter.export_student(self.student, self.outfile)
519        result = open(self.outfile, 'rb').read()
520        self.assertEqual(
521            result,
522            'gpa,level,level_session,level_verdict,total_credits,'
523            'validated_by,validation_date,'
524            'student_id,number_of_tickets,certcode\r\n'
525            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
526            )
527        return
528
529    def test_export_filtered(self):
530        # we can export studylevels of a filtered set of students
531        self.setup_student(self.student)
532        self.app['students'].addStudent(self.student)
533        notify(grok.ObjectModifiedEvent(self.student))
534
535        exporter = StudentStudyLevelExporter()
536        exporter.export_filtered(
537            self.student, self.outfile)
538        result = open(self.outfile, 'rb').read()
539        self.assertEqual(
540            result,
541            'gpa,level,level_session,level_verdict,total_credits,'
542            'validated_by,validation_date,'
543            'student_id,number_of_tickets,certcode\r\n'
544            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
545            )
546        return
547
548    def test_export_selected(self):
549        # we can export studylevels of a filtered set of students
550        self.setup_student(self.student)
551        self.app['students'].addStudent(self.student)
552        notify(grok.ObjectModifiedEvent(self.student))
553
554        exporter = StudentStudyLevelExporter()
555        exporter.export_selected(
556            self.app, self.outfile, selected=['A111111'])
557        result = open(self.outfile, 'rb').read()
558        self.assertEqual(
559            result,
560            'gpa,level,level_session,level_verdict,total_credits,'
561            'validated_by,validation_date,'
562            'student_id,number_of_tickets,certcode\r\n'
563            '0.0,100,2012,A,100,,,A111111,1,CERT1\r\n'
564            )
565        return
566
567class CourseTicketExporterTest(StudentImportExportSetup):
568
569    layer = FunctionalLayer
570
571    def setUp(self):
572        super(CourseTicketExporterTest, self).setUp()
573        self.setup_for_export()
574        return
575
576    def test_ifaces(self):
577        # make sure we fullfill interface contracts
578        obj = CourseTicketExporter()
579        verifyObject(ICSVStudentExporter, obj)
580        verifyClass(ICSVStudentExporter, CourseTicketExporter)
581        return
582
583    def test_get_as_utility(self):
584        # we can get an student exporter as utility
585        result = queryUtility(ICSVExporter, name="coursetickets")
586        self.assertTrue(result is not None)
587        return
588
589    def test_export_empty(self):
590        # we can export a nearly empty course ticket
591        ticket = CourseTicket()
592        exporter = CourseTicketExporter()
593        exporter.export([ticket], self.outfile)
594        result = open(self.outfile, 'rb').read()
595        self.assertEqual(
596            result,
597            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
598            'mandatory,passmark,score,semester,title,student_id,certcode,'
599            'display_fullname\r\n'
600            '0,0,,,,,,,0,,,,,,,\r\n'
601            )
602        return
603
604    def test_export(self):
605        # we can really export course tickets.
606        # set values we can expect in export file
607        self.setup_student(self.student)
608        study_course = self.student.get('studycourse')
609        study_level = study_course[study_course.keys()[0]]
610        ticket = study_level['CRS1']
611        exporter = CourseTicketExporter()
612        exporter.export([ticket], self.outfile)
613        result = open(self.outfile, 'rb').read()
614        self.assertEqual(
615            result,
616            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
617            'mandatory,passmark,score,semester,title,student_id,certcode,'
618            'display_fullname\r\n'
619            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
620            'Anna M. Tester\r\n'
621            )
622        return
623
624    def test_export_all(self):
625        # we can really export all course tickets
626        # set values we can expect in export file
627        self.setup_student(self.student)
628        exporter = CourseTicketExporter()
629        exporter.export_all(self.app, self.outfile)
630        result = open(self.outfile, 'rb').read()
631        self.assertEqual(
632            result,
633            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
634            'mandatory,passmark,score,semester,title,student_id,certcode,'
635            'display_fullname\r\n'
636            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
637            'Anna M. Tester\r\n'
638            )
639        return
640
641    def test_export_student(self):
642        # we can really export all course tickets of a certain student
643        self.setup_student(self.student)
644        exporter = CourseTicketExporter()
645        exporter.export_student(self.student, self.outfile)
646        result = open(self.outfile, 'rb').read()
647        self.assertEqual(
648            result,
649            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
650            'mandatory,passmark,score,semester,title,student_id,certcode,'
651            'display_fullname\r\n'
652            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
653            'Anna M. Tester\r\n'
654            )
655        return
656
657    def test_export_filtered(self):
658        # we can export course tickets of a filtered set of students
659        self.setup_student(self.student)
660        self.app['students'].addStudent(self.student)
661        notify(grok.ObjectModifiedEvent(self.student))
662
663        exporter = CourseTicketExporter()
664        exporter.export_filtered(
665            self.student, self.outfile)
666        result = open(self.outfile, 'rb').read()
667        self.assertEqual(
668            result,
669            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
670            'mandatory,passmark,score,semester,title,student_id,certcode,'
671            'display_fullname\r\n'
672            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
673            'Anna M. Tester\r\n'
674            )
675        # if the coursetickets catalog is used to filter students
676        # and (course) code is not None
677        # only course tickets which belong to this course are exported
678        exporter.export_filtered(
679            self.student, self.outfile, catalog='coursetickets', code='CRS1')
680        result = open(self.outfile, 'rb').read()
681        self.assertEqual(
682            result,
683            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
684            'mandatory,passmark,score,semester,title,student_id,certcode,'
685            'display_fullname\r\n'
686            '1,1,CRS1,100,DEP1,FAC1,100,2012,0,100,,2,Course 1,A111111,CERT1,'
687            'Anna M. Tester\r\n'
688            )
689        exporter.export_filtered(
690            self.student, self.outfile, catalog='coursetickets', code='any code')
691        result = open(self.outfile, 'rb').read()
692        self.assertEqual(
693            result,
694            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
695            'mandatory,passmark,score,semester,title,student_id,certcode,'
696            'display_fullname\r\n'
697            )
698        # Also tickets in probating levels are exported. Therefore
699        # we change the level attribute to fake a 110 level.
700        self.student['studycourse']['100'].level = 110
701        notify(grok.ObjectModifiedEvent(self.student['studycourse']['100']['CRS1']))
702        exporter.export_filtered(
703            self.student, self.outfile, catalog='coursetickets', code='CRS1', level='100')
704        result = open(self.outfile, 'rb').read()
705        self.assertEqual(
706            result,
707            'automatic,carry_over,code,credits,dcode,fcode,level,level_session,'
708            'mandatory,passmark,score,semester,title,student_id,certcode,'
709            'display_fullname\r\n'
710            '1,1,CRS1,100,DEP1,FAC1,110,2012,0,100,,2,Course 1,A111111,CERT1,'
711            'Anna M. Tester\r\n'
712            )
713        return
714
715class StudentPaymentExporterTest(StudentImportExportSetup):
716
717    layer = FunctionalLayer
718
719    def setUp(self):
720        super(StudentPaymentExporterTest, self).setUp()
721        self.setup_for_export()
722        return
723
724    def test_ifaces(self):
725        # make sure we fullfill interface contracts
726        obj = StudentPaymentExporter()
727        verifyObject(ICSVStudentExporter, obj)
728        verifyClass(ICSVStudentExporter, StudentPaymentExporter)
729        return
730
731    def test_get_as_utility(self):
732        # we can get a payments exporter as utility
733        result = queryUtility(ICSVExporter, name="studentpayments")
734        self.assertTrue(result is not None)
735        return
736
737    def test_export_empty(self):
738        # we can export a nearly empty payment
739        payment = StudentOnlinePayment()
740        payment.creation_date = datetime.datetime(2012, 4, 1, 13, 12, 1)
741        exporter = StudentPaymentExporter()
742        exporter.export([payment], self.outfile)
743        result = open(self.outfile, 'rb').read()
744        self.assertEqual(
745            result,
746            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
747            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
748            'r_code,r_desc,student_id,state,current_session\r\n'
749
750            ',0.0,2012-04-01 13:12:01#,,1,,,,,unpaid,,0.0,,,,,\r\n'
751            )
752        return
753
754    def test_export(self):
755        # we can really export student payments.
756        # set values we can expect in export file
757        self.setup_student(self.student)
758        payment = self.student['payments']['my-payment']
759        exporter = StudentPaymentExporter()
760        exporter.export([payment], self.outfile)
761        result = open(self.outfile, 'rb').read()
762        self.assertEqual(
763            result,
764            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
765            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
766            'r_code,r_desc,student_id,state,current_session\r\n'
767
768            '666,12.12,2012-04-01 13:12:01#,schoolfee,1,my-id,'
769            'p-item,100,2012,paid,2012-04-01 14:12:01#,12.12,'
770            'r-code,,A111111,created,2012\r\n'
771            )
772        return
773
774    def test_export_all(self):
775        # we can really export all payments
776        # set values we can expect in export file
777        self.setup_student(self.student)
778        exporter = StudentPaymentExporter()
779        exporter.export_all(self.app, self.outfile)
780        result = open(self.outfile, 'rb').read()
781        self.assertEqual(
782            result,
783            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
784            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
785            'r_code,r_desc,student_id,state,current_session\r\n'
786
787            '666,12.12,2012-04-01 13:12:01#,schoolfee,1,my-id,'
788            'p-item,100,2012,paid,2012-04-01 14:12:01#,12.12,'
789            'r-code,,A111111,created,2012\r\n'
790            )
791        return
792
793    def test_export_student(self):
794        # we can really export all payments of a certain student
795        # set values we can expect in export file
796        self.setup_student(self.student)
797        exporter = StudentPaymentExporter()
798        exporter.export_student(self.student, self.outfile)
799        result = open(self.outfile, 'rb').read()
800        self.assertEqual(
801            result,
802            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
803            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
804            'r_code,r_desc,student_id,state,current_session\r\n'
805
806            '666,12.12,2012-04-01 13:12:01#,schoolfee,1,my-id,'
807            'p-item,100,2012,paid,2012-04-01 14:12:01#,12.12,'
808            'r-code,,A111111,created,2012\r\n'
809            )
810        return
811
812    def test_export_filtered(self):
813        # we can export payments of a filtered set of students
814        self.setup_student(self.student)
815        self.app['students'].addStudent(self.student)
816        notify(grok.ObjectModifiedEvent(self.student))
817
818        exporter = StudentPaymentExporter()
819        exporter.export_filtered(
820            self.student, self.outfile, current_level=200)
821        result = open(self.outfile, 'rb').read()
822        self.assertEqual(
823            result,
824            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
825            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
826            'r_code,r_desc,student_id,state,current_session\r\n'
827
828            '666,12.12,2012-04-01 13:12:01#,schoolfee,1,my-id,'
829            'p-item,100,2012,paid,2012-04-01 14:12:01#,12.12,'
830            'r-code,,A111111,created,2012\r\n'
831            )
832        return
833
834    def test_export_filtered_by_date(self):
835        # payments_start and payments_end are being ignored
836        self.setup_student(self.student)
837        self.app['students'].addStudent(self.student)
838        notify(grok.ObjectModifiedEvent(self.student))
839        exporter = StudentPaymentExporter()
840        # A key xxx does not exist
841        self.assertRaises(
842            KeyError, exporter.export_filtered, self.app, self.outfile,
843            current_session=None,
844            current_level=None, xxx='nonsense')
845        # payments_start and payments_end do exist but must match format '%Y-%m-%d'
846        self.assertRaises(
847            ValueError, exporter.export_filtered, self.app, self.outfile,
848            current_session=None, current_level=None,
849            payments_start='nonsense', payments_end='nonsense')
850        # If they match the format they are ignored by get_filtered and the
851        # exporter works properly
852        exporter.export_filtered(
853            self.app, self.outfile,
854            current_session=None, current_level=None,
855            payments_start='01/04/2012', payments_end='02/04/2012')
856        result = open(self.outfile, 'rb').read()
857        self.assertEqual(
858            result,
859            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
860            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
861            'r_code,r_desc,student_id,state,current_session\r\n'
862
863            '666,12.12,2012-04-01 13:12:01#,schoolfee,1,my-id,'
864            'p-item,100,2012,paid,2012-04-01 14:12:01#,12.12,'
865            'r-code,,A111111,created,2012\r\n'
866            )
867        # no results if payment_date is outside the given period
868        exporter.export_filtered(
869            self.app, self.outfile,
870            current_session=None, current_level=None,
871            payments_start='31/03/2012', payments_end='01/04/2012')
872        result = open(self.outfile, 'rb').read()
873        self.assertEqual(
874            result,
875            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
876            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
877            'r_code,r_desc,student_id,state,current_session\r\n'
878            )
879        exporter.export_filtered(
880            self.app, self.outfile,
881            current_session=None, current_level=None,
882            payments_start='02/04/2012', payments_end='03/04/2012')
883        result = open(self.outfile, 'rb').read()
884        self.assertEqual(
885            result,
886            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
887            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
888            'r_code,r_desc,student_id,state,current_session\r\n'
889            )
890        # no results if payment_date is not set
891        self.payment.payment_date = None
892        exporter.export_filtered(
893            self.app, self.outfile,
894            current_session=None, current_level=None,
895            payments_start='01/04/2012', payments_end='02/04/2012')
896        result = open(self.outfile, 'rb').read()
897       
898        return
899
900class StudentUnpaidPaymentExporterTest(StudentImportExportSetup):
901
902    layer = FunctionalLayer
903
904    def setUp(self):
905        super(StudentUnpaidPaymentExporterTest, self).setUp()
906        self.setup_for_export()
907        return
908
909    def test_export_all(self):
910        # we can really export all payments
911        # set values we can expect in export file
912        self.setup_student(self.student)
913        exporter = StudentUnpaidPaymentExporter()
914        exporter.export_all(self.app, self.outfile)
915        result = open(self.outfile, 'rb').read()
916        # No unpaid ticket exists
917        self.assertEqual(
918            result,
919            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
920            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
921            'r_code,r_desc,student_id,state,current_session\r\n'
922            )
923        # Make ticket unpaid
924        self.payment.p_state = 'unpaid'
925        exporter.export_all(self.app, self.outfile)
926        result = open(self.outfile, 'rb').read()
927        self.assertEqual(
928            result,
929            'ac,amount_auth,creation_date,p_category,p_current,p_id,'
930            'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,'
931            'r_code,r_desc,student_id,state,current_session\r\n'
932
933            '666,12.12,2012-04-01 13:12:01#,schoolfee,1,my-id,'
934            'p-item,100,2012,unpaid,2012-04-01 14:12:01#,12.12,'
935            'r-code,,A111111,created,2012\r\n'
936            )
937        return
938
939class BursaryDataExporterTest(StudentImportExportSetup):
940
941    layer = FunctionalLayer
942
943    def setUp(self):
944        super(BursaryDataExporterTest, self).setUp()
945        self.setup_for_export()
946        return
947
948    def test_export_all(self):
949        # we can really export all payments
950        # set values we can expect in export file
951        self.setup_student(self.student)
952        exporter = DataForBursaryExporter()
953        exporter.export_all(self.app, self.outfile)
954        result = open(self.outfile, 'rb').read()
955        self.assertEqual(
956            result,
957            'ac,amount_auth,creation_date,p_category,p_current,p_id,p_item,'
958            'p_level,p_session,p_state,payment_date,r_amount_approved,r_code,'
959            'r_desc,student_id,matric_number,reg_number,firstname,middlename,lastname,'
960            'state,current_session,entry_session,entry_mode,faccode,depcode,certcode\r\n'
961
962            '666,12.12,2012-04-01 13:12:01#,schoolfee,1,my-id,p-item,100,2012,'
963            'paid,2012-04-01 14:12:01#,12.12,r-code,,A111111,234,123,'
964            'Anna,M.,Tester,created,2012,2010,ug_ft,NA,NA,CERT1\r\n'
965            )
966        return
967
968class BedTicketExporterTest(StudentImportExportSetup):
969
970    layer = FunctionalLayer
971
972    def setUp(self):
973        super(BedTicketExporterTest, self).setUp()
974        self.setup_for_export()
975        return
976
977    def test_ifaces(self):
978        # make sure we fullfill interface contracts
979        obj = BedTicketExporter()
980        verifyObject(ICSVStudentExporter, obj)
981        verifyClass(ICSVStudentExporter, BedTicketExporter)
982        return
983
984    def test_get_as_utility(self):
985        # we can get a bedtickets exporter as utility
986        result = queryUtility(ICSVExporter, name="bedtickets")
987        self.assertTrue(result is not None)
988        return
989
990    def test_export_empty(self):
991        # we can export a nearly empty bedticket
992        bedticket = BedTicket()
993        bed = self.app['hostels']['hall-1']['hall-1_A_101_A']
994        bedticket.bed = bed
995        exporter = BedTicketExporter()
996        exporter.export([bedticket], self.outfile)
997        result = open(self.outfile, 'rb').read()
998        self.assertMatches(
999            result,
1000            'bed,bed_coordinates,bed_type,booking_code,booking_date,'
1001            'booking_session,student_id,actual_bed_type\r\n'
1002            'hall-1_A_101_A,,,,<YYYY-MM-DD hh:mm:ss>.<6-DIGITS>#,,,regular_male_fr\r\n'
1003            )
1004        return
1005
1006    def test_export(self):
1007        # we can really export student bedtickets.
1008        # set values we can expect in export file
1009        self.setup_student(self.student)
1010        bedticket = self.student['accommodation']['2004']
1011        exporter = BedTicketExporter()
1012        exporter.export([bedticket], self.outfile)
1013        result = open(self.outfile, 'rb').read()
1014        self.assertMatches(
1015            result,
1016            'bed,bed_coordinates,bed_type,booking_code,booking_date,'
1017            'booking_session,student_id,actual_bed_type\r\n'
1018            'hall-1_A_101_A,,any bed type,,<YYYY-MM-DD hh:mm:ss>.<6-DIGITS>#,2004,'
1019            'A111111,regular_male_fr\r\n'
1020            )
1021        return
1022
1023    def test_export_all(self):
1024        # we can really export all bedtickets
1025        # set values we can expect in export file
1026        self.setup_student(self.student)
1027        exporter = BedTicketExporter()
1028        exporter.export_all(self.app, self.outfile)
1029        result = open(self.outfile, 'rb').read()
1030        self.assertMatches(
1031            result,
1032            'bed,bed_coordinates,bed_type,booking_code,booking_date,'
1033            'booking_session,student_id,actual_bed_type\r\n'
1034            'hall-1_A_101_A,,any bed type,,<YYYY-MM-DD hh:mm:ss>.<6-DIGITS>#,2004,'
1035            'A111111,regular_male_fr\r\n'
1036            )
1037        return
1038
1039    def test_export_student(self):
1040        # we can really export all bedtickets of a certain student
1041        # set values we can expect in export file
1042        self.setup_student(self.student)
1043        exporter = BedTicketExporter()
1044        exporter.export_student(self.student, self.outfile)
1045        result = open(self.outfile, 'rb').read()
1046        self.assertMatches(
1047            result,
1048            'bed,bed_coordinates,bed_type,booking_code,booking_date,'
1049            'booking_session,student_id,actual_bed_type\r\n'
1050            'hall-1_A_101_A,,any bed type,,<YYYY-MM-DD hh:mm:ss>.<6-DIGITS>#,2004,'
1051            'A111111,regular_male_fr\r\n'
1052            )
1053        return
1054
1055    def test_export_filtered(self):
1056        # we can export payments of a filtered set of students
1057        self.setup_student(self.student)
1058        self.app['students'].addStudent(self.student)
1059        notify(grok.ObjectModifiedEvent(self.student))
1060
1061        exporter = BedTicketExporter()
1062        exporter.export_filtered(
1063            self.student, self.outfile, current_level=200)
1064        result = open(self.outfile, 'rb').read()
1065        self.assertMatches(
1066            result,
1067            'bed,bed_coordinates,bed_type,booking_code,booking_date,'
1068            'booking_session,student_id,actual_bed_type\r\n'
1069            'hall-1_A_101_A,,any bed type,,<YYYY-MM-DD hh:mm:ss>.<6-DIGITS>#,'
1070            '2004,A111111,regular_male_fr\r\n')
1071        return
1072
1073
1074class StudentPaymentsOverviewExporterTest(StudentImportExportSetup):
1075
1076    layer = FunctionalLayer
1077
1078    def setUp(self):
1079        super(StudentPaymentsOverviewExporterTest, self).setUp()
1080        self.setup_for_export()
1081        return
1082
1083    def test_ifaces(self):
1084        # make sure we fullfill interface contracts
1085        obj = StudentPaymentsOverviewExporter()
1086        verifyObject(ICSVStudentExporter, obj)
1087        verifyClass(ICSVStudentExporter, StudentPaymentsOverviewExporter)
1088        return
1089
1090    def test_get_as_utility(self):
1091        # we can get a payments exporter as utility
1092        result = queryUtility(ICSVExporter, name="paymentsoverview")
1093        self.assertTrue(result is not None)
1094        return
1095
1096    def test_export(self):
1097        self.setup_student(self.student)
1098        exporter = StudentPaymentsOverviewExporter()
1099        exporter.export([self.student], self.outfile)
1100        result = open(self.outfile, 'rb').read()
1101        self.assertTrue(
1102            'student_id,matric_number,display_fullname,state,certcode,'
1103            'faccode,depcode,is_postgrad,'
1104            'current_level,current_session,current_mode,'
1105            '%s\r\n'
1106            'A111111,234,Anna M. Tester,created,CERT1,NA,NA,0,200,2012,ug_ft,'
1107            % year_range_str in result
1108            )
1109        return
1110
1111    def test_export_all(self):
1112        # we can really export students
1113        # set values we can expect in export file
1114        self.setup_student(self.student)
1115        # We add successful payments.
1116        payment_2 = StudentOnlinePayment()
1117        payment_2.p_id = 'my-id'
1118        payment_2.p_session = curr_year - 5
1119        payment_2.amount_auth = 13.13
1120        payment_2.p_state = 'paid'
1121        payment_2.p_category = u'schoolfee'
1122        self.student['payments']['my-2ndpayment'] = payment_2
1123        # This one could be a balance payment.
1124        # The amount is being added.
1125        payment_3 = StudentOnlinePayment()
1126        payment_3.p_id = 'my-id_2'
1127        payment_3.p_session = curr_year - 5
1128        payment_3.amount_auth = 1.01
1129        payment_3.p_state = 'paid'
1130        payment_3.p_category = u'schoolfee'
1131        self.student['payments']['my-3rdpayment'] = payment_3
1132        # One session school fee has been waived
1133        payment_4 = StudentOnlinePayment()
1134        payment_4.p_id = 'my-id_2'
1135        payment_4.p_session = curr_year - 4
1136        payment_4.amount_auth = 1.01
1137        payment_4.p_state = 'waived'
1138        payment_4.p_category = u'schoolfee'
1139        self.student['payments']['my-4thpayment'] = payment_4
1140        exporter = StudentPaymentsOverviewExporter()
1141        exporter.export_all(self.app, self.outfile)
1142        result = open(self.outfile, 'rb').read()
1143        self.assertTrue(
1144            'student_id,matric_number,display_fullname,state,'
1145            'certcode,faccode,depcode,is_postgrad,'
1146            'current_level,current_session,current_mode,'
1147            '%s\r\nA111111,234,Anna M. Tester,created,CERT1,NA,NA,0,'
1148            '200,2012,ug_ft,,,,,14.14,waived,12.12,,,\r\n'
1149            % year_range_str in result
1150            )
1151        return
1152
1153class StudentStudyLevelsOverviewExporterTest(StudentImportExportSetup):
1154
1155    layer = FunctionalLayer
1156
1157    def setUp(self):
1158        super(StudentStudyLevelsOverviewExporterTest, self).setUp()
1159        self.setup_for_export()
1160        return
1161
1162    def test_ifaces(self):
1163        obj = StudentStudyLevelsOverviewExporter()
1164        verifyObject(ICSVStudentExporter, obj)
1165        verifyClass(ICSVStudentExporter, StudentStudyLevelsOverviewExporter)
1166        return
1167
1168    def test_get_as_utility(self):
1169        result = queryUtility(ICSVExporter, name="studylevelsoverview")
1170        self.assertTrue(result is not None)
1171        return
1172
1173    def test_export(self):
1174        self.setup_student(self.student)
1175        exporter = StudentStudyLevelsOverviewExporter()
1176        exporter.export([self.student], self.outfile)
1177        result = open(self.outfile, 'rb').read()
1178        self.assertEqual(
1179             'student_id,state,certcode,faccode,depcode,is_postgrad,'
1180             'entry_session,current_level,current_session,'
1181             '10,100,110,120,200,210,220,300,310,320,400,410,420,500,'
1182             '510,520,600,610,620,700,710,720,800,810,820,900,910,920,999\r\n'
1183             'A111111,created,CERT1,NA,NA,0,2010,200,2012,,2012'
1184             ',,,,,,,,,,,,,,,,,,,,,,,,,,,\r\n',
1185            result
1186            )
1187        return
1188
1189    def test_export_all(self):
1190        self.setup_student(self.student)
1191        exporter = StudentStudyLevelsOverviewExporter()
1192        exporter.export_all(self.app, self.outfile)
1193        result = open(self.outfile, 'rb').read()
1194        self.assertEqual(
1195            'student_id,state,certcode,faccode,depcode,is_postgrad,'
1196            'entry_session,current_level,current_session,'
1197            '10,100,110,120,200,210,220,300,310,320,400,410,420,500,'
1198            '510,520,600,610,620,700,710,720,800,810,820,900,910,920,999\r\n'
1199            'A111111,created,CERT1,NA,NA,0,2010,200,2012,,2012'
1200            ',,,,,,,,,,,,,,,,,,,,,,,,,,,\r\n',
1201            result
1202            )
1203        return
1204
1205class ComboCardExporterTest(StudentImportExportSetup):
1206
1207    layer = FunctionalLayer
1208
1209    def setUp(self):
1210        super(ComboCardExporterTest, self).setUp()
1211        self.setup_for_export()
1212        return
1213
1214    def create_passport_img(self, student):
1215        # create some passport file for `student`
1216        storage = getUtility(IExtFileStore)
1217        image_path = os.path.join(os.path.dirname(__file__), 'test_image.jpg')
1218        self.image_contents = open(image_path, 'rb').read()
1219        file_id = IFileStoreNameChooser(student).chooseName(
1220            attr='passport.jpg')
1221        storage.createFile(file_id, StringIO(self.image_contents))
1222
1223    def test_export_all(self):
1224        self.setup_student(self.student)
1225        self.create_passport_img(self.student)
1226        exporter = ComboCardDataExporter()
1227        exporter.export_all(self.app, self.outfile)
1228        result = open(self.outfile, 'rb').read()
1229        self.assertTrue(
1230            'display_fullname,student_id,matric_number,certificate,faculty,'
1231            'department,passport_path\r\nAnna M. Tester,A111111,234,'
1232            'Unnamed Certificate,Faculty of Unnamed Faculty (NA),'
1233            'Department of Unnamed Department (NA),'
1234            'students/00110/A111111/passport_A111111.jpg\r\n'
1235            in result
1236            )
1237        return
Note: See TracBrowser for help on using the repository browser.