[9918] | 1 | # -*- coding: utf-8 -*- |
---|
[7193] | 2 | ## $Id: test_batching.py 16834 2022-02-24 12:48:18Z henrik $ |
---|
[6840] | 3 | ## |
---|
| 4 | ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann |
---|
| 5 | ## This program is free software; you can redistribute it and/or modify |
---|
| 6 | ## it under the terms of the GNU General Public License as published by |
---|
| 7 | ## the Free Software Foundation; either version 2 of the License, or |
---|
| 8 | ## (at your option) any later version. |
---|
[7193] | 9 | ## |
---|
[6840] | 10 | ## This program is distributed in the hope that it will be useful, |
---|
| 11 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
| 12 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
| 13 | ## GNU General Public License for more details. |
---|
[7193] | 14 | ## |
---|
[6840] | 15 | ## You should have received a copy of the GNU General Public License |
---|
| 16 | ## along with this program; if not, write to the Free Software |
---|
| 17 | ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
| 18 | ## |
---|
[7933] | 19 | """Unit tests for students-related data processors. |
---|
[6840] | 20 | """ |
---|
| 21 | import os |
---|
| 22 | import shutil |
---|
| 23 | import tempfile |
---|
| 24 | import unittest |
---|
[7515] | 25 | import datetime |
---|
[9028] | 26 | import grok |
---|
[7623] | 27 | from time import time |
---|
[9028] | 28 | from zope.event import notify |
---|
[9960] | 29 | from zope.component import createObject, queryUtility |
---|
[6840] | 30 | from zope.component.hooks import setSite, clearSite |
---|
[9960] | 31 | from zope.catalog.interfaces import ICatalog |
---|
[6840] | 32 | from zope.interface.verify import verifyClass, verifyObject |
---|
[9028] | 33 | from hurry.workflow.interfaces import IWorkflowState |
---|
[6840] | 34 | |
---|
[7811] | 35 | from waeup.kofa.app import University |
---|
[12623] | 36 | from waeup.kofa.interfaces import ( |
---|
| 37 | IBatchProcessor, FatalCSVError, IUserAccount, DuplicationError) |
---|
[7811] | 38 | from waeup.kofa.students.batching import ( |
---|
[7536] | 39 | StudentProcessor, StudentStudyCourseProcessor, |
---|
[7623] | 40 | StudentStudyLevelProcessor, CourseTicketProcessor, |
---|
[16828] | 41 | StudentOnlinePaymentProcessor, StudentVerdictProcessor, |
---|
[16831] | 42 | ) |
---|
[8411] | 43 | from waeup.kofa.students.payments import StudentOnlinePayment |
---|
[7811] | 44 | from waeup.kofa.students.student import Student |
---|
[8340] | 45 | from waeup.kofa.students.studylevel import StudentStudyLevel, CourseTicket |
---|
[9426] | 46 | from waeup.kofa.students.accommodation import BedTicket |
---|
[7811] | 47 | from waeup.kofa.testing import FunctionalLayer, FunctionalTestCase |
---|
[8354] | 48 | from waeup.kofa.university.faculty import Faculty |
---|
| 49 | from waeup.kofa.university.department import Department |
---|
[9426] | 50 | from waeup.kofa.hostels.hostel import Hostel, Bed, NOT_OCCUPIED |
---|
[6840] | 51 | |
---|
[8354] | 52 | |
---|
[6840] | 53 | STUDENT_SAMPLE_DATA = open( |
---|
| 54 | os.path.join(os.path.dirname(__file__), 'sample_student_data.csv'), |
---|
| 55 | 'rb').read() |
---|
| 56 | |
---|
| 57 | STUDENT_HEADER_FIELDS = STUDENT_SAMPLE_DATA.split( |
---|
| 58 | '\n')[0].split(',') |
---|
| 59 | |
---|
[6848] | 60 | STUDENT_SAMPLE_DATA_UPDATE = open( |
---|
| 61 | os.path.join(os.path.dirname(__file__), 'sample_student_data_update.csv'), |
---|
| 62 | 'rb').read() |
---|
| 63 | |
---|
| 64 | STUDENT_HEADER_FIELDS_UPDATE = STUDENT_SAMPLE_DATA_UPDATE.split( |
---|
| 65 | '\n')[0].split(',') |
---|
| 66 | |
---|
[6851] | 67 | STUDENT_SAMPLE_DATA_UPDATE2 = open( |
---|
| 68 | os.path.join(os.path.dirname(__file__), 'sample_student_data_update2.csv'), |
---|
| 69 | 'rb').read() |
---|
| 70 | |
---|
| 71 | STUDENT_HEADER_FIELDS_UPDATE2 = STUDENT_SAMPLE_DATA_UPDATE2.split( |
---|
| 72 | '\n')[0].split(',') |
---|
| 73 | |
---|
[8309] | 74 | STUDENT_SAMPLE_DATA_UPDATE3 = open( |
---|
| 75 | os.path.join(os.path.dirname(__file__), 'sample_student_data_update3.csv'), |
---|
| 76 | 'rb').read() |
---|
| 77 | |
---|
| 78 | STUDENT_HEADER_FIELDS_UPDATE3 = STUDENT_SAMPLE_DATA_UPDATE3.split( |
---|
| 79 | '\n')[0].split(',') |
---|
| 80 | |
---|
| 81 | STUDENT_SAMPLE_DATA_UPDATE4 = open( |
---|
| 82 | os.path.join(os.path.dirname(__file__), 'sample_student_data_update4.csv'), |
---|
| 83 | 'rb').read() |
---|
| 84 | |
---|
| 85 | STUDENT_HEADER_FIELDS_UPDATE4 = STUDENT_SAMPLE_DATA_UPDATE4.split( |
---|
| 86 | '\n')[0].split(',') |
---|
| 87 | |
---|
[6840] | 88 | STUDYCOURSE_SAMPLE_DATA = open( |
---|
| 89 | os.path.join(os.path.dirname(__file__), 'sample_studycourse_data.csv'), |
---|
| 90 | 'rb').read() |
---|
| 91 | |
---|
| 92 | STUDYCOURSE_HEADER_FIELDS = STUDYCOURSE_SAMPLE_DATA.split( |
---|
| 93 | '\n')[0].split(',') |
---|
| 94 | |
---|
[16831] | 95 | FIRST_STUDYCOURSE_SAMPLE_DATA = open( |
---|
| 96 | os.path.join(os.path.dirname(__file__), 'sample_first_studycourse_data.csv'), |
---|
| 97 | 'rb').read() |
---|
| 98 | |
---|
| 99 | FIRST_STUDYCOURSE_HEADER_FIELDS = FIRST_STUDYCOURSE_SAMPLE_DATA.split( |
---|
| 100 | '\n')[0].split(',') |
---|
| 101 | |
---|
[9960] | 102 | TRANSFER_SAMPLE_DATA = open( |
---|
| 103 | os.path.join(os.path.dirname(__file__), 'sample_transfer_data.csv'), |
---|
| 104 | 'rb').read() |
---|
| 105 | |
---|
| 106 | TRANSFER_HEADER_FIELDS = TRANSFER_SAMPLE_DATA.split( |
---|
| 107 | '\n')[0].split(',') |
---|
| 108 | |
---|
[7951] | 109 | VERDICT_SAMPLE_DATA = open( |
---|
| 110 | os.path.join(os.path.dirname(__file__), 'sample_verdict_data.csv'), |
---|
| 111 | 'rb').read() |
---|
| 112 | |
---|
| 113 | VERDICT_HEADER_FIELDS = VERDICT_SAMPLE_DATA.split( |
---|
| 114 | '\n')[0].split(',') |
---|
| 115 | |
---|
[7273] | 116 | STUDENT_SAMPLE_DATA_MIGRATION = open( |
---|
[9272] | 117 | os.path.join(os.path.dirname(__file__), |
---|
| 118 | 'sample_student_data_migration.csv'), |
---|
[7273] | 119 | 'rb').read() |
---|
| 120 | |
---|
| 121 | STUDENT_HEADER_FIELDS_MIGRATION = STUDENT_SAMPLE_DATA_MIGRATION.split( |
---|
| 122 | '\n')[0].split(',') |
---|
| 123 | |
---|
[8289] | 124 | STUDENT_SAMPLE_DATA_DUPLICATES = open( |
---|
[9272] | 125 | os.path.join(os.path.dirname(__file__), |
---|
| 126 | 'sample_student_data_duplicates.csv'), |
---|
[8289] | 127 | 'rb').read() |
---|
| 128 | |
---|
| 129 | STUDENT_HEADER_FIELDS_DUPLICATES = STUDENT_SAMPLE_DATA_DUPLICATES.split( |
---|
| 130 | '\n')[0].split(',') |
---|
| 131 | |
---|
[9918] | 132 | STUDENT_SAMPLE_DATA_EXTASCII = open( |
---|
| 133 | os.path.join(os.path.dirname(__file__), |
---|
| 134 | 'sample_student_data_extascii.csv'), |
---|
| 135 | 'rb').read() |
---|
| 136 | |
---|
| 137 | STUDENT_HEADER_FIELDS_EXTASCII = STUDENT_SAMPLE_DATA_EXTASCII.split( |
---|
| 138 | '\n')[0].split(',') |
---|
| 139 | |
---|
[7536] | 140 | STUDYLEVEL_SAMPLE_DATA = open( |
---|
| 141 | os.path.join(os.path.dirname(__file__), 'sample_studylevel_data.csv'), |
---|
| 142 | 'rb').read() |
---|
| 143 | |
---|
| 144 | STUDYLEVEL_HEADER_FIELDS = STUDYLEVEL_SAMPLE_DATA.split( |
---|
| 145 | '\n')[0].split(',') |
---|
| 146 | |
---|
[16831] | 147 | FIRST_STUDYLEVEL_SAMPLE_DATA = open( |
---|
| 148 | os.path.join(os.path.dirname(__file__), 'sample_first_studylevel_data.csv'), |
---|
| 149 | 'rb').read() |
---|
| 150 | |
---|
| 151 | FIRST_STUDYLEVEL_HEADER_FIELDS = FIRST_STUDYLEVEL_SAMPLE_DATA.split( |
---|
| 152 | '\n')[0].split(',') |
---|
| 153 | |
---|
[7548] | 154 | COURSETICKET_SAMPLE_DATA = open( |
---|
| 155 | os.path.join(os.path.dirname(__file__), 'sample_courseticket_data.csv'), |
---|
| 156 | 'rb').read() |
---|
| 157 | |
---|
| 158 | COURSETICKET_HEADER_FIELDS = COURSETICKET_SAMPLE_DATA.split( |
---|
| 159 | '\n')[0].split(',') |
---|
| 160 | |
---|
[16831] | 161 | FIRST_COURSETICKET_SAMPLE_DATA = open( |
---|
| 162 | os.path.join(os.path.dirname(__file__), 'sample_first_courseticket_data.csv'), |
---|
| 163 | 'rb').read() |
---|
| 164 | |
---|
| 165 | FIRST_COURSETICKET_HEADER_FIELDS = FIRST_COURSETICKET_SAMPLE_DATA.split( |
---|
| 166 | '\n')[0].split(',') |
---|
| 167 | |
---|
[7623] | 168 | PAYMENT_SAMPLE_DATA = open( |
---|
| 169 | os.path.join(os.path.dirname(__file__), 'sample_payment_data.csv'), |
---|
| 170 | 'rb').read() |
---|
| 171 | |
---|
| 172 | PAYMENT_HEADER_FIELDS = PAYMENT_SAMPLE_DATA.split( |
---|
| 173 | '\n')[0].split(',') |
---|
| 174 | |
---|
[8884] | 175 | PAYMENT_CREATE_SAMPLE_DATA = open( |
---|
| 176 | os.path.join(os.path.dirname(__file__), 'sample_create_payment_data.csv'), |
---|
| 177 | 'rb').read() |
---|
| 178 | |
---|
| 179 | PAYMENT_CREATE_HEADER_FIELDS = PAYMENT_CREATE_SAMPLE_DATA.split( |
---|
| 180 | '\n')[0].split(',') |
---|
| 181 | |
---|
[14366] | 182 | curr_year = datetime.datetime.now().year |
---|
| 183 | |
---|
[7947] | 184 | class StudentImportExportSetup(FunctionalTestCase): |
---|
| 185 | |
---|
| 186 | layer = FunctionalLayer |
---|
| 187 | |
---|
| 188 | def setUp(self): |
---|
| 189 | super(StudentImportExportSetup, self).setUp() |
---|
| 190 | self.dc_root = tempfile.mkdtemp() |
---|
| 191 | self.workdir = tempfile.mkdtemp() |
---|
| 192 | app = University() |
---|
| 193 | app['datacenter'].setStoragePath(self.dc_root) |
---|
| 194 | self.getRootFolder()['app'] = app |
---|
| 195 | self.app = self.getRootFolder()['app'] |
---|
| 196 | setSite(app) |
---|
| 197 | |
---|
| 198 | # Populate university |
---|
| 199 | self.certificate = createObject('waeup.Certificate') |
---|
| 200 | self.certificate.code = 'CERT1' |
---|
| 201 | self.certificate.application_category = 'basic' |
---|
| 202 | self.certificate.start_level = 200 |
---|
| 203 | self.certificate.end_level = 500 |
---|
[9799] | 204 | self.certificate.study_mode = u'ug_ft' |
---|
[7947] | 205 | self.app['faculties']['fac1'] = Faculty() |
---|
| 206 | self.app['faculties']['fac1']['dep1'] = Department() |
---|
| 207 | self.app['faculties']['fac1']['dep1'].certificates.addCertificate( |
---|
| 208 | self.certificate) |
---|
[8626] | 209 | |
---|
[9426] | 210 | # Create a hostel with two beds |
---|
| 211 | hostel = Hostel() |
---|
| 212 | hostel.hostel_id = u'hall-1' |
---|
| 213 | hostel.hostel_name = u'Hall 1' |
---|
| 214 | self.app['hostels'].addHostel(hostel) |
---|
| 215 | bed = Bed() |
---|
| 216 | bed.bed_id = u'hall-1_A_101_A' |
---|
| 217 | bed.bed_number = 1 |
---|
| 218 | bed.owner = NOT_OCCUPIED |
---|
| 219 | bed.bed_type = u'regular_male_fr' |
---|
| 220 | self.app['hostels'][hostel.hostel_id].addBed(bed) |
---|
| 221 | bed = Bed() |
---|
| 222 | bed.bed_id = u'hall-1_A_101_B' |
---|
| 223 | bed.bed_number = 2 |
---|
| 224 | bed.owner = NOT_OCCUPIED |
---|
| 225 | bed.bed_type = u'regular_female_fr' |
---|
| 226 | self.app['hostels'][hostel.hostel_id].addBed(bed) |
---|
| 227 | |
---|
[8626] | 228 | self.logfile = os.path.join( |
---|
| 229 | self.app['datacenter'].storage, 'logs', 'students.log') |
---|
[7947] | 230 | return |
---|
| 231 | |
---|
| 232 | def tearDown(self): |
---|
| 233 | super(StudentImportExportSetup, self).tearDown() |
---|
| 234 | shutil.rmtree(self.workdir) |
---|
| 235 | shutil.rmtree(self.dc_root) |
---|
| 236 | clearSite() |
---|
| 237 | return |
---|
| 238 | |
---|
[8340] | 239 | def setup_for_export(self): |
---|
| 240 | student = Student() |
---|
| 241 | student.student_id = u'A111111' |
---|
| 242 | self.app['students'][student.student_id] = self.student = student |
---|
| 243 | self.outfile = os.path.join(self.workdir, 'myoutput.csv') |
---|
| 244 | return |
---|
[7947] | 245 | |
---|
[8340] | 246 | def setup_student(self, student): |
---|
| 247 | # set predictable values for `student` |
---|
[9028] | 248 | student.matric_number = u'234' |
---|
[8340] | 249 | student.adm_code = u'my adm code' |
---|
| 250 | student.clr_code = u'my clr code' |
---|
| 251 | student.perm_address = u'Studentroad 21\nLagos 123456\n' |
---|
[9028] | 252 | student.reg_number = u'123' |
---|
[8340] | 253 | student.firstname = u'Anna' |
---|
| 254 | student.lastname = u'Tester' |
---|
| 255 | student.middlename = u'M.' |
---|
| 256 | student.date_of_birth = datetime.date(1981, 2, 4) |
---|
| 257 | student.sex = 'f' |
---|
| 258 | student.email = 'anna@sample.com' |
---|
| 259 | student.phone = u'+234-123-12345' |
---|
| 260 | student.notice = u'Some notice\nin lines.' |
---|
| 261 | student.nationality = u'NG' |
---|
| 262 | |
---|
| 263 | student['studycourse'].certificate = self.certificate |
---|
| 264 | student['studycourse'].entry_mode = 'ug_ft' |
---|
| 265 | student['studycourse'].entry_session = 2010 |
---|
| 266 | student['studycourse'].current_session = 2012 |
---|
| 267 | student['studycourse'].current_level = int(self.certificate.start_level) |
---|
| 268 | |
---|
| 269 | study_level = StudentStudyLevel() |
---|
| 270 | study_level.level_session = 2012 |
---|
| 271 | study_level.level_verdict = "A" |
---|
| 272 | study_level.level = 100 |
---|
| 273 | student['studycourse'].addStudentStudyLevel( |
---|
| 274 | self.certificate, study_level) |
---|
| 275 | |
---|
| 276 | ticket = CourseTicket() |
---|
| 277 | ticket.automatic = True |
---|
| 278 | ticket.carry_over = True |
---|
| 279 | ticket.code = u'CRS1' |
---|
| 280 | ticket.title = u'Course 1' |
---|
| 281 | ticket.fcode = u'FAC1' |
---|
| 282 | ticket.dcode = u'DEP1' |
---|
| 283 | ticket.credits = 100 |
---|
| 284 | ticket.passmark = 100 |
---|
| 285 | ticket.semester = 2 |
---|
[8920] | 286 | study_level[ticket.code] = ticket |
---|
[9426] | 287 | |
---|
| 288 | bedticket = BedTicket() |
---|
| 289 | bedticket.booking_session = 2004 |
---|
| 290 | bedticket.bed_type = u'any bed type' |
---|
| 291 | bedticket.bed = self.app['hostels']['hall-1']['hall-1_A_101_A'] |
---|
| 292 | student['accommodation'].addBedTicket(bedticket) |
---|
| 293 | |
---|
[8411] | 294 | self.add_payment(student) |
---|
[8340] | 295 | return student |
---|
| 296 | |
---|
[8411] | 297 | def add_payment(self, student): |
---|
| 298 | # get a payment with all fields set |
---|
| 299 | payment = StudentOnlinePayment() |
---|
[14366] | 300 | payment.creation_date = datetime.datetime(curr_year-6, 4, 1, 13, 12, 1) |
---|
[8411] | 301 | payment.p_id = 'my-id' |
---|
[10842] | 302 | payment.p_category = u'schoolfee' |
---|
[10296] | 303 | payment.p_state = 'paid' |
---|
[8411] | 304 | payment.ac = u'666' |
---|
| 305 | payment.p_item = u'p-item' |
---|
| 306 | payment.p_level = 100 |
---|
[14366] | 307 | payment.p_session = curr_year - 6 |
---|
| 308 | payment.payment_date = datetime.datetime(curr_year-6, 4, 1, 14, 12, 1) |
---|
[10296] | 309 | payment.amount_auth = 12.12 |
---|
[8411] | 310 | payment.r_amount_approved = 12.12 |
---|
| 311 | payment.r_code = u'r-code' |
---|
| 312 | # XXX: there is no addPayment method to give predictable names |
---|
[11756] | 313 | self.payment = student['payments']['my-payment'] = payment |
---|
[8411] | 314 | return payment |
---|
[8340] | 315 | |
---|
[8411] | 316 | |
---|
[9028] | 317 | class StudentProcessorTest(StudentImportExportSetup): |
---|
[6840] | 318 | |
---|
| 319 | layer = FunctionalLayer |
---|
| 320 | |
---|
| 321 | def setUp(self): |
---|
[7933] | 322 | super(StudentProcessorTest, self).setUp() |
---|
[6840] | 323 | |
---|
| 324 | # Add student with subobjects |
---|
| 325 | student = Student() |
---|
| 326 | self.app['students'].addStudent(student) |
---|
[9028] | 327 | student = self.setup_student(student) |
---|
| 328 | notify(grok.ObjectModifiedEvent(student)) |
---|
[6840] | 329 | self.student = self.app['students'][student.student_id] |
---|
[9028] | 330 | |
---|
[7933] | 331 | self.processor = StudentProcessor() |
---|
[6840] | 332 | self.csv_file = os.path.join(self.workdir, 'sample_student_data.csv') |
---|
[6851] | 333 | self.csv_file_update = os.path.join( |
---|
| 334 | self.workdir, 'sample_student_data_update.csv') |
---|
| 335 | self.csv_file_update2 = os.path.join( |
---|
| 336 | self.workdir, 'sample_student_data_update2.csv') |
---|
[8309] | 337 | self.csv_file_update3 = os.path.join( |
---|
| 338 | self.workdir, 'sample_student_data_update3.csv') |
---|
| 339 | self.csv_file_update4 = os.path.join( |
---|
| 340 | self.workdir, 'sample_student_data_update4.csv') |
---|
[7273] | 341 | self.csv_file_migration = os.path.join( |
---|
| 342 | self.workdir, 'sample_student_data_migration.csv') |
---|
[8289] | 343 | self.csv_file_duplicates = os.path.join( |
---|
| 344 | self.workdir, 'sample_student_data_duplicates.csv') |
---|
[9918] | 345 | self.csv_file_extascii = os.path.join( |
---|
| 346 | self.workdir, 'sample_student_data_extascii.csv') |
---|
[6840] | 347 | open(self.csv_file, 'wb').write(STUDENT_SAMPLE_DATA) |
---|
[6848] | 348 | open(self.csv_file_update, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE) |
---|
[6851] | 349 | open(self.csv_file_update2, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE2) |
---|
[8309] | 350 | open(self.csv_file_update3, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE3) |
---|
| 351 | open(self.csv_file_update4, 'wb').write(STUDENT_SAMPLE_DATA_UPDATE4) |
---|
[7273] | 352 | open(self.csv_file_migration, 'wb').write(STUDENT_SAMPLE_DATA_MIGRATION) |
---|
[8289] | 353 | open(self.csv_file_duplicates, 'wb').write(STUDENT_SAMPLE_DATA_DUPLICATES) |
---|
[9918] | 354 | open(self.csv_file_extascii, 'wb').write(STUDENT_SAMPLE_DATA_EXTASCII) |
---|
[6840] | 355 | |
---|
| 356 | def test_interface(self): |
---|
| 357 | # Make sure we fulfill the interface contracts. |
---|
[7933] | 358 | assert verifyObject(IBatchProcessor, self.processor) is True |
---|
[6840] | 359 | assert verifyClass( |
---|
| 360 | IBatchProcessor, StudentProcessor) is True |
---|
| 361 | |
---|
| 362 | def test_parentsExist(self): |
---|
[7933] | 363 | self.assertFalse(self.processor.parentsExist(None, dict())) |
---|
| 364 | self.assertTrue(self.processor.parentsExist(None, self.app)) |
---|
[6840] | 365 | |
---|
| 366 | def test_entryExists(self): |
---|
[7933] | 367 | assert self.processor.entryExists( |
---|
[7534] | 368 | dict(student_id='ID_NONE'), self.app) is False |
---|
[7933] | 369 | assert self.processor.entryExists( |
---|
[7534] | 370 | dict(reg_number='123'), self.app) is True |
---|
[6840] | 371 | |
---|
| 372 | def test_getParent(self): |
---|
[7933] | 373 | parent = self.processor.getParent(None, self.app) |
---|
[6840] | 374 | assert parent is self.app['students'] |
---|
| 375 | |
---|
| 376 | def test_getEntry(self): |
---|
[7933] | 377 | assert self.processor.getEntry( |
---|
[6840] | 378 | dict(student_id='ID_NONE'), self.app) is None |
---|
[7933] | 379 | assert self.processor.getEntry( |
---|
[6840] | 380 | dict(student_id=self.student.student_id), self.app) is self.student |
---|
| 381 | |
---|
| 382 | def test_addEntry(self): |
---|
| 383 | new_student = Student() |
---|
[7933] | 384 | self.processor.addEntry( |
---|
[6840] | 385 | new_student, dict(), self.app) |
---|
| 386 | assert len(self.app['students'].keys()) == 2 |
---|
| 387 | |
---|
[7548] | 388 | def test_checkConversion(self): |
---|
[8490] | 389 | # Make sure we can check conversions and that the stud_id |
---|
| 390 | # counter is not raised during such checks. |
---|
| 391 | initial_stud_id = self.app['students']._curr_stud_id |
---|
[7933] | 392 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[8287] | 393 | dict(reg_number='1', state='admitted')) |
---|
[7548] | 394 | self.assertEqual(len(errs),0) |
---|
[8287] | 395 | # Empty state is allowed |
---|
[7933] | 396 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[8287] | 397 | dict(reg_number='1', state='')) |
---|
[8284] | 398 | self.assertEqual(len(errs),0) |
---|
[8287] | 399 | #self.assertTrue(('state', 'no value provided') in errs) |
---|
[7933] | 400 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[8287] | 401 | dict(reg_number='1', state='nonsense')) |
---|
[7548] | 402 | self.assertEqual(len(errs),1) |
---|
[8287] | 403 | self.assertTrue(('state', 'not allowed') in errs) |
---|
[8490] | 404 | new_stud_id = self.app['students']._curr_stud_id |
---|
| 405 | self.assertEqual(initial_stud_id, new_stud_id) |
---|
| 406 | return |
---|
[7548] | 407 | |
---|
[9028] | 408 | def test_checkUpdateRequirements(self): |
---|
| 409 | # Make sure that pg students can't be updated with wrong transition. |
---|
| 410 | err = self.processor.checkUpdateRequirements(self.student, |
---|
| 411 | dict(reg_number='1', state='returning'), self.app) |
---|
| 412 | self.assertTrue(err is None) |
---|
| 413 | self.certificate.study_mode = 'pg_ft' |
---|
| 414 | err = self.processor.checkUpdateRequirements(self.student, |
---|
| 415 | dict(reg_number='1', state='returning'), self.app) |
---|
| 416 | self.assertEqual(err, 'State not allowed (pg student).') |
---|
| 417 | IWorkflowState(self.student).setState('school fee paid') |
---|
| 418 | err = self.processor.checkUpdateRequirements(self.student, |
---|
| 419 | dict(reg_number='1', transition='reset6'), self.app) |
---|
| 420 | self.assertEqual(err, 'Transition not allowed (pg student).') |
---|
| 421 | err = self.processor.checkUpdateRequirements(self.student, |
---|
| 422 | dict(reg_number='1', transition='register_courses'), self.app) |
---|
| 423 | self.assertEqual(err, 'Transition not allowed (pg student).') |
---|
| 424 | |
---|
| 425 | |
---|
[6840] | 426 | def test_delEntry(self): |
---|
[6848] | 427 | assert self.student.student_id in self.app['students'].keys() |
---|
[7933] | 428 | self.processor.delEntry( |
---|
[6851] | 429 | dict(reg_number=self.student.reg_number), self.app) |
---|
[6848] | 430 | assert self.student.student_id not in self.app['students'].keys() |
---|
| 431 | |
---|
[6840] | 432 | def test_import(self): |
---|
[8489] | 433 | self.assertEqual(self.app['students']._curr_stud_id, 1000001) |
---|
[7933] | 434 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6840] | 435 | self.csv_file, STUDENT_HEADER_FIELDS) |
---|
[12810] | 436 | self.assertEqual(num_warns, 0) |
---|
| 437 | # Nine students have been added. |
---|
[9296] | 438 | self.assertEqual(len(self.app['students']), 10) |
---|
[12811] | 439 | # Three empty rows have been skipped. |
---|
| 440 | self.assertEqual(num, 12) |
---|
[7643] | 441 | self.assertEqual(self.app['students']['X666666'].reg_number,'1') |
---|
[8354] | 442 | self.assertEqual( |
---|
| 443 | self.app['students']['X666666'].state, 'courses validated') |
---|
[16819] | 444 | # history was successfully impotrted |
---|
| 445 | history = self.app['students']['X666666'].history.messages |
---|
| 446 | self.assertEqual(history[0], |
---|
| 447 | '2012-10-23 00:05:39 WAT - Record created by System Admin') |
---|
| 448 | self.assertTrue( |
---|
| 449 | "State 'courses validated' imported by system" in history[3]) |
---|
| 450 | # history import has been logged |
---|
| 451 | logcontent = open(self.logfile).read() |
---|
| 452 | self.assertTrue( |
---|
| 453 | "history=[u'2012-10-23 00:05:39 WAT - Record created by System Admin'" |
---|
| 454 | in logcontent) |
---|
[8491] | 455 | # Two new student_ids have been created. |
---|
| 456 | self.assertEqual(self.app['students']._curr_stud_id, 1000003) |
---|
[6840] | 457 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 458 | |
---|
[9919] | 459 | def test_import_extascii(self): |
---|
[9918] | 460 | self.assertEqual(self.app['students']._curr_stud_id, 1000001) |
---|
| 461 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 462 | self.csv_file_extascii, STUDENT_HEADER_FIELDS_EXTASCII) |
---|
[9961] | 463 | self.assertEqual(num_warns,0) |
---|
[10014] | 464 | self.assertEqual(len(self.app['students']), 3) |
---|
[9918] | 465 | self.assertEqual(self.app['students']['X111111'].reg_number,'1') |
---|
| 466 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 467 | |
---|
[6848] | 468 | def test_import_update(self): |
---|
[7933] | 469 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6848] | 470 | self.csv_file, STUDENT_HEADER_FIELDS) |
---|
| 471 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[7933] | 472 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6848] | 473 | self.csv_file_update, STUDENT_HEADER_FIELDS_UPDATE, 'update') |
---|
| 474 | self.assertEqual(num_warns,0) |
---|
[8287] | 475 | # state has changed |
---|
[8284] | 476 | self.assertEqual(self.app['students']['X666666'].state,'admitted') |
---|
[8287] | 477 | # state has not changed |
---|
[9272] | 478 | self.assertEqual(self.app['students']['Y777777'].state, |
---|
| 479 | 'courses validated') |
---|
[6848] | 480 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 481 | |
---|
[6851] | 482 | def test_import_update2(self): |
---|
[7933] | 483 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6851] | 484 | self.csv_file, STUDENT_HEADER_FIELDS) |
---|
| 485 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[15628] | 486 | container = self.app['students'] |
---|
| 487 | self.assertEqual( |
---|
| 488 | IUserAccount(container['X666666']).checkPassword('test1234'), True) |
---|
[7933] | 489 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6851] | 490 | self.csv_file_update2, STUDENT_HEADER_FIELDS_UPDATE2, 'update') |
---|
| 491 | self.assertEqual(num_warns,0) |
---|
[8210] | 492 | # The phone import value of Pieri was None. |
---|
| 493 | # Confirm that phone has not been cleared. |
---|
| 494 | for key in container.keys(): |
---|
| 495 | if container[key].firstname == 'Aaren': |
---|
| 496 | aaren = container[key] |
---|
| 497 | break |
---|
| 498 | self.assertEqual(aaren.phone, '--1234') |
---|
| 499 | # The phone import value of Claus was a deletion marker. |
---|
| 500 | # Confirm that phone has been cleared. |
---|
| 501 | for key in container.keys(): |
---|
| 502 | if container[key].firstname == 'Claus': |
---|
| 503 | claus = container[key] |
---|
| 504 | break |
---|
| 505 | assert claus.phone is None |
---|
[15628] | 506 | # The password of X666666 has been removed |
---|
| 507 | self.assertEqual( |
---|
| 508 | IUserAccount(container['X666666']).password, None) |
---|
[6851] | 509 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 510 | |
---|
[8309] | 511 | def test_import_update3(self): |
---|
| 512 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 513 | self.csv_file, STUDENT_HEADER_FIELDS) |
---|
| 514 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 515 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 516 | self.csv_file_update3, STUDENT_HEADER_FIELDS_UPDATE3, 'update') |
---|
| 517 | content = open(fail_file).read() |
---|
| 518 | self.assertEqual( |
---|
| 519 | content, |
---|
[14553] | 520 | 'reg_number,student_id,transition,firstname,--ERRORS--\r\n' |
---|
| 521 | '<IGNORE>,X666666,request_clearance,<IGNORE>,Transition not allowed.\r\n' |
---|
| 522 | '<IGNORE>,X666666,<IGNORE>,XXX,RequiredMissing: firstname\r\n' |
---|
[8309] | 523 | ) |
---|
[14553] | 524 | self.assertEqual(num_warns,2) |
---|
[8309] | 525 | self.assertEqual(self.app['students']['Y777777'].state,'returning') |
---|
[15421] | 526 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8309] | 527 | |
---|
| 528 | def test_import_update4(self): |
---|
| 529 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 530 | self.csv_file, STUDENT_HEADER_FIELDS) |
---|
| 531 | self.assertRaises( |
---|
| 532 | FatalCSVError, self.processor.doImport, self.csv_file_update4, |
---|
| 533 | STUDENT_HEADER_FIELDS_UPDATE4, 'update') |
---|
[16012] | 534 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8309] | 535 | |
---|
[6848] | 536 | def test_import_remove(self): |
---|
[7933] | 537 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6848] | 538 | self.csv_file, STUDENT_HEADER_FIELDS) |
---|
| 539 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[7933] | 540 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6848] | 541 | self.csv_file_update, STUDENT_HEADER_FIELDS_UPDATE, 'remove') |
---|
| 542 | self.assertEqual(num_warns,0) |
---|
| 543 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 544 | |
---|
[7273] | 545 | def test_import_migration_data(self): |
---|
[7933] | 546 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[7273] | 547 | self.csv_file_migration, STUDENT_HEADER_FIELDS_MIGRATION) |
---|
[8284] | 548 | content = open(fail_file).read() |
---|
[16819] | 549 | self.assertEqual(num_warns,3) |
---|
[8284] | 550 | assert len(self.app['students'].keys()) == 5 |
---|
[7983] | 551 | self.assertEqual( |
---|
| 552 | content, |
---|
[16819] | 553 | 'reg_number,firstname,student_id,sex,email,phone,state,date_of_birth,lastname,password,matric_number,history,--ERRORS--\r\n' |
---|
| 554 | '4,John,D123456,m,aa@aa.ng,1234,nonsense,1990-01-05,Wolter,mypw1,100003,<IGNORE>,state: not allowed\r\n' |
---|
| 555 | '5,John,E123456,x,aa@aa.ng,1234,<IGNORE>,1990-01-06,Kennedy,<IGNORE>,100004,<IGNORE>,sex: Invalid value\r\n' |
---|
| 556 | '7,Ulli,G123456,m,aa@aa.ng,1234,<IGNORE>,1990-01-06,Meyer,<IGNORE>,100007,nonsense,history: malformed string\r\n' |
---|
[7983] | 557 | ) |
---|
[8354] | 558 | students = self.app['students'] |
---|
| 559 | self.assertTrue('A123456' in students.keys()) |
---|
| 560 | self.assertEqual(students['A123456'].state, 'clearance started') |
---|
| 561 | self.assertEqual(students['A123456'].date_of_birth, |
---|
| 562 | datetime.date(1990, 1, 2)) |
---|
| 563 | self.assertFalse(students['A123456'].clearance_locked) |
---|
| 564 | self.assertEqual(students['B123456'].state, 'cleared') |
---|
| 565 | self.assertEqual(students['B123456'].date_of_birth, |
---|
| 566 | datetime.date(1990, 1, 3)) |
---|
| 567 | self.assertTrue(students['B123456'].clearance_locked) |
---|
[16819] | 568 | history = students['A123456'].history.messages |
---|
| 569 | self.assertEqual(history[0], |
---|
| 570 | '2012-10-23 00:05:39 WAT - Record created by System Admin') |
---|
[7522] | 571 | self.assertTrue( |
---|
[16819] | 572 | "State 'clearance started' imported by system" in history[3]) |
---|
[8287] | 573 | # state was empty and student is thus in state created |
---|
[8354] | 574 | self.assertEqual(students['F123456'].state,'created') |
---|
| 575 | # passwords were set correctly |
---|
| 576 | self.assertEqual( |
---|
| 577 | IUserAccount(students['A123456']).checkPassword('mypw1'), True) |
---|
| 578 | self.assertEqual( |
---|
| 579 | IUserAccount(students['C123456']).checkPassword('mypw1'), True) |
---|
[7273] | 580 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 581 | |
---|
[8289] | 582 | def test_import_duplicate_data(self): |
---|
| 583 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 584 | self.csv_file_duplicates, STUDENT_HEADER_FIELDS_DUPLICATES) |
---|
| 585 | content = open(fail_file).read() |
---|
[8488] | 586 | self.assertEqual(num_warns,4) |
---|
[8289] | 587 | self.assertEqual( |
---|
| 588 | content, |
---|
| 589 | 'reg_number,firstname,student_id,sex,email,phone,state,date_of_birth,lastname,password,matric_number,--ERRORS--\r\n' |
---|
[12415] | 590 | '1,Aaren,B123456,m,aa@aa.ng,1234,cleared,1990-01-03,Finau,mypw1,100001,reg_number: Invalid input\r\n' |
---|
| 591 | '2,Aaren,C123456,m,aa@aa.ng,1234,admitted,1990-01-04,Berson,mypw1,100000,matric_number: Invalid input\r\n' |
---|
[12981] | 592 | '1,Frank,F123456,m,aa@aa.ng,1234,<IGNORE>,1990-01-06,Meyer,<IGNORE>,100000,reg_number: Invalid input; matric_number: Invalid input\r\n' |
---|
| 593 | '3,Uli,A123456,m,aa@aa.ng,1234,<IGNORE>,1990-01-07,Schulz,<IGNORE>,100002,This object already exists.\r\n' |
---|
[8289] | 594 | ) |
---|
| 595 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[7536] | 596 | |
---|
[7951] | 597 | class StudentStudyCourseProcessorTest(StudentImportExportSetup): |
---|
[6840] | 598 | |
---|
| 599 | def setUp(self): |
---|
[7933] | 600 | super(StudentStudyCourseProcessorTest, self).setUp() |
---|
[6840] | 601 | |
---|
[9029] | 602 | # Add student with subobjects |
---|
| 603 | student = Student() |
---|
| 604 | self.app['students'].addStudent(student) |
---|
| 605 | student = self.setup_student(student) |
---|
| 606 | notify(grok.ObjectModifiedEvent(student)) |
---|
| 607 | self.student = self.app['students'][student.student_id] |
---|
| 608 | |
---|
[6840] | 609 | # Import students with subobjects |
---|
| 610 | student_file = os.path.join(self.workdir, 'sample_student_data.csv') |
---|
| 611 | open(student_file, 'wb').write(STUDENT_SAMPLE_DATA) |
---|
| 612 | num, num_warns, fin_file, fail_file = StudentProcessor().doImport( |
---|
| 613 | student_file, STUDENT_HEADER_FIELDS) |
---|
| 614 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 615 | |
---|
[7933] | 616 | self.processor = StudentStudyCourseProcessor() |
---|
[7536] | 617 | self.csv_file = os.path.join( |
---|
| 618 | self.workdir, 'sample_studycourse_data.csv') |
---|
| 619 | open(self.csv_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA) |
---|
[9960] | 620 | self.csv_file_transfer = os.path.join( |
---|
| 621 | self.workdir, 'sample_transfer_data.csv') |
---|
| 622 | open(self.csv_file_transfer, 'wb').write(TRANSFER_SAMPLE_DATA) |
---|
[6840] | 623 | return |
---|
| 624 | |
---|
| 625 | def test_interface(self): |
---|
| 626 | # Make sure we fulfill the interface contracts. |
---|
[7933] | 627 | assert verifyObject(IBatchProcessor, self.processor) is True |
---|
[6840] | 628 | assert verifyClass( |
---|
| 629 | IBatchProcessor, StudentStudyCourseProcessor) is True |
---|
| 630 | |
---|
| 631 | def test_entryExists(self): |
---|
[7933] | 632 | assert self.processor.entryExists( |
---|
[7534] | 633 | dict(reg_number='REG_NONE'), self.app) is False |
---|
[7933] | 634 | assert self.processor.entryExists( |
---|
[7534] | 635 | dict(reg_number='1'), self.app) is True |
---|
[6840] | 636 | |
---|
| 637 | def test_getEntry(self): |
---|
[7933] | 638 | student = self.processor.getEntry( |
---|
[7534] | 639 | dict(reg_number='1'), self.app).__parent__ |
---|
| 640 | self.assertEqual(student.reg_number,'1') |
---|
[6840] | 641 | |
---|
[7548] | 642 | def test_checkConversion(self): |
---|
[7933] | 643 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 644 | dict(reg_number='1', certificate='CERT1', current_level='200')) |
---|
| 645 | self.assertEqual(len(errs),0) |
---|
[7933] | 646 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 647 | dict(reg_number='1', certificate='CERT999')) |
---|
| 648 | self.assertEqual(len(errs),1) |
---|
| 649 | self.assertTrue(('certificate', u'Invalid value') in errs) |
---|
[7933] | 650 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 651 | dict(reg_number='1', certificate='CERT1', current_level='100')) |
---|
| 652 | self.assertEqual(len(errs),1) |
---|
| 653 | self.assertTrue(('current_level','not in range') in errs) |
---|
| 654 | # If we import only current_level, no conversion checking is done. |
---|
[7933] | 655 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 656 | dict(reg_number='1', current_level='100')) |
---|
| 657 | self.assertEqual(len(errs),0) |
---|
| 658 | |
---|
[9029] | 659 | def test_checkUpdateRequirements(self): |
---|
[9735] | 660 | # Current level must be in range of certificate. |
---|
| 661 | # Since row has passed the converter, current_level is an integer. |
---|
[9272] | 662 | err = self.processor.checkUpdateRequirements( |
---|
| 663 | self.student['studycourse'], |
---|
[9735] | 664 | dict(reg_number='1', current_level=100), self.app) |
---|
| 665 | self.assertEqual(err, 'current_level not in range.') |
---|
| 666 | err = self.processor.checkUpdateRequirements( |
---|
| 667 | self.student['studycourse'], |
---|
| 668 | dict(reg_number='1', current_level=200), self.app) |
---|
[9029] | 669 | self.assertTrue(err is None) |
---|
[9735] | 670 | # We can update pg students. |
---|
| 671 | self.student['studycourse'].certificate.start_level=999 |
---|
| 672 | self.student['studycourse'].certificate.end_level=999 |
---|
[9272] | 673 | err = self.processor.checkUpdateRequirements( |
---|
| 674 | self.student['studycourse'], |
---|
[9037] | 675 | dict(reg_number='1', current_level=999), self.app) |
---|
[9029] | 676 | self.assertTrue(err is None) |
---|
[9735] | 677 | # Make sure that pg students can't be updated with wrong transition. |
---|
[9029] | 678 | IWorkflowState(self.student).setState('returning') |
---|
[9272] | 679 | err = self.processor.checkUpdateRequirements( |
---|
| 680 | self.student['studycourse'], |
---|
[9037] | 681 | dict(reg_number='1', current_level=999), self.app) |
---|
[9029] | 682 | self.assertEqual(err, 'Not a pg student.') |
---|
[9735] | 683 | # If certificate is not given in row (and has thus |
---|
| 684 | # successfully passed checkConversion) the certificate |
---|
| 685 | # attribute must be set. |
---|
| 686 | self.student['studycourse'].certificate = None |
---|
| 687 | err = self.processor.checkUpdateRequirements( |
---|
| 688 | self.student['studycourse'], |
---|
| 689 | dict(reg_number='1', current_level=100), self.app) |
---|
| 690 | self.assertEqual(err, 'No certificate to check level.') |
---|
[9960] | 691 | # When transferring students the method also checks |
---|
| 692 | # if the former studycourse is complete. |
---|
| 693 | err = self.processor.checkUpdateRequirements( |
---|
| 694 | self.student['studycourse'], |
---|
| 695 | dict(reg_number='1', certificate='CERT1', current_level=200, |
---|
| 696 | entry_mode='transfer'), self.app) |
---|
| 697 | self.assertEqual(err, 'Former study course record incomplete.') |
---|
| 698 | self.student['studycourse'].certificate = self.certificate |
---|
| 699 | self.student['studycourse'].entry_session = 2005 |
---|
| 700 | # The method doesn't care if current_level |
---|
| 701 | # is not in range of CERT1. This is done by checkConversion |
---|
| 702 | # if certificate is in row. |
---|
| 703 | err = self.processor.checkUpdateRequirements( |
---|
| 704 | self.student['studycourse'], |
---|
| 705 | dict(reg_number='1', certificate='CERT1', current_level=200, |
---|
| 706 | entry_mode='transfer'), self.app) |
---|
| 707 | self.assertTrue(err is None) |
---|
[15163] | 708 | # In state 'transcript validated' studycourses are locked. |
---|
| 709 | IWorkflowState(self.student).setState('transcript validated') |
---|
| 710 | err = self.processor.checkUpdateRequirements( |
---|
| 711 | self.student['studycourse'], |
---|
| 712 | dict(reg_number='1', current_level=999), self.app) |
---|
| 713 | self.assertEqual(err, 'Studycourse is locked.') |
---|
| 714 | # In state 'graduated' studycourses are also locked. |
---|
| 715 | IWorkflowState(self.student).setState('graduated') |
---|
| 716 | err = self.processor.checkUpdateRequirements( |
---|
| 717 | self.student['studycourse'], |
---|
| 718 | dict(reg_number='1', current_level=999), self.app) |
---|
| 719 | self.assertEqual(err, 'Studycourse is locked.') |
---|
| 720 | # In state 'transcript released' studycourses are also locked ... |
---|
| 721 | IWorkflowState(self.student).setState('transcript released') |
---|
| 722 | err = self.processor.checkUpdateRequirements( |
---|
| 723 | self.student['studycourse'], |
---|
| 724 | dict(reg_number='1', current_level=999), self.app) |
---|
| 725 | self.assertEqual(err, 'Studycourse is locked.') |
---|
| 726 | # ... but not in state 'transcript requested'. |
---|
| 727 | IWorkflowState(self.student).setState('transcript requested') |
---|
| 728 | err = self.processor.checkUpdateRequirements( |
---|
| 729 | self.student['studycourse'], |
---|
| 730 | dict(reg_number='1', current_level=999), self.app) |
---|
| 731 | self.assertTrue(err is None) |
---|
[9029] | 732 | |
---|
[6840] | 733 | def test_import(self): |
---|
[7933] | 734 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[6840] | 735 | self.csv_file, STUDYCOURSE_HEADER_FIELDS,'update') |
---|
[7529] | 736 | self.assertEqual(num_warns,1) |
---|
[7958] | 737 | content = open(fail_file).read() |
---|
| 738 | self.assertTrue('current_level: not in range' in content) |
---|
[7933] | 739 | studycourse = self.processor.getEntry(dict(reg_number='1'), self.app) |
---|
[6840] | 740 | self.assertEqual(studycourse.certificate.code, u'CERT1') |
---|
| 741 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 742 | |
---|
[9960] | 743 | def test_import_transfer(self): |
---|
| 744 | self.certificate2 = createObject('waeup.Certificate') |
---|
| 745 | self.certificate2.code = 'CERT2' |
---|
| 746 | self.certificate2.application_category = 'basic' |
---|
| 747 | self.certificate2.start_level = 200 |
---|
| 748 | self.certificate2.end_level = 500 |
---|
| 749 | self.certificate2.study_mode = u'ug_pt' |
---|
| 750 | self.app['faculties']['fac1']['dep1'].certificates.addCertificate( |
---|
| 751 | self.certificate2) |
---|
| 752 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 753 | self.csv_file_transfer, TRANSFER_HEADER_FIELDS,'update') |
---|
| 754 | self.assertEqual(num_warns,0) |
---|
| 755 | self.assertEqual(self.student['studycourse'].certificate.code, 'CERT2') |
---|
| 756 | self.assertEqual(self.student['studycourse_1'].certificate.code, 'CERT1') |
---|
[16828] | 757 | self.assertEqual(self.student['studycourse'].entry_mode, 'transferred') |
---|
[9960] | 758 | self.assertEqual(self.student['studycourse_1'].entry_mode, 'ug_ft') |
---|
| 759 | self.assertEqual(self.student.current_mode, 'ug_pt') |
---|
| 760 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[16819] | 761 | # Transfer has bee logged. |
---|
[9960] | 762 | logcontent = open(self.logfile).read() |
---|
| 763 | self.assertTrue( |
---|
| 764 | 'INFO - system - K1000000 - transferred from CERT1 to CERT2\n' |
---|
| 765 | in logcontent) |
---|
| 766 | self.assertTrue( |
---|
| 767 | 'INFO - system - ' |
---|
[16834] | 768 | 'StudentStudyCourse Processor - ' |
---|
[16828] | 769 | 'sample_transfer_data - K1000000 - updated: entry_mode=transferred, ' |
---|
[9960] | 770 | 'certificate=CERT2, current_session=2009, current_level=300' |
---|
| 771 | in logcontent) |
---|
| 772 | # A history message has been added. |
---|
| 773 | history = ' '.join(self.student.history.messages) |
---|
| 774 | self.assertTrue( |
---|
| 775 | "Transferred from CERT1 to CERT2 by system" in history) |
---|
| 776 | # The catalog has been updated |
---|
| 777 | cat = queryUtility(ICatalog, name='students_catalog') |
---|
| 778 | results = list( |
---|
| 779 | cat.searchResults( |
---|
| 780 | certcode=('CERT2', 'CERT2'))) |
---|
| 781 | self.assertTrue(results[0] is self.student) |
---|
| 782 | results = list( |
---|
| 783 | cat.searchResults( |
---|
| 784 | current_session=(2009, 2009))) |
---|
| 785 | self.assertTrue(results[0] is self.student) |
---|
| 786 | results = list( |
---|
| 787 | cat.searchResults( |
---|
| 788 | certcode=('CERT1', 'CERT1'))) |
---|
| 789 | self.assertEqual(len(results), 0) |
---|
| 790 | |
---|
[7951] | 791 | class StudentStudyLevelProcessorTest(StudentImportExportSetup): |
---|
| 792 | |
---|
| 793 | def setUp(self): |
---|
[7933] | 794 | super(StudentStudyLevelProcessorTest, self).setUp() |
---|
[7536] | 795 | |
---|
| 796 | # Import students with subobjects |
---|
| 797 | student_file = os.path.join(self.workdir, 'sample_student_data.csv') |
---|
| 798 | open(student_file, 'wb').write(STUDENT_SAMPLE_DATA) |
---|
| 799 | num, num_warns, fin_file, fail_file = StudentProcessor().doImport( |
---|
| 800 | student_file, STUDENT_HEADER_FIELDS) |
---|
| 801 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 802 | |
---|
| 803 | # Update study courses |
---|
| 804 | studycourse_file = os.path.join( |
---|
| 805 | self.workdir, 'sample_studycourse_data.csv') |
---|
| 806 | open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA) |
---|
[7933] | 807 | processor = StudentStudyCourseProcessor() |
---|
| 808 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
[7536] | 809 | studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update') |
---|
| 810 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 811 | |
---|
[7933] | 812 | self.processor = StudentStudyLevelProcessor() |
---|
[7536] | 813 | self.csv_file = os.path.join( |
---|
| 814 | self.workdir, 'sample_studylevel_data.csv') |
---|
| 815 | open(self.csv_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA) |
---|
| 816 | |
---|
| 817 | def test_interface(self): |
---|
| 818 | # Make sure we fulfill the interface contracts. |
---|
[7933] | 819 | assert verifyObject(IBatchProcessor, self.processor) is True |
---|
[7536] | 820 | assert verifyClass( |
---|
| 821 | IBatchProcessor, StudentStudyLevelProcessor) is True |
---|
| 822 | |
---|
[7548] | 823 | def test_checkConversion(self): |
---|
[7933] | 824 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 825 | dict(reg_number='1', level='220')) |
---|
| 826 | self.assertEqual(len(errs),0) |
---|
[7933] | 827 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[9302] | 828 | dict(reg_number='1', level='999')) |
---|
| 829 | self.assertEqual(len(errs),0) |
---|
| 830 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
| 831 | dict(reg_number='1', level='1000')) |
---|
[16812] | 832 | self.assertEqual(len(errs),0) |
---|
| 833 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
| 834 | dict(reg_number='1', level='1100')) |
---|
[7548] | 835 | self.assertEqual(len(errs),1) |
---|
[12873] | 836 | self.assertTrue(('level', u'Invalid value') in errs) |
---|
[7933] | 837 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 838 | dict(reg_number='1', level='xyz')) |
---|
| 839 | self.assertEqual(len(errs),1) |
---|
[12873] | 840 | self.assertTrue(('level', u'Invalid value') in errs) |
---|
[7548] | 841 | |
---|
[7536] | 842 | def test_import(self): |
---|
[7933] | 843 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[7536] | 844 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create') |
---|
[15066] | 845 | self.assertEqual(num_warns,3) |
---|
[7933] | 846 | assert self.processor.entryExists( |
---|
[7536] | 847 | dict(reg_number='1', level='100'), self.app) is True |
---|
[7933] | 848 | studylevel = self.processor.getEntry( |
---|
[7536] | 849 | dict(reg_number='1', level='100'), self.app) |
---|
| 850 | self.assertEqual(studylevel.__parent__.certificate.code, u'CERT1') |
---|
| 851 | self.assertEqual(studylevel.level_session, 2008) |
---|
[12981] | 852 | self.assertEqual(studylevel.level_verdict, '0') |
---|
[7536] | 853 | self.assertEqual(studylevel.level, 100) |
---|
[8626] | 854 | logcontent = open(self.logfile).read() |
---|
| 855 | # Logging message from updateEntry, |
---|
| 856 | self.assertTrue( |
---|
[9706] | 857 | 'INFO - system - StudentStudyLevel Processor - ' |
---|
| 858 | 'sample_studylevel_data - K1000000 - updated: ' |
---|
| 859 | 'level=100, level_verdict=C, level_session=2009' |
---|
[8626] | 860 | in logcontent) |
---|
[15066] | 861 | content = open(fail_file).read() |
---|
| 862 | self.assertEqual( |
---|
| 863 | content, |
---|
| 864 | 'reg_number,level_verdict,level_session,matric_number,level,' |
---|
| 865 | '--ERRORS--\r\n' |
---|
| 866 | '1,A,2008,<IGNORE>,111,level: Invalid value\r\n' |
---|
| 867 | '1,A,2008,<IGNORE>,nonsense,level: Invalid value\r\n' |
---|
| 868 | '1,A,2008,<IGNORE>,<IGNORE>,level: Invalid value\r\n' |
---|
| 869 | ) |
---|
| 870 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8626] | 871 | |
---|
[8231] | 872 | def test_import_update(self): |
---|
| 873 | # We perform the same import twice, |
---|
| 874 | # the second time in update mode. The number |
---|
| 875 | # of warnings must be the same. |
---|
| 876 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 877 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create') |
---|
[9272] | 878 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8231] | 879 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 880 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'update') |
---|
[15066] | 881 | self.assertEqual(num_warns,3) |
---|
[9977] | 882 | studylevel = self.processor.getEntry( |
---|
| 883 | dict(reg_number='1', level='100'), self.app) |
---|
| 884 | self.assertEqual(studylevel.level, 100) |
---|
[15066] | 885 | content = open(fail_file).read() |
---|
| 886 | self.assertEqual( |
---|
| 887 | content, |
---|
| 888 | 'reg_number,level_verdict,level_session,matric_number,level,' |
---|
| 889 | '--ERRORS--\r\n' |
---|
| 890 | '1,A,2008,<IGNORE>,111,level: Invalid value\r\n' |
---|
| 891 | '1,A,2008,<IGNORE>,nonsense,level: Invalid value\r\n' |
---|
| 892 | '1,A,2008,<IGNORE>,<IGNORE>,Cannot update: no such entry\r\n' |
---|
| 893 | ) |
---|
[8231] | 894 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[7536] | 895 | |
---|
[15163] | 896 | def test_import_update_locked(self): |
---|
| 897 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 898 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create') |
---|
| 899 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 900 | # In state 'transcript validated' studylevels can't be edited |
---|
| 901 | student = self.app['students']['X666666'] |
---|
| 902 | IWorkflowState(student).setState('transcript validated') |
---|
| 903 | # Two more records could't be imported because course tickets |
---|
| 904 | # of X666666 are locked |
---|
| 905 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 906 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'update') |
---|
| 907 | self.assertEqual(num_warns,5) |
---|
| 908 | content = open(fail_file).read() |
---|
| 909 | self.assertEqual( |
---|
| 910 | content, |
---|
| 911 | 'reg_number,level_verdict,level_session,matric_number,level,--ERRORS--\r\n' |
---|
| 912 | '1,<IGNORE>,2008,<IGNORE>,100,Studylevel is locked.\r\n' |
---|
| 913 | '1,<IGNORE>,2008,<IGNORE>,200,Studylevel is locked.\r\n' |
---|
| 914 | '1,A,2008,<IGNORE>,111,level: Invalid value\r\n' |
---|
| 915 | '1,A,2008,<IGNORE>,nonsense,level: Invalid value\r\n' |
---|
| 916 | '1,A,2008,<IGNORE>,<IGNORE>,Cannot update: no such entry\r\n' |
---|
| 917 | ) |
---|
| 918 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 919 | |
---|
[9302] | 920 | def test_import_remove(self): |
---|
| 921 | # We perform the same import twice, |
---|
| 922 | # the second time in remove mode. The number |
---|
| 923 | # of warnings must be the same. |
---|
| 924 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 925 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create') |
---|
| 926 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 927 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 928 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'remove') |
---|
| 929 | assert self.processor.entryExists( |
---|
| 930 | dict(reg_number='1', level='100'), self.app) is False |
---|
[15066] | 931 | self.assertEqual(num_warns,3) |
---|
[9302] | 932 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 933 | |
---|
[7951] | 934 | class CourseTicketProcessorTest(StudentImportExportSetup): |
---|
[7548] | 935 | |
---|
| 936 | def setUp(self): |
---|
[7933] | 937 | super(CourseTicketProcessorTest, self).setUp() |
---|
[7548] | 938 | |
---|
| 939 | # Import students with subobjects |
---|
| 940 | student_file = os.path.join(self.workdir, 'sample_student_data.csv') |
---|
| 941 | open(student_file, 'wb').write(STUDENT_SAMPLE_DATA) |
---|
| 942 | num, num_warns, fin_file, fail_file = StudentProcessor().doImport( |
---|
| 943 | student_file, STUDENT_HEADER_FIELDS) |
---|
| 944 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 945 | |
---|
[8920] | 946 | # Add course and certificate course |
---|
[7548] | 947 | self.course = createObject('waeup.Course') |
---|
| 948 | self.course.code = 'COURSE1' |
---|
| 949 | self.course.semester = 1 |
---|
| 950 | self.course.credits = 10 |
---|
| 951 | self.course.passmark = 40 |
---|
| 952 | self.app['faculties']['fac1']['dep1'].courses.addCourse( |
---|
| 953 | self.course) |
---|
[9272] | 954 | self.app['faculties']['fac1']['dep1'].certificates[ |
---|
| 955 | 'CERT1'].addCertCourse( |
---|
[7548] | 956 | self.course, level=100) |
---|
| 957 | |
---|
| 958 | # Update study courses |
---|
| 959 | studycourse_file = os.path.join( |
---|
| 960 | self.workdir, 'sample_studycourse_data.csv') |
---|
| 961 | open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA) |
---|
[7933] | 962 | processor = StudentStudyCourseProcessor() |
---|
| 963 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
[7548] | 964 | studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update') |
---|
| 965 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 966 | |
---|
| 967 | # Import study levels |
---|
[7933] | 968 | processor = StudentStudyLevelProcessor() |
---|
[7548] | 969 | studylevel_file = os.path.join( |
---|
| 970 | self.workdir, 'sample_studylevel_data.csv') |
---|
| 971 | open(studylevel_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA) |
---|
[7933] | 972 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
[7548] | 973 | studylevel_file, STUDYLEVEL_HEADER_FIELDS,'create') |
---|
| 974 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 975 | |
---|
[7933] | 976 | self.processor = CourseTicketProcessor() |
---|
[7548] | 977 | self.csv_file = os.path.join( |
---|
| 978 | self.workdir, 'sample_courseticket_data.csv') |
---|
| 979 | open(self.csv_file, 'wb').write(COURSETICKET_SAMPLE_DATA) |
---|
| 980 | |
---|
| 981 | def test_interface(self): |
---|
| 982 | # Make sure we fulfill the interface contracts. |
---|
[7933] | 983 | assert verifyObject(IBatchProcessor, self.processor) is True |
---|
[7548] | 984 | assert verifyClass( |
---|
| 985 | IBatchProcessor, CourseTicketProcessor) is True |
---|
| 986 | |
---|
| 987 | def test_checkConversion(self): |
---|
[7933] | 988 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 989 | dict(reg_number='1', code='COURSE1', level='220')) |
---|
| 990 | self.assertEqual(len(errs),0) |
---|
[7933] | 991 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[7548] | 992 | dict(reg_number='1', code='COURSE2', level='220')) |
---|
| 993 | self.assertEqual(len(errs),1) |
---|
| 994 | self.assertTrue(('code','non-existent') in errs) |
---|
| 995 | |
---|
| 996 | def test_import(self): |
---|
[7933] | 997 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[7548] | 998 | self.csv_file, COURSETICKET_HEADER_FIELDS,'create') |
---|
[9420] | 999 | fail_file = open(fail_file).read() |
---|
| 1000 | self.assertEqual(num_warns,5) |
---|
| 1001 | self.assertEqual(fail_file, |
---|
[16000] | 1002 | 'reg_number,code,mandatory,level,level_session,ticket_session,score,unlock_score,matric_number,--ERRORS--\r\n' |
---|
| 1003 | '1,COURSE1,<IGNORE>,nonsense,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,Not all parents do exist yet.\r\n' |
---|
| 1004 | '1,NONSENSE,<IGNORE>,100,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,code: non-existent\r\n' |
---|
| 1005 | '1,COURSE1,<IGNORE>,200,2004,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: does not match 2008\r\n' |
---|
| 1006 | '1,COURSE1,<IGNORE>,300,2008,<IGNORE>,6,<IGNORE>,<IGNORE>,level object: does not exist\r\n' |
---|
| 1007 | '1,COURSE1,<IGNORE>,300,2008X,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: Invalid value\r\n') |
---|
[7933] | 1008 | assert self.processor.entryExists( |
---|
[9272] | 1009 | dict(reg_number='1', level='100', code='COURSE1'), |
---|
| 1010 | self.app) is True |
---|
[7933] | 1011 | courseticket = self.processor.getEntry( |
---|
[7548] | 1012 | dict(reg_number='1', level='100', code='COURSE1'), self.app) |
---|
[9272] | 1013 | self.assertEqual(courseticket.__parent__.__parent__.certificate.code, |
---|
| 1014 | u'CERT1') |
---|
[7548] | 1015 | self.assertEqual(courseticket.score, 1) |
---|
[7665] | 1016 | self.assertEqual(courseticket.mandatory, True) |
---|
[7548] | 1017 | self.assertEqual(courseticket.fcode, 'NA') |
---|
| 1018 | self.assertEqual(courseticket.dcode, 'NA') |
---|
| 1019 | self.assertEqual(courseticket.code, 'COURSE1') |
---|
| 1020 | self.assertEqual(courseticket.title, 'Unnamed Course') |
---|
| 1021 | self.assertEqual(courseticket.credits, 10) |
---|
| 1022 | self.assertEqual(courseticket.passmark, 40) |
---|
| 1023 | self.assertEqual(courseticket.semester, 1) |
---|
[10016] | 1024 | self.assertEqual(courseticket.level, 100) |
---|
| 1025 | self.assertEqual(courseticket.level_session, 2008) |
---|
[7548] | 1026 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8626] | 1027 | logcontent = open(self.logfile).read() |
---|
| 1028 | # Logging message from updateEntry, |
---|
| 1029 | self.assertTrue( |
---|
[9706] | 1030 | 'INFO - system - CourseTicket Processor - ' |
---|
| 1031 | 'sample_courseticket_data - K1000000 - 100 - ' |
---|
| 1032 | 'updated: code=COURSE1, ' |
---|
[8626] | 1033 | 'mandatory=False, score=3' |
---|
| 1034 | in logcontent) |
---|
| 1035 | |
---|
[10016] | 1036 | # The catalog has been updated |
---|
| 1037 | cat = queryUtility(ICatalog, name='coursetickets_catalog') |
---|
| 1038 | results = list( |
---|
| 1039 | cat.searchResults( |
---|
| 1040 | level=(100, 100))) |
---|
| 1041 | self.assertEqual(len(results),3) |
---|
| 1042 | |
---|
[8231] | 1043 | def test_import_update(self): |
---|
| 1044 | # We perform the same import twice, |
---|
| 1045 | # the second time in update mode. The number |
---|
| 1046 | # of warnings must be the same. |
---|
| 1047 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1048 | self.csv_file, COURSETICKET_HEADER_FIELDS,'create') |
---|
| 1049 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1050 | self.csv_file, COURSETICKET_HEADER_FIELDS,'update') |
---|
[9420] | 1051 | fail_file = open(fail_file).read() |
---|
| 1052 | self.assertEqual(num_warns,5) |
---|
| 1053 | self.assertEqual(fail_file, |
---|
[16000] | 1054 | 'reg_number,code,mandatory,level,level_session,ticket_session,score,unlock_score,matric_number,--ERRORS--\r\n' |
---|
| 1055 | '1,COURSE1,<IGNORE>,nonsense,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,Cannot update: no such entry\r\n' |
---|
| 1056 | '1,NONSENSE,<IGNORE>,100,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,code: non-existent\r\n' |
---|
| 1057 | '1,COURSE1,<IGNORE>,200,2004,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: does not match 2008\r\n' |
---|
| 1058 | '1,COURSE1,<IGNORE>,300,2008,<IGNORE>,6,<IGNORE>,<IGNORE>,level object: does not exist\r\n' |
---|
| 1059 | '1,COURSE1,<IGNORE>,300,2008X,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: Invalid value\r\n') |
---|
[8231] | 1060 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1061 | |
---|
[8888] | 1062 | def test_import_remove(self): |
---|
| 1063 | # We perform the same import twice, |
---|
| 1064 | # the second time in remove mode. The number |
---|
| 1065 | # of warnings must be the same. |
---|
| 1066 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1067 | self.csv_file, COURSETICKET_HEADER_FIELDS,'create') |
---|
[9272] | 1068 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8888] | 1069 | assert self.processor.entryExists( |
---|
| 1070 | dict(reg_number='1', level='100', code='COURSE1'), self.app) is True |
---|
| 1071 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1072 | self.csv_file, COURSETICKET_HEADER_FIELDS,'remove') |
---|
[9420] | 1073 | self.assertEqual(num_warns,5) |
---|
[8888] | 1074 | assert self.processor.entryExists( |
---|
| 1075 | dict(reg_number='1', level='100', code='COURSE1'), self.app) is False |
---|
| 1076 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1077 | logcontent = open(self.logfile).read() |
---|
| 1078 | self.assertTrue( |
---|
| 1079 | 'INFO - system - K1000000 - Course ticket in 100 removed: COURSE1' |
---|
| 1080 | in logcontent) |
---|
| 1081 | |
---|
[15163] | 1082 | def test_import_update_locked(self): |
---|
| 1083 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1084 | self.csv_file, COURSETICKET_HEADER_FIELDS,'create') |
---|
| 1085 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1086 | # In state 'transcript validated' course tickets can't edited |
---|
| 1087 | student = self.app['students']['X666666'] |
---|
| 1088 | IWorkflowState(student).setState('transcript validated') |
---|
| 1089 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1090 | self.csv_file, COURSETICKET_HEADER_FIELDS,'update') |
---|
| 1091 | fail_file = open(fail_file).read() |
---|
| 1092 | # Two more records could't be imported because course tickets |
---|
| 1093 | # of X666666 are locked |
---|
| 1094 | self.assertEqual(num_warns,7) |
---|
| 1095 | self.assertEqual(fail_file, |
---|
[16000] | 1096 | 'reg_number,code,mandatory,level,level_session,ticket_session,score,unlock_score,matric_number,--ERRORS--\r\n' |
---|
| 1097 | '1,COURSE1,True,100,<IGNORE>,<IGNORE>,1,<IGNORE>,<IGNORE>,Studycourse is locked.\r\n' |
---|
| 1098 | '1,COURSE1,True,200,2008,<IGNORE>,1,<IGNORE>,<IGNORE>,Studycourse is locked.\r\n' |
---|
| 1099 | '1,COURSE1,<IGNORE>,nonsense,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,Cannot update: no such entry\r\n' |
---|
| 1100 | '1,NONSENSE,<IGNORE>,100,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,code: non-existent\r\n' |
---|
| 1101 | '1,COURSE1,<IGNORE>,200,2004,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: does not match 2008\r\n' |
---|
| 1102 | '1,COURSE1,<IGNORE>,300,2008,<IGNORE>,6,<IGNORE>,<IGNORE>,level object: does not exist\r\n' |
---|
| 1103 | '1,COURSE1,<IGNORE>,300,2008X,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: Invalid value\r\n') |
---|
[15163] | 1104 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1105 | |
---|
[16012] | 1106 | def test_import_create_locked(self): |
---|
| 1107 | # In state 'transcript validated' course tickets can't be created |
---|
| 1108 | student = self.app['students']['X666666'] |
---|
| 1109 | IWorkflowState(student).setState('transcript validated') |
---|
| 1110 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1111 | self.csv_file, COURSETICKET_HEADER_FIELDS,'create') |
---|
| 1112 | fail_file = open(fail_file).read() |
---|
| 1113 | self.assertEqual(num_warns,7) |
---|
| 1114 | self.assertEqual(fail_file, |
---|
| 1115 | 'reg_number,code,mandatory,level,level_session,ticket_session,score,unlock_score,matric_number,--ERRORS--\r\n' |
---|
| 1116 | '1,COURSE1,True,100,<IGNORE>,<IGNORE>,1,<IGNORE>,<IGNORE>,Studycourse is locked.\r\n' |
---|
| 1117 | '1,COURSE1,True,200,2008,<IGNORE>,1,<IGNORE>,<IGNORE>,Studycourse is locked.\r\n' |
---|
| 1118 | '1,COURSE1,<IGNORE>,nonsense,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,Not all parents do exist yet.\r\n' |
---|
| 1119 | '1,NONSENSE,<IGNORE>,100,<IGNORE>,<IGNORE>,5,<IGNORE>,<IGNORE>,code: non-existent\r\n' |
---|
| 1120 | '1,COURSE1,<IGNORE>,200,2004,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: does not match 2008\r\n' |
---|
| 1121 | '1,COURSE1,<IGNORE>,300,2008,<IGNORE>,6,<IGNORE>,<IGNORE>,level object: does not exist\r\n' |
---|
| 1122 | '1,COURSE1,<IGNORE>,300,2008X,<IGNORE>,6,<IGNORE>,<IGNORE>,level_session: Invalid value\r\n') |
---|
| 1123 | self.assertFalse(self.processor.entryExists( |
---|
| 1124 | dict(reg_number='1', level='100', code='COURSE1'), |
---|
| 1125 | self.app)) |
---|
| 1126 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1127 | |
---|
| 1128 | def test_import_remove_locked(self): |
---|
| 1129 | # We perform the same import twice, |
---|
| 1130 | # the second time in remove mode. |
---|
| 1131 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1132 | self.csv_file, COURSETICKET_HEADER_FIELDS,'create') |
---|
| 1133 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1134 | assert self.processor.entryExists( |
---|
| 1135 | dict(reg_number='1', level='100', code='COURSE1'), self.app) is True |
---|
| 1136 | # In state 'transcript validated' course tickets can't be removed |
---|
| 1137 | student = self.app['students']['X666666'] |
---|
| 1138 | IWorkflowState(student).setState('transcript validated') |
---|
| 1139 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1140 | self.csv_file, COURSETICKET_HEADER_FIELDS,'remove') |
---|
| 1141 | self.assertEqual(num_warns,7) |
---|
| 1142 | assert self.processor.entryExists( |
---|
| 1143 | dict(reg_number='1', level='100', code='COURSE1'), self.app) is True |
---|
| 1144 | logcontent = open(self.logfile).read() |
---|
| 1145 | self.assertFalse( |
---|
| 1146 | 'INFO - system - X666666 - Course ticket in 100 removed: COURSE1' |
---|
| 1147 | in logcontent) |
---|
| 1148 | fail_file = open(fail_file).read() |
---|
| 1149 | self.assertEqual(fail_file, |
---|
| 1150 | 'reg_number,code,matric_number,level,--ERRORS--\r\n' |
---|
| 1151 | '1,COURSE1,<IGNORE>,100,Studycourse is locked.\r\n' |
---|
| 1152 | '1,COURSE1,<IGNORE>,200,Studycourse is locked.\r\n' |
---|
| 1153 | '1,COURSE1,<IGNORE>,nonsense,Cannot remove: no such entry\r\n' |
---|
| 1154 | '1,NONSENSE,<IGNORE>,100,Cannot remove: no such entry\r\n' |
---|
| 1155 | '1,COURSE1,<IGNORE>,200,Studycourse is locked.\r\n' |
---|
| 1156 | '1,COURSE1,<IGNORE>,300,Cannot remove: no such entry\r\n' |
---|
| 1157 | '1,COURSE1,<IGNORE>,300,Cannot remove: no such entry\r\n' |
---|
| 1158 | ) |
---|
| 1159 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1160 | |
---|
[16828] | 1161 | def test_import_firstcourse_with_content(self): |
---|
[16831] | 1162 | processor = StudentStudyCourseProcessor() |
---|
[16828] | 1163 | csv_file = os.path.join( |
---|
[16831] | 1164 | self.workdir, 'sample_first_studycourse_data.csv') |
---|
| 1165 | open(csv_file, 'wb').write(FIRST_STUDYCOURSE_SAMPLE_DATA) |
---|
[16828] | 1166 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
[16831] | 1167 | csv_file, FIRST_STUDYCOURSE_HEADER_FIELDS,'create') |
---|
[16828] | 1168 | self.assertEqual(num_warns,1) |
---|
| 1169 | content = open(fail_file).read() |
---|
| 1170 | self.assertTrue('current_level: not in range' in content) |
---|
[16831] | 1171 | studycourse = processor.getEntry( |
---|
| 1172 | dict(reg_number='1', previous='1'), self.app) |
---|
[16828] | 1173 | self.assertEqual(studycourse.certificate.code, u'CERT1') |
---|
| 1174 | self.assertEqual(studycourse.__name__, u'studycourse_1') |
---|
| 1175 | cat = queryUtility(ICatalog, name='students_catalog') |
---|
| 1176 | results = list(cat.searchResults(reg_number=('1', '1'))) |
---|
| 1177 | self.assertEqual([i for i in results[0].keys()], |
---|
| 1178 | [u'accommodation', u'payments', u'studycourse', u'studycourse_1']) |
---|
[16831] | 1179 | |
---|
| 1180 | processor = StudentStudyLevelProcessor() |
---|
[16828] | 1181 | csv_file = os.path.join( |
---|
[16831] | 1182 | self.workdir, 'sample_first_studylevel_data.csv') |
---|
| 1183 | open(csv_file, 'wb').write(FIRST_STUDYLEVEL_SAMPLE_DATA) |
---|
[16828] | 1184 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
[16831] | 1185 | csv_file, FIRST_STUDYLEVEL_HEADER_FIELDS,'create') |
---|
[16828] | 1186 | self.assertEqual(num_warns,3) |
---|
| 1187 | assert processor.entryExists( |
---|
| 1188 | dict(reg_number='1', level='100'), self.app) is True |
---|
| 1189 | studylevel = processor.getEntry( |
---|
| 1190 | dict(reg_number='1', level='100'), self.app) |
---|
| 1191 | self.assertEqual(studylevel.__parent__.certificate.code, u'CERT1') |
---|
| 1192 | self.assertEqual(studylevel.level_session, 2008) |
---|
| 1193 | self.assertEqual(studylevel.level_verdict, '0') |
---|
| 1194 | self.assertEqual(studylevel.level, 100) |
---|
| 1195 | self.assertEqual([i for i in results[0]['studycourse_1'].keys()], |
---|
| 1196 | [u'100', u'200']) |
---|
[16831] | 1197 | |
---|
| 1198 | processor = CourseTicketProcessor() |
---|
[16828] | 1199 | csv_file = os.path.join( |
---|
[16831] | 1200 | self.workdir, 'sample_first_courseticket_data.csv') |
---|
| 1201 | open(csv_file, 'wb').write(FIRST_COURSETICKET_SAMPLE_DATA) |
---|
[16828] | 1202 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
[16831] | 1203 | csv_file, FIRST_COURSETICKET_HEADER_FIELDS,'create') |
---|
[16828] | 1204 | assert processor.entryExists( |
---|
[16831] | 1205 | dict(reg_number='1', level='100', |
---|
| 1206 | code='COURSE1', previous='1'), self.app) is True |
---|
[16828] | 1207 | self.assertEqual([i for i in results[0]['studycourse_1']['100'].keys()], |
---|
| 1208 | [u'COURSE1']) |
---|
| 1209 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1210 | |
---|
[7951] | 1211 | class PaymentProcessorTest(StudentImportExportSetup): |
---|
[7548] | 1212 | |
---|
[7623] | 1213 | def setUp(self): |
---|
[7933] | 1214 | super(PaymentProcessorTest, self).setUp() |
---|
[7623] | 1215 | |
---|
| 1216 | # Add student with payment |
---|
| 1217 | student = Student() |
---|
| 1218 | student.firstname = u'Anna' |
---|
| 1219 | student.lastname = u'Tester' |
---|
| 1220 | student.reg_number = u'123' |
---|
| 1221 | student.matric_number = u'234' |
---|
| 1222 | self.app['students'].addStudent(student) |
---|
| 1223 | self.student = self.app['students'][student.student_id] |
---|
| 1224 | payment = createObject(u'waeup.StudentOnlinePayment') |
---|
[8884] | 1225 | payment.p_id = 'p120' |
---|
[12623] | 1226 | payment.p_session = 2012 |
---|
| 1227 | payment.p_category = 'schoolfee' |
---|
| 1228 | payment.p_state = 'paid' |
---|
[7623] | 1229 | self.student['payments'][payment.p_id] = payment |
---|
| 1230 | |
---|
| 1231 | # Import students with subobjects |
---|
| 1232 | student_file = os.path.join(self.workdir, 'sample_student_data.csv') |
---|
| 1233 | open(student_file, 'wb').write(STUDENT_SAMPLE_DATA) |
---|
| 1234 | num, num_warns, fin_file, fail_file = StudentProcessor().doImport( |
---|
| 1235 | student_file, STUDENT_HEADER_FIELDS) |
---|
| 1236 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1237 | |
---|
[7933] | 1238 | self.processor = StudentOnlinePaymentProcessor() |
---|
[7623] | 1239 | self.csv_file = os.path.join( |
---|
| 1240 | self.workdir, 'sample_payment_data.csv') |
---|
| 1241 | open(self.csv_file, 'wb').write(PAYMENT_SAMPLE_DATA) |
---|
[8884] | 1242 | self.csv_file2 = os.path.join( |
---|
| 1243 | self.workdir, 'sample_create_payment_data.csv') |
---|
| 1244 | open(self.csv_file2, 'wb').write(PAYMENT_CREATE_SAMPLE_DATA) |
---|
[7623] | 1245 | |
---|
| 1246 | def test_interface(self): |
---|
| 1247 | # Make sure we fulfill the interface contracts. |
---|
[7933] | 1248 | assert verifyObject(IBatchProcessor, self.processor) is True |
---|
[7623] | 1249 | assert verifyClass( |
---|
| 1250 | IBatchProcessor, StudentOnlinePaymentProcessor) is True |
---|
| 1251 | |
---|
| 1252 | def test_getEntry(self): |
---|
[7933] | 1253 | assert self.processor.getEntry( |
---|
[7623] | 1254 | dict(student_id='ID_NONE', p_id='nonsense'), self.app) is None |
---|
[7933] | 1255 | assert self.processor.getEntry( |
---|
[8884] | 1256 | dict(student_id=self.student.student_id, p_id='p120'), |
---|
| 1257 | self.app) is self.student['payments']['p120'] |
---|
[7933] | 1258 | assert self.processor.getEntry( |
---|
[8884] | 1259 | dict(student_id=self.student.student_id, p_id='XXXXXX112'), |
---|
| 1260 | self.app) is self.student['payments']['p120'] |
---|
[7623] | 1261 | |
---|
[8886] | 1262 | def test_delEntry(self): |
---|
| 1263 | assert self.processor.getEntry( |
---|
| 1264 | dict(student_id=self.student.student_id, p_id='p120'), |
---|
| 1265 | self.app) is self.student['payments']['p120'] |
---|
| 1266 | self.assertEqual(len(self.student['payments'].keys()),1) |
---|
| 1267 | self.processor.delEntry( |
---|
| 1268 | dict(student_id=self.student.student_id, p_id='p120'), |
---|
| 1269 | self.app) |
---|
| 1270 | assert self.processor.getEntry( |
---|
| 1271 | dict(student_id=self.student.student_id, p_id='p120'), |
---|
| 1272 | self.app) is None |
---|
| 1273 | self.assertEqual(len(self.student['payments'].keys()),0) |
---|
| 1274 | |
---|
[7623] | 1275 | def test_addEntry(self): |
---|
| 1276 | self.assertEqual(len(self.student['payments'].keys()),1) |
---|
| 1277 | payment1 = createObject(u'waeup.StudentOnlinePayment') |
---|
| 1278 | payment1.p_id = 'p234' |
---|
[7933] | 1279 | self.processor.addEntry( |
---|
[7623] | 1280 | payment1, dict(student_id=self.student.student_id, p_id='p234'), |
---|
| 1281 | self.app) |
---|
| 1282 | self.assertEqual(len(self.student['payments'].keys()),2) |
---|
| 1283 | self.assertEqual(self.student['payments']['p234'].p_id, 'p234') |
---|
| 1284 | payment2 = createObject(u'waeup.StudentOnlinePayment') |
---|
| 1285 | payment1.p_id = 'nonsense' |
---|
| 1286 | # payment1.p_id will be replaced if p_id doesn't start with 'p' |
---|
[9467] | 1287 | # and is not an old PIN |
---|
[7933] | 1288 | self.processor.addEntry( |
---|
[7623] | 1289 | payment2, dict(student_id=self.student.student_id, p_id='XXXXXX456'), |
---|
| 1290 | self.app) |
---|
| 1291 | self.assertEqual(len(self.student['payments'].keys()),3) |
---|
[8884] | 1292 | self.assertEqual(self.student['payments']['p560'].p_id, 'p560') |
---|
[12623] | 1293 | # Requirement added on 19/02/2015: same payment must not exist. |
---|
| 1294 | payment3 = createObject(u'waeup.StudentOnlinePayment') |
---|
| 1295 | payment3.p_id = 'p456' |
---|
| 1296 | payment3.p_session = 2012 |
---|
| 1297 | payment3.p_category = 'schoolfee' |
---|
| 1298 | self.assertRaises( |
---|
| 1299 | DuplicationError, self.processor.addEntry, payment3, |
---|
| 1300 | dict(student_id=self.student.student_id, p_id='p456'), self.app) |
---|
[13872] | 1301 | logcontent = open(self.logfile).read() |
---|
| 1302 | self.assertTrue( |
---|
| 1303 | 'INFO - system - StudentOnlinePayment Processor - K1000000 - ' |
---|
| 1304 | 'previous update cancelled' |
---|
| 1305 | in logcontent) |
---|
[7623] | 1306 | |
---|
| 1307 | def test_checkConversion(self): |
---|
[7933] | 1308 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[12996] | 1309 | dict(p_id='<IGNORE>'), mode='create') |
---|
| 1310 | self.assertEqual(len(errs),0) |
---|
| 1311 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
| 1312 | dict(p_id='<IGNORE>'), mode='update') |
---|
| 1313 | self.assertEqual(len(errs),1) |
---|
| 1314 | self.assertEqual(errs[0], ('p_id', u'missing')) |
---|
| 1315 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[8884] | 1316 | dict(p_id='3816951266236341955')) |
---|
[7623] | 1317 | self.assertEqual(len(errs),0) |
---|
[7933] | 1318 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[8884] | 1319 | dict(p_id='p1266236341955')) |
---|
[7623] | 1320 | self.assertEqual(len(errs),0) |
---|
[7933] | 1321 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[9467] | 1322 | dict(p_id='ABC-11-1234567890')) |
---|
| 1323 | self.assertEqual(len(errs),0) |
---|
| 1324 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[8884] | 1325 | dict(p_id='nonsense')) |
---|
[7623] | 1326 | self.assertEqual(len(errs),1) |
---|
[12996] | 1327 | self.assertEqual(errs[0], ('p_id', u'invalid format')) |
---|
[8884] | 1328 | timestamp = ("%d" % int(time()*10000))[1:] |
---|
[7623] | 1329 | p_id = "p%s" % timestamp |
---|
[7933] | 1330 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
[8884] | 1331 | dict(p_id=p_id)) |
---|
[7623] | 1332 | self.assertEqual(len(errs),0) |
---|
[12513] | 1333 | dup_payment = createObject(u'waeup.StudentOnlinePayment') |
---|
| 1334 | dup_payment.p_id = 'XYZ-99-1234567890' |
---|
| 1335 | self.student['payments'][dup_payment.p_id] = dup_payment |
---|
| 1336 | errs, inv_errs, conv_dict = self.processor.checkConversion( |
---|
| 1337 | dict(p_id='XYZ-99-1234567890'), mode='create') |
---|
| 1338 | self.assertEqual(len(errs),1) |
---|
| 1339 | self.assertEqual(errs[0], ('p_id', u'p_id exists in K1000000 ')) |
---|
[7623] | 1340 | |
---|
| 1341 | def test_import(self): |
---|
[7933] | 1342 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
[7623] | 1343 | self.csv_file, PAYMENT_HEADER_FIELDS,'create') |
---|
| 1344 | self.assertEqual(num_warns,0) |
---|
[9467] | 1345 | |
---|
[7933] | 1346 | payment = self.processor.getEntry(dict(reg_number='1', |
---|
[8884] | 1347 | p_id='p2907979737440'), self.app) |
---|
| 1348 | self.assertEqual(payment.p_id, 'p2907979737440') |
---|
[9151] | 1349 | self.assertTrue(payment.p_current) |
---|
[8167] | 1350 | cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S") |
---|
[8200] | 1351 | self.assertEqual(cdate, "2010-11-26 18:59:33") |
---|
| 1352 | self.assertEqual(str(payment.creation_date.tzinfo),'UTC') |
---|
[9467] | 1353 | |
---|
[9039] | 1354 | payment = self.processor.getEntry(dict(matric_number='100001', |
---|
| 1355 | p_id='p2907125937570'), self.app) |
---|
| 1356 | self.assertEqual(payment.p_id, 'p2907125937570') |
---|
[9466] | 1357 | self.assertEqual(payment.amount_auth, 19500.1) |
---|
[9151] | 1358 | self.assertFalse(payment.p_current) |
---|
[9039] | 1359 | cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S") |
---|
| 1360 | # Ooooh, still the old problem, see |
---|
| 1361 | # http://mail.dzug.org/mailman/archives/zope/2006-August/001153.html. |
---|
| 1362 | # WAT is interpreted as GMT-1 and not GMT+1 |
---|
| 1363 | self.assertEqual(cdate, "2010-11-25 21:16:33") |
---|
| 1364 | self.assertEqual(str(payment.creation_date.tzinfo),'UTC') |
---|
[9467] | 1365 | |
---|
| 1366 | payment = self.processor.getEntry(dict(reg_number='3', |
---|
| 1367 | p_id='ABC-11-1234567890'), self.app) |
---|
| 1368 | self.assertEqual(payment.amount_auth, 19500.6) |
---|
| 1369 | |
---|
[7623] | 1370 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8626] | 1371 | logcontent = open(self.logfile).read() |
---|
[8886] | 1372 | # Logging message from updateEntry |
---|
[8626] | 1373 | self.assertTrue( |
---|
[11699] | 1374 | 'INFO - system - StudentOnlinePayment Processor - ' |
---|
[9706] | 1375 | 'sample_payment_data - K1000001 - updated: ' |
---|
[9151] | 1376 | 'p_item=BTECHBDT, creation_date=2010-02-15 13:19:01+00:00, ' |
---|
| 1377 | 'p_category=schoolfee, amount_auth=19500.0, p_current=True, ' |
---|
[12623] | 1378 | 'p_session=2009, ' |
---|
[9151] | 1379 | 'p_id=p1266236341955, r_code=00, r_amount_approved=19500.0, ' |
---|
| 1380 | 'p_state=paid' |
---|
[8626] | 1381 | in logcontent) |
---|
[9467] | 1382 | self.assertTrue( |
---|
[11699] | 1383 | 'INFO - system - StudentOnlinePayment Processor - ' |
---|
[9706] | 1384 | 'sample_payment_data - K1000001 - updated: ' |
---|
[9467] | 1385 | 'p_item=BTECHBDT, creation_date=2010-02-15 13:19:01+00:00, ' |
---|
| 1386 | 'p_category=schoolfee, amount_auth=19500.6, p_current=True, ' |
---|
[12623] | 1387 | 'p_session=2011, ' |
---|
[9467] | 1388 | 'p_id=ABC-11-1234567890, r_code=SC, r_amount_approved=19500.0, ' |
---|
| 1389 | 'p_state=paid' |
---|
| 1390 | in logcontent) |
---|
[8626] | 1391 | |
---|
[8231] | 1392 | def test_import_update(self): |
---|
| 1393 | # We perform the same import twice, |
---|
| 1394 | # the second time in update mode. The number |
---|
[12996] | 1395 | # of warnings increases becaus one p_id is missing. |
---|
[8231] | 1396 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1397 | self.csv_file, PAYMENT_HEADER_FIELDS,'create') |
---|
[9272] | 1398 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8231] | 1399 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1400 | self.csv_file, PAYMENT_HEADER_FIELDS,'update') |
---|
[12996] | 1401 | self.assertEqual(num_warns,1) |
---|
| 1402 | content = open(fail_file).read() |
---|
[8231] | 1403 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[12996] | 1404 | self.assertTrue('p_id: missing' in content) |
---|
[8231] | 1405 | |
---|
[8886] | 1406 | def test_import_remove(self): |
---|
[12009] | 1407 | # We perform the same import twice, |
---|
| 1408 | # the second time in remove mode. The number |
---|
[12996] | 1409 | # of warnings increases becaus one p_id is missing. |
---|
[8886] | 1410 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1411 | self.csv_file, PAYMENT_HEADER_FIELDS,'create') |
---|
[9272] | 1412 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[8886] | 1413 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1414 | self.csv_file, PAYMENT_HEADER_FIELDS,'remove') |
---|
[12996] | 1415 | self.assertEqual(num_warns,1) |
---|
| 1416 | content = open(fail_file).read() |
---|
| 1417 | self.assertTrue('p_id: missing' in content) |
---|
[8886] | 1418 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1419 | logcontent = open(self.logfile).read() |
---|
| 1420 | self.assertTrue( |
---|
| 1421 | 'INFO - system - K1000001 - Payment ticket removed: p1266236341955' |
---|
| 1422 | in logcontent) |
---|
| 1423 | |
---|
[12996] | 1424 | def test_import_same_payment_exists(self): |
---|
[8884] | 1425 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1426 | self.csv_file2, PAYMENT_CREATE_HEADER_FIELDS,'create') |
---|
[12623] | 1427 | # One payment with same session and category exists |
---|
| 1428 | self.assertEqual(num_warns,1) |
---|
| 1429 | content = open(fail_file).read() |
---|
| 1430 | self.assertTrue( |
---|
| 1431 | '1,942,online,BTECHBDT,2010/11/26 19:59:33.744 GMT+1,0,' |
---|
[16821] | 1432 | '19500,schoolfee,19500,2015,unpaid,' |
---|
[12868] | 1433 | 'Same payment has already been made.' |
---|
[12623] | 1434 | in content) |
---|
[8884] | 1435 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
[12623] | 1436 | self.assertEqual(len(self.app['students']['X666666']['payments']), 13) |
---|
[8884] | 1437 | |
---|
[9294] | 1438 | class StudentVerdictProcessorTest(StudentImportExportSetup): |
---|
[8884] | 1439 | |
---|
[9294] | 1440 | def setUp(self): |
---|
| 1441 | super(StudentVerdictProcessorTest, self).setUp() |
---|
| 1442 | |
---|
| 1443 | # Import students with subobjects |
---|
| 1444 | student_file = os.path.join(self.workdir, 'sample_student_data.csv') |
---|
| 1445 | open(student_file, 'wb').write(STUDENT_SAMPLE_DATA) |
---|
| 1446 | num, num_warns, fin_file, fail_file = StudentProcessor().doImport( |
---|
| 1447 | student_file, STUDENT_HEADER_FIELDS) |
---|
| 1448 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1449 | |
---|
| 1450 | # Update study courses |
---|
| 1451 | studycourse_file = os.path.join( |
---|
| 1452 | self.workdir, 'sample_studycourse_data.csv') |
---|
| 1453 | open(studycourse_file, 'wb').write(STUDYCOURSE_SAMPLE_DATA) |
---|
| 1454 | processor = StudentStudyCourseProcessor() |
---|
| 1455 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
| 1456 | studycourse_file, STUDYCOURSE_HEADER_FIELDS,'update') |
---|
| 1457 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1458 | # Import study levels |
---|
| 1459 | self.csv_file = os.path.join( |
---|
| 1460 | self.workdir, 'sample_studylevel_data.csv') |
---|
| 1461 | open(self.csv_file, 'wb').write(STUDYLEVEL_SAMPLE_DATA) |
---|
| 1462 | processor = StudentStudyLevelProcessor() |
---|
| 1463 | num, num_warns, fin_file, fail_file = processor.doImport( |
---|
| 1464 | self.csv_file, STUDYLEVEL_HEADER_FIELDS,'create') |
---|
| 1465 | content = open(fail_file).read() |
---|
| 1466 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1467 | |
---|
| 1468 | self.processor = StudentVerdictProcessor() |
---|
| 1469 | self.csv_file = os.path.join( |
---|
| 1470 | self.workdir, 'sample_verdict_data.csv') |
---|
| 1471 | open(self.csv_file, 'wb').write(VERDICT_SAMPLE_DATA) |
---|
| 1472 | return |
---|
| 1473 | |
---|
| 1474 | def test_import(self): |
---|
| 1475 | studycourse = self.processor.getEntry(dict(matric_number='100000'), |
---|
| 1476 | self.app) |
---|
[12981] | 1477 | self.assertEqual(studycourse['200'].level_verdict, '0') |
---|
[9294] | 1478 | student = self.processor.getParent( |
---|
| 1479 | dict(matric_number='100000'), self.app) |
---|
| 1480 | num, num_warns, fin_file, fail_file = self.processor.doImport( |
---|
| 1481 | self.csv_file, VERDICT_HEADER_FIELDS,'update') |
---|
| 1482 | self.assertEqual(num_warns,5) |
---|
| 1483 | self.assertEqual(studycourse.current_verdict, '0') |
---|
| 1484 | self.assertEqual(student.state, 'returning') |
---|
| 1485 | self.assertEqual(studycourse.current_level, 200) |
---|
| 1486 | self.assertEqual(studycourse['200'].level_verdict, '0') |
---|
| 1487 | student = self.processor.getParent( |
---|
| 1488 | dict(matric_number='100005'), self.app) |
---|
| 1489 | self.assertEqual(student.state, 'returning') |
---|
[9295] | 1490 | self.assertEqual(student['studycourse'].current_verdict, 'A') |
---|
[9294] | 1491 | self.assertEqual(studycourse.current_level, 200) |
---|
[9295] | 1492 | self.assertEqual(student['studycourse']['200'].validated_by, 'System') |
---|
| 1493 | self.assertTrue(isinstance( |
---|
| 1494 | student['studycourse']['200'].validation_date, datetime.datetime)) |
---|
[9296] | 1495 | student = self.processor.getParent( |
---|
| 1496 | dict(matric_number='100008'), self.app) |
---|
| 1497 | self.assertEqual(student['studycourse']['200'].validated_by, 'Juliana') |
---|
[9294] | 1498 | content = open(fail_file).read() |
---|
| 1499 | self.assertEqual( |
---|
| 1500 | content, |
---|
[9296] | 1501 | 'current_session,current_level,bypass_validation,current_verdict,' |
---|
| 1502 | 'matric_number,validated_by,--ERRORS--\r\n' |
---|
| 1503 | '2008,100,False,B,100001,<IGNORE>,Current level does not correspond.\r\n' |
---|
| 1504 | '2007,200,<IGNORE>,C,100002,<IGNORE>,Current session does not correspond.\r\n' |
---|
| 1505 | '2008,200,<IGNORE>,A,100003,<IGNORE>,Student in wrong state.\r\n' |
---|
| 1506 | '2008,200,<IGNORE>,<IGNORE>,100004,<IGNORE>,No verdict in import file.\r\n' |
---|
| 1507 | '2008,200,True,A,100007,<IGNORE>,Study level object is missing.\r\n' |
---|
[9294] | 1508 | ) |
---|
| 1509 | logcontent = open(self.logfile).read() |
---|
| 1510 | self.assertMatches( |
---|
[9706] | 1511 | '... INFO - system - Verdict Processor (special processor, ' |
---|
| 1512 | 'update only) - sample_verdict_data - X666666 - ' |
---|
| 1513 | 'updated: current_verdict=0...', |
---|
[9294] | 1514 | logcontent) |
---|
| 1515 | self.assertMatches( |
---|
| 1516 | '... INFO - system - X666666 - Returned...', |
---|
| 1517 | logcontent) |
---|
| 1518 | shutil.rmtree(os.path.dirname(fin_file)) |
---|
| 1519 | |
---|
[6840] | 1520 | def test_suite(): |
---|
| 1521 | suite = unittest.TestSuite() |
---|
| 1522 | for testcase in [ |
---|
[7933] | 1523 | StudentProcessorTest,StudentStudyCourseProcessorTest, |
---|
| 1524 | StudentStudyLevelProcessorTest,CourseTicketProcessorTest, |
---|
[7951] | 1525 | PaymentProcessorTest,StudentVerdictProcessorTest]: |
---|
[6840] | 1526 | suite.addTest(unittest.TestLoader().loadTestsFromTestCase( |
---|
| 1527 | testcase |
---|
| 1528 | ) |
---|
| 1529 | ) |
---|
| 1530 | return suite |
---|
[7536] | 1531 | |
---|
| 1532 | |
---|