- Timestamp:
- 20 Sep 2012, 08:49:37 (12 years ago)
- Location:
- main/waeup.kofa/branches/uli-zc-async
- Files:
-
- 1 deleted
- 29 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.kofa/branches/uli-zc-async
- Property svn:mergeinfo changed
-
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/authentication.py
r8757 r9209 102 102 def checkPassword(self, password): 103 103 """Check whether the given `password` matches the one stored. 104 105 We additionally check if student account has been suspended. 104 106 """ 105 107 if not isinstance(password, basestring): … … 107 109 if not getattr(self.context, 'password', None): 108 110 # unset/empty passwords do never match 111 return False 112 if self.context.suspended == True: 109 113 return False 110 114 passwordmanager = getUtility(IPasswordManager, 'SSHA') -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/batching.py
r8736 r9209 26 26 import grok 27 27 import csv 28 from time import time 28 29 from zope.interface import Interface 29 30 from zope.schema import getFields … … 42 43 IStudentOnlinePayment, IStudentVerdictUpdate) 43 44 from waeup.kofa.students.workflow import ( 44 IMPORTABLE_STATES, IMPORTABLE_TRANSITIONS) 45 IMPORTABLE_STATES, IMPORTABLE_TRANSITIONS, 46 FORBIDDEN_POSTGRAD_TRANS, FORBIDDEN_POSTGRAD_STATES) 45 47 from waeup.kofa.utils.batching import BatchProcessor 46 48 … … 170 172 if transition not in allowed_transitions: 171 173 return 'Transition not allowed.' 174 if transition in FORBIDDEN_POSTGRAD_TRANS and \ 175 obj.is_postgrad: 176 return 'Transition not allowed (pg student).' 177 state = row.get('state', IGNORE_MARKER) 178 if state not in (IGNORE_MARKER, ''): 179 if state in FORBIDDEN_POSTGRAD_STATES and \ 180 obj.is_postgrad: 181 return 'State not allowed (pg student).' 172 182 return None 173 183 … … 183 193 184 194 # Update password 195 # XXX: Tale DELETION_MARKER into consideration 185 196 if row.has_key('password'): 186 197 passwd = row.get('password', IGNORE_MARKER) … … 222 233 parent = self.getParent(row, site) 223 234 if hasattr(obj,'student_id'): 224 # Update mode: the student exists and we can get the student_id 235 # Update mode: the student exists and we can get the student_id. 236 # Create mode: the record contains the student_id 225 237 parent.logger.info( 226 238 '%s - Student record updated: %s' … … 259 271 errs, inv_errs, conv_dict = converter.fromStringDict( 260 272 row, self.factory_name, mode=mode) 261 if row.has_key('transition') and \ 262 not row['transition'] in IMPORTABLE_TRANSITIONS: 263 if row['transition'] not in (IGNORE_MARKER, ''): 264 errs.append(('transition','not allowed')) 265 if row.has_key('state') and \ 266 not row['state'] in IMPORTABLE_STATES: 267 if row['state'] not in (IGNORE_MARKER, ''): 268 errs.append(('state','not allowed')) 269 else: 270 # state is an attribute of Student and must not 271 # be changed if empty 272 conv_dict['state'] = IGNORE_MARKER 273 273 if row.has_key('transition'): 274 if row['transition'] not in IMPORTABLE_TRANSITIONS: 275 if row['transition'] not in (IGNORE_MARKER, ''): 276 errs.append(('transition','not allowed')) 277 if row.has_key('state'): 278 if row['state'] not in IMPORTABLE_STATES: 279 if row['state'] not in (IGNORE_MARKER, ''): 280 errs.append(('state','not allowed')) 281 else: 282 # State is an attribute of Student and must not 283 # be changed if empty. 284 conv_dict['state'] = IGNORE_MARKER 274 285 try: 275 286 # Correct stud_id counter. As the IConverter for students … … 291 302 grok.baseclass() 292 303 293 #: required fields beside 'student_id', 'reg_number' and 'matric_number' 304 # additional available fields 305 # beside 'student_id', 'reg_number' and 'matric_number' 294 306 additional_fields = [] 295 307 296 #: header fields additional required308 #: header fields additionally required 297 309 additional_headers = [] 298 310 … … 361 373 return errs, inv_errs, conv_dict 362 374 375 def getMapping(self, path, headerfields, mode): 376 """Get a mapping from CSV file headerfields to actually used fieldnames. 377 """ 378 result = dict() 379 reader = csv.reader(open(path, 'rb')) 380 raw_header = reader.next() 381 for num, field in enumerate(headerfields): 382 if field not in ['student_id', 'reg_number', 'matric_number', 383 'p_id', 'code', 'level' 384 ] and mode == 'remove': 385 continue 386 if field == u'--IGNORE--': 387 # Skip ignored columns in failed and finished data files. 388 continue 389 result[raw_header[num]] = field 390 return result 391 363 392 364 393 class StudentStudyCourseProcessor(StudentProcessorBase): … … 406 435 StudentStudyCourseProcessor, self).checkConversion(row, mode=mode) 407 436 # We have to check if current_level is in range of certificate. 408 if conv_dict.has_key('certificate'): 409 cert = conv_dict['certificate'] 410 if conv_dict['current_level'] < cert.start_level or \ 411 conv_dict['current_level'] > cert.end_level+120: 412 errs.append(('current_level','not in range')) 437 if conv_dict.has_key('certificate') and \ 438 conv_dict.has_key('current_level'): 439 cert = conv_dict['certificate'] 440 level = conv_dict['current_level'] 441 if level < cert.start_level or level > cert.end_level+120: 442 errs.append(('current_level','not in range')) 413 443 return errs, inv_errs, conv_dict 444 445 def checkUpdateRequirements(self, obj, row, site): 446 """Checks requirements the object must fulfill when being updated. 447 448 Returns error messages as strings in case of requirement 449 problems. 450 """ 451 current_level = row.get('current_level', None) 452 if current_level == 999 and \ 453 obj.__parent__.state in FORBIDDEN_POSTGRAD_STATES: 454 return 'Not a pg student.' 455 return None 414 456 415 457 class StudentStudyLevelProcessor(StudentProcessorBase): … … 427 469 428 470 location_fields = [] 471 429 472 additional_fields = ['level'] 430 473 additional_headers = ['level'] … … 509 552 items_changed = super(CourseTicketProcessor, self).updateEntry( 510 553 obj, row, site) 554 parent = self.getParent(row, site) 511 555 student = self.getParent(row, site).__parent__.__parent__ 512 556 student.__parent__.logger.info( 513 '%s - Course ticket updated: %s'514 % (student.student_id, items_changed))557 '%s - Course ticket in %s updated: %s' 558 % (student.student_id, parent.level, items_changed)) 515 559 return 516 560 … … 528 572 return 529 573 574 def delEntry(self, row, site): 575 ticket = self.getEntry(row, site) 576 parent = self.getParent(row, site) 577 if ticket is not None: 578 student = self._getStudent(row, site) 579 student.__parent__.logger.info('%s - Course ticket in %s removed: %s' 580 % (student.student_id, parent.level, ticket.code)) 581 del parent[ticket.code] 582 return 583 530 584 def checkConversion(self, row, mode='ignore'): 531 585 """Validates all values in row. … … 552 606 grok.name(util_name) 553 607 554 name = u' Payment Processor'608 name = u'Student Payment Processor' 555 609 iface = IStudentOnlinePayment 556 610 factory_name = 'waeup.StudentOnlinePayment' … … 558 612 location_fields = [] 559 613 additional_fields = ['p_id'] 560 additional_headers = ['p_id'] 614 additional_headers = [] 615 616 def checkHeaders(self, headerfields, mode='ignore'): 617 super(StudentOnlinePaymentProcessor, self).checkHeaders(headerfields) 618 if mode in ('update', 'remove') and not 'p_id' in headerfields: 619 raise FatalCSVError( 620 "Need p_id for import in update and remove modes!") 621 return True 561 622 562 623 def parentsExist(self, row, site): … … 573 634 if payments is None: 574 635 return None 636 p_id = row.get('p_id', None) 637 if p_id is None: 638 return None 575 639 # We can use the hash symbol at the end of p_id in import files 576 640 # to avoid annoying automatic number transformation 577 641 # by Excel or Calc 578 p_id = row['p_id'].strip('#') 579 if p_id.startswith('p'): 580 entry = payments.get(p_id) 581 else: 582 # For data migration from old SRP 583 entry = payments.get('p' + p_id[6:]) 642 p_id = p_id.strip('#') 643 if not p_id.startswith('p'): 644 # For data migration from old SRP only 645 p_id = 'p' + p_id[7:] + '0' 646 entry = payments.get(p_id) 584 647 return entry 585 648 … … 600 663 if not p_id.startswith('p'): 601 664 # For data migration from old SRP 602 obj.p_id = 'p' + p_id[ 6:]665 obj.p_id = 'p' + p_id[7:] + '0' 603 666 parent[obj.p_id] = obj 604 667 else: … … 606 669 return 607 670 671 def delEntry(self, row, site): 672 payment = self.getEntry(row, site) 673 parent = self.getParent(row, site) 674 if payment is not None: 675 student = self._getStudent(row, site) 676 student.__parent__.logger.info('%s - Payment ticket removed: %s' 677 % (student.student_id, payment.p_id)) 678 del parent[payment.p_id] 679 return 680 608 681 def checkConversion(self, row, mode='ignore'): 609 682 """Validates all values in row. … … 613 686 614 687 # We have to check p_id. 615 p_id = row['p_id'].strip('#') 688 p_id = row.get('p_id', None) 689 if not p_id: 690 timestamp = ("%d" % int(time()*10000))[1:] 691 p_id = "p%s" % timestamp 692 conv_dict['p_id'] = p_id 693 return errs, inv_errs, conv_dict 694 else: 695 p_id = p_id.strip('#') 616 696 if p_id.startswith('p'): 617 697 if not len(p_id) == 14: -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser.py
r9166 r9209 43 43 from waeup.kofa.interfaces import ( 44 44 IKofaObject, IUserAccount, IExtFileStore, IPasswordValidator, IContactForm, 45 IKofaUtils, IUniversity )45 IKofaUtils, IUniversity, IObjectHistory) 46 46 from waeup.kofa.interfaces import MessageFactory as _ 47 47 from waeup.kofa.widgets.datewidget import ( … … 53 53 IUGStudentClearance,IPGStudentClearance, 54 54 IStudentPersonal, IStudentBase, IStudentStudyCourse, 55 IStudentStudyCourseTransfer, 55 56 IStudentAccommodation, IStudentStudyLevel, 56 57 ICourseTicket, ICourseTicketAdd, IStudentPaymentsContainer, 57 IStudentOnlinePayment, IBedTicket, IStudentsUtils, IStudentRequestPW 58 IStudentOnlinePayment, IStudentPreviousPayment, 59 IBedTicket, IStudentsUtils, IStudentRequestPW 58 60 ) 59 61 from waeup.kofa.students.catalog import search 60 62 from waeup.kofa.students.workflow import (CREATED, ADMITTED, PAID, 61 CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED) 63 CLEARANCE, REQUESTED, RETURNING, CLEARED, REGISTERED, VALIDATED, 64 FORBIDDEN_POSTGRAD_TRANS) 62 65 from waeup.kofa.students.studylevel import StudentStudyLevel, CourseTicket 63 66 from waeup.kofa.students.vocabularies import StudyLevelSource … … 65 68 from waeup.kofa.hostels.hostel import NOT_OCCUPIED 66 69 from waeup.kofa.utils.helpers import get_current_principal, to_timezone 70 from waeup.kofa.mandates.mandate import PasswordMandate 67 71 68 72 grok.context(IKofaObject) # Make IKofaObject the default context … … 91 95 return 92 96 97 def translated_values(view): 98 lang = view.request.cookies.get('kofa.language') 99 for value in view.context.values(): 100 value_dict = dict([i for i in value.__dict__.items()]) 101 value_dict['mandatory_bool'] = value.mandatory 102 value_dict['mandatory'] = translate(str(value.mandatory), 'zope', 103 target_language=lang) 104 value_dict['carry_over'] = translate(str(value.carry_over), 'zope', 105 target_language=lang) 106 value_dict['automatic'] = translate(str(value.automatic), 'zope', 107 target_language=lang) 108 yield value_dict 109 93 110 class StudentsBreadcrumb(Breadcrumb): 94 111 """A breadcrumb for the students container. … … 116 133 """ 117 134 grok.context(IStudentStudyCourse) 118 title = _('Study Course') 135 136 def title(self): 137 if self.context.is_current: 138 return _('Study Course') 139 else: 140 return _('Previous Study Course') 119 141 120 142 class PaymentsBreadcrumb(Breadcrumb): … … 279 301 grok.require('waeup.viewStudent') 280 302 grok.template('basepage') 281 form_fields = grok.AutoFields(IStudentBase).omit('password' )303 form_fields = grok.AutoFields(IStudentBase).omit('password', 'suspended') 282 304 pnav = 4 283 305 284 306 @property 285 307 def label(self): 286 return _('${a}: Base Data', 308 if self.context.suspended: 309 return _('${a}: Base Data (account deactivated)', 310 mapping = {'a':self.context.display_fullname}) 311 return _('${a}: Base Data', 287 312 mapping = {'a':self.context.display_fullname}) 288 313 … … 292 317 return _('set') 293 318 return _('unset') 319 320 class StudentBasePDFFormPage(KofaDisplayFormPage): 321 """ Page to display student base data in pdf files. 322 """ 323 form_fields = grok.AutoFields(IStudentBase).omit( 324 'password', 'suspended', 'phone', 'adm_code', 'sex') 294 325 295 326 class ContactStudentForm(ContactAdminForm): … … 330 361 return 331 362 363 class ExportPDFAdmissionSlipPage(UtilityView, grok.View): 364 """Deliver a PDF Admission slip. 365 """ 366 grok.context(IStudent) 367 grok.name('admission_slip.pdf') 368 grok.require('waeup.viewStudent') 369 prefix = 'form' 370 371 form_fields = grok.AutoFields(IStudentBase).select('student_id', 'reg_number') 372 373 @property 374 def label(self): 375 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 376 return translate(_('Admission Letter of'), 377 'waeup.kofa', target_language=portal_language) \ 378 + ' %s' % self.context.display_fullname 379 380 def render(self): 381 students_utils = getUtility(IStudentsUtils) 382 return students_utils.renderPDFAdmissionLetter(self, 383 self.context.student) 384 332 385 class StudentBaseManageFormPage(KofaEditFormPage): 333 386 """ View to manage student base data … … 336 389 grok.name('manage_base') 337 390 grok.require('waeup.manageStudent') 338 form_fields = grok.AutoFields(IStudentBase).omit('student_id') 391 form_fields = grok.AutoFields(IStudentBase).omit( 392 'student_id', 'adm_code', 'suspended') 339 393 grok.template('basemanagepage') 340 394 label = _('Manage base data') … … 362 416 allowed_transitions = [t for t in self.wf_info.getManualTransitions() 363 417 if not t[0].startswith('pay')] 418 if self.context.is_postgrad: 419 allowed_transitions = [t for t in allowed_transitions 420 if not t[0] in FORBIDDEN_POSTGRAD_TRANS] 364 421 return [dict(name='', title=_('No transition'))] +[ 365 422 dict(name=x, title=y) for x, y in allowed_transitions] … … 396 453 return 397 454 455 class StudentActivatePage(UtilityView, grok.View): 456 """ Activate student account 457 """ 458 grok.context(IStudent) 459 grok.name('activate') 460 grok.require('waeup.manageStudent') 461 462 def update(self): 463 self.context.suspended = False 464 self.context.writeLogMessage(self, 'account activated') 465 history = IObjectHistory(self.context) 466 history.addMessage('Student account activated') 467 self.flash(_('Student account has been activated.')) 468 self.redirect(self.url(self.context)) 469 return 470 471 def render(self): 472 return 473 474 class StudentDeactivatePage(UtilityView, grok.View): 475 """ Deactivate student account 476 """ 477 grok.context(IStudent) 478 grok.name('deactivate') 479 grok.require('waeup.manageStudent') 480 481 def update(self): 482 self.context.suspended = True 483 self.context.writeLogMessage(self, 'account deactivated') 484 history = IObjectHistory(self.context) 485 history.addMessage('Student account deactivated') 486 self.flash(_('Student account has been deactivated.')) 487 self.redirect(self.url(self.context)) 488 return 489 490 def render(self): 491 return 492 398 493 class StudentClearanceDisplayFormPage(KofaDisplayFormPage): 399 494 """ Page to display student clearance data … … 411 506 def form_fields(self): 412 507 if self.context.is_postgrad: 413 form_fields = grok.AutoFields(IPGStudentClearance).omit('clearance_locked') 414 else: 415 form_fields = grok.AutoFields(IUGStudentClearance).omit('clearance_locked') 508 form_fields = grok.AutoFields( 509 IPGStudentClearance).omit('clearance_locked') 510 else: 511 form_fields = grok.AutoFields( 512 IUGStudentClearance).omit('clearance_locked') 416 513 return form_fields 417 514 … … 432 529 def form_fields(self): 433 530 if self.context.is_postgrad: 434 form_fields = grok.AutoFields(IPGStudentClearance).omit('clearance_locked') 435 else: 436 form_fields = grok.AutoFields(IUGStudentClearance).omit('clearance_locked') 531 form_fields = grok.AutoFields( 532 IPGStudentClearance).omit('clearance_locked') 533 else: 534 form_fields = grok.AutoFields( 535 IUGStudentClearance).omit('clearance_locked') 437 536 return form_fields 438 537 … … 446 545 def label(self): 447 546 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 448 return translate(_('Clearance Slip of 547 return translate(_('Clearance Slip of'), 449 548 'waeup.kofa', target_language=portal_language) \ 450 549 + ' %s' % self.context.display_fullname 451 550 551 def _signatures(self): 552 if self.context.state == CLEARED: 553 return (_('Student Signature'), _('Clearance Officer Signature')) 554 return 555 452 556 def render(self): 453 studentview = StudentBase DisplayFormPage(self.context.student,557 studentview = StudentBasePDFFormPage(self.context.student, 454 558 self.request) 455 559 students_utils = getUtility(IStudentsUtils) 456 560 return students_utils.renderPDF( 457 561 self, 'clearance.pdf', 458 self.context.student, studentview )562 self.context.student, studentview, signatures=self._signatures()) 459 563 460 564 class StudentClearanceManageFormPage(KofaEditFormPage): … … 475 579 def form_fields(self): 476 580 if self.context.is_postgrad: 477 form_fields = grok.AutoFields(IPGStudentClearance) 478 else: 479 form_fields = grok.AutoFields(IUGStudentClearance) 581 form_fields = grok.AutoFields(IPGStudentClearance).omit('clr_code') 582 else: 583 form_fields = grok.AutoFields(IUGStudentClearance).omit('clr_code') 480 584 return form_fields 481 585 … … 557 661 mapping = {'a':self.context.display_fullname}) 558 662 559 class StudentPersonal EditFormPage(KofaEditFormPage):560 """ Page to editpersonal data663 class StudentPersonalManageFormPage(KofaEditFormPage): 664 """ Page to manage personal data 561 665 """ 562 666 grok.context(IStudent) 563 grok.name(' edit_personal')564 grok.require('waeup. handleStudent')667 grok.name('manage_personal') 668 grok.require('waeup.manageStudent') 565 669 form_fields = grok.AutoFields(IStudentPersonal) 566 label = _(' Editpersonal data')670 label = _('Manage personal data') 567 671 pnav = 4 568 672 … … 572 676 return 573 677 678 class StudentPersonalEditFormPage(StudentPersonalManageFormPage): 679 """ Page to edit personal data 680 """ 681 grok.name('edit_personal') 682 grok.require('waeup.handleStudent') 683 label = _('Edit personal data') 684 pnav = 4 685 574 686 class StudyCourseDisplayFormPage(KofaDisplayFormPage): 575 687 """ Page to display the student study course data … … 578 690 grok.name('index') 579 691 grok.require('waeup.viewStudent') 580 form_fields = grok.AutoFields(IStudentStudyCourse)581 692 grok.template('studycoursepage') 582 693 pnav = 4 583 694 584 695 @property 696 def form_fields(self): 697 if self.context.is_postgrad: 698 form_fields = grok.AutoFields(IStudentStudyCourse).omit( 699 'current_verdict', 'previous_verdict') 700 else: 701 form_fields = grok.AutoFields(IStudentStudyCourse) 702 return form_fields 703 704 @property 585 705 def label(self): 586 return _('${a}: Study Course', 587 mapping = {'a':self.context.__parent__.display_fullname}) 706 if self.context.is_current: 707 return _('${a}: Study Course', 708 mapping = {'a':self.context.__parent__.display_fullname}) 709 else: 710 return _('${a}: Previous Study Course', 711 mapping = {'a':self.context.__parent__.display_fullname}) 588 712 589 713 @property … … 606 730 return 607 731 732 @property 733 def prev_studycourses(self): 734 if self.context.is_current: 735 if self.context.__parent__.get('studycourse_2', None) is not None: 736 return ( 737 {'href':self.url(self.context.student) + '/studycourse_1', 738 'title':_('First Study Course, ')}, 739 {'href':self.url(self.context.student) + '/studycourse_2', 740 'title':_('Second Study Course')} 741 ) 742 if self.context.__parent__.get('studycourse_1', None) is not None: 743 return ( 744 {'href':self.url(self.context.student) + '/studycourse_1', 745 'title':_('First Study Course')}, 746 ) 747 return 748 608 749 class StudyCourseManageFormPage(KofaEditFormPage): 609 750 """ Page to edit the student study course data … … 613 754 grok.require('waeup.manageStudent') 614 755 grok.template('studycoursemanagepage') 615 form_fields = grok.AutoFields(IStudentStudyCourse)616 756 label = _('Manage study course') 617 757 pnav = 4 … … 620 760 tabthreeactions = [_('Add study level')] 621 761 762 @property 763 def form_fields(self): 764 if self.context.is_postgrad: 765 form_fields = grok.AutoFields(IStudentStudyCourse).omit( 766 'current_verdict', 'previous_verdict') 767 else: 768 form_fields = grok.AutoFields(IStudentStudyCourse) 769 return form_fields 770 622 771 def update(self): 772 if not self.context.is_current: 773 emit_lock_message(self) 774 return 623 775 super(StudyCourseManageFormPage, self).update() 624 776 tabs.need() … … 685 837 return 686 838 839 class StudentTransferFormPage(KofaAddFormPage): 840 """Page to transfer the student. 841 """ 842 grok.context(IStudent) 843 grok.name('transfer') 844 grok.require('waeup.manageStudent') 845 label = _('Transfer student') 846 form_fields = grok.AutoFields(IStudentStudyCourseTransfer).omit( 847 'entry_mode', 'entry_session') 848 pnav = 4 849 850 def update(self): 851 super(StudentTransferFormPage, self).update() 852 warning.need() 853 return 854 855 @jsaction(_('Transfer')) 856 def transferStudent(self, **data): 857 error = self.context.transfer(**data) 858 if error == -1: 859 self.flash(_('Current level does not match certificate levels.')) 860 elif error == -2: 861 self.flash(_('Former study course record incomplete.')) 862 elif error == -3: 863 self.flash(_('Maximum number of transfers exceeded.')) 864 else: 865 self.flash(_('Successfully transferred.')) 866 return 867 687 868 class StudyLevelDisplayFormPage(KofaDisplayFormPage): 688 869 """ Page to display student study levels … … 692 873 grok.require('waeup.viewStudent') 693 874 form_fields = grok.AutoFields(IStudentStudyLevel) 875 form_fields[ 876 'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') 694 877 grok.template('studylevelpage') 695 878 pnav = 4 … … 702 885 @property 703 886 def translated_values(self): 704 lang = self.request.cookies.get('kofa.language') 705 for value in self.context.values(): 706 value_dict = dict([i for i in value.__dict__.items()]) 707 value_dict['mandatory'] = translate(str(value.mandatory), 'zope', 708 target_language=lang) 709 value_dict['carry_over'] = translate(str(value.carry_over), 'zope', 710 target_language=lang) 711 value_dict['automatic'] = translate(str(value.automatic), 'zope', 712 target_language=lang) 713 yield value_dict 887 return translated_values(self) 714 888 715 889 @property … … 771 945 Mand = translate(_('Mand.'), 'waeup.kofa', target_language=portal_language) 772 946 Score = translate(_('Score'), 'waeup.kofa', target_language=portal_language) 773 studentview = StudentBase DisplayFormPage(self.context.student,947 studentview = StudentBasePDFFormPage(self.context.student, 774 948 self.request) 775 949 students_utils = getUtility(IStudentsUtils) … … 796 970 grok.require('waeup.manageStudent') 797 971 grok.template('studylevelmanagepage') 798 form_fields = grok.AutoFields(IStudentStudyLevel) 972 form_fields = grok.AutoFields(IStudentStudyLevel).omit( 973 'validation_date', 'validated_by') 799 974 pnav = 4 800 975 taboneactions = [_('Save'),_('Cancel')] … … 803 978 804 979 def update(self): 980 if not self.context.__parent__.is_current: 981 emit_lock_message(self) 982 return 805 983 super(StudyLevelManageFormPage, self).update() 806 984 tabs.need() … … 813 991 datatable.need() 814 992 return 993 994 @property 995 def translated_values(self): 996 return translated_values(self) 815 997 816 998 @property … … 861 1043 862 1044 def update(self): 1045 if not self.context.__parent__.is_current: 1046 emit_lock_message(self) 1047 return 863 1048 if str(self.context.__parent__.current_level) != self.context.__name__: 864 1049 self.flash(_('This level does not correspond current level.')) … … 883 1068 884 1069 def update(self): 1070 if not self.context.__parent__.is_current: 1071 emit_lock_message(self) 1072 return 885 1073 if str(self.context.__parent__.current_level) != self.context.__name__: 886 1074 self.flash(_('This level does not correspond current level.')) … … 918 1106 pnav = 4 919 1107 1108 def update(self): 1109 if not self.context.__parent__.is_current: 1110 emit_lock_message(self) 1111 return 1112 super(CourseTicketAddFormPage, self).update() 1113 return 1114 920 1115 @action(_('Add course ticket')) 921 1116 def addCourseTicket(self, **data): … … 924 1119 ticket.automatic = False 925 1120 ticket.carry_over = False 926 ticket.code = course.code927 ticket.title = course.title928 ticket.fcode = course.__parent__.__parent__.__parent__.code929 ticket.dcode = course.__parent__.__parent__.code930 ticket.credits = course.credits931 ticket.passmark = course.passmark932 ticket.semester = course.semester933 1121 try: 934 self.context.addCourseTicket(ticket )1122 self.context.addCourseTicket(ticket, course) 935 1123 except KeyError: 936 1124 self.flash(_('The ticket exists.')) … … 1030 1218 mapping = {'a': ', '.join(deleted)})) 1031 1219 self.context.writeLogMessage( 1032 self,'removed: % 1220 self,'removed: %s' % ', '.join(deleted)) 1033 1221 self.redirect(self.url(self.context)) 1034 1222 return … … 1052 1240 def createTicket(self, **data): 1053 1241 p_category = data['p_category'] 1242 previous_session = data.get('p_session', None) 1243 previous_level = data.get('p_level', None) 1054 1244 student = self.context.__parent__ 1055 1245 if p_category == 'bed_allocation' and student[ … … 1062 1252 return 1063 1253 students_utils = getUtility(IStudentsUtils) 1064 error, payment = students_utils.setPaymentDetails(p_category, student) 1254 error, payment = students_utils.setPaymentDetails( 1255 p_category, student, previous_session, previous_level) 1065 1256 if error is not None: 1066 1257 self.flash(error) 1258 if 'previous session' in error: 1259 self.redirect(self.url(self.context) + '/@@addpp') 1260 return 1067 1261 self.redirect(self.url(self.context)) 1068 1262 return … … 1071 1265 self.redirect(self.url(self.context)) 1072 1266 return 1267 1268 class PreviousPaymentAddFormPage(OnlinePaymentAddFormPage): 1269 """ Page to add an online payment ticket for previous sessions 1270 """ 1271 grok.context(IStudentPaymentsContainer) 1272 grok.name('addpp') 1273 grok.require('waeup.payStudent') 1274 form_fields = grok.AutoFields(IStudentPreviousPayment).select( 1275 'p_category', 'p_session', 'p_level') 1276 label = _('Add previous session online payment') 1277 pnav = 4 1073 1278 1074 1279 class OnlinePaymentDisplayFormPage(KofaDisplayFormPage): … … 1149 1354 # self.redirect(self.url(self.context)) 1150 1355 # return 1151 studentview = StudentBase DisplayFormPage(self.context.student,1356 studentview = StudentBasePDFFormPage(self.context.student, 1152 1357 self.request) 1153 1358 students_utils = getUtility(IStudentsUtils) … … 1226 1431 buttonname = _('Create bed ticket') 1227 1432 notice = '' 1433 with_ac = True 1228 1434 1229 1435 def update(self, SUBMIT=None): … … 1266 1472 self.redirect(self.url(self.context)) 1267 1473 return 1268 self.ac_series = self.request.form.get('ac_series', None) 1269 self.ac_number = self.request.form.get('ac_number', None) 1474 if self.with_ac: 1475 self.ac_series = self.request.form.get('ac_series', None) 1476 self.ac_number = self.request.form.get('ac_number', None) 1270 1477 if SUBMIT is None: 1271 1478 return 1272 pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) 1273 code = get_access_code(pin) 1274 if not code: 1275 self.flash(_('Activation code is invalid.')) 1276 return 1479 if self.with_ac: 1480 pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) 1481 code = get_access_code(pin) 1482 if not code: 1483 self.flash(_('Activation code is invalid.')) 1484 return 1277 1485 # Search and book bed 1278 1486 cat = queryUtility(ICatalog, name='beds_catalog', default=None) … … 1280 1488 owner=(student.student_id,student.student_id)) 1281 1489 if len(entries): 1282 # If bed space has bee manually allocated use this bed1490 # If bed space has been manually allocated use this bed 1283 1491 bed = [entry for entry in entries][0] 1284 1492 else: … … 1296 1504 mapping = {'a':acc_details['bt']})) 1297 1505 return 1298 # Mark pin as used (this also fires a pin related transition) 1299 if code.state == USED: 1300 self.flash(_('Activation code has already been used.')) 1301 return 1302 else: 1303 comment = _(u'invalidated') 1304 # Here we know that the ac is in state initialized so we do not 1305 # expect an exception, but the owner might be different 1306 if not invalidate_accesscode( 1307 pin,comment,self.context.student.student_id): 1308 self.flash(_('You are not the owner of this access code.')) 1506 if self.with_ac: 1507 # Mark pin as used (this also fires a pin related transition) 1508 if code.state == USED: 1509 self.flash(_('Activation code has already been used.')) 1309 1510 return 1511 else: 1512 comment = _(u'invalidated') 1513 # Here we know that the ac is in state initialized so we do not 1514 # expect an exception, but the owner might be different 1515 if not invalidate_accesscode( 1516 pin,comment,self.context.student.student_id): 1517 self.flash(_('You are not the owner of this access code.')) 1518 return 1310 1519 # Create bed ticket 1311 1520 bedticket = createObject(u'waeup.BedTicket') 1312 bedticket.booking_code = pin 1521 if self.with_ac: 1522 bedticket.booking_code = pin 1313 1523 bedticket.booking_session = acc_details['booking_session'] 1314 1524 bedticket.bed_type = acc_details['bt'] 1315 1525 bedticket.bed = bed 1316 1526 hall_title = bed.__parent__.hostel_name 1317 coordinates = bed. getBedCoordinates()[1:]1527 coordinates = bed.coordinates[1:] 1318 1528 block, room_nr, bed_nr = coordinates 1319 1529 bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = { … … 1338 1548 grok.require('waeup.handleAccommodation') 1339 1549 form_fields = grok.AutoFields(IBedTicket) 1550 form_fields['booking_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') 1340 1551 pnav = 4 1341 1552 … … 1364 1575 def label(self): 1365 1576 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 1366 return translate(_('Bed Allocation: '), 1577 #return translate(_('Bed Allocation: '), 1578 # 'waeup.kofa', target_language=portal_language) \ 1579 # + ' %s' % self.context.bed_coordinates 1580 return translate(_('Bed Allocation Slip'), 1367 1581 'waeup.kofa', target_language=portal_language) \ 1368 + ' %s' % self.context. bed_coordinates1582 + ' %s' % self.context.getSessionString() 1369 1583 1370 1584 def render(self): 1371 studentview = StudentBase DisplayFormPage(self.context.student,1585 studentview = StudentBasePDFFormPage(self.context.student, 1372 1586 self.request) 1373 1587 students_utils = getUtility(IStudentsUtils) … … 1431 1645 self.context.bed = new_bed 1432 1646 hall_title = new_bed.__parent__.hostel_name 1433 coordinates = new_bed. getBedCoordinates()[1:]1647 coordinates = new_bed.coordinates[1:] 1434 1648 block, room_nr, bed_nr = coordinates 1435 1649 bc = _('${a}, Block ${b}, Room ${c}, Bed ${d} (${e})', mapping = { … … 1569 1783 self.flash(_('Activation code is invalid.')) 1570 1784 return 1785 if code.state == USED: 1786 self.flash(_('Activation code has already been used.')) 1787 return 1571 1788 # Mark pin as used (this also fires a pin related transition) 1572 1789 # and fire transition start_clearance 1573 if code.state == USED: 1574 self.flash(_('Activation code has already been used.')) 1575 return 1576 else: 1577 comment = _(u"invalidated") 1578 # Here we know that the ac is in state initialized so we do not 1579 # expect an exception, but the owner might be different 1580 if not invalidate_accesscode(pin,comment,self.context.student_id): 1581 self.flash(_('You are not the owner of this access code.')) 1582 return 1583 self.context.clr_code = pin 1790 comment = _(u"invalidated") 1791 # Here we know that the ac is in state initialized so we do not 1792 # expect an exception, but the owner might be different 1793 if not invalidate_accesscode(pin, comment, self.context.student_id): 1794 self.flash(_('You are not the owner of this access code.')) 1795 return 1796 self.context.clr_code = pin 1584 1797 IWorkflowInfo(self.context).fireTransition('start_clearance') 1585 1798 self.flash(_('Clearance process has been started.')) … … 1598 1811 def form_fields(self): 1599 1812 if self.context.is_postgrad: 1600 form_fields = grok.AutoFields(IPGStudentClearance).omit('clearance_locked') 1601 else: 1602 form_fields = grok.AutoFields(IUGStudentClearance).omit('clearance_locked') 1813 form_fields = grok.AutoFields(IPGStudentClearance).omit( 1814 'clearance_locked', 'clr_code') 1815 else: 1816 form_fields = grok.AutoFields(IUGStudentClearance).omit( 1817 'clearance_locked', 'clr_code') 1603 1818 return form_fields 1604 1819 … … 1627 1842 return 1628 1843 self.flash(_('Clearance form has been saved.')) 1629 self.redirect(self.url(self.context,'request_clearance')) 1844 if self.context.clr_code: 1845 self.redirect(self.url(self.context, 'request_clearance')) 1846 else: 1847 # We bypass the request_clearance page if student 1848 # has been imported in state 'clearance started' and 1849 # no clr_code was entered before. 1850 state = IWorkflowState(self.context).getState() 1851 if state != CLEARANCE: 1852 # This shouldn't happen, but the application officer 1853 # might have forgotten to lock the form after changing the state 1854 self.flash(_('This form cannot be submitted. Wrong state!')) 1855 return 1856 IWorkflowInfo(self.context).fireTransition('request_clearance') 1857 self.flash(_('Clearance has been requested.')) 1858 self.redirect(self.url(self.context)) 1630 1859 return 1631 1860 … … 1647 1876 return 1648 1877 pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) 1649 if self.context.clr_code != pin:1878 if self.context.clr_code and self.context.clr_code != pin: 1650 1879 self.flash(_("This isn't your CLR access code.")) 1651 1880 return 1652 1881 state = IWorkflowState(self.context).getState() 1653 # This shouldn't happen, but the application officer1654 # might have forgotten to lock the form after changing the state1655 1882 if state != CLEARANCE: 1883 # This shouldn't happen, but the application officer 1884 # might have forgotten to lock the form after changing the state 1656 1885 self.flash(_('This form cannot be submitted. Wrong state!')) 1657 1886 return … … 1673 1902 1674 1903 def update(self, SUBMIT=None): 1904 if not self.context.is_current: 1905 emit_lock_message(self) 1906 return 1907 super(StartSessionPage, self).update() 1675 1908 if not self.context.next_session_allowed: 1676 1909 self.flash(_("You are not entitled to start session.")) … … 1730 1963 1731 1964 def update(self): 1965 if not self.context.is_current: 1966 emit_lock_message(self) 1967 return 1732 1968 if self.context.student.state != PAID: 1733 1969 emit_lock_message(self) … … 1762 1998 1763 1999 def update(self): 2000 if not self.context.__parent__.is_current: 2001 emit_lock_message(self) 2002 return 1764 2003 if self.context.student.state != PAID: 1765 2004 emit_lock_message(self) … … 1776 2015 level_title = translate(self.context.level_title, 'waeup.kofa', 1777 2016 target_language=lang) 1778 return _(' Add and remove course tickets of study level${a}',2017 return _('Edit course list of ${a}', 1779 2018 mapping = {'a':level_title}) 1780 2019 … … 1785 2024 total_credits += val.credits 1786 2025 return total_credits 2026 2027 @property 2028 def translated_values(self): 2029 return translated_values(self) 1787 2030 1788 2031 @action(_('Add course ticket')) … … 1847 2090 ticket = createObject(u'waeup.CourseTicket') 1848 2091 course = data['course'] 1849 for name in ['code', 'title', 'credits', 'passmark', 'semester']:1850 setattr(ticket, name, getattr(course, name))1851 2092 ticket.automatic = False 2093 ticket.carry_over = False 1852 2094 try: 1853 self.context.addCourseTicket(ticket )2095 self.context.addCourseTicket(ticket, course) 1854 2096 except KeyError: 1855 2097 self.flash(_('The ticket exists.')) … … 1927 2169 grok.template('requestpw') 1928 2170 form_fields = grok.AutoFields(IStudentRequestPW).select( 1929 'firstname',' reg_number','email')2171 'firstname','number','email') 1930 2172 label = _('Request password for first-time login') 1931 2173 … … 1948 2190 return True 1949 2191 1950 @action(_(' Get login credentials'), style='primary')2192 @action(_('Send login credentials to email address'), style='primary') 1951 2193 def get_credentials(self, **data): 1952 2194 if not self.captcha_result.is_valid: … … 1954 2196 # No need to flash something. 1955 2197 return 1956 reg_number = data.get('reg_number','')2198 number = data.get('number','') 1957 2199 firstname = data.get('firstname','') 1958 2200 cat = getUtility(ICatalog, name='students_catalog') 1959 2201 results = list( 1960 cat.searchResults(reg_number=(reg_number, reg_number))) 2202 cat.searchResults(reg_number=(number, number))) 2203 if not results: 2204 results = list( 2205 cat.searchResults(matric_number=(number, number))) 1961 2206 if results: 1962 2207 student = results[0] … … 1984 2229 kofa_utils = getUtility(IKofaUtils) 1985 2230 password = kofa_utils.genPassword() 1986 IUserAccount(student).setPassword(password) 2231 mandate = PasswordMandate() 2232 mandate.params['password'] = password 2233 mandate.params['user'] = student 2234 site = grok.getSite() 2235 site['mandates'].addMandate(mandate) 1987 2236 # Send email with credentials 1988 login_url = self.url(grok.getSite(), 'login') 2237 args = {'mandate_id':mandate.mandate_id} 2238 mandate_url = self.url(site) + '/mandate?%s' % urlencode(args) 2239 url_info = u'Confirmation link: %s' % mandate_url 1989 2240 msg = _('You have successfully requested a password for the') 1990 2241 if kofa_utils.sendCredentials(IUserAccount(student), 1991 password, login_url, msg):2242 password, url_info, msg): 1992 2243 email_sent = student.email 1993 2244 else: … … 1995 2246 self._redirect(email=email_sent, password=password, 1996 2247 student_id=student.student_id) 2248 ob_class = self.__implemented__.__name__.replace('waeup.kofa.','') 2249 self.context.logger.info( 2250 '%s - %s (%s) - %s' % (ob_class, number, student.student_id, email_sent)) 1997 2251 return 1998 2252 -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser_templates/basepage.pt
r7811 r9209 18 18 <tal:password replace="view/hasPassword" /> 19 19 </td> 20 < tr>20 </tr> 21 21 <tal:files content="structure provider:files" /> 22 22 </tbody> -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser_templates/requestpw.pt
r8782 r9209 17 17 </table> 18 18 <p i18n:translate=""> 19 Your student record will be looked up and an email with your login 20 credentials will be sent to the address provided. 19 Your student record will be looked up and 20 your login credentials will be sent to the email address given above. 21 To be able to proceed you must provide a valid email address! 21 22 </p> 22 23 <div tal:condition="view/availableActions"> -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser_templates/requestpwmailsent.pt
r8782 r9209 29 29 <tr> 30 30 </table> 31 <p i18n:translate="">32 Print this page and proceed to the31 <p> 32 <span i18n:translate="">Print this page and proceed to the</span> 33 33 <a tal:attributes="href python: view.url(layout.site, 'login')">login form</a>. 34 Please note that passwords are case-sensitive,34 <span i18n:translate="">Please note that passwords are case-sensitive, 35 35 <br />when entering your credentials, and keep your password secret! 36 </span> 36 37 </p> 37 38 <p tal:condition = "view/email"> -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser_templates/studycoursepage.pt
r7876 r9209 47 47 </td> 48 48 </tr> 49 <tr tal:condition="view/prev_studycourses"> 50 <td i18n:translate=""> 51 Previous Study Courses: 52 </td> 53 <td> 54 <span tal:repeat="value view/prev_studycourses"> 55 <a tal:attributes="href value/href" tal:content="value/title"> 56 FACULTY 57 </a> 58 </span> 59 </td> 60 </tr> 49 61 </tbody> 50 62 </table> -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser_templates/studyleveleditpage.pt
r8553 r9209 22 22 </thead> 23 23 <tbody> 24 <tr tal:repeat="value context/values" class="gradeC">24 <tr tal:repeat="value view/translated_values" class="gradeC"> 25 25 <td> 26 26 <input type="checkbox" name="val_id" 27 27 tal:attributes="value value/__name__" 28 tal:condition="not: value/mandatory " />28 tal:condition="not: value/mandatory_bool" /> 29 29 </td> 30 30 <td tal:content="value/semester">SEMESTER</td> … … 36 36 <td tal:content="value/fcode">FACULTY</td> 37 37 <td tal:content="value/credits">CREDITS</td> 38 <td tal:content="value/score ">SCORE</td>38 <td tal:content="value/score|nothing">SCORE</td> 39 39 <td tal:content="value/carry_over">CO</td> 40 40 </tr> -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser_templates/studylevelmanagepage.pt
r7811 r9209 46 46 </thead> 47 47 <tbody> 48 <tr tal:repeat="value context/values" class="gradeC">48 <tr tal:repeat="value view/translated_values" class="gradeC"> 49 49 <td> 50 50 <input type="checkbox" name="val_id" … … 60 60 <td tal:content="value/credits">CREDITS</td> 61 61 <td tal:content="value/mandatory">MANDATORY</td> 62 <td tal:content="value/score ">SCORE</td>62 <td tal:content="value/score|nothing">SCORE</td> 63 63 <td tal:content="value/carry_over">CO</td> 64 64 <td tal:content="value/automatic">AUTO</td> -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/browser_templates/studylevelpage.pt
r8141 r9209 34 34 <th i18n:translate="">Code</th> 35 35 <th i18n:translate="">Title</th> 36 <th i18n:translate="">Dep t.</th>37 <th i18n:translate="">Fac t.</th>36 <th i18n:translate="">Dep.</th> 37 <th i18n:translate="">Fac.</th> 38 38 <th i18n:translate="">Cred.</th> 39 39 <th i18n:translate="">Mand.</th> -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/dynamicroles.py
r7811 r9209 51 51 """Get roles for principal with id `principal_id`. 52 52 53 Different to the default implementation, this method also 54 takes into account local roles set on any department connected 55 to the context student. 56 57 If the given principal has at least one of the 58 `external_rolenames` roles granted for the external object, it 59 additionally gets `additional_rolename` role for the context 60 student. 61 62 For the additional roles the `extra_attrib` and all its parent 63 objects are looked up, because 'role inheritance' does not 64 work on that basic level of permission handling. 65 66 Some advantages of this approach: 67 68 - we don't have to store extra local roles for clearance 69 officers in ZODB for each student 70 71 - when local roles on a department change, we don't have to 72 update thousands of students; the local role is assigned 73 dynamically. 74 75 Disadvantage: 76 77 - More expensive role lookups when a clearance officer wants 78 to see an student form. 79 80 This implementation is designed to be usable also for other 81 contexts than students. You can inherit from it and set 82 different role names to lookup/set easily via the static class 83 attributes. 53 See waeup.kofa.applicants.dynamicroles.ApplicantPrincipalRoleManager 54 for further information. 84 55 """ 85 56 apr_manager = AnnotationPrincipalRoleManager(self._context) … … 118 89 result.append( 119 90 ('waeup.StudentsOfficer', setting)) 91 elif 'UGClearanceOfficer' in role_id: 92 if not self._context.is_postgrad: 93 result.append( 94 ('waeup.StudentsClearanceOfficer', setting)) 95 else: 96 # Otherwise grant at least view permissions. 97 result.append( 98 ('waeup.StudentsOfficer', setting)) 99 elif 'PGClearanceOfficer' in role_id: 100 if self._context.is_postgrad: 101 result.append( 102 ('waeup.StudentsClearanceOfficer', setting)) 103 else: 104 # Otherwise grant at least view permissions. 105 result.append( 106 ('waeup.StudentsOfficer', setting)) 120 107 elif role_id in self.rolename_mapping.keys(): 121 108 # Grant additional role -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/export.py
r8736 r9209 113 113 if name == 'history': 114 114 value = value.messages 115 if name == 'phone' and value is not None: 116 # Append hash '#' to phone numbers to circumvent 117 # unwanted excel automatic 118 value = str('%s#' % value) 115 119 return super( 116 120 StudentsExporter, self).mangle_value( -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/interfaces.py
r9004 r9209 43 43 def getValues(self, context): 44 44 verdicts_dict = getUtility(IStudentsUtils).VERDICTS_DICT 45 return verdicts_dict.keys()45 return sorted(verdicts_dict.keys()) 46 46 47 47 def getToken(self, context, value): … … 50 50 def getTitle(self, context, value): 51 51 verdicts_dict = getUtility(IStudentsUtils).VERDICTS_DICT 52 if value != '0': 53 return verdicts_dict[value] + ' (%s)' % value 52 54 return verdicts_dict[value] 53 55 … … 65 67 """ 66 68 67 def setPaymentDetails(category, student): 69 def setPaymentDetails(category, student, previous_session=None, 70 previous_level=None,): 68 71 """Create Payment object and set the payment data of a student for 69 72 the payment category specified. … … 151 154 faccode = Attribute('The faculty code of any chosen study course') 152 155 current_session = Attribute('The current session of the student') 156 current_level = Attribute('The current level of the student') 153 157 current_mode = Attribute('The current mode of the student') 158 current_verdict = Attribute('The current verdict of the student') 154 159 fullname = Attribute('All name parts separated by hyphens') 155 160 display_fullname = Attribute('The fullname of an applicant') 161 is_postgrad = Attribute('True if postgraduate student') 162 163 suspended = schema.Bool( 164 title = _(u'Account suspended'), 165 default = False, 166 required = False, 167 ) 156 168 157 169 student_id = schema.TextLine( … … 198 210 title = _(u'PWD Activation Code'), 199 211 required = False, 200 readonly = True,212 readonly = False, 201 213 ) 202 214 … … 212 224 ) 213 225 226 def transfer(certificate, current_session, 227 current_level, current_verdict): 228 """ Creates a new studycourse and backups the old one. 229 230 """ 231 214 232 class IUGStudentClearance(IKofaObject): 215 233 """Representation of undergraduate student clearance data. … … 225 243 title = _(u'Clearance form locked'), 226 244 default = False, 245 required = False, 227 246 ) 228 247 … … 230 249 title = _(u'CLR Activation Code'), 231 250 required = False, 232 readonly = True,251 readonly = False, 233 252 ) 234 253 … … 288 307 login for the the first time. 289 308 """ 290 reg_number = schema.TextLine(291 title = u'Registration Number',309 number = schema.TextLine( 310 title = _(u'Registr. or Matric. Number'), 292 311 required = True, 293 312 ) … … 346 365 title = _(u'Current Verdict'), 347 366 source = VerdictSource(), 348 default = ' NY',367 default = '0', 349 368 required = False, 350 369 ) … … 353 372 title = _(u'Previous Verdict'), 354 373 source = VerdictSource(), 355 default = 'NY', 356 required = False, 357 ) 374 default = '0', 375 required = False, 376 ) 377 378 class IStudentStudyCourseTransfer(IStudentStudyCourse): 379 """An student transfers. 380 381 """ 382 383 certificate = schema.Choice( 384 title = _(u'Certificate'), 385 source = CertificateSource(), 386 required = True, 387 ) 388 389 current_level = schema.Choice( 390 title = _(u'Current Level'), 391 source = StudyLevelSource(), 392 required = True, 393 readonly = False, 394 ) 395 396 397 IStudentStudyCourseTransfer['certificate'].order = IStudentStudyCourse[ 398 'certificate'].order 399 IStudentStudyCourseTransfer['current_level'].order = IStudentStudyCourse[ 400 'current_level'].order 358 401 359 402 class IStudentVerdictUpdate(IKofaObject): … … 385 428 """ 386 429 level = Attribute('The level code') 387 validation_date = Attribute('The date of validation')388 validated_by = Attribute('User Id of course adviser')389 430 390 431 level_session = schema.Choice( 391 432 title = _(u'Session'), 392 433 source = academic_sessions_vocab, 393 required = True,434 required = False, 394 435 ) 395 436 … … 397 438 title = _(u'Verdict'), 398 439 source = VerdictSource(), 399 default = 'NY', 400 required = False, 401 ) 402 403 def addCourseTicket(courseticket): 440 default = '0', 441 required = False, 442 ) 443 444 validated_by = schema.TextLine( 445 title = _(u'Validated by'), 446 default = None, 447 required = False, 448 ) 449 450 validation_date = schema.Datetime( 451 title = _(u'Validation Date'), 452 required = False, 453 readonly = False, 454 ) 455 456 def addCourseTicket(ticket, course): 404 457 """Add a course ticket object. 405 458 """ … … 520 573 """ 521 574 575 p_current = schema.Bool( 576 title = _(u'Current Session Payment'), 577 default = True, 578 required = False, 579 ) 580 522 581 p_level = schema.Int( 523 582 title = _(u'Payment Level'), … … 543 602 IStudentOnlinePayment['p_level'].order = IStudentOnlinePayment[ 544 603 'p_session'].order 604 605 class IStudentPreviousPayment(IOnlinePayment): 606 """An interface for adding previous session payments. 607 608 """ 609 610 p_session = schema.Choice( 611 title = _(u'Payment Session'), 612 source = academic_sessions_vocab, 613 required = True, 614 ) 615 616 p_level = schema.Choice( 617 title = _(u'Payment Level'), 618 source = StudyLevelSource(), 619 required = True, 620 ) 545 621 546 622 class ICSVStudentExporter(ICSVExporter): -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/payments.py
r8736 r9209 97 97 """Process student after payment was made. 98 98 """ 99 error = self._createActivationCodes() 100 if error is not None: 101 return False, error, error 99 if self.p_current: 100 error = self._createActivationCodes() 101 if error is not None: 102 return False, error, error 102 103 log = 'successful payment: %s' % self.p_id 103 104 msg = _('Successful payment') … … 107 108 """Process student after payment was approved. 108 109 """ 109 error = self._createActivationCodes() 110 if error is not None: 111 return False, error, error 110 if self.p_current: 111 error = self._createActivationCodes() 112 if error is not None: 113 return False, error, error 112 114 log = 'payment approved: %s' % self.p_id 113 115 msg = _('Payment approved') -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/student.py
r8737 r9209 28 28 from zope.interface import implementedBy 29 29 from zope.securitypolicy.interfaces import IPrincipalRoleManager 30 from zope.schema.interfaces import ConstraintNotSatisfied 30 31 31 32 from waeup.kofa.image import KofaImageFile … … 130 131 131 132 @property 133 def current_level(self): 134 level = getattr( 135 self.get('studycourse', None), 'current_level', None) 136 return level 137 138 @property 139 def current_verdict(self): 140 level = getattr( 141 self.get('studycourse', None), 'current_verdict', None) 142 return level 143 144 @property 132 145 def current_mode(self): 133 146 certificate = getattr( … … 142 155 self.get('studycourse', None), 'is_postgrad', False) 143 156 return is_postgrad 157 158 def transfer(self, certificate, current_session=None, 159 current_level=None, current_verdict=None, previous_verdict=None): 160 """ Creates a new studycourse and backups the old one. 161 162 """ 163 studycourse = createObject(u'waeup.StudentStudyCourse') 164 try: 165 studycourse.certificate = certificate 166 studycourse.entry_mode = 'transfer' 167 studycourse.current_session = current_session 168 studycourse.current_level = current_level 169 studycourse.current_verdict = current_verdict 170 studycourse.previous_verdict = previous_verdict 171 except ConstraintNotSatisfied: 172 return -1 173 old = self['studycourse'] 174 if getattr(old, 'entry_session', None) is None or\ 175 getattr(old, 'certificate', None) is None: 176 return -2 177 studycourse.entry_session = old.entry_session 178 # Students can be transferred only two times. 179 if 'studycourse_1' in self.keys(): 180 if 'studycourse_2' in self.keys(): 181 return -3 182 self['studycourse_2'] = old 183 else: 184 self['studycourse_1'] = old 185 del self['studycourse'] 186 self['studycourse'] = studycourse 187 self.__parent__.logger.info( 188 '%s - transferred from %s to %s' % ( 189 self.student_id, old.certificate.code, studycourse.certificate.code)) 190 history = IObjectHistory(self) 191 history.addMessage('Transferred from %s to %s' % ( 192 old.certificate.code, studycourse.certificate.code)) 193 return 144 194 145 195 -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/studycourse.py
r8736 r9209 61 61 @property 62 62 def is_postgrad(self): 63 if self.certificate is None: 64 return False 63 65 return self.certificate.study_mode.startswith('pg') 64 #return cert.start_level == 999 or cert.end_level == 999 66 67 @property 68 def is_current(self): 69 if '_' in self.__name__: 70 return False 71 return True 65 72 66 73 def addStudentStudyLevel(self, cert, studylevel): -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/studylevel.py
r8736 r9209 37 37 super(StudentStudyLevel, self).__init__() 38 38 self.level = None 39 self.validation_date = None40 self.validated_by = None41 39 return 42 40 … … 56 54 return studylevelsource.factory.getTitle(self.__parent__, self.level) 57 55 58 def addCourseTicket(self, courseticket):56 def addCourseTicket(self, ticket, course): 59 57 """Add a course ticket object. 60 58 """ 61 if not ICourseTicket.providedBy( courseticket):59 if not ICourseTicket.providedBy(ticket): 62 60 raise TypeError( 63 61 'StudentStudyLeves contain only ICourseTicket instances') 64 self[courseticket.code] = courseticket 62 ticket.code = course.code 63 ticket.title = course.title 64 ticket.fcode = course.__parent__.__parent__.__parent__.code 65 ticket.dcode = course.__parent__.__parent__.code 66 ticket.credits = course.credits 67 ticket.passmark = course.passmark 68 ticket.semester = course.semester 69 self[ticket.code] = ticket 65 70 return 66 71 … … 87 92 88 93 A course ticket contains a copy of the original course and 89 course referrer data. If the courses and/or their referrers are removed, the 90 corresponding tickets remain unchanged. So we do not need any event 94 certificate course data. If the courses and/or the referrin certificate 95 courses are removed, the corresponding tickets remain unchanged. 96 So we do not need any event 91 97 triggered actions on course tickets. 92 98 """ -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/tests/sample_payment_data.csv
r8244 r9209 1 matric_number,p_state,p_category,p_id,reg_number,session_id,r_amount_approved,p_item,amount_auth,r_card_num,r_code,creation_date,type 2 ,paid,schoolfee,3816951290797973744#,1,2010,19500,BTECHBDT,19500,0942,00,2010/11/26 19:59:33.744 GMT+1,online 3 100001,unpaid,schoolfee,3816951290712593757,,2010,0,BTECHBDT,19500,0942,Z0,2010-11-25 20:16:33.757 GMT+1,online4 ,paid,schoolfee,p1266236341955,3,2009,19500,BTECHBDT,19500,0615,00,2010/02/15 13:19:01,online 1 matric_number,p_state,p_category,p_id,reg_number,session_id,r_amount_approved,p_item,amount_auth,r_card_num,r_code,creation_date,type,p_current 2 ,paid,schoolfee,3816951290797973744#,1,2010,19500,BTECHBDT,19500,0942,00,2010/11/26 19:59:33.744 GMT+1,online,1 3 100001,unpaid,schoolfee,3816951290712593757,,2010,0,BTECHBDT,19500,0942,Z0,2010-11-25 20:16:33.757 WAT,online,0 4 ,paid,schoolfee,p1266236341955,3,2009,19500,BTECHBDT,19500,0615,00,2010/02/15 13:19:01,online,1 -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/tests/test_authentication.py
r8351 r9209 65 65 email = None 66 66 phone = None 67 suspended = False 67 68 68 69 -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/tests/test_batching.py
r8626 r9209 23 23 import unittest 24 24 import datetime 25 import grok 25 26 from time import time 27 from zope.event import notify 26 28 from zope.component import createObject 27 29 from zope.component.hooks import setSite, clearSite 28 30 from zope.interface.verify import verifyClass, verifyObject 31 from hurry.workflow.interfaces import IWorkflowState 29 32 30 33 from waeup.kofa.app import University … … 126 129 '\n')[0].split(',') 127 130 131 PAYMENT_CREATE_SAMPLE_DATA = open( 132 os.path.join(os.path.dirname(__file__), 'sample_create_payment_data.csv'), 133 'rb').read() 134 135 PAYMENT_CREATE_HEADER_FIELDS = PAYMENT_CREATE_SAMPLE_DATA.split( 136 '\n')[0].split(',') 137 128 138 class StudentImportExportSetup(FunctionalTestCase): 129 139 … … 171 181 def setup_student(self, student): 172 182 # set predictable values for `student` 173 student.matric_number = u' M123456'183 student.matric_number = u'234' 174 184 student.adm_code = u'my adm code' 175 185 student.clearance_locked = False 176 186 student.clr_code = u'my clr code' 177 187 student.perm_address = u'Studentroad 21\nLagos 123456\n' 178 student.reg_number = u'123456' 179 student.student_id = u'A111111' 188 student.reg_number = u'123' 180 189 student.firstname = u'Anna' 181 190 student.lastname = u'Tester' … … 211 220 ticket.passmark = 100 212 221 ticket.semester = 2 213 study_level .addCourseTicket(ticket)222 study_level[ticket.code] = ticket 214 223 self.add_payment(student) 215 224 return student … … 232 241 233 242 234 235 class StudentProcessorTest(FunctionalTestCase): 243 class StudentProcessorTest(StudentImportExportSetup): 236 244 237 245 layer = FunctionalLayer … … 239 247 def setUp(self): 240 248 super(StudentProcessorTest, self).setUp() 241 # Setup a sample site for each test242 app = University()243 self.dc_root = tempfile.mkdtemp()244 app['datacenter'].setStoragePath(self.dc_root)245 246 # Prepopulate the ZODB...247 self.getRootFolder()['app'] = app248 # we add the site immediately after creation to the249 # ZODB. Catalogs and other local utilities are not setup250 # before that step.251 self.app = self.getRootFolder()['app']252 # Set site here. Some of the following setup code might need253 # to access grok.getSite() and should get our new app then254 setSite(app)255 249 256 250 # Add student with subobjects 257 251 student = Student() 258 student.firstname = u'Anna'259 student.lastname = u'Tester'260 student.reg_number = u'123'261 student.matric_number = u'234'262 252 self.app['students'].addStudent(student) 253 student = self.setup_student(student) 254 notify(grok.ObjectModifiedEvent(student)) 263 255 self.student = self.app['students'][student.student_id] 256 264 257 self.processor = StudentProcessor() 265 258 self.workdir = tempfile.mkdtemp() … … 285 278 open(self.csv_file_duplicates, 'wb').write(STUDENT_SAMPLE_DATA_DUPLICATES) 286 279 287 def tearDown(self):288 super(StudentProcessorTest, self).tearDown()289 shutil.rmtree(self.workdir)290 shutil.rmtree(self.dc_root)291 clearSite()292 return293 294 280 def test_interface(self): 295 281 # Make sure we fulfill the interface contracts. … … 343 329 self.assertEqual(initial_stud_id, new_stud_id) 344 330 return 331 332 def test_checkUpdateRequirements(self): 333 # Make sure that pg students can't be updated with wrong transition. 334 err = self.processor.checkUpdateRequirements(self.student, 335 dict(reg_number='1', state='returning'), self.app) 336 self.assertTrue(err is None) 337 self.certificate.study_mode = 'pg_ft' 338 err = self.processor.checkUpdateRequirements(self.student, 339 dict(reg_number='1', state='returning'), self.app) 340 self.assertEqual(err, 'State not allowed (pg student).') 341 IWorkflowState(self.student).setState('school fee paid') 342 err = self.processor.checkUpdateRequirements(self.student, 343 dict(reg_number='1', transition='reset6'), self.app) 344 self.assertEqual(err, 'Transition not allowed (pg student).') 345 err = self.processor.checkUpdateRequirements(self.student, 346 dict(reg_number='1', transition='register_courses'), self.app) 347 self.assertEqual(err, 'Transition not allowed (pg student).') 348 345 349 346 350 def test_delEntry(self): … … 486 490 super(StudentStudyCourseProcessorTest, self).setUp() 487 491 492 # Add student with subobjects 493 student = Student() 494 self.app['students'].addStudent(student) 495 student = self.setup_student(student) 496 notify(grok.ObjectModifiedEvent(student)) 497 self.student = self.app['students'][student.student_id] 498 488 499 # Import students with subobjects 489 500 student_file = os.path.join(self.workdir, 'sample_student_data.csv') … … 532 543 dict(reg_number='1', current_level='100')) 533 544 self.assertEqual(len(errs),0) 545 546 def test_checkUpdateRequirements(self): 547 # Make sure that pg students can't be updated with wrong transition. 548 err = self.processor.checkUpdateRequirements(self.student['studycourse'], 549 dict(reg_number='1', current_level='100'), self.app) 550 self.assertTrue(err is None) 551 # Since row has passed the converter, current_level is an integer. 552 err = self.processor.checkUpdateRequirements(self.student['studycourse'], 553 dict(reg_number='1', current_level=999), self.app) 554 self.assertTrue(err is None) 555 IWorkflowState(self.student).setState('returning') 556 err = self.processor.checkUpdateRequirements(self.student['studycourse'], 557 dict(reg_number='1', current_level=999), self.app) 558 self.assertEqual(err, 'Not a pg student.') 534 559 535 560 def test_import(self): … … 667 692 self.assertEqual(num_warns,2) 668 693 shutil.rmtree(os.path.dirname(fin_file)) 669 670 694 671 695 class CourseTicketProcessorTest(StudentImportExportSetup): … … 681 705 shutil.rmtree(os.path.dirname(fin_file)) 682 706 683 # Add course and c ourse referrer707 # Add course and certificate course 684 708 self.course = createObject('waeup.Course') 685 709 self.course.code = 'COURSE1' … … 689 713 self.app['faculties']['fac1']['dep1'].courses.addCourse( 690 714 self.course) 691 self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addC ourseRef(715 self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse( 692 716 self.course, level=100) 693 717 … … 755 779 # Logging message from updateEntry, 756 780 self.assertTrue( 757 'INFO - system - K1000000 - Course ticket updated: code=COURSE1, '781 'INFO - system - K1000000 - Course ticket in 100 updated: code=COURSE1, ' 758 782 'mandatory=False, score=3' 759 783 in logcontent) … … 769 793 self.assertEqual(num_warns,2) 770 794 shutil.rmtree(os.path.dirname(fin_file)) 795 796 def test_import_remove(self): 797 # We perform the same import twice, 798 # the second time in remove mode. The number 799 # of warnings must be the same. 800 num, num_warns, fin_file, fail_file = self.processor.doImport( 801 self.csv_file, COURSETICKET_HEADER_FIELDS,'create') 802 assert self.processor.entryExists( 803 dict(reg_number='1', level='100', code='COURSE1'), self.app) is True 804 num, num_warns, fin_file, fail_file = self.processor.doImport( 805 self.csv_file, COURSETICKET_HEADER_FIELDS,'remove') 806 self.assertEqual(num_warns,2) 807 assert self.processor.entryExists( 808 dict(reg_number='1', level='100', code='COURSE1'), self.app) is False 809 shutil.rmtree(os.path.dirname(fin_file)) 810 logcontent = open(self.logfile).read() 811 self.assertTrue( 812 'INFO - system - K1000000 - Course ticket in 100 removed: COURSE1' 813 in logcontent) 771 814 772 815 class PaymentProcessorTest(StudentImportExportSetup): … … 784 827 self.student = self.app['students'][student.student_id] 785 828 payment = createObject(u'waeup.StudentOnlinePayment') 786 payment.p_id = 'p12 3'829 payment.p_id = 'p120' 787 830 self.student['payments'][payment.p_id] = payment 788 831 … … 798 841 self.workdir, 'sample_payment_data.csv') 799 842 open(self.csv_file, 'wb').write(PAYMENT_SAMPLE_DATA) 843 self.csv_file2 = os.path.join( 844 self.workdir, 'sample_create_payment_data.csv') 845 open(self.csv_file2, 'wb').write(PAYMENT_CREATE_SAMPLE_DATA) 800 846 801 847 def test_interface(self): … … 809 855 dict(student_id='ID_NONE', p_id='nonsense'), self.app) is None 810 856 assert self.processor.getEntry( 811 dict(student_id=self.student.student_id, p_id='p12 3'),812 self.app) is self.student['payments']['p12 3']857 dict(student_id=self.student.student_id, p_id='p120'), 858 self.app) is self.student['payments']['p120'] 813 859 assert self.processor.getEntry( 814 dict(student_id=self.student.student_id, p_id='XXXXXX123'), 815 self.app) is self.student['payments']['p123'] 860 dict(student_id=self.student.student_id, p_id='XXXXXX112'), 861 self.app) is self.student['payments']['p120'] 862 863 def test_delEntry(self): 864 assert self.processor.getEntry( 865 dict(student_id=self.student.student_id, p_id='p120'), 866 self.app) is self.student['payments']['p120'] 867 self.assertEqual(len(self.student['payments'].keys()),1) 868 self.processor.delEntry( 869 dict(student_id=self.student.student_id, p_id='p120'), 870 self.app) 871 assert self.processor.getEntry( 872 dict(student_id=self.student.student_id, p_id='p120'), 873 self.app) is None 874 self.assertEqual(len(self.student['payments'].keys()),0) 816 875 817 876 def test_addEntry(self): … … 831 890 self.app) 832 891 self.assertEqual(len(self.student['payments'].keys()),3) 833 self.assertEqual(self.student['payments']['p 456'].p_id, 'p456')892 self.assertEqual(self.student['payments']['p560'].p_id, 'p560') 834 893 835 894 def test_checkConversion(self): 836 895 errs, inv_errs, conv_dict = self.processor.checkConversion( 837 dict( reg_number='1',p_id='3816951266236341955'))896 dict(p_id='3816951266236341955')) 838 897 self.assertEqual(len(errs),0) 839 898 errs, inv_errs, conv_dict = self.processor.checkConversion( 840 dict( reg_number='1',p_id='p1266236341955'))899 dict(p_id='p1266236341955')) 841 900 self.assertEqual(len(errs),0) 842 901 errs, inv_errs, conv_dict = self.processor.checkConversion( 843 dict( reg_number='1',p_id='nonsense'))902 dict(p_id='nonsense')) 844 903 self.assertEqual(len(errs),1) 845 timestamp = "%d" % int(time()*1000)904 timestamp = ("%d" % int(time()*10000))[1:] 846 905 p_id = "p%s" % timestamp 847 906 errs, inv_errs, conv_dict = self.processor.checkConversion( 848 dict( reg_number='1',p_id=p_id))907 dict(p_id=p_id)) 849 908 self.assertEqual(len(errs),0) 850 909 … … 854 913 self.assertEqual(num_warns,0) 855 914 payment = self.processor.getEntry(dict(reg_number='1', 856 p_id='p1290797973744'), self.app) 857 self.assertEqual(payment.p_id, 'p1290797973744') 915 p_id='p2907979737440'), self.app) 916 self.assertEqual(payment.p_id, 'p2907979737440') 917 self.assertTrue(payment.p_current) 858 918 cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S") 859 919 self.assertEqual(cdate, "2010-11-26 18:59:33") 860 920 self.assertEqual(str(payment.creation_date.tzinfo),'UTC') 861 shutil.rmtree(os.path.dirname(fin_file)) 862 921 payment = self.processor.getEntry(dict(matric_number='100001', 922 p_id='p2907125937570'), self.app) 923 self.assertEqual(payment.p_id, 'p2907125937570') 924 self.assertFalse(payment.p_current) 925 cdate = payment.creation_date.strftime("%Y-%m-%d %H:%M:%S") 926 # Ooooh, still the old problem, see 927 # http://mail.dzug.org/mailman/archives/zope/2006-August/001153.html. 928 # WAT is interpreted as GMT-1 and not GMT+1 929 self.assertEqual(cdate, "2010-11-25 21:16:33") 930 self.assertEqual(str(payment.creation_date.tzinfo),'UTC') 931 shutil.rmtree(os.path.dirname(fin_file)) 863 932 logcontent = open(self.logfile).read() 864 # Logging message from updateEntry ,933 # Logging message from updateEntry 865 934 self.assertTrue( 866 935 'INFO - system - K1000001 - Payment ticket updated: ' 867 'p_i d=p1266236341955, p_item=BTECHBDT, '868 ' creation_date=2010-02-15 13:19:01+00:00, r_code=00, '869 ' r_amount_approved=19500.0, p_category=schoolfee, '870 ' amount_auth=19500.0,p_state=paid'936 'p_item=BTECHBDT, creation_date=2010-02-15 13:19:01+00:00, ' 937 'p_category=schoolfee, amount_auth=19500.0, p_current=True, ' 938 'p_id=p1266236341955, r_code=00, r_amount_approved=19500.0, ' 939 'p_state=paid' 871 940 in logcontent) 872 941 … … 881 950 self.assertEqual(num_warns,0) 882 951 shutil.rmtree(os.path.dirname(fin_file)) 952 953 def test_import_remove(self): 954 num, num_warns, fin_file, fail_file = self.processor.doImport( 955 self.csv_file, PAYMENT_HEADER_FIELDS,'create') 956 num, num_warns, fin_file, fail_file = self.processor.doImport( 957 self.csv_file, PAYMENT_HEADER_FIELDS,'remove') 958 self.assertEqual(num_warns,0) 959 shutil.rmtree(os.path.dirname(fin_file)) 960 logcontent = open(self.logfile).read() 961 self.assertTrue( 962 'INFO - system - K1000001 - Payment ticket removed: p1266236341955' 963 in logcontent) 964 965 def test_import_wo_pid(self): 966 num, num_warns, fin_file, fail_file = self.processor.doImport( 967 self.csv_file2, PAYMENT_CREATE_HEADER_FIELDS,'create') 968 self.assertEqual(num_warns,0) 969 shutil.rmtree(os.path.dirname(fin_file)) 970 self.assertEqual(len(self.app['students']['X666666']['payments']), 50) 971 883 972 884 973 def test_suite(): -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/tests/test_browser.py
r8779 r9209 118 118 self.manage_clearance_path = self.student_path + '/manage_clearance' 119 119 self.edit_personal_path = self.student_path + '/edit_personal' 120 self.manage_personal_path = self.student_path + '/manage_personal' 120 121 self.studycourse_path = self.student_path + '/studycourse' 121 122 self.payments_path = self.student_path + '/payments' … … 170 171 self.app['faculties']['fac1']['dep1'].courses.addCourse( 171 172 self.course) 172 self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addC ourseRef(173 self.app['faculties']['fac1']['dep1'].certificates['CERT1'].addCertCourse( 173 174 self.course, level=100) 174 175 … … 403 404 layer = FunctionalLayer 404 405 406 def test_student_properties(self): 407 self.student['studycourse'].current_level = 100 408 self.assertEqual(self.student.current_level, 100) 409 self.student['studycourse'].current_session = 2011 410 self.assertEqual(self.student.current_session, 2011) 411 self.student['studycourse'].current_verdict = 'A' 412 self.assertEqual(self.student.current_verdict, 'A') 413 return 414 405 415 def test_basic_auth(self): 406 416 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') … … 454 464 self.assertEqual(self.browser.headers['Status'], '200 Ok') 455 465 self.assertEqual(self.browser.url, self.personal_path) 466 self.browser.getLink("Manage").click() 467 self.assertEqual(self.browser.headers['Status'], '200 Ok') 468 self.assertEqual(self.browser.url, self.manage_personal_path) 469 self.browser.open(self.personal_path) 456 470 self.browser.getLink("Edit").click() 457 471 self.assertEqual(self.browser.headers['Status'], '200 Ok') … … 474 488 self.assertEqual(self.browser.headers['Status'], '200 Ok') 475 489 self.assertEqual(self.browser.url, self.history_path) 476 self.assertMatches('... Student admitted by Manager...',490 self.assertMatches('...Admitted by Manager...', 477 491 self.browser.contents) 478 492 # Only the Application Slip does not exist … … 754 768 self.assertEqual(student['studycourse'].current_session, 2005) # +1 755 769 self.assertEqual(student['studycourse'].current_level, 200) # +100 756 self.assertEqual(student['studycourse'].current_verdict, ' NY') # NY= not set770 self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = Zero = not set 757 771 self.assertEqual(student['studycourse'].previous_verdict, 'A') 758 772 self.browser.getControl(name="transition").value = ['register_courses'] … … 762 776 self.browser.getControl(name="transition").value = ['return'] 763 777 self.browser.getControl("Save").click() 778 return 779 780 def test_manage_pg_workflow(self): 781 # Managers can pass through the whole workflow 782 IWorkflowState(self.student).setState('school fee paid') 783 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 784 student = self.app['students'][self.student_id] 785 self.browser.open(self.manage_student_path) 786 self.assertTrue('<option value="reset6">' in self.browser.contents) 787 self.assertTrue('<option value="register_courses">' in self.browser.contents) 788 self.assertTrue('<option value="reset5">' in self.browser.contents) 789 self.certificate.study_mode = 'pg_ft' 790 self.browser.open(self.manage_student_path) 791 self.assertFalse('<option value="reset6">' in self.browser.contents) 792 self.assertFalse('<option value="register_courses">' in self.browser.contents) 793 self.assertTrue('<option value="reset5">' in self.browser.contents) 764 794 return 765 795 … … 776 806 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 777 807 self.browser.open(datacenter_path) 778 self.browser.getLink('Upload CSV file').click()808 self.browser.getLink('Upload data').click() 779 809 filecontents = StringIO(open('students.csv', 'rb').read()) 780 810 filewidget = self.browser.getControl(name='uploadfile:file') 781 811 filewidget.add_file(filecontents, 'text/plain', 'students.csv') 782 812 self.browser.getControl(name='SUBMIT').click() 783 self.browser.getLink(' Batch processing').click()813 self.browser.getLink('Process data').click() 784 814 button = lookup_submit_value( 785 815 'select', 'students_zope.mgr.csv', self.browser) … … 802 832 """) 803 833 self.browser.open(datacenter_path) 804 self.browser.getLink('Upload CSV file').click()834 self.browser.getLink('Upload data').click() 805 835 filecontents = StringIO(open('studycourses.csv', 'rb').read()) 806 836 filewidget = self.browser.getControl(name='uploadfile:file') 807 837 filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv') 808 838 self.browser.getControl(name='SUBMIT').click() 809 self.browser.getLink(' Batch processing').click()839 self.browser.getLink('Process data').click() 810 840 button = lookup_submit_value( 811 841 'select', 'studycourses_zope.mgr.csv', self.browser) … … 917 947 self.assertTrue('clearance started' in self.browser.contents) 918 948 self.browser.open(self.history_path) 919 self.assertTrue("Reset to 'clearance ' by My Public Name" in949 self.assertTrue("Reset to 'clearance started' by My Public Name" in 920 950 self.browser.contents) 921 951 IWorkflowInfo(self.student).fireTransition('request_clearance') … … 946 976 self.app['users'].addUser('mrsadvise', 'mrsadvisesecret') 947 977 self.app['users']['mrsadvise'].email = 'mradvise@foo.ng' 948 self.app['users']['mrsadvise'].title = 'Helen Procter'978 self.app['users']['mrsadvise'].title = u'Helen Procter' 949 979 # Assign local CourseAdviser100 role for a certificate 950 980 cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1'] … … 997 1027 # the 100L CA does see the 'Validate' button 998 1028 self.browser.open(L110_student_path) 999 self.assertFalse('Validate ' in self.browser.contents)1029 self.assertFalse('Validate courses' in self.browser.contents) 1000 1030 IWorkflowInfo(self.student).fireTransition('register_courses') 1001 1031 self.browser.open(L110_student_path) 1002 self.assertFalse('Validate ' in self.browser.contents)1032 self.assertFalse('Validate courses' in self.browser.contents) 1003 1033 self.student['studycourse'].current_level = 110 1004 1034 self.browser.open(L110_student_path) 1005 self.assertTrue('Validate ' in self.browser.contents)1035 self.assertTrue('Validate courses' in self.browser.contents) 1006 1036 # ... but a 100L CA does not see the button on other levels 1007 1037 studylevel2 = StudentStudyLevel() … … 1011 1041 L200_student_path = self.studycourse_path + '/200' 1012 1042 self.browser.open(L200_student_path) 1013 self.assertFalse('Validate ' in self.browser.contents)1043 self.assertFalse('Validate courses' in self.browser.contents) 1014 1044 self.browser.open(L110_student_path) 1015 1045 self.browser.getLink("Validate courses").click() 1016 1046 self.assertTrue('Course list has been validated' in self.browser.contents) 1017 1047 self.assertTrue('courses validated' in self.browser.contents) 1048 self.assertEqual(self.student['studycourse']['110'].validated_by, 1049 'Helen Procter') 1050 self.assertMatches( 1051 '<YYYY-MM-DD hh:mm:ss>', 1052 self.student['studycourse']['110'].validation_date.strftime( 1053 "%Y-%m-%d %H:%M:%S")) 1018 1054 self.browser.getLink("Reject courses").click() 1019 1055 self.assertTrue('Course list request has been annulled.' … … 1023 1059 '/contactstudent?subject=%s' % urlmessage) 1024 1060 self.assertTrue('school fee paid' in self.browser.contents) 1061 self.assertTrue(self.student['studycourse']['110'].validated_by is None) 1062 self.assertTrue(self.student['studycourse']['110'].validation_date is None) 1025 1063 IWorkflowInfo(self.student).fireTransition('register_courses') 1026 1064 self.browser.open(L110_student_path) … … 1142 1180 # and can perform actions 1143 1181 IWorkflowInfo(self.student).fireTransition('admit') 1182 # Students can't login if their account is suspended/deactivated 1183 self.student.suspended = True 1144 1184 self.browser.open(self.login_path) 1145 1185 self.browser.getControl(name="form.login").value = self.student_id 1146 1186 self.browser.getControl(name="form.password").value = 'spwd' 1147 1187 self.browser.getControl("Login").click() 1148 # Student can upload a passport picture 1188 self.assertTrue( 1189 'Your account has been deactivated.' in self.browser.contents) 1190 self.student.suspended = False 1191 self.browser.getControl("Login").click() 1192 self.assertTrue( 1193 'You logged in.' in self.browser.contents) 1194 # Admitted student can upload a passport picture 1149 1195 self.browser.open(self.student_path + '/change_portrait') 1150 1196 ctrl = self.browser.getControl(name='passportuploadedit') … … 1157 1203 '<img align="middle" height="125px" src="passport.jpg" />' 1158 1204 in self.browser.contents) 1205 # Students can open admission letter 1206 self.browser.getLink("Base Data").click() 1207 self.browser.getLink("Download admission letter").click() 1208 self.assertEqual(self.browser.headers['Status'], '200 Ok') 1209 self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf') 1159 1210 # Student can view the clearance data 1211 self.browser.open(self.student_path) 1160 1212 self.browser.getLink("Clearance Data").click() 1161 1213 # Student can't open clearance edit form before starting clearance … … 1170 1222 self.assertMatches('...Not all required fields filled...', 1171 1223 self.browser.contents) 1172 self.student.email = 'aa@aa.ng' 1224 self.browser.open(self.student_path + '/edit_base') 1225 self.browser.getControl(name="form.email").value = 'aa@aa.ng' 1226 self.browser.getControl("Save").click() 1173 1227 self.browser.open(self.student_path + '/start_clearance') 1174 1228 self.browser.getControl(name="ac_series").value = '3' … … 1185 1239 # Set the correct owner 1186 1240 self.existing_clrac.owner = self.student_id 1241 # clr_code might be set (and thus returns None) due importing 1242 # an empty clr_code column. 1243 self.student.clr_code = None 1187 1244 self.browser.getControl("Start clearance now").click() 1188 1245 self.assertMatches('...Clearance process has been started...', … … 1235 1292 self.browser.getControl("Create course list now").click() 1236 1293 self.browser.getLink("100").click() 1237 self.browser.getLink(" Add and remove courses").click()1294 self.browser.getLink("Edit course list").click() 1238 1295 self.browser.getControl("Add course ticket").click() 1239 1296 self.browser.getControl(name="form.course").value = ['COURSE1'] … … 1248 1305 self.browser.getControl("Create course list now").click() 1249 1306 self.browser.getLink("200").click() 1250 self.browser.getLink(" Add and remove courses").click()1307 self.browser.getLink("Edit course list").click() 1251 1308 self.browser.getControl("Add course ticket").click() 1252 1309 self.browser.getControl(name="form.course").value = ['COURSE1'] … … 1286 1343 self.assertEqual(self.student.state, 'courses registered') 1287 1344 return 1345 1346 def test_student_clearance_wo_clrcode(self): 1347 IWorkflowState(self.student).setState('clearance started') 1348 self.browser.open(self.login_path) 1349 self.browser.getControl(name="form.login").value = self.student_id 1350 self.browser.getControl(name="form.password").value = 'spwd' 1351 self.browser.getControl("Login").click() 1352 self.student.clearance_locked = False 1353 self.browser.open(self.edit_clearance_path) 1354 self.browser.getControl(name="form.date_of_birth").value = '09/10/1961' 1355 self.browser.getControl("Save and request clearance").click() 1356 self.assertMatches('...Clearance has been requested...', 1357 self.browser.contents) 1288 1358 1289 1359 def test_manage_payments(self): … … 1516 1586 # The new SFE-0 pin can be used for starting new session 1517 1587 self.browser.open(self.studycourse_path) 1518 self.browser.getLink('Start session').click()1588 self.browser.getLink('Start new session').click() 1519 1589 pin = self.app['accesscodes']['SFE-0'].keys()[0] 1520 1590 parts = pin.split('-')[1:] … … 1528 1598 return 1529 1599 1530 def test_postgraduate_payments(self): 1600 def test_student_previous_payments(self): 1601 configuration = createObject('waeup.SessionConfiguration') 1602 configuration.academic_session = 2000 1603 configuration.clearance_fee = 3456.0 1604 configuration.booking_fee = 123.4 1605 self.student['studycourse'].entry_session = 2002 1606 self.app['configuration'].addSessionConfiguration(configuration) 1607 configuration2 = createObject('waeup.SessionConfiguration') 1608 configuration2.academic_session = 2003 1609 configuration2.clearance_fee = 3456.0 1610 configuration2.booking_fee = 123.4 1611 self.student['studycourse'].entry_session = 2002 1612 self.app['configuration'].addSessionConfiguration(configuration2) 1613 # Login 1614 self.browser.open(self.login_path) 1615 self.browser.getControl(name="form.login").value = self.student_id 1616 self.browser.getControl(name="form.password").value = 'spwd' 1617 self.browser.getControl("Login").click() 1618 1619 # Students can add previous school fee payment tickets in any state. 1620 IWorkflowState(self.student).setState('courses registered') 1621 self.browser.open(self.payments_path) 1622 self.browser.getControl("Add online payment ticket").click() 1623 self.browser.getControl(name="form.p_category").value = ['schoolfee'] 1624 self.browser.getControl("Create ticket").click() 1625 1626 # Amount cannot be determined since the state is not 1627 # 'cleared' or 'returning' 1628 self.assertMatches('...Amount could not be determined...', 1629 self.browser.contents) 1630 self.assertMatches('...Would you like to pay for a previous session?...', 1631 self.browser.contents) 1632 1633 # Previous session payment form is provided 1634 self.browser.getControl(name="form.p_category").value = ['schoolfee'] 1635 self.browser.getControl(name="form.p_session").value = ['2000'] 1636 self.browser.getControl(name="form.p_level").value = ['300'] 1637 self.browser.getControl("Create ticket").click() 1638 self.assertMatches('...The previous session must not fall below...', 1639 self.browser.contents) 1640 self.browser.getControl(name="form.p_category").value = ['schoolfee'] 1641 self.browser.getControl(name="form.p_session").value = ['2004'] 1642 self.browser.getControl(name="form.p_level").value = ['300'] 1643 self.browser.getControl("Create ticket").click() 1644 self.assertMatches('...This is not a previous session...', 1645 self.browser.contents) 1646 self.browser.getControl(name="form.p_category").value = ['schoolfee'] 1647 self.browser.getControl(name="form.p_session").value = ['2003'] 1648 self.browser.getControl(name="form.p_level").value = ['300'] 1649 self.browser.getControl("Create ticket").click() 1650 self.assertMatches('...ticket created...', 1651 self.browser.contents) 1652 ctrl = self.browser.getControl(name='val_id') 1653 value = ctrl.options[0] 1654 self.browser.getLink(value).click() 1655 self.assertMatches('...Amount Authorized...', 1656 self.browser.contents) 1657 self.assertEqual(self.student['payments'][value].amount_auth, 20000.0) 1658 1659 # Payment session is properly set 1660 self.assertEqual(self.student['payments'][value].p_session, 2003) 1661 self.assertEqual(self.student['payments'][value].p_level, 300) 1662 1663 # We simulate the approval 1664 self.browser.open(self.browser.url + '/fake_approve') 1665 self.assertMatches('...Payment approved...', 1666 self.browser.contents) 1667 1668 # No AC has been created 1669 self.assertEqual(len(self.app['accesscodes']['SFE-0'].keys()), 0) 1670 self.assertTrue(self.student['payments'][value].ac is None) 1671 1672 # Current payment flag is set False 1673 self.assertFalse(self.student['payments'][value].p_current) 1674 return 1675 1676 def test_student_postgraduate_payments(self): 1531 1677 self.certificate.study_mode = 'pg_ft' 1532 1678 self.certificate.start_level = 999 … … 1552 1698 self.browser.contents) 1553 1699 # Payment session and level are current ones. 1554 # Postgrads have to school_fee_1.1700 # Postgrads have to pay school_fee_1. 1555 1701 self.assertEqual(self.student['payments'][value].amount_auth, 40000.0) 1556 1702 self.assertEqual(self.student['payments'][value].p_session, 2004) … … 1565 1711 # The new SFE-0 pin can be used for starting session 1566 1712 self.browser.open(self.studycourse_path) 1567 self.browser.getLink('Start session').click()1713 self.browser.getLink('Start new session').click() 1568 1714 pin = self.app['accesscodes']['SFE-0'].keys()[0] 1569 1715 parts = pin.split('-')[1:] … … 1605 1751 # The new SFE-1 pin can be used for starting new session 1606 1752 self.browser.open(self.studycourse_path) 1607 self.browser.getLink('Start session').click()1753 self.browser.getLink('Start new session').click() 1608 1754 self.browser.getControl(name="ac_series").value = sfeseries 1609 1755 self.browser.getControl(name="ac_number").value = sfenumber … … 1847 1993 self.browser.getControl(name="form.identifier").value = '123' 1848 1994 self.browser.getControl(name="form.email").value = 'aa@aa.ng' 1849 self.browser.getControl(" Getlogin credentials").click()1995 self.browser.getControl("Send login credentials").click() 1850 1996 self.assertTrue('An email with' in self.browser.contents) 1851 1852 def test_reindex(self):1853 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')1854 self.browser.open('http://localhost/app/reindex')1855 self.assertTrue('No catalog name provided' in self.browser.contents)1856 self.browser.open('http://localhost/app/reindex?ctlg=xyz')1857 self.assertTrue('xyz_catalog does not exist' in self.browser.contents)1858 cat = queryUtility(ICatalog, name='students_catalog')1859 results = cat.searchResults(student_id=(None, None))1860 self.assertEqual(len(results),1)1861 cat.clear()1862 results = cat.searchResults(student_id=(None, None))1863 self.assertEqual(len(results),0)1864 self.browser.open('http://localhost/app/reindex?ctlg=students')1865 self.assertTrue('1 students re-indexed' in self.browser.contents)1866 results = cat.searchResults(student_id=(None, None))1867 self.assertEqual(len(results),1)1868 1997 1869 1998 def test_change_current_mode(self): … … 1888 2017 self.assertTrue('Employer' in self.browser.contents) 1889 2018 2019 def test_activate_deactivate_buttons(self): 2020 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 2021 self.browser.open(self.student_path) 2022 self.browser.getLink("Deactivate").click() 2023 self.assertTrue( 2024 'Student account has been deactivated.' in self.browser.contents) 2025 self.assertTrue( 2026 'Base Data (account deactivated)' in self.browser.contents) 2027 self.assertTrue(self.student.suspended) 2028 self.browser.getLink("Activate").click() 2029 self.assertTrue( 2030 'Student account has been activated.' in self.browser.contents) 2031 self.assertFalse( 2032 'Base Data (account deactivated)' in self.browser.contents) 2033 self.assertFalse(self.student.suspended) 2034 # History messages have been added ... 2035 self.browser.getLink("History").click() 2036 self.assertTrue( 2037 'Student account deactivated by Manager<br />' in self.browser.contents) 2038 self.assertTrue( 2039 'Student account activated by Manager<br />' in self.browser.contents) 2040 # ... and actions have been logged. 2041 logfile = os.path.join( 2042 self.app['datacenter'].storage, 'logs', 'students.log') 2043 logcontent = open(logfile).read() 2044 self.assertTrue('zope.mgr - students.browser.StudentDeactivatePage - ' 2045 'K1000000 - account deactivated' in logcontent) 2046 self.assertTrue('zope.mgr - students.browser.StudentActivatePage - ' 2047 'K1000000 - account activated' in logcontent) 2048 2049 def test_student_transfer(self): 2050 # Add second certificate 2051 self.certificate2 = createObject('waeup.Certificate') 2052 self.certificate2.code = u'CERT2' 2053 self.certificate2.study_mode = 'ug_ft' 2054 self.certificate2.start_level = 999 2055 self.certificate2.end_level = 999 2056 self.app['faculties']['fac1']['dep1'].certificates.addCertificate( 2057 self.certificate2) 2058 2059 # Add study level to old study course 2060 studylevel = createObject(u'waeup.StudentStudyLevel') 2061 studylevel.level = 200 2062 self.student['studycourse'].addStudentStudyLevel( 2063 self.certificate, studylevel) 2064 2065 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 2066 self.browser.open(self.student_path) 2067 self.browser.getLink("Transfer").click() 2068 self.browser.getControl(name="form.certificate").value = ['CERT2'] 2069 self.browser.getControl(name="form.current_session").value = ['2011'] 2070 self.browser.getControl(name="form.current_level").value = ['200'] 2071 self.browser.getControl("Transfer").click() 2072 self.assertTrue( 2073 'Current level does not match certificate levels' 2074 in self.browser.contents) 2075 self.browser.getControl(name="form.current_level").value = ['999'] 2076 self.browser.getControl("Transfer").click() 2077 self.assertTrue('Successfully transferred' in self.browser.contents) 2078 2079 # Add study level to new study course 2080 studylevel = createObject(u'waeup.StudentStudyLevel') 2081 studylevel.level = 200 2082 self.student['studycourse'].addStudentStudyLevel( 2083 self.certificate, studylevel) 2084 2085 # Edit and add pages are locked for old study courses 2086 self.browser.open(self.student_path + '/studycourse/manage') 2087 self.assertFalse('The requested form is locked' in self.browser.contents) 2088 self.browser.open(self.student_path + '/studycourse_1/manage') 2089 self.assertTrue('The requested form is locked' in self.browser.contents) 2090 2091 self.browser.open(self.student_path + '/studycourse/start_session') 2092 self.assertFalse('The requested form is locked' in self.browser.contents) 2093 self.browser.open(self.student_path + '/studycourse_1/start_session') 2094 self.assertTrue('The requested form is locked' in self.browser.contents) 2095 2096 IWorkflowState(self.student).setState('school fee paid') 2097 self.browser.open(self.student_path + '/studycourse/add') 2098 self.assertFalse('The requested form is locked' in self.browser.contents) 2099 self.browser.open(self.student_path + '/studycourse_1/add') 2100 self.assertTrue('The requested form is locked' in self.browser.contents) 2101 2102 self.browser.open(self.student_path + '/studycourse/200/manage') 2103 self.assertFalse('The requested form is locked' in self.browser.contents) 2104 self.browser.open(self.student_path + '/studycourse_1/200/manage') 2105 self.assertTrue('The requested form is locked' in self.browser.contents) 2106 2107 self.browser.open(self.student_path + '/studycourse/200/validate_courses') 2108 self.assertFalse('The requested form is locked' in self.browser.contents) 2109 self.browser.open(self.student_path + '/studycourse_1/200/validate_courses') 2110 self.assertTrue('The requested form is locked' in self.browser.contents) 2111 2112 self.browser.open(self.student_path + '/studycourse/200/reject_courses') 2113 self.assertFalse('The requested form is locked' in self.browser.contents) 2114 self.browser.open(self.student_path + '/studycourse_1/200/reject_courses') 2115 self.assertTrue('The requested form is locked' in self.browser.contents) 2116 2117 self.browser.open(self.student_path + '/studycourse/200/add') 2118 self.assertFalse('The requested form is locked' in self.browser.contents) 2119 self.browser.open(self.student_path + '/studycourse_1/200/add') 2120 self.assertTrue('The requested form is locked' in self.browser.contents) 2121 2122 self.browser.open(self.student_path + '/studycourse/200/edit') 2123 self.assertFalse('The requested form is locked' in self.browser.contents) 2124 self.browser.open(self.student_path + '/studycourse_1/200/edit') 2125 self.assertTrue('The requested form is locked' in self.browser.contents) 2126 1890 2127 class StudentRequestPWTests(StudentsFullSetup): 1891 2128 # Tests for student registration … … 1894 2131 1895 2132 def test_request_pw(self): 1896 # Student with wrong reg_number can't be found.2133 # Student with wrong number can't be found. 1897 2134 self.browser.open('http://localhost/app/requestpw') 1898 2135 self.browser.getControl(name="form.firstname").value = 'Anna' 1899 self.browser.getControl(name="form. reg_number").value = 'anynumber'2136 self.browser.getControl(name="form.number").value = 'anynumber' 1900 2137 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 1901 self.browser.getControl(" Getlogin credentials").click()2138 self.browser.getControl("Send login credentials").click() 1902 2139 self.assertTrue('No student record found.' 1903 2140 in self.browser.contents) … … 1906 2143 self.browser.open('http://localhost/app/requestpw') 1907 2144 self.browser.getControl(name="form.firstname").value = 'Johnny' 1908 self.browser.getControl(name="form. reg_number").value = '123'2145 self.browser.getControl(name="form.number").value = '123' 1909 2146 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 1910 self.browser.getControl(" Getlogin credentials").click()2147 self.browser.getControl("Send login credentials").click() 1911 2148 self.assertTrue('No student record found.' 1912 2149 in self.browser.contents) … … 1914 2151 # password has been set and used. 1915 2152 self.browser.getControl(name="form.firstname").value = 'Anna' 1916 self.browser.getControl(name="form. reg_number").value = '123'1917 self.browser.getControl(" Getlogin credentials").click()2153 self.browser.getControl(name="form.number").value = '123' 2154 self.browser.getControl("Send login credentials").click() 1918 2155 self.assertTrue('Your password has already been set and used.' 1919 2156 in self.browser.contents) … … 1922 2159 # The firstname field, used for verification, is not case-sensitive. 1923 2160 self.browser.getControl(name="form.firstname").value = 'aNNa' 1924 self.browser.getControl(name="form. reg_number").value = '123'2161 self.browser.getControl(name="form.number").value = '123' 1925 2162 self.browser.getControl(name="form.email").value = 'new@yy.zz' 1926 self.browser.getControl(" Getlogin credentials").click()2163 self.browser.getControl("Send login credentials").click() 1927 2164 # Yeah, we succeded ... 2165 self.assertTrue('Your password request was successful.' 2166 in self.browser.contents) 2167 # We can also use the matric_number instead. 2168 self.browser.open('http://localhost/app/requestpw') 2169 self.browser.getControl(name="form.firstname").value = 'aNNa' 2170 self.browser.getControl(name="form.number").value = '234' 2171 self.browser.getControl(name="form.email").value = 'new@yy.zz' 2172 self.browser.getControl("Send login credentials").click() 1928 2173 self.assertTrue('Your password request was successful.' 1929 2174 in self.browser.contents) … … 1934 2179 email=('new@yy.zz', 'new@yy.zz'))) 1935 2180 self.assertEqual(self.student,results[0]) 2181 logfile = os.path.join( 2182 self.app['datacenter'].storage, 'logs', 'main.log') 2183 logcontent = open(logfile).read() 2184 self.assertTrue('zope.anybody - students.browser.StudentRequestPasswordPage - ' 2185 '234 (K1000000) - new@yy.zz' in logcontent) 1936 2186 return -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/tests/test_dynamicroles.py
r7811 r9209 25 25 Test as APRMTest, Manageable) 26 26 from waeup.kofa.testing import FunctionalLayer 27 from waeup.kofa.app import University 27 28 from waeup.kofa.students.tests.test_browser import StudentsFullSetup 28 29 from waeup.kofa.students import Student, StudentPrincipalRoleManager … … 48 49 def setUp(self): 49 50 super(StudentPrincipalRoleManagerFunctionalTests, self).setUp() 50 self.officer_role = 'waeup.StudentsClearanceOfficer'51 51 # assign clearance permissions for a virtual officer 52 52 prm = IPrincipalRoleManager(self.app['faculties']['fac1']['dep1']) 53 53 prm.assignRoleToPrincipal('waeup.local.ClearanceOfficer', 'alice') 54 prm.assignRoleToPrincipal('waeup.local.PGClearanceOfficer', 'bob') 55 prm.assignRoleToPrincipal('waeup.local.UGClearanceOfficer', 'anne') 54 56 return 55 57 … … 74 76 # student 75 77 prm = IPrincipalRoleManager(self.student) 76 result = prm.getRolesForPrincipal(' bob')78 result = prm.getRolesForPrincipal('claus') 77 79 self.assertEqual(result, []) 78 80 return … … 83 85 prm = IPrincipalRoleManager(self.student) 84 86 result = prm.getRolesForPrincipal('alice') 85 self.assertEqual(result, [(self.officer_role, Allow)]) 87 self.assertEqual(result, [('waeup.StudentsClearanceOfficer', Allow)]) 88 # Student is a UG student 89 self.assertFalse(self.student.is_postgrad) 90 result = prm.getRolesForPrincipal('bob') 91 self.assertEqual(result, [('waeup.StudentsOfficer', Allow)]) 92 result = prm.getRolesForPrincipal('anne') 93 self.assertEqual(result, [('waeup.StudentsClearanceOfficer', Allow)]) 94 # Make student a PG student 95 self.certificate.study_mode = u'pg_ft' 96 self.assertTrue(self.student.is_postgrad) 97 result = prm.getRolesForPrincipal('bob') 98 # The dynamic roles changed 99 self.assertEqual(result, [('waeup.StudentsClearanceOfficer', Allow)]) 100 result = prm.getRolesForPrincipal('anne') 101 self.assertEqual(result, [('waeup.StudentsOfficer', Allow)]) 86 102 return -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/tests/test_export.py
r8621 r9209 44 44 'adm_code,clearance_locked,clr_code,date_of_birth,email,employer,' 45 45 'firstname,lastname,matric_number,middlename,nationality,' 46 'perm_address,phone,reg_number,sex,student_id, password,'46 'perm_address,phone,reg_number,sex,student_id,suspended,password,' 47 47 'state,history,certcode\r\n' 48 48 49 49 'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,' 50 'Anna,Tester, M123456,M.,NG,"Studentroad 21\nLagos 123456\n",'51 '+234-123-12345 ,123456,f,A111111,,created'50 'Anna,Tester,234,M.,NG,"Studentroad 21\nLagos 123456\n",' 51 '+234-123-12345#,123,f,A111111,0,,created' 52 52 in result 53 53 ) … … 64 64 'adm_code,clearance_locked,clr_code,date_of_birth,email,employer,' 65 65 'firstname,lastname,matric_number,middlename,nationality,' 66 'perm_address,phone,reg_number,sex,student_id, password,'66 'perm_address,phone,reg_number,sex,student_id,suspended,password,' 67 67 'state,history,certcode\r\n' 68 68 69 69 'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,' 70 'Anna,Tester, M123456,M.,NG,"Studentroad 21\nLagos 123456\n",'71 '+234-123-12345 ,123456,f,A111111,,created'70 'Anna,Tester,234,M.,NG,"Studentroad 21\nLagos 123456\n",' 71 '+234-123-12345#,123,f,A111111,0,,created' 72 72 in result 73 73 ) … … 83 83 'adm_code,clearance_locked,clr_code,date_of_birth,email,employer,' 84 84 'firstname,lastname,matric_number,middlename,nationality,' 85 'perm_address,phone,reg_number,sex,student_id, password,'85 'perm_address,phone,reg_number,sex,student_id,suspended,password,' 86 86 'state,history,certcode\r\n' 87 87 88 88 'my adm code,0,my clr code,1981-02-04#,anna@sample.com,,' 89 'Anna,Tester, M123456,M.,NG,"Studentroad 21\nLagos 123456\n",'90 '+234-123-12345 ,123456,f,A111111,,created'89 'Anna,Tester,234,M.,NG,"Studentroad 21\nLagos 123456\n",' 90 '+234-123-12345#,123,f,A111111,0,,created' 91 91 in result 92 92 ) … … 127 127 'entry_mode,entry_session,previous_verdict,student_id\r\n' 128 128 129 ',,, NY,,,NY,\r\n'129 ',,,0,,,0,\r\n' 130 130 ) 131 131 return … … 144 144 'entry_mode,entry_session,previous_verdict,student_id\r\n' 145 145 146 'CERT1,200,2012, NY,ug_ft,2010,NY,A111111\r\n'146 'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n' 147 147 ) 148 148 return … … 160 160 'entry_mode,entry_session,previous_verdict,student_id\r\n' 161 161 162 'CERT1,200,2012, NY,ug_ft,2010,NY,A111111\r\n'162 'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n' 163 163 ) 164 164 return … … 175 175 'entry_mode,entry_session,previous_verdict,student_id\r\n' 176 176 177 'CERT1,200,2012, NY,ug_ft,2010,NY,A111111\r\n'177 'CERT1,200,2012,0,ug_ft,2010,0,A111111\r\n' 178 178 ) 179 179 return … … 209 209 self.assertEqual( 210 210 result, 211 'level,level_session,level_verdict,student_id\r\n' 212 ',,NY,\r\n' 211 'level,level_session,level_verdict,validated_by,validation_date,' 212 'student_id\r\n' 213 ',,0,,,\r\n' 213 214 ) 214 215 return … … 225 226 self.assertEqual( 226 227 result, 227 'level,level_session,level_verdict,student_id\r\n' 228 '100,2012,A,A111111\r\n' 228 'level,level_session,level_verdict,validated_by,validation_date,' 229 'student_id\r\n' 230 '100,2012,A,,,A111111\r\n' 229 231 ) 230 232 return … … 239 241 self.assertEqual( 240 242 result, 241 'level,level_session,level_verdict,student_id\r\n' 242 '100,2012,A,A111111\r\n' 243 'level,level_session,level_verdict,validated_by,validation_date,' 244 'student_id\r\n' 245 '100,2012,A,,,A111111\r\n' 243 246 ) 244 247 return … … 252 255 self.assertEqual( 253 256 result, 254 'level,level_session,level_verdict,student_id\r\n' 255 '100,2012,A,A111111\r\n' 257 'level,level_session,level_verdict,validated_by,validation_date,' 258 'student_id\r\n' 259 '100,2012,A,,,A111111\r\n' 256 260 ) 257 261 return … … 371 375 self.assertEqual( 372 376 result, 373 'ac,amount_auth,creation_date,p_category,p_ id,'377 'ac,amount_auth,creation_date,p_category,p_current,p_id,' 374 378 'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,' 375 379 'r_code,r_desc,student_id\r\n' 376 380 377 ',0.0,2012-04-01 13:12:01,schoolfee, ,,,,unpaid,,0.0,,,\r\n'381 ',0.0,2012-04-01 13:12:01,schoolfee,1,,,,,unpaid,,0.0,,,\r\n' 378 382 ) 379 383 return … … 389 393 self.assertEqual( 390 394 result, 391 'ac,amount_auth,creation_date,p_category,p_ id,'395 'ac,amount_auth,creation_date,p_category,p_current,p_id,' 392 396 'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,' 393 397 'r_code,r_desc,student_id\r\n' 394 398 395 '666,0.0,2012-04-01 13:12:01,schoolfee, my-id,'399 '666,0.0,2012-04-01 13:12:01,schoolfee,1,my-id,' 396 400 'p-item,100,2012,unpaid,2012-04-01 14:12:01,12.12,' 397 401 'r-code,,A111111\r\n' … … 408 412 self.assertEqual( 409 413 result, 410 'ac,amount_auth,creation_date,p_category,p_ id,'414 'ac,amount_auth,creation_date,p_category,p_current,p_id,' 411 415 'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,' 412 416 'r_code,r_desc,student_id\r\n' 413 417 414 '666,0.0,2012-04-01 13:12:01,schoolfee, my-id,'418 '666,0.0,2012-04-01 13:12:01,schoolfee,1,my-id,' 415 419 'p-item,100,2012,unpaid,2012-04-01 14:12:01,12.12,' 416 420 'r-code,,A111111\r\n' … … 427 431 self.assertEqual( 428 432 result, 429 'ac,amount_auth,creation_date,p_category,p_ id,'433 'ac,amount_auth,creation_date,p_category,p_current,p_id,' 430 434 'p_item,p_level,p_session,p_state,payment_date,r_amount_approved,' 431 435 'r_code,r_desc,student_id\r\n' 432 436 433 '666,0.0,2012-04-01 13:12:01,schoolfee, my-id,'437 '666,0.0,2012-04-01 13:12:01,schoolfee,1,my-id,' 434 438 'p-item,100,2012,unpaid,2012-04-01 14:12:01,12.12,' 435 439 'r-code,,A111111\r\n' -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/tests/test_student.py
r8735 r9209 21 21 import re 22 22 import unittest 23 import grok 23 24 from cStringIO import StringIO 24 25 from datetime import tzinfo 25 from zope.component import getUtility 26 from zope.component import getUtility, queryUtility, createObject 27 from zope.catalog.interfaces import ICatalog 26 28 from zope.component.interfaces import IFactory 29 from zope.event import notify 27 30 from zope.interface import verify 31 from zope.schema.interfaces import RequiredMissing 28 32 from waeup.kofa.interfaces import IExtFileStore, IFileStoreNameChooser 29 33 from waeup.kofa.students.export import EXPORTER_NAMES … … 129 133 studylevel = StudentStudyLevel() 130 134 self.assertRaises( 131 TypeError, studylevel.addCourseTicket, department )135 TypeError, studylevel.addCourseTicket, department, department) 132 136 133 137 def test_booking_date(self): … … 236 240 return 237 241 242 class StudentTransferTests(StudentImportExportSetup): 243 244 layer = FunctionalLayer 245 246 def setUp(self): 247 super(StudentTransferTests, self).setUp() 248 249 # Add additional certificate 250 self.certificate2 = createObject('waeup.Certificate') 251 self.certificate2.code = 'CERT2' 252 self.certificate2.application_category = 'basic' 253 self.certificate2.start_level = 200 254 self.certificate2.end_level = 500 255 self.app['faculties']['fac1']['dep1'].certificates.addCertificate( 256 self.certificate2) 257 258 # Add student with subobjects 259 student = Student() 260 self.app['students'].addStudent(student) 261 student = self.setup_student(student) 262 notify(grok.ObjectModifiedEvent(student)) 263 self.student = self.app['students'][student.student_id] 264 return 265 266 def test_transfer_student(self): 267 self.assertRaises( 268 RequiredMissing, self.student.transfer, self.certificate2) 269 error = self.student.transfer(self.certificate2, current_session=1000) 270 self.assertTrue(error == -1) 271 error = self.student.transfer(self.certificate2, current_session=2013) 272 self.assertTrue(error == None) 273 self.assertEqual(self.student['studycourse_1'].certificate.code, 'CERT1') 274 self.assertEqual(self.student['studycourse'].certificate.code, 'CERT2') 275 self.assertEqual(self.student['studycourse_1'].current_session, 2012) 276 self.assertEqual(self.student['studycourse'].current_session, 2013) 277 self.assertEqual(self.student['studycourse'].entry_session, 278 self.student['studycourse_1'].entry_session) 279 self.assertEqual(self.student['studycourse_1'].__name__, 'studycourse_1') 280 logfile = os.path.join( 281 self.app['datacenter'].storage, 'logs', 'students.log') 282 logcontent = open(logfile).read() 283 self.assertTrue('system - K1000000 - transferred from CERT1 to CERT2' 284 in logcontent) 285 messages = ' '.join(self.student.history.messages) 286 self.assertMatches( 287 '...<YYYY-MM-DD hh:mm:ss> UTC - ' 288 'Transferred from CERT1 to CERT2 by system', messages) 289 290 # The students_catalog has been updated. 291 cat = queryUtility(ICatalog, name='students_catalog') 292 results = cat.searchResults(certcode=('CERT1', 'CERT1')) 293 results = [x for x in results] 294 self.assertEqual(len(results), 0) 295 results = cat.searchResults(certcode=('CERT2', 'CERT2')) 296 results = [x for x in results] 297 self.assertEqual(len(results), 1) 298 assert results[0] is self.app['students'][self.student.student_id] 299 results = cat.searchResults(current_session=(2013,2013)) 300 results = [x for x in results] 301 self.assertEqual(len(results), 1) 302 assert results[0] is self.app['students'][self.student.student_id] 303 304 # Students can be transferred (only) two times. 305 error = self.student.transfer(self.certificate, 306 current_session=2013) 307 self.assertTrue(error == None) 308 error = self.student.transfer(self.certificate2, 309 current_session=2013) 310 self.assertTrue(error == -3) 311 self.assertEqual([i for i in self.student.keys()], 312 [u'accommodation', u'payments', u'studycourse', 313 u'studycourse_1', u'studycourse_2']) 314 315 # The students_catalog has been updated again. 316 cat = queryUtility(ICatalog, name='students_catalog') 317 results = cat.searchResults(certcode=('CERT1', 'CERT1')) 318 results = [x for x in results] 319 self.assertEqual(len(results), 1) 320 assert results[0] is self.app['students'][self.student.student_id] 321 return 238 322 239 323 class StudentFactoryTest(FunctionalTestCase): -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/utils.py
r8708 r9209 19 19 """ 20 20 import grok 21 from random import SystemRandom as r22 21 from time import time 23 from datetime import datetime24 from zope.i18n import translate25 from zope.component import getUtility, createObject26 from reportlab.pdfgen import canvas27 22 from reportlab.lib import colors 28 23 from reportlab.lib.units import cm 29 from reportlab.lib.enums import TA_RIGHT30 24 from reportlab.lib.pagesizes import A4 31 from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle 32 from reportlab.platypus import (Frame, Paragraph, Image, PageBreak, Table, 33 Spacer) 34 from reportlab.platypus.tables import TableStyle 35 from reportlab.platypus.flowables import PageBreak 36 from zope.component import getUtility 25 from reportlab.lib.styles import getSampleStyleSheet 26 from reportlab.platypus import Paragraph, Image, Table, Spacer 27 from zope.component import getUtility, createObject 37 28 from zope.formlib.form import setUpEditWidgets 38 29 from zope.i18n import translate 39 30 from waeup.kofa.interfaces import ( 40 31 IExtFileStore, IKofaUtils, RETURNING, PAID, CLEARED) 41 32 from waeup.kofa.interfaces import MessageFactory as _ 42 33 from waeup.kofa.students.interfaces import IStudentsUtils 43 from waeup.kofa.utils.helpers import now44 34 45 35 SLIP_STYLE = [ … … 127 117 #data.append([Spacer(1, 12)]) 128 118 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 119 120 f_label = formatted_label(size=12) % _('Name') 121 f_label = Paragraph(f_label, style["Normal"]) 122 f_text = formatted_text(studentview.context.display_fullname, size=12) 123 f_text = Paragraph(f_text, style["Normal"]) 124 data_right.append([f_label,f_text]) 125 129 126 for widget in studentview.widgets: 130 if widget.name == 'form.adm_code':127 if 'name' in widget.name: 131 128 continue 132 129 f_label = formatted_label(size=12) % translate( … … 137 134 f_text = Paragraph(f_text, style["Normal"]) 138 135 data_right.append([f_label,f_text]) 136 137 if hasattr(studentview.context, 'certcode'): 138 f_label = formatted_label(size=12) % _('Study Course') 139 f_label = Paragraph(f_label, style["Normal"]) 140 f_text = formatted_text( 141 studentview.context['studycourse'].certificate.longtitle(), size=12) 142 f_text = Paragraph(f_text, style["Normal"]) 143 data_right.append([f_label,f_text]) 144 145 f_label = formatted_label(size=12) % _('Department') 146 f_label = Paragraph(f_label, style["Normal"]) 147 f_text = formatted_text( 148 studentview.context[ 149 'studycourse'].certificate.__parent__.__parent__.longtitle(), 150 size=12) 151 f_text = Paragraph(f_text, style["Normal"]) 152 data_right.append([f_label,f_text]) 153 154 f_label = formatted_label(size=12) % _('Faculty') 155 f_label = Paragraph(f_label, style["Normal"]) 156 f_text = formatted_text( 157 studentview.context[ 158 'studycourse'].certificate.__parent__.__parent__.__parent__.longtitle(), 159 size=12) 160 f_text = Paragraph(f_text, style["Normal"]) 161 data_right.append([f_label,f_text]) 162 139 163 table_left = Table(data_left,style=SLIP_STYLE) 140 164 table_right = Table(data_right,style=SLIP_STYLE, colWidths=[5*cm, 6*cm]) … … 165 189 return table 166 190 191 def get_signature_table(signatures, lang='en'): 192 """Return a reportlab table containing signature fields (with date). 193 """ 194 style = getSampleStyleSheet() 195 space_width = 0.4 # width in cm of space between signatures 196 table_width = 16.0 # supposed width of signature table in cms 197 # width of signature cells in cm... 198 sig_col_width = table_width - ((len(signatures) - 1) * space_width) 199 sig_col_width = sig_col_width / len(signatures) 200 data = [] 201 col_widths = [] # widths of columns 202 203 sig_style = [ 204 ('VALIGN',(0,-1),(-1,-1),'TOP'), 205 ('FONT', (0,0), (-1,-1), 'Helvetica-BoldOblique', 12), 206 ('BOTTOMPADDING', (0,0), (-1,0), 36), 207 ('TOPPADDING', (0,-1), (-1,-1), 0), 208 ] 209 for num, elem in enumerate(signatures): 210 # draw a line above each signature cell (not: empty cells in between) 211 sig_style.append( 212 ('LINEABOVE', (num*2,-1), (num*2, -1), 1, colors.black)) 213 214 row = [] 215 for signature in signatures: 216 row.append(trans(_('Date:'), lang)) 217 row.append('') 218 if len(signatures) > 1: 219 col_widths.extend([sig_col_width*cm, space_width*cm]) 220 else: 221 col_widths.extend([sig_col_width/2*cm, sig_col_width/2*cm]) 222 row.append('') # empty spaceholder on right 223 data.append(row[:-1]) 224 data.extend(([''],)*3) # insert 3 empty rows... 225 row = [] 226 for signature in signatures: 227 row.append(Paragraph(trans(signature, lang), style["Normal"])) 228 row.append('') 229 data.append(row[:-1]) 230 table = Table(data, style=sig_style, repeatRows=len(data), 231 colWidths=col_widths) 232 return table 233 167 234 def docs_as_flowables(view, lang='en'): 168 235 """Create reportlab flowables out of scanned docs. … … 190 257 if img_path is None: 191 258 pass 192 elif not img_path .endswith('.jpg'):259 elif not img_path[-4:] in ('.jpg', '.JPG'): 193 260 # reportlab requires jpg images, I think. 194 f_text = Paragraph('%s ( Not displayable)' % (261 f_text = Paragraph('%s (not displayable)' % ( 195 262 viewlet.title,), ENTRY1_STYLE) 196 263 else: … … 202 269 return data 203 270 204 def insert_footer(pdf,width,style,text=None, number_of_pages=1):205 """Render the whole footer frame.206 """207 story = []208 frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)209 tz = getUtility(IKofaUtils).tzinfo210 timestamp = now(tz).strftime("%d/%m/%Y %H:%M:%S %Z")211 left_text = '<font size=10>%s</font>' % timestamp212 story.append(Paragraph(left_text, style["Normal"]))213 frame_footer.addFromList(story,pdf)214 story = []215 frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)216 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE217 right_text = translate(_('<font size=10>${a} Page ${b} of ${c}</font>',218 mapping = {'a':text, 'b':pdf.getPageNumber(), 'c':number_of_pages}),219 'waeup.kofa', target_language=portal_language)220 story.append(Paragraph(right_text, style["Right"]))221 frame_footer.addFromList(story,pdf)222 223 271 class StudentsUtils(grok.GlobalUtility): 224 272 """A collection of methods subject to customization. … … 227 275 228 276 def getReturningData(self, student): 229 """ This method defineswhat happens after school fee payment277 """ Define what happens after school fee payment 230 278 depending on the student's senate verdict. 231 279 … … 238 286 239 287 def setReturningData(self, student): 240 """ This method defines what happens after school fee payment 241 depending on the student's senate verdict. It folllows 242 the same algorithm as getReturningData but it also sets the new 243 values 244 245 In the base configuration current level is always increased 246 by 100 no matter which verdict has been assigned. 288 """ Define what happens after school fee payment 289 depending on the student's senate verdict. 290 291 This method folllows the same algorithm as getReturningData but 292 it also sets the new values. 247 293 """ 248 294 new_session, new_level = self.getReturningData(student) … … 250 296 student['studycourse'].current_session = new_session 251 297 verdict = student['studycourse'].current_verdict 252 student['studycourse'].current_verdict = ' NY'298 student['studycourse'].current_verdict = '0' 253 299 student['studycourse'].previous_verdict = verdict 254 300 return 255 301 256 def setPaymentDetails(self, category, student): 302 def setPaymentDetails(self, category, student, 303 previous_session, previous_level): 257 304 """Create Payment object and set the payment data of a student for 258 305 the payment category specified. 259 306 260 307 """ 261 details = {}262 308 p_item = u'' 263 309 amount = 0.0 264 error = u'' 265 p_session = student['studycourse'].current_session 266 p_level = student['studycourse'].current_level 310 if previous_session: 311 p_session = previous_session 312 p_level = previous_level 313 p_current = False 314 else: 315 p_session = student['studycourse'].current_session 316 p_level = student['studycourse'].current_level 317 p_current = True 267 318 session = str(p_session) 268 319 try: … … 276 327 except (AttributeError, TypeError): 277 328 return _('Study course data are incomplete.'), None 278 if student.state == CLEARED: 279 amount = getattr(certificate, 'school_fee_1', 0.0) 280 elif student.state == RETURNING: 281 # In case of returning school fee payment the payment session 282 # and level contain the values of the session the student 283 # has paid for. 284 p_session, p_level = self.getReturningData(student) 285 amount = getattr(certificate, 'school_fee_2', 0.0) 286 elif student.is_postgrad and student.state == PAID: 287 # Returning postgraduate students also pay for the next session 288 # but their level always remains the same. 289 p_session += 1 290 amount = getattr(certificate, 'school_fee_2', 0.0) 329 if previous_session: 330 if previous_session < student['studycourse'].entry_session: 331 return _('The previous session must not fall below ' 332 'your entry session.'), None 333 if previous_session > student['studycourse'].current_session - 1: 334 return _('This is not a previous session.'), None 335 if previous_level == 100: 336 amount = getattr(certificate, 'school_fee_1', 0.0) 337 else: 338 amount = getattr(certificate, 'school_fee_2', 0.0) 339 else: 340 if student.state == CLEARED: 341 amount = getattr(certificate, 'school_fee_1', 0.0) 342 elif student.state == RETURNING: 343 # In case of returning school fee payment the payment session 344 # and level contain the values of the session the student 345 # has paid for. 346 p_session, p_level = self.getReturningData(student) 347 amount = getattr(certificate, 'school_fee_2', 0.0) 348 elif student.is_postgrad and student.state == PAID: 349 # Returning postgraduate students also pay for the next session 350 # but their level always remains the same. 351 p_session += 1 352 amount = getattr(certificate, 'school_fee_2', 0.0) 291 353 elif category == 'clearance': 292 p_item = student['studycourse'].certificate.code 354 try: 355 p_item = student['studycourse'].certificate.code 356 except (AttributeError, TypeError): 357 return _('Study course data are incomplete.'), None 293 358 amount = academic_session.clearance_fee 294 359 elif category == 'bed_allocation': … … 296 361 amount = academic_session.booking_fee 297 362 if amount in (0.0, None): 298 return _(u'Amount could not be determined.'), None 363 return _('Amount could not be determined.' + 364 ' Would you like to pay for a previous session?'), None 299 365 for key in student['payments'].keys(): 300 366 ticket = student['payments'][key] … … 303 369 ticket.p_item == p_item and \ 304 370 ticket.p_session == p_session: 305 return _('This type of payment has already been made.'), None 371 return _('This type of payment has already been made.' + 372 ' Would you like to pay for a previous session?'), None 306 373 payment = createObject(u'waeup.StudentOnlinePayment') 307 timestamp = "%d" % int(time()*1000)374 timestamp = ("%d" % int(time()*10000))[1:] 308 375 payment.p_id = "p%s" % timestamp 309 376 payment.p_category = category … … 311 378 payment.p_session = p_session 312 379 payment.p_level = p_level 380 payment.p_current = p_current 313 381 payment.amount_auth = amount 314 382 return None, payment … … 330 398 entry_session = studycourse.entry_session 331 399 current_level = studycourse.current_level 332 if not (entry_session and current_level andcertificate):333 return 400 if None in (entry_session, current_level, certificate): 401 return d 334 402 end_level = certificate.end_level 335 if entry_session == grok.getSite()['hostels'].accommodation_session: 403 if current_level == 10: 404 bt = 'pr' 405 elif entry_session == grok.getSite()['hostels'].accommodation_session: 336 406 bt = 'fr' 337 407 elif current_level >= end_level: … … 355 425 return available_beds[0] 356 426 427 def renderPDFAdmissionLetter(self, view, student=None): 428 """Render pdf admission letter. 429 """ 430 # XXX: we have to fix the import problems here. 431 from waeup.kofa.browser.interfaces import IPDFCreator 432 from waeup.kofa.browser.pdf import format_html, NOTE_STYLE 433 if student is None: 434 return 435 style = getSampleStyleSheet() 436 creator = getUtility(IPDFCreator) 437 data = [] 438 doc_title = view.label 439 author = '%s (%s)' % (view.request.principal.title, 440 view.request.principal.id) 441 footer_text = view.label 442 if getattr(student, 'student_id', None) is not None: 443 footer_text = "%s - %s - " % (student.student_id, footer_text) 444 445 # Admission text 446 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 447 inst_name = grok.getSite()['configuration'].name 448 text = trans(_( 449 'This is to inform you that you have been provisionally' 450 ' admitted into ${a} as follows:', mapping = {'a': inst_name}), 451 portal_language) 452 html = format_html(text) 453 data.append(Paragraph(html, NOTE_STYLE)) 454 data.append(Spacer(1, 20)) 455 456 # Student data 457 data.append(render_student_data(view)) 458 459 # Insert history 460 data.append(Spacer(1, 20)) 461 datelist = student.history.messages[0].split()[0].split('-') 462 creation_date = u'%s/%s/%s' % (datelist[2], datelist[1], datelist[0]) 463 text = trans(_( 464 'Your Kofa student record was created on ${a}.', 465 mapping = {'a': creation_date}), 466 portal_language) 467 html = format_html(text) 468 data.append(Paragraph(html, NOTE_STYLE)) 469 470 # Create pdf stream 471 view.response.setHeader( 472 'Content-Type', 'application/pdf') 473 pdf_stream = creator.create_pdf( 474 data, None, doc_title, author=author, footer=footer_text, 475 note=None) 476 return pdf_stream 477 357 478 def renderPDF(self, view, filename='slip.pdf', student=None, 358 479 studentview=None, tableheader=None, tabledata=None, 359 note=None ):480 note=None, signatures=None): 360 481 """Render pdf slips for various pages. 361 482 """ … … 373 494 footer_text = "%s - %s - " % (student.student_id, footer_text) 374 495 496 # Insert history 497 if not filename.startswith('payment'): 498 data.extend(creator.fromStringList(student.history.messages)) 499 375 500 # Insert student data table 376 501 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE … … 381 506 382 507 # Insert widgets 383 data.append(Paragraph(view.title, style["Heading3"])) 384 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 385 separators = getattr(self, 'SEPARATORS_DICT', {}) 386 table = creator.getWidgetsTable( 387 view.form_fields, view.context, None, lang=portal_language, 388 separators=separators) 389 data.append(table) 508 if view.form_fields: 509 data.append(Paragraph(view.title, style["Heading3"])) 510 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 511 separators = getattr(self, 'SEPARATORS_DICT', {}) 512 table = creator.getWidgetsTable( 513 view.form_fields, view.context, None, lang=portal_language, 514 separators=separators) 515 data.append(table) 390 516 391 517 # Insert scanned docs … … 399 525 contenttable = render_table_data(tableheader,tabledata) 400 526 data.append(contenttable) 527 528 # Insert signatures 529 if signatures: 530 data.append(Spacer(1, 20)) 531 signaturetable = get_signature_table(signatures) 532 data.append(signaturetable) 401 533 402 534 view.response.setHeader( … … 412 544 413 545 VERDICTS_DICT = { 414 ' NY': _('(not yet)'),546 '0': _('(not yet)'), 415 547 'A': 'Successful student', 416 548 'B': 'Student with carryover courses', -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/viewlets.py
r8736 r9209 1 ## $Id$1 3## $Id$ 2 2 ## 3 3 ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann … … 150 150 151 151 class ContactActionButton(ManageActionButton): 152 grok.order( 4)152 grok.order(5) 153 153 grok.context(IStudent) 154 154 grok.view(StudentBaseDisplayFormPage) … … 165 165 text = _('Manage') 166 166 target = 'manage_base' 167 168 class AdmissionSlipActionButton(ManageActionButton): 169 grok.order(4) 170 grok.context(IStudent) 171 grok.view(StudentBaseDisplayFormPage) 172 grok.require('waeup.viewStudent') 173 icon = 'actionicon_pdf.png' 174 text = _('Download admission letter') 175 target = 'admission_slip.pdf' 176 177 class StudentTransfernButton(ManageActionButton): 178 grok.order(6) 179 grok.context(IStudent) 180 grok.view(StudentBaseDisplayFormPage) 181 grok.require('waeup.manageStudent') 182 text = _('Transfer student') 183 target = 'transfer' 184 icon = 'actionicon_redo.png' 185 186 class StudentDeactivateActionButton(ManageActionButton): 187 grok.order(7) 188 grok.context(IStudent) 189 grok.view(StudentBaseDisplayFormPage) 190 grok.require('waeup.manageStudent') 191 text = _('Deactivate account') 192 target = 'deactivate' 193 icon = 'actionicon_traffic_lights_red.png' 194 195 @property 196 def target_url(self): 197 if self.context.suspended: 198 return '' 199 return self.view.url(self.view.context, self.target) 200 201 @property 202 def onclick(self): 203 return "return window.confirm(%s);" % _( 204 "'A history message will be added. Are you sure?'") 205 206 class StudentActivateActionButton(ManageActionButton): 207 grok.order(7) 208 grok.context(IStudent) 209 grok.view(StudentBaseDisplayFormPage) 210 grok.require('waeup.manageStudent') 211 text = _('Activate account') 212 target = 'activate' 213 icon = 'actionicon_traffic_lights_green.png' 214 215 @property 216 def target_url(self): 217 if not self.context.suspended: 218 return '' 219 return self.view.url(self.view.context, self.target) 220 221 @property 222 def onclick(self): 223 return "return window.confirm(%s);" % _( 224 "'A history message will be added. Are you sure?'") 167 225 168 226 class StudentClearanceManageActionButton(ManageActionButton): … … 231 289 target = 'view_personal' 232 290 291 class StudentPersonalManageActionButton(ManageActionButton): 292 grok.order(1) 293 grok.context(IStudent) 294 grok.view(StudentPersonalDisplayFormPage) 295 grok.require('waeup.manageStudent') 296 text = _('Manage') 297 target = 'manage_personal' 298 233 299 class StudentPersonalEditActionButton(ManageActionButton): 234 grok.order( 1)300 grok.order(2) 235 301 grok.context(IStudent) 236 302 grok.view(StudentPersonalDisplayFormPage) 237 grok.require('waeup. viewStudent')303 grok.require('waeup.handleStudent') 238 304 text = _('Edit') 239 305 target = 'edit_personal' … … 247 313 target = 'manage' 248 314 315 @property 316 def target_url(self): 317 if self.context.is_current: 318 return self.view.url(self.view.context, self.target) 319 return False 320 249 321 class StudyLevelManageActionButton(ManageActionButton): 250 322 grok.order(1) … … 254 326 text = _('Manage') 255 327 target = 'manage' 328 329 @property 330 def target_url(self): 331 is_current = self.context.__parent__.is_current 332 if not is_current: 333 return '' 334 return self.view.url(self.view.context, self.target) 256 335 257 336 class StudentValidateCoursesActionButton(ManageActionButton): … … 266 345 @property 267 346 def target_url(self): 347 is_current = self.context.__parent__.is_current 268 348 if self.context.student.state != REGISTERED or \ 269 str(self.context.__parent__.current_level) != self.context.__name__: 349 str(self.context.__parent__.current_level) != self.context.__name__ or\ 350 not is_current: 270 351 return '' 271 352 return self.view.url(self.view.context, self.target) … … 282 363 @property 283 364 def target_url(self): 365 is_current = self.context.__parent__.is_current 284 366 if self.context.student.state not in (VALIDATED, REGISTERED) or \ 285 str(self.context.__parent__.current_level) != self.context.__name__: 367 str(self.context.__parent__.current_level) != self.context.__name__ or\ 368 not is_current: 286 369 return '' 287 370 return self.view.url(self.view.context, self.target) … … 296 379 target = 'course_registration.pdf' 297 380 381 @property 382 def target_url(self): 383 is_current = self.context.__parent__.is_current 384 if not is_current: 385 return '' 386 return self.view.url(self.view.context, self.target) 387 298 388 class CourseTicketManageActionButton(ManageActionButton): 299 389 grok.order(1) … … 313 403 314 404 class PaymentReceiptActionButton(ManageActionButton): 315 grok.order( 1)405 grok.order(9) # This button should always be the last one. 316 406 grok.context(IStudentOnlinePayment) 317 407 grok.view(OnlinePaymentDisplayFormPage) … … 327 417 return self.view.url(self.view.context, self.target) 328 418 329 330 419 class ApprovePaymentActionButton(ManageActionButton): 331 grok.order( 2)420 grok.order(8) 332 421 grok.context(IStudentOnlinePayment) 333 422 grok.view(OnlinePaymentDisplayFormPage) … … 437 526 grok.require('waeup.handleStudent') 438 527 icon = 'actionicon_start.gif' 439 text = _('Start session')528 text = _('Start new session') 440 529 target = 'start_session' 441 530 442 531 @property 443 532 def target_url(self): 444 if self.context.next_session_allowed :533 if self.context.next_session_allowed and self.context.is_current: 445 534 return self.view.url(self.view.context, self.target) 446 535 return False … … 460 549 condition2 = str(student['studycourse'].current_level) in \ 461 550 self.view.context.keys() 462 if condition1 or condition2: 551 condition3 = not self.context.is_current 552 if condition1 or condition2 or condition3: 463 553 return '' 464 554 return self.view.url(self.view.context, self.target) … … 469 559 grok.view(StudyLevelDisplayFormPage) 470 560 grok.require('waeup.handleStudent') 471 text = _(' Add and remove courses')561 text = _('Edit course list') 472 562 target = 'edit' 473 563 … … 478 568 condition2 = student[ 479 569 'studycourse'].current_level != self.view.context.level 480 if condition1 or condition2: 570 is_current = self.context.__parent__.is_current 571 if condition1 or condition2 or not is_current: 481 572 return '' 482 573 return self.view.url(self.view.context, self.target) … … 542 633 @property 543 634 def targets(self): 544 student _url = self.view.application_url() + (545 '/students/%s' % self.request.principal.id)546 #app_slip = getUtility(IExtFileStore).getFileByContext(547 # self.context.student, 'application_slip')635 student = grok.getSite()['students'][self.request.principal.id] 636 student_url = self.view.url(student) 637 app_slip = getUtility(IExtFileStore).getFileByContext( 638 student, 'application_slip') 548 639 targets = [] 549 #if app_slip:550 #targets = [{'url':student_url + '/application_slip', 'title':'Application Slip'},]640 if app_slip: 641 targets = [{'url':student_url + '/application_slip', 'title':'Application Slip'},] 551 642 targets += [ 552 643 {'url':student_url, 'title':'Base Data'}, -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/vocabularies.py
r8766 r9209 36 36 nats_vocab = SimpleKofaVocabulary(*COUNTRIES) 37 37 38 def study_levels(studycourse): 39 if studycourse.certificate is not None: 40 start_level = int(studycourse.certificate.start_level) 41 end_level = int(studycourse.certificate.end_level) 38 def study_levels(context): 39 certificate = getattr(context, 'certificate', None) 40 if certificate is not None: 41 start_level = int(certificate.start_level) 42 end_level = int(certificate.end_level) 42 43 if start_level == 999 or end_level == 999: 43 44 levels = [999] … … 50 51 else: 51 52 # default level range 52 levels = [level for level in range(100,1000,10) if level % 100 < 30] 53 levels = [10, ] 54 levels += [level for level in range(100,1000,10) if level % 100 < 30] 55 levels.append(999) 53 56 return levels 54 57 … … 68 71 69 72 def getTitle(self, context, value): 70 if context.certificate is not None: 71 start_level = int(context.certificate.start_level) 72 end_level = int(context.certificate.end_level) 73 certificate = getattr(context, 'certificate', None) 74 if certificate is not None: 75 start_level = int(certificate.start_level) 76 end_level = int(certificate.end_level) 73 77 else: 74 78 # default level range 75 start_level = 10 079 start_level = 10 76 80 end_level = 1000 77 81 if start_level == 999 or end_level == 999: … … 79 83 return _('Error: wrong level id ${value}', 80 84 mapping={'value': value}) 85 if value == 999: 81 86 return course_levels.by_value[999].title 82 87 if value < start_level or value > end_level + 120: -
main/waeup.kofa/branches/uli-zc-async/src/waeup/kofa/students/workflow.py
r8471 r9209 2 2 """ 3 3 import grok 4 from datetime import datetime 4 5 from zope.component import getUtility 5 6 from hurry.workflow.workflow import Transition, WorkflowState, NullCondition … … 12 13 from waeup.kofa.workflow import KofaWorkflow, KofaWorkflowInfo 13 14 from waeup.kofa.students.interfaces import IStudent, IStudentsUtils 15 from waeup.kofa.utils.helpers import get_current_principal 14 16 15 17 16 18 IMPORTABLE_STATES = (ADMITTED, CLEARANCE, REQUESTED, CLEARED, PAID, RETURNING, 17 19 REGISTERED, VALIDATED) 20 21 FORBIDDEN_POSTGRAD_STATES = (RETURNING, REGISTERED, VALIDATED) 18 22 19 23 REGISTRATION_TRANSITIONS = ( … … 23 27 source = None, 24 28 condition = NullCondition, 25 msg = _(' Student record created'),29 msg = _('Record created'), 26 30 destination = CREATED), 27 31 … … 29 33 transition_id = 'admit', 30 34 title = _('Admit student'), 31 msg = _(' Student admitted'),35 msg = _('Admitted'), 32 36 source = CREATED, 33 37 destination = ADMITTED), … … 63 67 Transition( 64 68 transition_id = 'reset3', 65 title = _('Reset to clearance '),66 msg = _("Reset to 'clearance '"),69 title = _('Reset to clearance started'), 70 msg = _("Reset to 'clearance started'"), 67 71 source = REQUESTED, 68 72 destination = CLEARANCE), … … 77 81 Transition( 78 82 transition_id = 'reset4', 79 title = _('Reset to clearance '),80 msg = _("Reset to 'clearance '"),83 title = _('Reset to clearance started'), 84 msg = _("Reset to 'clearance started'"), 81 85 source = CLEARED, 82 86 destination = CLEARANCE), … … 85 89 transition_id = 'pay_first_school_fee', 86 90 title = _('Pay school fee'), 87 msg = _(' School fee paid'),91 msg = _('First school fee payment made'), 88 92 source = CLEARED, 89 93 destination = PAID), … … 92 96 transition_id = 'approve_first_school_fee', 93 97 title = _('Approve payment'), 94 msg = _(' School fee payment approved'),98 msg = _('First school fee payment approved'), 95 99 source = CLEARED, 96 100 destination = PAID), … … 106 110 transition_id = 'pay_school_fee', 107 111 title = _('Pay school fee'), 108 msg = _(' Payment made'),112 msg = _('School fee payment made'), 109 113 source = RETURNING, 110 114 destination = PAID), … … 112 116 Transition( 113 117 transition_id = 'pay_pg_fee', 114 title = _('Pay postgraduateschool fee'),115 msg = _('P ayment made'),118 title = _('Pay PG school fee'), 119 msg = _('PG school fee payment made'), 116 120 source = PAID, 117 121 destination = PAID), … … 119 123 Transition( 120 124 transition_id = 'approve_school_fee', 121 title = _('Approve payment'),125 title = _('Approve school fee payment'), 122 126 msg = _('School fee payment approved'), 123 127 source = RETURNING, … … 126 130 Transition( 127 131 transition_id = 'approve_pg_fee', 128 title = _('Approve postgraduate payment'),129 msg = _(' School fee payment approved'),132 title = _('Approve PG school fee payment'), 133 msg = _('PG school fee payment approved'), 130 134 source = PAID, 131 135 destination = PAID), … … 147 151 Transition( 148 152 transition_id = 'reset7', 149 title = _('Reset to paid'),150 msg = _("Reset to ' paid'"),153 title = _('Reset to school fee paid'), 154 msg = _("Reset to 'school fee paid'"), 151 155 source = REGISTERED, 152 156 destination = PAID), … … 161 165 Transition( 162 166 transition_id = 'reset8', 163 title = _('Reset to paid'),164 msg = _("Reset to ' paid'"),167 title = _('Reset to school fee paid'), 168 msg = _("Reset to 'school fee paid'"), 165 169 source = VALIDATED, 166 170 destination = PAID), … … 175 179 Transition( 176 180 transition_id = 'reset9', 177 title = _('Reset to validated'),178 msg = _("Reset to ' validated'"),181 title = _('Reset to courses validated'), 182 msg = _("Reset to 'courses validated'"), 179 183 source = RETURNING, 180 184 destination = VALIDATED), … … 183 187 IMPORTABLE_TRANSITIONS = [i.transition_id for i in REGISTRATION_TRANSITIONS] 184 188 189 FORBIDDEN_POSTGRAD_TRANS = ['reset6', 'register_courses'] 185 190 LOCK_CLEARANCE_TRANS = ('reset2', 'request_clearance') 186 191 UNLOCK_CLEARANCE_TRANS = ('reset3', 'reset4', 'start_clearance') … … 212 217 213 218 Lock and unlock clearance form. 214 Trig er actions after school fee payment.219 Trigger actions after school fee payment. 215 220 """ 216 221 … … 231 236 new_session = obj['studycourse'].current_session + 1 232 237 obj['studycourse'].current_session = new_session 238 elif event.transition.transition_id == 'validate_courses': 239 current_level = obj['studycourse'].current_level 240 level_object = obj['studycourse'].get(str(current_level), None) 241 if level_object is not None: 242 user = get_current_principal() 243 if user is None: 244 usertitle = 'system' 245 else: 246 usertitle = getattr(user, 'public_name', None) 247 if not usertitle: 248 usertitle = user.title 249 level_object.validated_by = usertitle 250 level_object.validation_date = datetime.utcnow() 251 elif event.transition.transition_id == 'reset8': 252 current_level = obj['studycourse'].current_level 253 level_object = obj['studycourse'].get(str(current_level), None) 254 if level_object is not None: 255 level_object.validated_by = None 256 level_object.validation_date = None 233 257 # In some tests we don't have a students container 234 258 try:
Note: See TracChangeset for help on using the changeset viewer.