- Timestamp:
- 10 Sep 2012, 11:05:07 (12 years ago)
- Location:
- main/waeup.kofa/branches/uli-async-update
- Files:
-
- 1 deleted
- 28 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.kofa/branches/uli-async-update
- Property svn:mergeinfo changed
-
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/authentication.py
r8757 r9169 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-async-update/src/waeup/kofa/students/batching.py
r8736 r9169 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-async-update/src/waeup/kofa/students/browser.py
r9166 r9169 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): … … 336 367 grok.name('manage_base') 337 368 grok.require('waeup.manageStudent') 338 form_fields = grok.AutoFields(IStudentBase).omit('student_id') 369 form_fields = grok.AutoFields(IStudentBase).omit( 370 'student_id', 'adm_code', 'suspended') 339 371 grok.template('basemanagepage') 340 372 label = _('Manage base data') … … 362 394 allowed_transitions = [t for t in self.wf_info.getManualTransitions() 363 395 if not t[0].startswith('pay')] 396 if self.context.is_postgrad: 397 allowed_transitions = [t for t in allowed_transitions 398 if not t[0] in FORBIDDEN_POSTGRAD_TRANS] 364 399 return [dict(name='', title=_('No transition'))] +[ 365 400 dict(name=x, title=y) for x, y in allowed_transitions] … … 396 431 return 397 432 433 class StudentActivatePage(UtilityView, grok.View): 434 """ Activate student account 435 """ 436 grok.context(IStudent) 437 grok.name('activate') 438 grok.require('waeup.manageStudent') 439 440 def update(self): 441 self.context.suspended = False 442 self.context.writeLogMessage(self, 'account activated') 443 history = IObjectHistory(self.context) 444 history.addMessage('Student account activated') 445 self.flash(_('Student account has been activated.')) 446 self.redirect(self.url(self.context)) 447 return 448 449 def render(self): 450 return 451 452 class StudentDeactivatePage(UtilityView, grok.View): 453 """ Deactivate student account 454 """ 455 grok.context(IStudent) 456 grok.name('deactivate') 457 grok.require('waeup.manageStudent') 458 459 def update(self): 460 self.context.suspended = True 461 self.context.writeLogMessage(self, 'account deactivated') 462 history = IObjectHistory(self.context) 463 history.addMessage('Student account deactivated') 464 self.flash(_('Student account has been deactivated.')) 465 self.redirect(self.url(self.context)) 466 return 467 468 def render(self): 469 return 470 398 471 class StudentClearanceDisplayFormPage(KofaDisplayFormPage): 399 472 """ Page to display student clearance data … … 411 484 def form_fields(self): 412 485 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') 486 form_fields = grok.AutoFields( 487 IPGStudentClearance).omit('clearance_locked') 488 else: 489 form_fields = grok.AutoFields( 490 IUGStudentClearance).omit('clearance_locked') 416 491 return form_fields 417 492 … … 432 507 def form_fields(self): 433 508 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') 509 form_fields = grok.AutoFields( 510 IPGStudentClearance).omit('clearance_locked') 511 else: 512 form_fields = grok.AutoFields( 513 IUGStudentClearance).omit('clearance_locked') 437 514 return form_fields 438 515 … … 446 523 def label(self): 447 524 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE 448 return translate(_('Clearance Slip of 525 return translate(_('Clearance Slip of'), 449 526 'waeup.kofa', target_language=portal_language) \ 450 527 + ' %s' % self.context.display_fullname 451 528 529 def _signatures(self): 530 if self.context.state == CLEARED: 531 return (_('Student Signature'), _('Clearance Officer Signature')) 532 return 533 452 534 def render(self): 453 studentview = StudentBase DisplayFormPage(self.context.student,535 studentview = StudentBasePDFFormPage(self.context.student, 454 536 self.request) 455 537 students_utils = getUtility(IStudentsUtils) 456 538 return students_utils.renderPDF( 457 539 self, 'clearance.pdf', 458 self.context.student, studentview )540 self.context.student, studentview, signatures=self._signatures()) 459 541 460 542 class StudentClearanceManageFormPage(KofaEditFormPage): … … 475 557 def form_fields(self): 476 558 if self.context.is_postgrad: 477 form_fields = grok.AutoFields(IPGStudentClearance) 478 else: 479 form_fields = grok.AutoFields(IUGStudentClearance) 559 form_fields = grok.AutoFields(IPGStudentClearance).omit('clr_code') 560 else: 561 form_fields = grok.AutoFields(IUGStudentClearance).omit('clr_code') 480 562 return form_fields 481 563 … … 557 639 mapping = {'a':self.context.display_fullname}) 558 640 559 class StudentPersonal EditFormPage(KofaEditFormPage):560 """ Page to editpersonal data641 class StudentPersonalManageFormPage(KofaEditFormPage): 642 """ Page to manage personal data 561 643 """ 562 644 grok.context(IStudent) 563 grok.name(' edit_personal')564 grok.require('waeup. handleStudent')645 grok.name('manage_personal') 646 grok.require('waeup.manageStudent') 565 647 form_fields = grok.AutoFields(IStudentPersonal) 566 label = _(' Editpersonal data')648 label = _('Manage personal data') 567 649 pnav = 4 568 650 … … 572 654 return 573 655 656 class StudentPersonalEditFormPage(StudentPersonalManageFormPage): 657 """ Page to edit personal data 658 """ 659 grok.name('edit_personal') 660 grok.require('waeup.handleStudent') 661 label = _('Edit personal data') 662 pnav = 4 663 574 664 class StudyCourseDisplayFormPage(KofaDisplayFormPage): 575 665 """ Page to display the student study course data … … 578 668 grok.name('index') 579 669 grok.require('waeup.viewStudent') 580 form_fields = grok.AutoFields(IStudentStudyCourse)581 670 grok.template('studycoursepage') 582 671 pnav = 4 583 672 584 673 @property 674 def form_fields(self): 675 if self.context.is_postgrad: 676 form_fields = grok.AutoFields(IStudentStudyCourse).omit( 677 'current_verdict', 'previous_verdict') 678 else: 679 form_fields = grok.AutoFields(IStudentStudyCourse) 680 return form_fields 681 682 @property 585 683 def label(self): 586 return _('${a}: Study Course', 587 mapping = {'a':self.context.__parent__.display_fullname}) 684 if self.context.is_current: 685 return _('${a}: Study Course', 686 mapping = {'a':self.context.__parent__.display_fullname}) 687 else: 688 return _('${a}: Previous Study Course', 689 mapping = {'a':self.context.__parent__.display_fullname}) 588 690 589 691 @property … … 606 708 return 607 709 710 @property 711 def prev_studycourses(self): 712 if self.context.is_current: 713 if self.context.__parent__.get('studycourse_2', None) is not None: 714 return ( 715 {'href':self.url(self.context.student) + '/studycourse_1', 716 'title':_('First Study Course, ')}, 717 {'href':self.url(self.context.student) + '/studycourse_2', 718 'title':_('Second Study Course')} 719 ) 720 if self.context.__parent__.get('studycourse_1', None) is not None: 721 return ( 722 {'href':self.url(self.context.student) + '/studycourse_1', 723 'title':_('First Study Course')}, 724 ) 725 return 726 608 727 class StudyCourseManageFormPage(KofaEditFormPage): 609 728 """ Page to edit the student study course data … … 613 732 grok.require('waeup.manageStudent') 614 733 grok.template('studycoursemanagepage') 615 form_fields = grok.AutoFields(IStudentStudyCourse)616 734 label = _('Manage study course') 617 735 pnav = 4 … … 620 738 tabthreeactions = [_('Add study level')] 621 739 740 @property 741 def form_fields(self): 742 if self.context.is_postgrad: 743 form_fields = grok.AutoFields(IStudentStudyCourse).omit( 744 'current_verdict', 'previous_verdict') 745 else: 746 form_fields = grok.AutoFields(IStudentStudyCourse) 747 return form_fields 748 622 749 def update(self): 750 if not self.context.is_current: 751 emit_lock_message(self) 752 return 623 753 super(StudyCourseManageFormPage, self).update() 624 754 tabs.need() … … 685 815 return 686 816 817 class StudentTransferFormPage(KofaAddFormPage): 818 """Page to transfer the student. 819 """ 820 grok.context(IStudent) 821 grok.name('transfer') 822 grok.require('waeup.manageStudent') 823 label = _('Transfer student') 824 form_fields = grok.AutoFields(IStudentStudyCourseTransfer).omit( 825 'entry_mode', 'entry_session') 826 pnav = 4 827 828 def update(self): 829 super(StudentTransferFormPage, self).update() 830 warning.need() 831 return 832 833 @jsaction(_('Transfer')) 834 def transferStudent(self, **data): 835 error = self.context.transfer(**data) 836 if error == -1: 837 self.flash(_('Current level does not match certificate levels.')) 838 elif error == -2: 839 self.flash(_('Former study course record incomplete.')) 840 elif error == -3: 841 self.flash(_('Maximum number of transfers exceeded.')) 842 else: 843 self.flash(_('Successfully transferred.')) 844 return 845 687 846 class StudyLevelDisplayFormPage(KofaDisplayFormPage): 688 847 """ Page to display student study levels … … 692 851 grok.require('waeup.viewStudent') 693 852 form_fields = grok.AutoFields(IStudentStudyLevel) 853 form_fields[ 854 'validation_date'].custom_widget = FriendlyDatetimeDisplayWidget('le') 694 855 grok.template('studylevelpage') 695 856 pnav = 4 … … 702 863 @property 703 864 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 865 return translated_values(self) 714 866 715 867 @property … … 771 923 Mand = translate(_('Mand.'), 'waeup.kofa', target_language=portal_language) 772 924 Score = translate(_('Score'), 'waeup.kofa', target_language=portal_language) 773 studentview = StudentBase DisplayFormPage(self.context.student,925 studentview = StudentBasePDFFormPage(self.context.student, 774 926 self.request) 775 927 students_utils = getUtility(IStudentsUtils) … … 796 948 grok.require('waeup.manageStudent') 797 949 grok.template('studylevelmanagepage') 798 form_fields = grok.AutoFields(IStudentStudyLevel) 950 form_fields = grok.AutoFields(IStudentStudyLevel).omit( 951 'validation_date', 'validated_by') 799 952 pnav = 4 800 953 taboneactions = [_('Save'),_('Cancel')] … … 803 956 804 957 def update(self): 958 if not self.context.__parent__.is_current: 959 emit_lock_message(self) 960 return 805 961 super(StudyLevelManageFormPage, self).update() 806 962 tabs.need() … … 813 969 datatable.need() 814 970 return 971 972 @property 973 def translated_values(self): 974 return translated_values(self) 815 975 816 976 @property … … 861 1021 862 1022 def update(self): 1023 if not self.context.__parent__.is_current: 1024 emit_lock_message(self) 1025 return 863 1026 if str(self.context.__parent__.current_level) != self.context.__name__: 864 1027 self.flash(_('This level does not correspond current level.')) … … 883 1046 884 1047 def update(self): 1048 if not self.context.__parent__.is_current: 1049 emit_lock_message(self) 1050 return 885 1051 if str(self.context.__parent__.current_level) != self.context.__name__: 886 1052 self.flash(_('This level does not correspond current level.')) … … 918 1084 pnav = 4 919 1085 1086 def update(self): 1087 if not self.context.__parent__.is_current: 1088 emit_lock_message(self) 1089 return 1090 super(CourseTicketAddFormPage, self).update() 1091 return 1092 920 1093 @action(_('Add course ticket')) 921 1094 def addCourseTicket(self, **data): … … 924 1097 ticket.automatic = False 925 1098 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 1099 try: 934 self.context.addCourseTicket(ticket )1100 self.context.addCourseTicket(ticket, course) 935 1101 except KeyError: 936 1102 self.flash(_('The ticket exists.')) … … 1030 1196 mapping = {'a': ', '.join(deleted)})) 1031 1197 self.context.writeLogMessage( 1032 self,'removed: % 1198 self,'removed: %s' % ', '.join(deleted)) 1033 1199 self.redirect(self.url(self.context)) 1034 1200 return … … 1052 1218 def createTicket(self, **data): 1053 1219 p_category = data['p_category'] 1220 previous_session = data.get('p_session', None) 1221 previous_level = data.get('p_level', None) 1054 1222 student = self.context.__parent__ 1055 1223 if p_category == 'bed_allocation' and student[ … … 1062 1230 return 1063 1231 students_utils = getUtility(IStudentsUtils) 1064 error, payment = students_utils.setPaymentDetails(p_category, student) 1232 error, payment = students_utils.setPaymentDetails( 1233 p_category, student, previous_session, previous_level) 1065 1234 if error is not None: 1066 1235 self.flash(error) 1236 if 'Would you like' in error: 1237 self.redirect(self.url(self.context) + '/@@addpp') 1238 return 1067 1239 self.redirect(self.url(self.context)) 1068 1240 return … … 1071 1243 self.redirect(self.url(self.context)) 1072 1244 return 1245 1246 class PreviousPaymentAddFormPage(OnlinePaymentAddFormPage): 1247 """ Page to add an online payment ticket for previous sessions 1248 """ 1249 grok.context(IStudentPaymentsContainer) 1250 grok.name('addpp') 1251 grok.require('waeup.payStudent') 1252 form_fields = grok.AutoFields(IStudentPreviousPayment).select( 1253 'p_category', 'p_session', 'p_level') 1254 label = _('Add previous session online payment') 1255 pnav = 4 1073 1256 1074 1257 class OnlinePaymentDisplayFormPage(KofaDisplayFormPage): … … 1149 1332 # self.redirect(self.url(self.context)) 1150 1333 # return 1151 studentview = StudentBase DisplayFormPage(self.context.student,1334 studentview = StudentBasePDFFormPage(self.context.student, 1152 1335 self.request) 1153 1336 students_utils = getUtility(IStudentsUtils) … … 1369 1552 1370 1553 def render(self): 1371 studentview = StudentBase DisplayFormPage(self.context.student,1554 studentview = StudentBasePDFFormPage(self.context.student, 1372 1555 self.request) 1373 1556 students_utils = getUtility(IStudentsUtils) … … 1569 1752 self.flash(_('Activation code is invalid.')) 1570 1753 return 1754 if code.state == USED: 1755 self.flash(_('Activation code has already been used.')) 1756 return 1571 1757 # Mark pin as used (this also fires a pin related transition) 1572 1758 # 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 1759 comment = _(u"invalidated") 1760 # Here we know that the ac is in state initialized so we do not 1761 # expect an exception, but the owner might be different 1762 if not invalidate_accesscode(pin, comment, self.context.student_id): 1763 self.flash(_('You are not the owner of this access code.')) 1764 return 1765 self.context.clr_code = pin 1584 1766 IWorkflowInfo(self.context).fireTransition('start_clearance') 1585 1767 self.flash(_('Clearance process has been started.')) … … 1598 1780 def form_fields(self): 1599 1781 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') 1782 form_fields = grok.AutoFields(IPGStudentClearance).omit( 1783 'clearance_locked', 'clr_code') 1784 else: 1785 form_fields = grok.AutoFields(IUGStudentClearance).omit( 1786 'clearance_locked', 'clr_code') 1603 1787 return form_fields 1604 1788 … … 1627 1811 return 1628 1812 self.flash(_('Clearance form has been saved.')) 1629 self.redirect(self.url(self.context,'request_clearance')) 1813 if self.context.clr_code: 1814 self.redirect(self.url(self.context, 'request_clearance')) 1815 else: 1816 # We bypass the request_clearance page if student 1817 # has been imported in state 'clearance started' and 1818 # no clr_code was entered before. 1819 state = IWorkflowState(self.context).getState() 1820 if state != CLEARANCE: 1821 # This shouldn't happen, but the application officer 1822 # might have forgotten to lock the form after changing the state 1823 self.flash(_('This form cannot be submitted. Wrong state!')) 1824 return 1825 IWorkflowInfo(self.context).fireTransition('request_clearance') 1826 self.flash(_('Clearance has been requested.')) 1827 self.redirect(self.url(self.context)) 1630 1828 return 1631 1829 … … 1647 1845 return 1648 1846 pin = '%s-%s-%s' % (self.ac_prefix, self.ac_series, self.ac_number) 1649 if self.context.clr_code != pin:1847 if self.context.clr_code and self.context.clr_code != pin: 1650 1848 self.flash(_("This isn't your CLR access code.")) 1651 1849 return 1652 1850 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 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 1656 1854 self.flash(_('This form cannot be submitted. Wrong state!')) 1657 1855 return … … 1673 1871 1674 1872 def update(self, SUBMIT=None): 1873 if not self.context.is_current: 1874 emit_lock_message(self) 1875 return 1876 super(StartSessionPage, self).update() 1675 1877 if not self.context.next_session_allowed: 1676 1878 self.flash(_("You are not entitled to start session.")) … … 1730 1932 1731 1933 def update(self): 1934 if not self.context.is_current: 1935 emit_lock_message(self) 1936 return 1732 1937 if self.context.student.state != PAID: 1733 1938 emit_lock_message(self) … … 1762 1967 1763 1968 def update(self): 1969 if not self.context.__parent__.is_current: 1970 emit_lock_message(self) 1971 return 1764 1972 if self.context.student.state != PAID: 1765 1973 emit_lock_message(self) … … 1776 1984 level_title = translate(self.context.level_title, 'waeup.kofa', 1777 1985 target_language=lang) 1778 return _(' Add and remove course tickets of study level${a}',1986 return _('Edit course list of ${a}', 1779 1987 mapping = {'a':level_title}) 1780 1988 … … 1785 1993 total_credits += val.credits 1786 1994 return total_credits 1995 1996 @property 1997 def translated_values(self): 1998 return translated_values(self) 1787 1999 1788 2000 @action(_('Add course ticket')) … … 1847 2059 ticket = createObject(u'waeup.CourseTicket') 1848 2060 course = data['course'] 1849 for name in ['code', 'title', 'credits', 'passmark', 'semester']:1850 setattr(ticket, name, getattr(course, name))1851 2061 ticket.automatic = False 2062 ticket.carry_over = False 1852 2063 try: 1853 self.context.addCourseTicket(ticket )2064 self.context.addCourseTicket(ticket, course) 1854 2065 except KeyError: 1855 2066 self.flash(_('The ticket exists.')) … … 1927 2138 grok.template('requestpw') 1928 2139 form_fields = grok.AutoFields(IStudentRequestPW).select( 1929 'firstname',' reg_number','email')2140 'firstname','number','email') 1930 2141 label = _('Request password for first-time login') 1931 2142 … … 1948 2159 return True 1949 2160 1950 @action(_(' Get login credentials'), style='primary')2161 @action(_('Send login credentials to email address'), style='primary') 1951 2162 def get_credentials(self, **data): 1952 2163 if not self.captcha_result.is_valid: … … 1954 2165 # No need to flash something. 1955 2166 return 1956 reg_number = data.get('reg_number','')2167 number = data.get('number','') 1957 2168 firstname = data.get('firstname','') 1958 2169 cat = getUtility(ICatalog, name='students_catalog') 1959 2170 results = list( 1960 cat.searchResults(reg_number=(reg_number, reg_number))) 2171 cat.searchResults(reg_number=(number, number))) 2172 if not results: 2173 results = list( 2174 cat.searchResults(matric_number=(number, number))) 1961 2175 if results: 1962 2176 student = results[0] … … 1984 2198 kofa_utils = getUtility(IKofaUtils) 1985 2199 password = kofa_utils.genPassword() 1986 IUserAccount(student).setPassword(password) 2200 mandate = PasswordMandate() 2201 mandate.params['password'] = password 2202 mandate.params['user'] = student 2203 site = grok.getSite() 2204 site['mandates'].addMandate(mandate) 1987 2205 # Send email with credentials 1988 login_url = self.url(grok.getSite(), 'login') 2206 args = {'mandate_id':mandate.mandate_id} 2207 mandate_url = self.url(site) + '/mandate?%s' % urlencode(args) 2208 url_info = u'Confirmation link: %s' % mandate_url 1989 2209 msg = _('You have successfully requested a password for the') 1990 2210 if kofa_utils.sendCredentials(IUserAccount(student), 1991 password, login_url, msg):2211 password, url_info, msg): 1992 2212 email_sent = student.email 1993 2213 else: … … 1995 2215 self._redirect(email=email_sent, password=password, 1996 2216 student_id=student.student_id) 2217 ob_class = self.__implemented__.__name__.replace('waeup.kofa.','') 2218 self.context.logger.info( 2219 '%s - %s (%s) - %s' % (ob_class, number, student.student_id, email_sent)) 1997 2220 return 1998 2221 -
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/browser_templates/requestpw.pt
r8782 r9169 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-async-update/src/waeup/kofa/students/browser_templates/requestpwmailsent.pt
r8782 r9169 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-async-update/src/waeup/kofa/students/browser_templates/studycoursepage.pt
r7876 r9169 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-async-update/src/waeup/kofa/students/browser_templates/studyleveleditpage.pt
r8553 r9169 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-async-update/src/waeup/kofa/students/browser_templates/studylevelmanagepage.pt
r7811 r9169 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-async-update/src/waeup/kofa/students/browser_templates/studylevelpage.pt
r8141 r9169 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-async-update/src/waeup/kofa/students/dynamicroles.py
r7811 r9169 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-async-update/src/waeup/kofa/students/export.py
r8736 r9169 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-async-update/src/waeup/kofa/students/interfaces.py
r9004 r9169 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') 154 158 fullname = Attribute('All name parts separated by hyphens') 155 159 display_fullname = Attribute('The fullname of an applicant') 160 is_postgrad = Attribute('True if postgraduate student') 161 162 suspended = schema.Bool( 163 title = _(u'Account suspended'), 164 default = False, 165 required = False, 166 ) 156 167 157 168 student_id = schema.TextLine( … … 198 209 title = _(u'PWD Activation Code'), 199 210 required = False, 200 readonly = True,211 readonly = False, 201 212 ) 202 213 … … 212 223 ) 213 224 225 def transfer(certificate, current_session, 226 current_level, current_verdict): 227 """ Creates a new studycourse and backups the old one. 228 229 """ 230 214 231 class IUGStudentClearance(IKofaObject): 215 232 """Representation of undergraduate student clearance data. … … 225 242 title = _(u'Clearance form locked'), 226 243 default = False, 244 required = False, 227 245 ) 228 246 … … 230 248 title = _(u'CLR Activation Code'), 231 249 required = False, 232 readonly = True,250 readonly = False, 233 251 ) 234 252 … … 288 306 login for the the first time. 289 307 """ 290 reg_number = schema.TextLine(291 title = u'Registration Number',308 number = schema.TextLine( 309 title = _(u'Registr. or Matric. Number'), 292 310 required = True, 293 311 ) … … 346 364 title = _(u'Current Verdict'), 347 365 source = VerdictSource(), 348 default = ' NY',366 default = '0', 349 367 required = False, 350 368 ) … … 353 371 title = _(u'Previous Verdict'), 354 372 source = VerdictSource(), 355 default = 'NY', 356 required = False, 357 ) 373 default = '0', 374 required = False, 375 ) 376 377 class IStudentStudyCourseTransfer(IStudentStudyCourse): 378 """An student transfers. 379 380 """ 381 382 certificate = schema.Choice( 383 title = _(u'Certificate'), 384 source = CertificateSource(), 385 required = True, 386 ) 387 388 current_level = schema.Choice( 389 title = _(u'Current Level'), 390 source = StudyLevelSource(), 391 required = True, 392 readonly = False, 393 ) 394 395 396 IStudentStudyCourseTransfer['certificate'].order = IStudentStudyCourse[ 397 'certificate'].order 398 IStudentStudyCourseTransfer['current_level'].order = IStudentStudyCourse[ 399 'current_level'].order 358 400 359 401 class IStudentVerdictUpdate(IKofaObject): … … 385 427 """ 386 428 level = Attribute('The level code') 387 validation_date = Attribute('The date of validation')388 validated_by = Attribute('User Id of course adviser')389 429 390 430 level_session = schema.Choice( 391 431 title = _(u'Session'), 392 432 source = academic_sessions_vocab, 393 required = True,433 required = False, 394 434 ) 395 435 … … 397 437 title = _(u'Verdict'), 398 438 source = VerdictSource(), 399 default = 'NY', 400 required = False, 401 ) 402 403 def addCourseTicket(courseticket): 439 default = '0', 440 required = False, 441 ) 442 443 validated_by = schema.TextLine( 444 title = _(u'Validated by'), 445 default = None, 446 required = False, 447 ) 448 449 validation_date = schema.Datetime( 450 title = _(u'Validation Date'), 451 required = False, 452 readonly = False, 453 ) 454 455 def addCourseTicket(ticket, course): 404 456 """Add a course ticket object. 405 457 """ … … 520 572 """ 521 573 574 p_current = schema.Bool( 575 title = _(u'Current Session Payment'), 576 default = True, 577 required = False, 578 ) 579 522 580 p_level = schema.Int( 523 581 title = _(u'Payment Level'), … … 543 601 IStudentOnlinePayment['p_level'].order = IStudentOnlinePayment[ 544 602 'p_session'].order 603 604 class IStudentPreviousPayment(IOnlinePayment): 605 """An interface for adding previous session payments. 606 607 """ 608 609 p_session = schema.Choice( 610 title = _(u'Payment Session'), 611 source = academic_sessions_vocab, 612 required = True, 613 ) 614 615 p_level = schema.Choice( 616 title = _(u'Payment Level'), 617 source = StudyLevelSource(), 618 required = True, 619 ) 545 620 546 621 class ICSVStudentExporter(ICSVExporter): -
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/payments.py
r8736 r9169 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-async-update/src/waeup/kofa/students/student.py
r8737 r9169 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 132 139 def current_mode(self): 133 140 certificate = getattr( … … 142 149 self.get('studycourse', None), 'is_postgrad', False) 143 150 return is_postgrad 151 152 def transfer(self, certificate, current_session=None, 153 current_level=None, current_verdict=None, previous_verdict=None): 154 """ Creates a new studycourse and backups the old one. 155 156 """ 157 studycourse = createObject(u'waeup.StudentStudyCourse') 158 try: 159 studycourse.certificate = certificate 160 studycourse.entry_mode = 'transfer' 161 studycourse.current_session = current_session 162 studycourse.current_level = current_level 163 studycourse.current_verdict = current_verdict 164 studycourse.previous_verdict = previous_verdict 165 except ConstraintNotSatisfied: 166 return -1 167 old = self['studycourse'] 168 if getattr(old, 'entry_session', None) is None or\ 169 getattr(old, 'certificate', None) is None: 170 return -2 171 studycourse.entry_session = old.entry_session 172 # Students can be transferred only two times. 173 if 'studycourse_1' in self.keys(): 174 if 'studycourse_2' in self.keys(): 175 return -3 176 self['studycourse_2'] = old 177 else: 178 self['studycourse_1'] = old 179 del self['studycourse'] 180 self['studycourse'] = studycourse 181 self.__parent__.logger.info( 182 '%s - transferred from %s to %s' % ( 183 self.student_id, old.certificate.code, studycourse.certificate.code)) 184 history = IObjectHistory(self) 185 history.addMessage('Transferred from %s to %s' % ( 186 old.certificate.code, studycourse.certificate.code)) 187 return 144 188 145 189 -
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/studycourse.py
r8736 r9169 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-async-update/src/waeup/kofa/students/studylevel.py
r8736 r9169 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-async-update/src/waeup/kofa/students/tests/sample_payment_data.csv
r8244 r9169 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-async-update/src/waeup/kofa/students/tests/test_authentication.py
r8351 r9169 65 65 email = None 66 66 phone = None 67 suspended = False 67 68 68 69 -
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/tests/test_batching.py
r8626 r9169 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-async-update/src/waeup/kofa/students/tests/test_browser.py
r8779 r9169 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 … … 454 455 self.assertEqual(self.browser.headers['Status'], '200 Ok') 455 456 self.assertEqual(self.browser.url, self.personal_path) 457 self.browser.getLink("Manage").click() 458 self.assertEqual(self.browser.headers['Status'], '200 Ok') 459 self.assertEqual(self.browser.url, self.manage_personal_path) 460 self.browser.open(self.personal_path) 456 461 self.browser.getLink("Edit").click() 457 462 self.assertEqual(self.browser.headers['Status'], '200 Ok') … … 474 479 self.assertEqual(self.browser.headers['Status'], '200 Ok') 475 480 self.assertEqual(self.browser.url, self.history_path) 476 self.assertMatches('... Student admitted by Manager...',481 self.assertMatches('...Admitted by Manager...', 477 482 self.browser.contents) 478 483 # Only the Application Slip does not exist … … 754 759 self.assertEqual(student['studycourse'].current_session, 2005) # +1 755 760 self.assertEqual(student['studycourse'].current_level, 200) # +100 756 self.assertEqual(student['studycourse'].current_verdict, ' NY') # NY= not set761 self.assertEqual(student['studycourse'].current_verdict, '0') # 0 = Zero = not set 757 762 self.assertEqual(student['studycourse'].previous_verdict, 'A') 758 763 self.browser.getControl(name="transition").value = ['register_courses'] … … 762 767 self.browser.getControl(name="transition").value = ['return'] 763 768 self.browser.getControl("Save").click() 769 return 770 771 def test_manage_pg_workflow(self): 772 # Managers can pass through the whole workflow 773 IWorkflowState(self.student).setState('school fee paid') 774 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 775 student = self.app['students'][self.student_id] 776 self.browser.open(self.manage_student_path) 777 self.assertTrue('<option value="reset6">' in self.browser.contents) 778 self.assertTrue('<option value="register_courses">' in self.browser.contents) 779 self.assertTrue('<option value="reset5">' in self.browser.contents) 780 self.certificate.study_mode = 'pg_ft' 781 self.browser.open(self.manage_student_path) 782 self.assertFalse('<option value="reset6">' in self.browser.contents) 783 self.assertFalse('<option value="register_courses">' in self.browser.contents) 784 self.assertTrue('<option value="reset5">' in self.browser.contents) 764 785 return 765 786 … … 776 797 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 777 798 self.browser.open(datacenter_path) 778 self.browser.getLink('Upload CSV file').click()799 self.browser.getLink('Upload data').click() 779 800 filecontents = StringIO(open('students.csv', 'rb').read()) 780 801 filewidget = self.browser.getControl(name='uploadfile:file') 781 802 filewidget.add_file(filecontents, 'text/plain', 'students.csv') 782 803 self.browser.getControl(name='SUBMIT').click() 783 self.browser.getLink(' Batch processing').click()804 self.browser.getLink('Process data').click() 784 805 button = lookup_submit_value( 785 806 'select', 'students_zope.mgr.csv', self.browser) … … 802 823 """) 803 824 self.browser.open(datacenter_path) 804 self.browser.getLink('Upload CSV file').click()825 self.browser.getLink('Upload data').click() 805 826 filecontents = StringIO(open('studycourses.csv', 'rb').read()) 806 827 filewidget = self.browser.getControl(name='uploadfile:file') 807 828 filewidget.add_file(filecontents, 'text/plain', 'studycourses.csv') 808 829 self.browser.getControl(name='SUBMIT').click() 809 self.browser.getLink(' Batch processing').click()830 self.browser.getLink('Process data').click() 810 831 button = lookup_submit_value( 811 832 'select', 'studycourses_zope.mgr.csv', self.browser) … … 917 938 self.assertTrue('clearance started' in self.browser.contents) 918 939 self.browser.open(self.history_path) 919 self.assertTrue("Reset to 'clearance ' by My Public Name" in940 self.assertTrue("Reset to 'clearance started' by My Public Name" in 920 941 self.browser.contents) 921 942 IWorkflowInfo(self.student).fireTransition('request_clearance') … … 946 967 self.app['users'].addUser('mrsadvise', 'mrsadvisesecret') 947 968 self.app['users']['mrsadvise'].email = 'mradvise@foo.ng' 948 self.app['users']['mrsadvise'].title = 'Helen Procter'969 self.app['users']['mrsadvise'].title = u'Helen Procter' 949 970 # Assign local CourseAdviser100 role for a certificate 950 971 cert = self.app['faculties']['fac1']['dep1'].certificates['CERT1'] … … 997 1018 # the 100L CA does see the 'Validate' button 998 1019 self.browser.open(L110_student_path) 999 self.assertFalse('Validate ' in self.browser.contents)1020 self.assertFalse('Validate courses' in self.browser.contents) 1000 1021 IWorkflowInfo(self.student).fireTransition('register_courses') 1001 1022 self.browser.open(L110_student_path) 1002 self.assertFalse('Validate ' in self.browser.contents)1023 self.assertFalse('Validate courses' in self.browser.contents) 1003 1024 self.student['studycourse'].current_level = 110 1004 1025 self.browser.open(L110_student_path) 1005 self.assertTrue('Validate ' in self.browser.contents)1026 self.assertTrue('Validate courses' in self.browser.contents) 1006 1027 # ... but a 100L CA does not see the button on other levels 1007 1028 studylevel2 = StudentStudyLevel() … … 1011 1032 L200_student_path = self.studycourse_path + '/200' 1012 1033 self.browser.open(L200_student_path) 1013 self.assertFalse('Validate ' in self.browser.contents)1034 self.assertFalse('Validate courses' in self.browser.contents) 1014 1035 self.browser.open(L110_student_path) 1015 1036 self.browser.getLink("Validate courses").click() 1016 1037 self.assertTrue('Course list has been validated' in self.browser.contents) 1017 1038 self.assertTrue('courses validated' in self.browser.contents) 1039 self.assertEqual(self.student['studycourse']['110'].validated_by, 1040 'Helen Procter') 1041 self.assertMatches( 1042 '<YYYY-MM-DD hh:mm:ss>', 1043 self.student['studycourse']['110'].validation_date.strftime( 1044 "%Y-%m-%d %H:%M:%S")) 1018 1045 self.browser.getLink("Reject courses").click() 1019 1046 self.assertTrue('Course list request has been annulled.' … … 1023 1050 '/contactstudent?subject=%s' % urlmessage) 1024 1051 self.assertTrue('school fee paid' in self.browser.contents) 1052 self.assertTrue(self.student['studycourse']['110'].validated_by is None) 1053 self.assertTrue(self.student['studycourse']['110'].validation_date is None) 1025 1054 IWorkflowInfo(self.student).fireTransition('register_courses') 1026 1055 self.browser.open(L110_student_path) … … 1142 1171 # and can perform actions 1143 1172 IWorkflowInfo(self.student).fireTransition('admit') 1173 # Students can't login if their account is suspended/deactivated 1174 self.student.suspended = True 1144 1175 self.browser.open(self.login_path) 1145 1176 self.browser.getControl(name="form.login").value = self.student_id 1146 1177 self.browser.getControl(name="form.password").value = 'spwd' 1147 1178 self.browser.getControl("Login").click() 1179 self.assertTrue( 1180 'Your account has been deactivated.' in self.browser.contents) 1181 self.student.suspended = False 1182 self.browser.getControl("Login").click() 1183 self.assertTrue( 1184 'You logged in.' in self.browser.contents) 1148 1185 # Student can upload a passport picture 1149 1186 self.browser.open(self.student_path + '/change_portrait') … … 1170 1207 self.assertMatches('...Not all required fields filled...', 1171 1208 self.browser.contents) 1172 self.student.email = 'aa@aa.ng' 1209 self.browser.open(self.student_path + '/edit_base') 1210 self.browser.getControl(name="form.email").value = 'aa@aa.ng' 1211 self.browser.getControl("Save").click() 1173 1212 self.browser.open(self.student_path + '/start_clearance') 1174 1213 self.browser.getControl(name="ac_series").value = '3' … … 1185 1224 # Set the correct owner 1186 1225 self.existing_clrac.owner = self.student_id 1226 # clr_code might be set (and thus returns None) due importing 1227 # an empty clr_code column. 1228 self.student.clr_code = None 1187 1229 self.browser.getControl("Start clearance now").click() 1188 1230 self.assertMatches('...Clearance process has been started...', … … 1235 1277 self.browser.getControl("Create course list now").click() 1236 1278 self.browser.getLink("100").click() 1237 self.browser.getLink(" Add and remove courses").click()1279 self.browser.getLink("Edit course list").click() 1238 1280 self.browser.getControl("Add course ticket").click() 1239 1281 self.browser.getControl(name="form.course").value = ['COURSE1'] … … 1248 1290 self.browser.getControl("Create course list now").click() 1249 1291 self.browser.getLink("200").click() 1250 self.browser.getLink(" Add and remove courses").click()1292 self.browser.getLink("Edit course list").click() 1251 1293 self.browser.getControl("Add course ticket").click() 1252 1294 self.browser.getControl(name="form.course").value = ['COURSE1'] … … 1286 1328 self.assertEqual(self.student.state, 'courses registered') 1287 1329 return 1330 1331 def test_student_clearance_wo_clrcode(self): 1332 IWorkflowState(self.student).setState('clearance started') 1333 self.browser.open(self.login_path) 1334 self.browser.getControl(name="form.login").value = self.student_id 1335 self.browser.getControl(name="form.password").value = 'spwd' 1336 self.browser.getControl("Login").click() 1337 self.student.clearance_locked = False 1338 self.browser.open(self.edit_clearance_path) 1339 self.browser.getControl(name="form.date_of_birth").value = '09/10/1961' 1340 self.browser.getControl("Save and request clearance").click() 1341 self.assertMatches('...Clearance has been requested...', 1342 self.browser.contents) 1288 1343 1289 1344 def test_manage_payments(self): … … 1516 1571 # The new SFE-0 pin can be used for starting new session 1517 1572 self.browser.open(self.studycourse_path) 1518 self.browser.getLink('Start session').click()1573 self.browser.getLink('Start new session').click() 1519 1574 pin = self.app['accesscodes']['SFE-0'].keys()[0] 1520 1575 parts = pin.split('-')[1:] … … 1528 1583 return 1529 1584 1530 def test_postgraduate_payments(self): 1585 def test_student_previous_payments(self): 1586 # Login 1587 self.browser.open(self.login_path) 1588 self.browser.getControl(name="form.login").value = self.student_id 1589 self.browser.getControl(name="form.password").value = 'spwd' 1590 self.browser.getControl("Login").click() 1591 1592 # Students can add previous school fee payment tickets in any state. 1593 IWorkflowState(self.student).setState('courses registered') 1594 self.browser.open(self.payments_path) 1595 self.browser.getControl("Add online payment ticket").click() 1596 self.browser.getControl(name="form.p_category").value = ['schoolfee'] 1597 self.browser.getControl("Create ticket").click() 1598 1599 # Amount cannot be determined since the state is not 1600 # 'cleared' or 'returning' 1601 self.assertMatches('...Amount could not be determined...', 1602 self.browser.contents) 1603 self.assertMatches('...Would you like to pay for a previous session?...', 1604 self.browser.contents) 1605 1606 # Previous session payment form is provided 1607 self.browser.getControl(name="form.p_category").value = ['schoolfee'] 1608 self.browser.getControl(name="form.p_session").value = ['2004'] 1609 self.browser.getControl(name="form.p_level").value = ['300'] 1610 self.browser.getControl("Create ticket").click() 1611 self.assertMatches('...ticket created...', 1612 self.browser.contents) 1613 ctrl = self.browser.getControl(name='val_id') 1614 value = ctrl.options[0] 1615 self.browser.getLink(value).click() 1616 self.assertMatches('...Amount Authorized...', 1617 self.browser.contents) 1618 self.assertEqual(self.student['payments'][value].amount_auth, 20000.0) 1619 1620 # Payment session is properly set 1621 self.assertEqual(self.student['payments'][value].p_session, 2004) 1622 self.assertEqual(self.student['payments'][value].p_level, 300) 1623 1624 # We simulate the approval 1625 self.browser.open(self.browser.url + '/fake_approve') 1626 self.assertMatches('...Payment approved...', 1627 self.browser.contents) 1628 1629 # No AC has been created 1630 self.assertEqual(len(self.app['accesscodes']['SFE-0'].keys()), 0) 1631 self.assertTrue(self.student['payments'][value].ac is None) 1632 1633 # Current payment flag is set False 1634 self.assertFalse(self.student['payments'][value].p_current) 1635 return 1636 1637 def test_student_postgraduate_payments(self): 1531 1638 self.certificate.study_mode = 'pg_ft' 1532 1639 self.certificate.start_level = 999 … … 1552 1659 self.browser.contents) 1553 1660 # Payment session and level are current ones. 1554 # Postgrads have to school_fee_1.1661 # Postgrads have to pay school_fee_1. 1555 1662 self.assertEqual(self.student['payments'][value].amount_auth, 40000.0) 1556 1663 self.assertEqual(self.student['payments'][value].p_session, 2004) … … 1565 1672 # The new SFE-0 pin can be used for starting session 1566 1673 self.browser.open(self.studycourse_path) 1567 self.browser.getLink('Start session').click()1674 self.browser.getLink('Start new session').click() 1568 1675 pin = self.app['accesscodes']['SFE-0'].keys()[0] 1569 1676 parts = pin.split('-')[1:] … … 1605 1712 # The new SFE-1 pin can be used for starting new session 1606 1713 self.browser.open(self.studycourse_path) 1607 self.browser.getLink('Start session').click()1714 self.browser.getLink('Start new session').click() 1608 1715 self.browser.getControl(name="ac_series").value = sfeseries 1609 1716 self.browser.getControl(name="ac_number").value = sfenumber … … 1850 1957 self.assertTrue('An email with' in self.browser.contents) 1851 1958 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 1869 1959 def test_change_current_mode(self): 1870 1960 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') … … 1888 1978 self.assertTrue('Employer' in self.browser.contents) 1889 1979 1980 def test_activate_deactivate_buttons(self): 1981 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 1982 self.browser.open(self.student_path) 1983 self.browser.getLink("Deactivate").click() 1984 self.assertTrue( 1985 'Student account has been deactivated.' in self.browser.contents) 1986 self.assertTrue( 1987 'Base Data (account deactivated)' in self.browser.contents) 1988 self.assertTrue(self.student.suspended) 1989 self.browser.getLink("Activate").click() 1990 self.assertTrue( 1991 'Student account has been activated.' in self.browser.contents) 1992 self.assertFalse( 1993 'Base Data (account deactivated)' in self.browser.contents) 1994 self.assertFalse(self.student.suspended) 1995 # History messages have been added ... 1996 self.browser.getLink("History").click() 1997 self.assertTrue( 1998 'Student account deactivated by Manager<br />' in self.browser.contents) 1999 self.assertTrue( 2000 'Student account activated by Manager<br />' in self.browser.contents) 2001 # ... and actions have been logged. 2002 logfile = os.path.join( 2003 self.app['datacenter'].storage, 'logs', 'students.log') 2004 logcontent = open(logfile).read() 2005 self.assertTrue('zope.mgr - students.browser.StudentDeactivatePage - ' 2006 'K1000000 - account deactivated' in logcontent) 2007 self.assertTrue('zope.mgr - students.browser.StudentActivatePage - ' 2008 'K1000000 - account activated' in logcontent) 2009 2010 def test_student_transfer(self): 2011 # Add second certificate 2012 self.certificate2 = createObject('waeup.Certificate') 2013 self.certificate2.code = u'CERT2' 2014 self.certificate2.study_mode = 'ug_ft' 2015 self.certificate2.start_level = 999 2016 self.certificate2.end_level = 999 2017 self.app['faculties']['fac1']['dep1'].certificates.addCertificate( 2018 self.certificate2) 2019 2020 # Add study level to old study course 2021 studylevel = createObject(u'waeup.StudentStudyLevel') 2022 studylevel.level = 200 2023 self.student['studycourse'].addStudentStudyLevel( 2024 self.certificate, studylevel) 2025 2026 self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') 2027 self.browser.open(self.student_path) 2028 self.browser.getLink("Transfer").click() 2029 self.browser.getControl(name="form.certificate").value = ['CERT2'] 2030 self.browser.getControl(name="form.current_session").value = ['2011'] 2031 self.browser.getControl(name="form.current_level").value = ['200'] 2032 self.browser.getControl("Transfer").click() 2033 self.assertTrue( 2034 'Current level does not match certificate levels' 2035 in self.browser.contents) 2036 self.browser.getControl(name="form.current_level").value = ['999'] 2037 self.browser.getControl("Transfer").click() 2038 self.assertTrue('Successfully transferred' in self.browser.contents) 2039 2040 # Add study level to new study course 2041 studylevel = createObject(u'waeup.StudentStudyLevel') 2042 studylevel.level = 200 2043 self.student['studycourse'].addStudentStudyLevel( 2044 self.certificate, studylevel) 2045 2046 # Edit and add pages are locked for old study courses 2047 self.browser.open(self.student_path + '/studycourse/manage') 2048 self.assertFalse('The requested form is locked' in self.browser.contents) 2049 self.browser.open(self.student_path + '/studycourse_1/manage') 2050 self.assertTrue('The requested form is locked' in self.browser.contents) 2051 2052 self.browser.open(self.student_path + '/studycourse/start_session') 2053 self.assertFalse('The requested form is locked' in self.browser.contents) 2054 self.browser.open(self.student_path + '/studycourse_1/start_session') 2055 self.assertTrue('The requested form is locked' in self.browser.contents) 2056 2057 IWorkflowState(self.student).setState('school fee paid') 2058 self.browser.open(self.student_path + '/studycourse/add') 2059 self.assertFalse('The requested form is locked' in self.browser.contents) 2060 self.browser.open(self.student_path + '/studycourse_1/add') 2061 self.assertTrue('The requested form is locked' in self.browser.contents) 2062 2063 self.browser.open(self.student_path + '/studycourse/200/manage') 2064 self.assertFalse('The requested form is locked' in self.browser.contents) 2065 self.browser.open(self.student_path + '/studycourse_1/200/manage') 2066 self.assertTrue('The requested form is locked' in self.browser.contents) 2067 2068 self.browser.open(self.student_path + '/studycourse/200/validate_courses') 2069 self.assertFalse('The requested form is locked' in self.browser.contents) 2070 self.browser.open(self.student_path + '/studycourse_1/200/validate_courses') 2071 self.assertTrue('The requested form is locked' in self.browser.contents) 2072 2073 self.browser.open(self.student_path + '/studycourse/200/reject_courses') 2074 self.assertFalse('The requested form is locked' in self.browser.contents) 2075 self.browser.open(self.student_path + '/studycourse_1/200/reject_courses') 2076 self.assertTrue('The requested form is locked' in self.browser.contents) 2077 2078 self.browser.open(self.student_path + '/studycourse/200/add') 2079 self.assertFalse('The requested form is locked' in self.browser.contents) 2080 self.browser.open(self.student_path + '/studycourse_1/200/add') 2081 self.assertTrue('The requested form is locked' in self.browser.contents) 2082 2083 self.browser.open(self.student_path + '/studycourse/200/edit') 2084 self.assertFalse('The requested form is locked' in self.browser.contents) 2085 self.browser.open(self.student_path + '/studycourse_1/200/edit') 2086 self.assertTrue('The requested form is locked' in self.browser.contents) 2087 1890 2088 class StudentRequestPWTests(StudentsFullSetup): 1891 2089 # Tests for student registration … … 1894 2092 1895 2093 def test_request_pw(self): 1896 # Student with wrong reg_number can't be found.2094 # Student with wrong number can't be found. 1897 2095 self.browser.open('http://localhost/app/requestpw') 1898 2096 self.browser.getControl(name="form.firstname").value = 'Anna' 1899 self.browser.getControl(name="form. reg_number").value = 'anynumber'2097 self.browser.getControl(name="form.number").value = 'anynumber' 1900 2098 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 1901 self.browser.getControl(" Getlogin credentials").click()2099 self.browser.getControl("Send login credentials").click() 1902 2100 self.assertTrue('No student record found.' 1903 2101 in self.browser.contents) … … 1906 2104 self.browser.open('http://localhost/app/requestpw') 1907 2105 self.browser.getControl(name="form.firstname").value = 'Johnny' 1908 self.browser.getControl(name="form. reg_number").value = '123'2106 self.browser.getControl(name="form.number").value = '123' 1909 2107 self.browser.getControl(name="form.email").value = 'xx@yy.zz' 1910 self.browser.getControl(" Getlogin credentials").click()2108 self.browser.getControl("Send login credentials").click() 1911 2109 self.assertTrue('No student record found.' 1912 2110 in self.browser.contents) … … 1914 2112 # password has been set and used. 1915 2113 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()2114 self.browser.getControl(name="form.number").value = '123' 2115 self.browser.getControl("Send login credentials").click() 1918 2116 self.assertTrue('Your password has already been set and used.' 1919 2117 in self.browser.contents) … … 1922 2120 # The firstname field, used for verification, is not case-sensitive. 1923 2121 self.browser.getControl(name="form.firstname").value = 'aNNa' 1924 self.browser.getControl(name="form. reg_number").value = '123'2122 self.browser.getControl(name="form.number").value = '123' 1925 2123 self.browser.getControl(name="form.email").value = 'new@yy.zz' 1926 self.browser.getControl(" Getlogin credentials").click()2124 self.browser.getControl("Send login credentials").click() 1927 2125 # Yeah, we succeded ... 2126 self.assertTrue('Your password request was successful.' 2127 in self.browser.contents) 2128 # We can also use the matric_number instead. 2129 self.browser.open('http://localhost/app/requestpw') 2130 self.browser.getControl(name="form.firstname").value = 'aNNa' 2131 self.browser.getControl(name="form.number").value = '234' 2132 self.browser.getControl(name="form.email").value = 'new@yy.zz' 2133 self.browser.getControl("Send login credentials").click() 1928 2134 self.assertTrue('Your password request was successful.' 1929 2135 in self.browser.contents) … … 1934 2140 email=('new@yy.zz', 'new@yy.zz'))) 1935 2141 self.assertEqual(self.student,results[0]) 2142 logfile = os.path.join( 2143 self.app['datacenter'].storage, 'logs', 'main.log') 2144 logcontent = open(logfile).read() 2145 self.assertTrue('zope.anybody - students.browser.StudentRequestPasswordPage - ' 2146 '234 (K1000000) - new@yy.zz' in logcontent) 1936 2147 return -
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/tests/test_dynamicroles.py
r7811 r9169 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-async-update/src/waeup/kofa/students/tests/test_export.py
r8621 r9169 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-async-update/src/waeup/kofa/students/tests/test_student.py
r8735 r9169 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-async-update/src/waeup/kofa/students/utils.py
r8708 r9169 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(studentview.context.certcode, size=12) 141 f_text = Paragraph(f_text, style["Normal"]) 142 data_right.append([f_label,f_text]) 143 139 144 table_left = Table(data_left,style=SLIP_STYLE) 140 145 table_right = Table(data_right,style=SLIP_STYLE, colWidths=[5*cm, 6*cm]) … … 165 170 return table 166 171 172 def get_signature_table(signatures, lang='en'): 173 """Return a reportlab table containing signature fields (with date). 174 """ 175 style = getSampleStyleSheet() 176 space_width = 0.4 # width in cm of space between signatures 177 table_width = 16.0 # supposed width of signature table in cms 178 # width of signature cells in cm... 179 sig_col_width = table_width - ((len(signatures) - 1) * space_width) 180 sig_col_width = sig_col_width / len(signatures) 181 data = [] 182 col_widths = [] # widths of columns 183 184 sig_style = [ 185 ('VALIGN',(0,-1),(-1,-1),'TOP'), 186 ('FONT', (0,0), (-1,-1), 'Helvetica-BoldOblique', 12), 187 ('BOTTOMPADDING', (0,0), (-1,0), 36), 188 ('TOPPADDING', (0,-1), (-1,-1), 0), 189 ] 190 for num, elem in enumerate(signatures): 191 # draw a line above each signature cell (not: empty cells in between) 192 sig_style.append( 193 ('LINEABOVE', (num*2,-1), (num*2, -1), 1, colors.black)) 194 195 row = [] 196 for signature in signatures: 197 row.append(trans(_('Date:'), lang)) 198 row.append('') 199 if len(signatures) > 1: 200 col_widths.extend([sig_col_width*cm, space_width*cm]) 201 else: 202 col_widths.extend([sig_col_width/2*cm, sig_col_width/2*cm]) 203 row.append('') # empty spaceholder on right 204 data.append(row[:-1]) 205 data.extend(([''],)*3) # insert 3 empty rows... 206 row = [] 207 for signature in signatures: 208 row.append(Paragraph(trans(signature, lang), style["Normal"])) 209 row.append('') 210 data.append(row[:-1]) 211 table = Table(data, style=sig_style, repeatRows=len(data), 212 colWidths=col_widths) 213 return table 214 167 215 def docs_as_flowables(view, lang='en'): 168 216 """Create reportlab flowables out of scanned docs. … … 190 238 if img_path is None: 191 239 pass 192 elif not img_path .endswith('.jpg'):240 elif not img_path[-4:] in ('.jpg', '.JPG'): 193 241 # reportlab requires jpg images, I think. 194 f_text = Paragraph('%s ( Not displayable)' % (242 f_text = Paragraph('%s (not displayable)' % ( 195 243 viewlet.title,), ENTRY1_STYLE) 196 244 else: … … 202 250 return data 203 251 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 252 class StudentsUtils(grok.GlobalUtility): 224 253 """A collection of methods subject to customization. … … 227 256 228 257 def getReturningData(self, student): 229 """ This method defineswhat happens after school fee payment258 """ Define what happens after school fee payment 230 259 depending on the student's senate verdict. 231 260 … … 238 267 239 268 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. 269 """ Define what happens after school fee payment 270 depending on the student's senate verdict. 271 272 This method folllows the same algorithm as getReturningData but 273 it also sets the new values. 247 274 """ 248 275 new_session, new_level = self.getReturningData(student) … … 250 277 student['studycourse'].current_session = new_session 251 278 verdict = student['studycourse'].current_verdict 252 student['studycourse'].current_verdict = ' NY'279 student['studycourse'].current_verdict = '0' 253 280 student['studycourse'].previous_verdict = verdict 254 281 return 255 282 256 def setPaymentDetails(self, category, student): 283 def setPaymentDetails(self, category, student, 284 previous_session, previous_level): 257 285 """Create Payment object and set the payment data of a student for 258 286 the payment category specified. 259 287 260 288 """ 261 details = {}262 289 p_item = u'' 263 290 amount = 0.0 264 error = u'' 265 p_session = student['studycourse'].current_session 266 p_level = student['studycourse'].current_level 291 if previous_session: 292 p_session = previous_session 293 p_level = previous_level 294 p_current = False 295 else: 296 p_session = student['studycourse'].current_session 297 p_level = student['studycourse'].current_level 298 p_current = True 267 299 session = str(p_session) 268 300 try: … … 276 308 except (AttributeError, TypeError): 277 309 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) 310 if previous_session: 311 if previous_level == 100: 312 amount = getattr(certificate, 'school_fee_1', 0.0) 313 else: 314 amount = getattr(certificate, 'school_fee_2', 0.0) 315 else: 316 if student.state == CLEARED: 317 amount = getattr(certificate, 'school_fee_1', 0.0) 318 elif student.state == RETURNING: 319 # In case of returning school fee payment the payment session 320 # and level contain the values of the session the student 321 # has paid for. 322 p_session, p_level = self.getReturningData(student) 323 amount = getattr(certificate, 'school_fee_2', 0.0) 324 elif student.is_postgrad and student.state == PAID: 325 # Returning postgraduate students also pay for the next session 326 # but their level always remains the same. 327 p_session += 1 328 amount = getattr(certificate, 'school_fee_2', 0.0) 291 329 elif category == 'clearance': 292 330 p_item = student['studycourse'].certificate.code … … 296 334 amount = academic_session.booking_fee 297 335 if amount in (0.0, None): 298 return _(u'Amount could not be determined.'), None 336 return _('Amount could not be determined.' + 337 ' Would you like to pay for a previous session?'), None 299 338 for key in student['payments'].keys(): 300 339 ticket = student['payments'][key] … … 303 342 ticket.p_item == p_item and \ 304 343 ticket.p_session == p_session: 305 return _('This type of payment has already been made.'), None 344 return _('This type of payment has already been made.' + 345 ' Would you like to pay for a previous session?'), None 306 346 payment = createObject(u'waeup.StudentOnlinePayment') 307 timestamp = "%d" % int(time()*1000)347 timestamp = ("%d" % int(time()*10000))[1:] 308 348 payment.p_id = "p%s" % timestamp 309 349 payment.p_category = category … … 311 351 payment.p_session = p_session 312 352 payment.p_level = p_level 353 payment.p_current = p_current 313 354 payment.amount_auth = amount 314 355 return None, payment … … 333 374 return 334 375 end_level = certificate.end_level 335 if entry_session == grok.getSite()['hostels'].accommodation_session: 376 if current_level == 10: 377 bt = 'pr' 378 elif entry_session == grok.getSite()['hostels'].accommodation_session: 336 379 bt = 'fr' 337 380 elif current_level >= end_level: … … 357 400 def renderPDF(self, view, filename='slip.pdf', student=None, 358 401 studentview=None, tableheader=None, tabledata=None, 359 note=None ):402 note=None, signatures=None): 360 403 """Render pdf slips for various pages. 361 404 """ … … 373 416 footer_text = "%s - %s - " % (student.student_id, footer_text) 374 417 418 # Insert history 419 if not filename.startswith('payment'): 420 data.extend(creator.fromStringList(student.history.messages)) 421 375 422 # Insert student data table 376 423 portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE … … 399 446 contenttable = render_table_data(tableheader,tabledata) 400 447 data.append(contenttable) 448 449 # Insert signatures 450 if signatures: 451 data.append(Spacer(1, 20)) 452 signaturetable = get_signature_table(signatures) 453 data.append(signaturetable) 401 454 402 455 view.response.setHeader( … … 412 465 413 466 VERDICTS_DICT = { 414 ' NY': _('(not yet)'),467 '0': _('(not yet)'), 415 468 'A': 'Successful student', 416 469 'B': 'Student with carryover courses', -
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/viewlets.py
r8736 r9169 1 ## $Id$1 3## $Id$ 2 2 ## 3 3 ## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann … … 166 166 target = 'manage_base' 167 167 168 class StudentTransfernButton(ManageActionButton): 169 grok.order(5) 170 grok.context(IStudent) 171 grok.view(StudentBaseDisplayFormPage) 172 grok.require('waeup.manageStudent') 173 text = _('Transfer student') 174 target = 'transfer' 175 icon = 'actionicon_redo.png' 176 177 class StudentDeactivateActionButton(ManageActionButton): 178 grok.order(6) 179 grok.context(IStudent) 180 grok.view(StudentBaseDisplayFormPage) 181 grok.require('waeup.manageStudent') 182 text = _('Deactivate account') 183 target = 'deactivate' 184 icon = 'actionicon_traffic_lights_red.png' 185 186 @property 187 def target_url(self): 188 if self.context.suspended: 189 return '' 190 return self.view.url(self.view.context, self.target) 191 192 @property 193 def onclick(self): 194 return "return window.confirm(%s);" % _( 195 "'A history message will be added. Are you sure?'") 196 197 class StudentActivateActionButton(ManageActionButton): 198 grok.order(6) 199 grok.context(IStudent) 200 grok.view(StudentBaseDisplayFormPage) 201 grok.require('waeup.manageStudent') 202 text = _('Activate account') 203 target = 'activate' 204 icon = 'actionicon_traffic_lights_green.png' 205 206 @property 207 def target_url(self): 208 if not self.context.suspended: 209 return '' 210 return self.view.url(self.view.context, self.target) 211 212 @property 213 def onclick(self): 214 return "return window.confirm(%s);" % _( 215 "'A history message will be added. Are you sure?'") 216 168 217 class StudentClearanceManageActionButton(ManageActionButton): 169 218 grok.order(1) … … 231 280 target = 'view_personal' 232 281 282 class StudentPersonalManageActionButton(ManageActionButton): 283 grok.order(1) 284 grok.context(IStudent) 285 grok.view(StudentPersonalDisplayFormPage) 286 grok.require('waeup.manageStudent') 287 text = _('Manage') 288 target = 'manage_personal' 289 233 290 class StudentPersonalEditActionButton(ManageActionButton): 234 grok.order( 1)291 grok.order(2) 235 292 grok.context(IStudent) 236 293 grok.view(StudentPersonalDisplayFormPage) 237 grok.require('waeup. viewStudent')294 grok.require('waeup.handleStudent') 238 295 text = _('Edit') 239 296 target = 'edit_personal' … … 247 304 target = 'manage' 248 305 306 @property 307 def target_url(self): 308 if self.context.is_current: 309 return self.view.url(self.view.context, self.target) 310 return False 311 249 312 class StudyLevelManageActionButton(ManageActionButton): 250 313 grok.order(1) … … 254 317 text = _('Manage') 255 318 target = 'manage' 319 320 @property 321 def target_url(self): 322 is_current = self.context.__parent__.is_current 323 if not is_current: 324 return '' 325 return self.view.url(self.view.context, self.target) 256 326 257 327 class StudentValidateCoursesActionButton(ManageActionButton): … … 266 336 @property 267 337 def target_url(self): 338 is_current = self.context.__parent__.is_current 268 339 if self.context.student.state != REGISTERED or \ 269 str(self.context.__parent__.current_level) != self.context.__name__: 340 str(self.context.__parent__.current_level) != self.context.__name__ or\ 341 not is_current: 270 342 return '' 271 343 return self.view.url(self.view.context, self.target) … … 282 354 @property 283 355 def target_url(self): 356 is_current = self.context.__parent__.is_current 284 357 if self.context.student.state not in (VALIDATED, REGISTERED) or \ 285 str(self.context.__parent__.current_level) != self.context.__name__: 358 str(self.context.__parent__.current_level) != self.context.__name__ or\ 359 not is_current: 286 360 return '' 287 361 return self.view.url(self.view.context, self.target) … … 296 370 target = 'course_registration.pdf' 297 371 372 @property 373 def target_url(self): 374 is_current = self.context.__parent__.is_current 375 if not is_current: 376 return '' 377 return self.view.url(self.view.context, self.target) 378 298 379 class CourseTicketManageActionButton(ManageActionButton): 299 380 grok.order(1) … … 313 394 314 395 class PaymentReceiptActionButton(ManageActionButton): 315 grok.order( 1)396 grok.order(9) # This button should always be the last one. 316 397 grok.context(IStudentOnlinePayment) 317 398 grok.view(OnlinePaymentDisplayFormPage) … … 327 408 return self.view.url(self.view.context, self.target) 328 409 329 330 410 class ApprovePaymentActionButton(ManageActionButton): 331 grok.order( 2)411 grok.order(8) 332 412 grok.context(IStudentOnlinePayment) 333 413 grok.view(OnlinePaymentDisplayFormPage) … … 437 517 grok.require('waeup.handleStudent') 438 518 icon = 'actionicon_start.gif' 439 text = _('Start session')519 text = _('Start new session') 440 520 target = 'start_session' 441 521 442 522 @property 443 523 def target_url(self): 444 if self.context.next_session_allowed :524 if self.context.next_session_allowed and self.context.is_current: 445 525 return self.view.url(self.view.context, self.target) 446 526 return False … … 460 540 condition2 = str(student['studycourse'].current_level) in \ 461 541 self.view.context.keys() 462 if condition1 or condition2: 542 condition3 = not self.context.is_current 543 if condition1 or condition2 or condition3: 463 544 return '' 464 545 return self.view.url(self.view.context, self.target) … … 469 550 grok.view(StudyLevelDisplayFormPage) 470 551 grok.require('waeup.handleStudent') 471 text = _(' Add and remove courses')552 text = _('Edit course list') 472 553 target = 'edit' 473 554 … … 478 559 condition2 = student[ 479 560 'studycourse'].current_level != self.view.context.level 480 if condition1 or condition2: 561 is_current = self.context.__parent__.is_current 562 if condition1 or condition2 or not is_current: 481 563 return '' 482 564 return self.view.url(self.view.context, self.target) -
main/waeup.kofa/branches/uli-async-update/src/waeup/kofa/students/vocabularies.py
r8766 r9169 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-async-update/src/waeup/kofa/students/workflow.py
r8471 r9169 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.