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