source: main/waeup.kofa/trunk/src/waeup/kofa/students/interfaces.py @ 9913

Last change on this file since 9913 was 9874, checked in by Henrik Bettermann, 12 years ago

Balance must not be negative or zero.

  • Property svn:keywords set to Id
File size: 21.5 KB
RevLine 
[7191]1## $Id: interfaces.py 9874 2013-01-12 08:51:37Z henrik $
[6621]2##
[7191]3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
[8409]18#from datetime import datetime
[7620]19from zope.component import getUtility
[7256]20from zope.interface import Attribute, Interface
[6621]21from zope import schema
[7620]22from zc.sourcefactory.contextual import BasicContextualSourceFactory
[9217]23from waeup.kofa.browser.interfaces import IStudentNavigationBase
[7811]24from waeup.kofa.interfaces import (
[8409]25    IKofaObject, academic_sessions_vocab, validate_email, ICSVExporter)
[7811]26from waeup.kofa.interfaces import MessageFactory as _
[8176]27from waeup.kofa.schema import TextLineChoice, FormattedDate, PhoneNumber
[7811]28from waeup.kofa.students.vocabularies import (
[7915]29    StudyLevelSource, contextual_reg_num_source, contextual_mat_num_source,
[9420]30    GenderSource, nats_vocab
[7915]31    )
[8160]32from waeup.kofa.payments.interfaces import (
[8174]33    IPaymentsContainer, IOnlinePayment)
[7915]34from waeup.kofa.university.vocabularies import (
[9864]35    CourseSource, StudyModeSource, CertificateSource, SemesterSource,
36    ContextualDictSourceFactoryBase)
[6621]37
[9864]38class PreviousPaymentCategorySource(ContextualDictSourceFactoryBase):
39    """A source that delivers all selectable categories of previous session
40    payments.
41    """
42    #: name of dict to deliver from kofa utils.
43    DICT_NAME = 'PREVIOUS_PAYMENT_CATEGORIES'
44
[9868]45class BalancePaymentCategorySource(ContextualDictSourceFactoryBase):
[9864]46    """A source that delivers all selectable items of balance payments.
47    """
48    #: name of dict to deliver from kofa utils.
[9868]49    DICT_NAME = 'BALANCE_PAYMENT_CATEGORIES'
[9864]50
[7620]51# VerdictSource can't be placed into the vocabularies module because it
52# requires importing IStudentsUtils which then leads to circular imports.
53class VerdictSource(BasicContextualSourceFactory):
54    """A verdicts source delivers all verdicts provided
55    in the portal.
56    """
57    def getValues(self, context):
[7841]58        verdicts_dict = getUtility(IStudentsUtils).VERDICTS_DICT
[8820]59        return sorted(verdicts_dict.keys())
[7620]60
61    def getToken(self, context, value):
62        return value
63
64    def getTitle(self, context, value):
[7841]65        verdicts_dict = getUtility(IStudentsUtils).VERDICTS_DICT
[8820]66        if value != '0':
67            return verdicts_dict[value] + ' (%s)' % value
[7688]68        return verdicts_dict[value]
[7620]69
[7681]70
[7150]71class IStudentsUtils(Interface):
72    """A collection of methods which are subject to customization.
73
74    """
[7841]75    def setReturningData(student):
76        """ This method defines what happens after school fee payment
77        depending on the student's senate verdict.
78
79        In the base configuration current level is always increased
80        by 100 no matter which verdict has been assigned.
81        """
82
[9148]83    def setPaymentDetails(category, student, previous_session=None,
84            previous_level=None,):
[8595]85        """Create Payment object and set the payment data of a student for
86        the payment category specified.
[7150]87
88        """
89
[7186]90    def getAccommodation_details(student):
[7150]91        """Determine the accommodation dates of a student.
92
93        """
94
[7186]95    def selectBed(available_beds):
[7150]96        """Select a bed from a list of available beds.
97
98        In the standard configuration we select the first bed found,
99        but can also randomize the selection if we like.
100        """
101
[7186]102    def renderPDF(view, subject='', filename='slip.pdf',):
[7150]103        """Render pdf slips for various pages.
104
105        """
106
[7819]107class IStudentsContainer(IKofaObject):
[7096]108    """A students container contains university students.
[6692]109
110    """
111    def addStudent(student):
112        """Add an IStudent object and subcontainers.
113
114        """
115
116    def archive(id=None):
117        """Create on-dist archive of students.
118
119        If id is `None`, all students are archived.
120
121        If id contains a single id string, only the respective
122        students are archived.
123
124        If id contains a list of id strings all of the respective
125        students types are saved to disk.
126        """
127
128    def clear(id=None, archive=True):
129        """Remove students of type given by 'id'.
130
131        Optionally archive the students.
132
133        If id is `None`, all students are archived.
134
135        If id contains a single id string, only the respective
136        students are archived.
137
138        If id contains a list of id strings all of the respective
139        student types are saved to disk.
140
141        If `archive` is ``False`` none of the archive-handling is done
142        and respective students are simply removed from the
143        database.
144        """
145
[8408]146    unique_student_id = Attribute("""A unique student id.""")
147
[9217]148class IStudentNavigation(IStudentNavigationBase):
[8735]149    """Interface needed for student navigation, logging, etc.
[7150]150
[6642]151    """
[8736]152    student = Attribute('Student object of context.')
[7150]153
[8735]154    def writeLogMessage(view, message):
155        """Write a view specific log message into students.log.
156
157        """
158
[7819]159class IStudentBase(IKofaObject):
[6631]160    """Representation of student base data.
[7150]161
[6621]162    """
[7062]163    history = Attribute('Object history, a list of messages')
[6637]164    state = Attribute('Returns the registration state of a student')
[6699]165    password = Attribute('Encrypted password of a student')
[9334]166    temp_password = Attribute(
167        'Dictionary with user name, timestamp and encrypted password')
[7203]168    certcode = Attribute('The certificate code of any chosen study course')
169    depcode = Attribute('The department code of any chosen study course')
170    faccode = Attribute('The faculty code of any chosen study course')
[9761]171    entry_session = Attribute('The entry session of the student')
[7062]172    current_session = Attribute('The current session of the student')
[9142]173    current_level = Attribute('The current level of the student')
[7641]174    current_mode = Attribute('The current mode of the student')
[9182]175    current_verdict = Attribute('The current verdict of the student')
[7364]176    fullname = Attribute('All name parts separated by hyphens')
177    display_fullname = Attribute('The fullname of an applicant')
[8969]178    is_postgrad = Attribute('True if postgraduate student')
[6637]179
[8983]180    suspended = schema.Bool(
181        title = _(u'Account suspended'),
182        default = False,
[9035]183        required = False,
[8983]184        )
185
[9702]186    suspended_comment = schema.Text(
187        title = _(u"Reasons for Deactivation"),
188        required = False,
189        description = _(
190            u'This message will be shown if and only if deactivated '
191            'students try to login.'),
192        )
193
[6665]194    student_id = schema.TextLine(
[7723]195        title = _(u'Student Id'),
[6849]196        required = False,
[6665]197        )
198
[7357]199    firstname = schema.TextLine(
[7723]200        title = _(u'First Name'),
[6621]201        required = True,
202        )
203
[7357]204    middlename = schema.TextLine(
[7723]205        title = _(u'Middle Name'),
[7357]206        required = False,
207        )
208
209    lastname = schema.TextLine(
[7723]210        title = _(u'Last Name (Surname)'),
[7357]211        required = True,
212        )
213
[6996]214    sex = schema.Choice(
[7723]215        title = _(u'Sex'),
[6996]216        source = GenderSource(),
217        required = True,
218        )
219
[6788]220    reg_number = TextLineChoice(
[7723]221        title = _(u'Registration Number'),
[6696]222        required = True,
223        readonly = False,
[6788]224        source = contextual_reg_num_source,
[6696]225        )
226
[6788]227    matric_number = TextLineChoice(
[7723]228        title = _(u'Matriculation Number'),
[6750]229        required = False,
230        readonly = False,
[6788]231        source = contextual_mat_num_source,
[6750]232        )
233
[6769]234    adm_code = schema.TextLine(
[7723]235        title = _(u'PWD Activation Code'),
[6769]236        required = False,
[8977]237        readonly = False,
[6769]238        )
239
[7133]240    email = schema.ASCIILine(
[7723]241        title = _(u'Email'),
[7133]242        required = False,
243        constraint=validate_email,
244        )
[8176]245    phone = PhoneNumber(
[7723]246        title = _(u'Phone'),
[7331]247        description = u'',
[7133]248        required = False,
249        )
250
[9334]251    def setTempPassword(user, password):
252        """Set a temporary password (LDAP-compatible) SSHA encoded for
253        officers.
254
255        """
256
257    def getTempPassword():
258        """Check if a temporary password has been set and if it
259        is not expired.
260
261        Return the temporary password if valid,
262        None otherwise. Unset the temporary password if expired.
263        """
264
[9131]265    def transfer(certificate, current_session,
266        current_level, current_verdict):
267        """ Creates a new studycourse and backups the old one.
268
269        """
270
[7993]271class IUGStudentClearance(IKofaObject):
272    """Representation of undergraduate student clearance data.
[7150]273
[6631]274    """
[9543]275    officer_comment = schema.Text(
276        title = _(u"Officer's Comment"),
277        required = False,
[6631]278        )
279
[6695]280    clearance_locked = schema.Bool(
[7723]281        title = _(u'Clearance form locked'),
[6695]282        default = False,
[9035]283        required = False,
[6695]284        )
285
[6769]286    clr_code = schema.TextLine(
[7723]287        title = _(u'CLR Activation Code'),
[6769]288        required = False,
[8977]289        readonly = False,
[6769]290        )
291
[9543]292    date_of_birth = FormattedDate(
293        title = _(u'Date of Birth'),
294        required = True,
295        show_year = True,
296        )
297
[7523]298    nationality = schema.Choice(
[8069]299        vocabulary = nats_vocab,
[7723]300        title = _(u'Nationality'),
[7983]301        required = False,
[7523]302        )
303
[7993]304class IPGStudentClearance(IUGStudentClearance):
305    """Representation of postgraduate student clearance data.
306
307    """
308    employer = schema.TextLine(
309        title = _(u'Employer'),
310        required = False,
311        readonly = False,
312        )
313
[7819]314class IStudentPersonal(IKofaObject):
[6631]315    """Representation of student personal data.
[7150]316
[6631]317    """
[9543]318    personal_updated = schema.Datetime(
319        title = _(u'Updated'),
320        required = False,
321        readonly = False,
322        )
323
[6651]324    perm_address = schema.Text(
[7723]325        title = _(u'Permanent Address'),
[6631]326        required = False,
327        )
328
[8008]329class IStudent(IStudentBase,IUGStudentClearance,IPGStudentClearance,
330    IStudentPersonal):
[6631]331    """Representation of a student.
[7150]332
[6631]333    """
334
[9563]335class IStudentPersonalEdit(IStudentPersonal):
336    """Interface for editing personal data by students.
337
338    Here we can repeat the fields from IStudentPersonal and set the
339    `required` if necessary.
340    """
341
342    perm_address = schema.Text(
343        title = _(u'Permanent Address'),
344        required = True,
345        )
346
[6849]347class IStudentUpdateByRegNo(IStudent):
348    """Representation of a student. Skip regular reg_number validation.
[7150]349
[6849]350    """
351    reg_number = schema.TextLine(
[7723]352        title = _(u'Registration Number'),
[6849]353        required = False,
354        )
355
356class IStudentUpdateByMatricNo(IStudent):
357    """Representation of a student. Skip regular matric_number validation.
[7150]358
[6849]359    """
360    matric_number = schema.TextLine(
[7723]361        title = _(u'Matriculation Number'),
[6849]362        required = False,
363        )
364
[8779]365class IStudentRequestPW(IStudent):
366    """Representation of an student for first-time password request.
367
368    This interface is used when students use the requestpw page to
369    login for the the first time.
370    """
[8854]371    number = schema.TextLine(
372        title = _(u'Registr. or Matric. Number'),
[8779]373        required = True,
374        )
375
376    firstname = schema.TextLine(
377        title = _(u'First Name'),
378        required = True,
379        )
380
381    email = schema.ASCIILine(
382        title = _(u'Email Address'),
383        required = True,
384        constraint=validate_email,
385        )
386
[7819]387class IStudentStudyCourse(IKofaObject):
[6633]388    """A container for student study levels.
389
390    """
[6648]391    certificate = schema.Choice(
[7723]392        title = _(u'Certificate'),
[6648]393        source = CertificateSource(),
[7209]394        required = False,
[6633]395        )
[6635]396
[6996]397    entry_mode = schema.Choice(
[7723]398        title = _(u'Entry Mode'),
[7681]399        source = StudyModeSource(),
[6996]400        required = True,
401        readonly = False,
402        )
403
404    entry_session = schema.Choice(
[7723]405        title = _(u'Entry Session'),
[6996]406        source = academic_sessions_vocab,
[7425]407        #default = datetime.now().year,
[6996]408        required = True,
409        readonly = False,
410        )
411
[6724]412    current_session = schema.Choice(
[7723]413        title = _(u'Current Session'),
[6744]414        source = academic_sessions_vocab,
[6724]415        required = True,
[6996]416        readonly = False,
[6724]417        )
418
419    current_level = schema.Choice(
[7723]420        title = _(u'Current Level'),
[6725]421        source = StudyLevelSource(),
422        required = False,
[6996]423        readonly = False,
[6724]424        )
425
426    current_verdict = schema.Choice(
[7723]427        title = _(u'Current Verdict'),
[7619]428        source = VerdictSource(),
[8820]429        default = '0',
[6725]430        required = False,
[6724]431        )
432
433    previous_verdict = schema.Choice(
[7723]434        title = _(u'Previous Verdict'),
[7619]435        source = VerdictSource(),
[8820]436        default = '0',
[6725]437        required = False,
[6724]438        )
439
[9138]440class IStudentStudyCourseTransfer(IStudentStudyCourse):
441    """An student transfers.
442
443    """
444
445    certificate = schema.Choice(
446        title = _(u'Certificate'),
447        source = CertificateSource(),
448        required = True,
449        )
450
451    current_level = schema.Choice(
452        title = _(u'Current Level'),
453        source = StudyLevelSource(),
454        required = True,
455        readonly = False,
456        )
457
458
459IStudentStudyCourseTransfer['certificate'].order = IStudentStudyCourse[
460    'certificate'].order
461IStudentStudyCourseTransfer['current_level'].order = IStudentStudyCourse[
462    'current_level'].order
463
[7951]464class IStudentVerdictUpdate(IKofaObject):
465    """A interface for verdict imports.
466
467    """
468
469    current_verdict = schema.Choice(
470        title = _(u'Current Verdict'),
471        source = VerdictSource(),
472        required = True,
473        )
474
475    current_session = schema.Choice(
476        title = _(u'Current Session'),
477        source = academic_sessions_vocab,
478        required = True,
479        )
480
481    current_level = schema.Choice(
482        title = _(u'Current Level'),
483        source = StudyLevelSource(),
484        required = True,
485        )
486
[9296]487    bypass_validation = schema.Bool(
488        title = _(u'Bypass validation'),
489        required = False,
490        )
491
492    validated_by = schema.TextLine(
493        title = _(u'Validated by'),
494        required = False,
495        )
496
[7819]497class IStudentStudyLevel(IKofaObject):
[6774]498    """A container for course tickets.
499
500    """
501    level = Attribute('The level code')
[9235]502    number_of_tickets = Attribute('Number of tickets contained in this level')
[9253]503    certcode = Attribute('The certificate code of the study course')
[9257]504    is_current_level = Attribute('Is this level the current level of the student?')
[6774]505
[6793]506    level_session = schema.Choice(
[7723]507        title = _(u'Session'),
[6793]508        source = academic_sessions_vocab,
[9437]509        required = True,
[6793]510        )
[6781]511
[6793]512    level_verdict = schema.Choice(
[7723]513        title = _(u'Verdict'),
[7619]514        source = VerdictSource(),
[8820]515        default = '0',
[6793]516        required = False,
517        )
518
[9161]519    validated_by = schema.TextLine(
520        title = _(u'Validated by'),
521        default = None,
522        required = False,
523        )
524
525    validation_date = schema.Datetime(
526        title = _(u'Validation Date'),
527        required = False,
528        readonly = False,
529        )
530
[9690]531    total_credits = schema.Int(
532        title = _(u'Total Credits'),
533        required = False,
534        readonly = True,
535        )
536
537    gpa = schema.Int(
538        title = _(u'GPA'),
539        required = False,
540        readonly = True,
541        )
542
[8920]543    def addCourseTicket(ticket, course):
[8182]544        """Add a course ticket object.
545        """
546
[9501]547    def addCertCourseTickets(cert):
548        """Collect all certificate courses and create course
549        tickets automatically.
550        """
551
[7819]552class ICourseTicket(IKofaObject):
[9420]553    """An interface for course tickets.
[6781]554
555    """
[6783]556    code = Attribute('code of the original course')
[9253]557    certcode = Attribute('certificate code of the study course')
[9684]558    grade = Attribute('grade calculated from score')
559    weight = Attribute('weight calculated from score')
[9698]560    removable_by_student = Attribute('Is student allowed to remove the ticket?')
[6781]561
[9420]562    title = schema.TextLine(
563        title = _(u'Title'),
564        required = False,
565        )
566
567    fcode = schema.TextLine(
568        title = _(u'Faculty Code'),
569        required = False,
570        )
571
572    dcode = schema.TextLine(
573        title = _(u'Department Code'),
574        required = False,
575        )
576
577    semester = schema.Choice(
578        title = _(u'Semester/Term'),
579        source = SemesterSource(),
580        required = False,
581        )
582
583    passmark = schema.Int(
584        title = _(u'Passmark'),
585        required = False,
586        )
587
588    credits = schema.Int(
589        title = _(u'Credits'),
590        required = False,
591        )
592
[7665]593    mandatory = schema.Bool(
[9320]594        title = _(u'Required'),
[6795]595        default = False,
596        required = False,
597        )
598
[6781]599    score = schema.Int(
[7723]600        title = _(u'Score'),
[9684]601        default = None,
[6781]602        required = False,
603        )
604
[7661]605    carry_over = schema.Bool(
[7723]606        title = _(u'Carry-over Course'),
[7661]607        default = False,
608        required = False,
609        )
610
[9420]611    automatic = schema.Bool(
612        title = _(u'Automatical Creation'),
613        default = False,
[9316]614        required = False,
615        )
616
[7633]617    def getLevel():
618        """Returns the id of the level the ticket has been added to.
619        """
620
621    def getLevelSession():
622        """Returns the session of the level the ticket has been added to.
623        """
624
[9420]625class ICourseTicketAdd(IKofaObject):
[7150]626    """An interface for adding course tickets.
[6795]627
628    """
629    course = schema.Choice(
[7723]630        title = _(u'Course'),
[6795]631        source = CourseSource(),
632        readonly = False,
633        )
634
[9420]635class ICourseTicketImport(ICourseTicket):
636    """An interface for importing course results and nothing more.
637
638    """
639    score = schema.Int(
640        title = _(u'Score'),
641        required = False,
642        readonly = False,
643        )
644
645    level_session = schema.Choice(
646        title = _(u'Level Session'),
647        source = academic_sessions_vocab,
648        required = False,
649        readonly = False,
650        )
651
[7819]652class IStudentAccommodation(IKofaObject):
[6635]653    """A container for student accommodation objects.
654
655    """
656
[9423]657    def addBedTicket(bedticket):
658        """Add a bed ticket object.
659        """
660
661
[7819]662class IBedTicket(IKofaObject):
[6989]663    """A ticket for accommodation booking.
664
665    """
[6996]666    bed = Attribute('The bed object.')
667
668    bed_coordinates = schema.TextLine(
[9424]669        title = _(u'Allocated Bed'),
[9423]670        required = True,
[7014]671        readonly = False,
[6992]672        )
673
[6996]674    bed_type = schema.TextLine(
[9424]675        title = _(u'Requested Bed Type'),
[9423]676        required = True,
[7014]677        readonly = False,
[6996]678        )
679
[6992]680    booking_session = schema.Choice(
[7723]681        title = _(u'Session'),
[6992]682        source = academic_sessions_vocab,
683        required = True,
[7014]684        readonly = True,
[6992]685        )
686
[8170]687    booking_date = schema.Datetime(
[7723]688        title = _(u'Booking Date'),
[6992]689        required = False,
[7014]690        readonly = True,
[6992]691        )
692
693    booking_code = schema.TextLine(
[7723]694        title = _(u'Booking Activation Code'),
[6992]695        required = False,
[7014]696        readonly = True,
[6992]697        )
698
[6994]699    def getSessionString():
[7633]700        """Returns the title of academic_sessions_vocab term.
[7150]701
[6994]702        """
[6992]703
[6860]704class IStudentPaymentsContainer(IPaymentsContainer):
[6635]705    """A container for student payment objects.
706
707    """
708
[6877]709class IStudentOnlinePayment(IOnlinePayment):
710    """A student payment via payment gateways.
711
712    """
713
[9148]714    p_current = schema.Bool(
715        title = _(u'Current Session Payment'),
716        default = True,
717        required = False,
718        )
719
[8268]720    p_level = schema.Int(
721        title = _(u'Payment Level'),
722        required = False,
723        )
[6877]724
[8422]725    def doAfterStudentPayment():
726        """Process student after payment was made.
727
728        """
729
[8453]730    def doAfterStudentPaymentApproval():
731        """Process student after payment was approved.
732
733        """
734
[8420]735    def approveStudentPayment():
[8422]736        """Approve payment and process student.
[8420]737
738        """
739
[8268]740IStudentOnlinePayment['p_level'].order = IStudentOnlinePayment[
741    'p_session'].order
[8408]742
[9864]743class IStudentPreviousPayment(IKofaObject):
[9148]744    """An interface for adding previous session payments.
745
746    """
747
[9864]748    p_category = schema.Choice(
749        title = _(u'Payment Category'),
750        default = u'schoolfee',
751        source = PreviousPaymentCategorySource(),
752        required = True,
753        )
754
[9148]755    p_session = schema.Choice(
756        title = _(u'Payment Session'),
757        source = academic_sessions_vocab,
758        required = True,
759        )
760
761    p_level = schema.Choice(
762        title = _(u'Payment Level'),
763        source = StudyLevelSource(),
764        required = True,
765        )
766
[9864]767class IStudentBalancePayment(IKofaObject):
768    """An interface for adding balances.
769
770    """
771
[9868]772    p_category = schema.Choice(
[9864]773        title = _(u'Payment Category'),
774        default = u'schoolfee',
775        required = True,
[9868]776        source = BalancePaymentCategorySource(),
[9864]777        )
778
779    balance_session = schema.Choice(
780        title = _(u'Payment Session'),
781        source = academic_sessions_vocab,
782        required = True,
783        )
784
785    balance_level = schema.Choice(
786        title = _(u'Payment Level'),
787        source = StudyLevelSource(),
788        required = True,
789        )
790
791    balance_amount = schema.Float(
792        title = _(u'Balance Amount'),
[9874]793        default = None,
[9864]794        required = True,
795        readonly = False,
796        description = _(
797            u'Balance in Naira '),
798        )
799
[8408]800class ICSVStudentExporter(ICSVExporter):
801    """A regular ICSVExporter that additionally supports exporting
802      data from a given student object.
803    """
[9801]804    def get_filtered(site, **kw):
[9797]805        """Get a filtered set of students.
806        """
[8408]807
808    def export_student(student, filepath=None):
809        """Export data for a given student.
810        """
[9734]811
[9797]812    def export_filtered(site, filepath=None, **kw):
[9734]813        """Export filtered set of students.
814        """
Note: See TracBrowser for help on using the repository browser.