source: main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py @ 6029

Last change on this file since 6029 was 6029, checked in by Henrik Bettermann, 14 years ago

Use WAuPDisplayFormPage for ApplicantsContainerPage?.

Fix traceback if description field is empty.

File size: 22.4 KB
Line 
1##
2## interfaces.py
3## Login : <uli@pu.smp.net>
4## Started on  Sun Jan 16 15:30:01 2011 Uli Fouquet
5## $Id$
6##
7## Copyright (C) 2011 Uli Fouquet
8## This program is free software; you can redistribute it and/or modify
9## it under the terms of the GNU General Public License as published by
10## the Free Software Foundation; either version 2 of the License, or
11## (at your option) any later version.
12##
13## This program is distributed in the hope that it will be useful,
14## but WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16## GNU General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with this program; if not, write to the Free Software
20## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21##
22"""Interfaces regarding student applicants and related components.
23"""
24import os
25import waeup.sirp.browser
26from hurry.file import HurryFile
27from grokcore.content.interfaces import IContainer
28from zc.sourcefactory.basic import BasicSourceFactory
29from zope import schema
30from zope.interface import Interface, Attribute
31from zope.pluggableauth.interfaces import IPrincipalInfo
32from zope.security.interfaces import IGroupClosureAwarePrincipal as IPrincipal
33from waeup.sirp.image.schema import ImageFile
34from waeup.sirp.image.image import WAeUPImageFile
35from waeup.sirp.interfaces import IWAeUPObject, SimpleWAeUPVocabulary
36
37IMAGE_PATH = os.path.join(
38    os.path.dirname(waeup.sirp.browser.__file__),
39    'static'
40    )
41DEFAULT_PASSPORT_IMAGE_MALE = WAeUPImageFile(
42    'passport.jpg',
43    open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg')).read(),
44    )
45DEFAULT_PASSPORT_IMAGE_FEMALE = WAeUPImageFile(
46    'passport.jpg',
47    open(os.path.join(IMAGE_PATH, 'placeholder_f.jpg')).read(),
48    )
49
50#: Categories of applications we support.
51#: Yet not complete nor correct.
52APPLICATION_CATEGORIES = (
53    ('Direct Entry Screening Exam (PDE)', 'pde'),
54    ('Post-UME', 'pume'),
55    ('Post-UDE', 'pude'),
56    ('PCE', 'pce'),
57    ('Common Entry Screening Test (CEST)', 'cest'),
58    )
59
60#: A :class:`waeup.sirp.interfaces.SimpleWAeUPVocabulary` of supported
61#: application categories.
62application_categories_vocab = SimpleWAeUPVocabulary(*APPLICATION_CATEGORIES)
63
64class GenderSource(BasicSourceFactory):
65    """A gender source delivers basically a mapping
66       ``{'m': 'male', 'f': 'female'}``
67
68       Using a source, we make sure that the tokens (which are
69       stored/expected for instance from CSV files) are something one
70       can expect and not cryptic IntIDs.
71    """
72    def getValues(self):
73        return ['m', 'f']
74
75    def getToken(self, value):
76        return value[0].lower()
77       
78    def getTitle(self, value):
79        if value == 'm':
80            return 'male'
81        if value == 'f':
82            return 'female'
83
84class IResultEntry(IWAeUPObject):
85    subject = schema.TextLine(
86        title = u'Subject',
87        description = u'The subject',
88        required=False,
89        )
90    score = schema.TextLine(
91        title = u'Score',
92        description = u'The score',
93        required=False,
94        )
95
96class IApplicantsRoot(IWAeUPObject, IContainer):
97    """A container for university applicants containers.
98    """
99    pass
100
101class IApplicantsContainer(IWAeUPObject):
102    """An applicants container contains university applicants.
103
104    """
105    title = schema.TextLine(
106        title = u'Title of the type of applicants stored here.',
107        required = True,
108        default = u'Untitled',
109        )
110   
111    description = schema.Text(
112        title = u'Human readable description in reST format',
113        required = False,
114        default = u'Not set.'
115        )
116
117    startdate = schema.Date(
118        title = u'Date when the application period starts',
119        required = False,
120        default = None,
121        )
122
123    enddate = schema.Date(
124        title = u'Date when the application period ends',
125        required = False,
126        default = None,
127        )
128
129    strict_deadline = schema.Bool(
130        title = u'Forbid additions after deadline (enddate)',
131        required = True,
132        default = True,
133        )
134
135    def archive(id=None):
136        """Create on-dist archive of applicants stored in this term.
137
138        If id is `None`, all applicants are archived.
139
140        If id contains a single id string, only the respective
141        applicants are archived.
142
143        If id contains a list of id strings all of the respective
144        applicants types are saved to disk.
145        """
146
147    def clear(id=None, archive=True):
148        """Remove applicants of type given by 'id'.
149
150        Optionally archive the applicants.
151       
152        If id is `None`, all applicants are archived.
153
154        If id contains a single id string, only the respective
155        applicants are archived.
156
157        If id contains a list of id strings all of the respective
158        applicant types are saved to disk.
159
160        If `archive` is ``False`` none of the archive-handling is done
161        and respective applicants are simply removed from the
162        database.
163        """
164class IApplicantBaseData(IWAeUPObject):
165    """The data for an applicant.
166
167    This is a base interface with no field (except ``reg_no``)
168    required. For use with importers, forms, etc., please use one of
169    the derived interfaces below, which set more fields to required
170    state, depending on use-case.
171    """
172    reg_no = schema.TextLine(
173        title = u'JAMB Registration Number',
174        )
175    access_code = schema.TextLine(
176        title = u'Access Code',
177        required = False,
178        )
179    serial = schema.TextLine(
180        title = u'Serial Number',
181        required = False,
182        )
183    course1 = schema.TextLine(
184        title = u'1st Choice Course of Study',
185        required = False,
186        )
187    course2 = schema.TextLine(
188        title = u'2nd Choice Course of Study',
189        required = False,
190        )
191    course3 = schema.TextLine(
192        title = u'3rd Choice Course of Study',
193        required = False,
194        )
195    firstname = schema.TextLine(
196        title = u'First Name',
197        required = False,
198        )
199    middlenames = schema.TextLine(
200        title = u'Middle Names',
201        required = False,
202        )
203    lastname = schema.TextLine(
204        title = u'Surname/Full Name',
205        required = False,
206        )
207    jamb_age = schema.Int(
208        title = u'Age (provided by JAMB)',
209        required = False,
210        )
211    date_of_birth = schema.Date(
212        title = u'Date of Birth',
213        required = False,
214        )
215    jamb_state = schema.TextLine(
216        title = u'State (provided by JAMB)',
217        required = False,
218        )
219    jamb_lga = schema.TextLine(
220        title = u'LGA (provided by JAMB)',
221        required = False,
222        )
223    lga = schema.TextLine(
224        # XXX: should be choice
225        title = u'State/LGA (confirmed by applicant)',
226        required = False,
227        )
228    sex = schema.Choice(
229        title = u'Sex',
230        source = GenderSource(),
231        default = u'm',
232        required = False,
233        )
234    email = schema.TextLine(
235        title = u'Email',
236        required = False,
237        )
238    phone = schema.TextLine(
239        title = u'Phone',
240        required = False,
241        )
242    #passport = schema.Bool(
243    #    title = u'Passport Photograph',
244    #    default = True,
245    #    required = False,
246    #    )
247    passport = ImageFile(
248        title = u'Passport Photograph',
249        default = DEFAULT_PASSPORT_IMAGE_MALE,
250        required = True,
251        )
252    aos = schema.TextLine(
253        # XXX: should be choice
254        title = u'Area of Specialisation',
255        required = False,
256        )
257    subj1 = schema.TextLine(
258        # XXX: should be choice
259        title = u'1st Choice of Study',
260        required = False,
261        )
262    subj2 = schema.TextLine(
263        # XXX: should be choice
264        title = u'2nd Choice of Study',
265        required = False,
266        )
267    subj3 = schema.TextLine(
268        # XXX: should be choice
269        title = u'3rd Choice of Study',
270        required = False,
271        )
272    #
273    # Higher Educational Data
274    #
275    hq_matric_no = schema.TextLine(
276        title = u'Former Matric Number',
277        required = False,
278        )
279    hq_type = schema.TextLine(
280        title = u'Higher Qualification',
281        required = False,
282        )
283    hq_grade = schema.TextLine(
284        title = u'Higher Qualification Grade',
285        required = False,
286        )
287    hq_school = schema.TextLine(
288        title = u'School Attended',
289        required = False,
290        )
291    hq_session = schema.TextLine(
292        title = u'Session Obtained',
293        required = False,
294        )
295    hq_disc = schema.TextLine(
296        title = u'Discipline',
297        required = False,
298        )
299    #
300    # First sitting data
301    #
302    fst_sit_fname = schema.TextLine(
303        title = u'Full Name',
304        required = False,
305        )
306    fst_sit_no = schema.TextLine(
307        title = u'Exam Number',
308        required = False,
309        )
310    fst_sit_date = schema.Date(
311        title = u'Exam Date (dd/mm/yyyy)',
312        required = False,
313        )
314    fst_sit_type = schema.TextLine(
315        # XXX: Should be choice
316        title = u'Exam Type',
317        required = False,
318        )
319    fst_sit_results = schema.List(
320        title = u'Results',
321        required = False,
322        value_type = schema.Object(
323            title = u'Entries',
324            schema = IResultEntry,
325            required = False,
326            )
327        )
328    scd_sit_fname = schema.TextLine(
329        title = u'Full Name',
330        required = False,
331        )
332    scd_sit_no = schema.TextLine(
333        title = u'Exam Number',
334        required = False,
335        )
336    scd_sit_date = schema.Date(
337        title = u'Exam Date (dd/mm/yyyy)',
338        required = False,
339        )
340    scd_sit_type = schema.TextLine(
341        # XXX: Should be choice
342        title = u'Exam Type',
343        required = False,
344        )
345    scd_sit_results = schema.TextLine(
346        # XXX: Should be nested list of choices
347        title = u'Results',
348        required = False,
349        )
350    #
351    # JAMB scores
352    #
353    eng_score = schema.TextLine(
354        title = u"'English' score",
355        required = False,
356        )
357    subj1score = schema.TextLine(
358        title = u'1st Choice of Study Score',
359        required = False,
360        )
361    subj2score = schema.TextLine(
362        title = u'2nd Choice of Study Score',
363        required = False,
364        )
365    subj3score = schema.TextLine(
366        title = u'3rd Choice of Study Score',
367        required = False,
368        )
369    # XXX: Total score???
370   
371    #
372    # Application Data
373    #
374    application_date = schema.Date(
375        title = u'Application Date',
376        required = False,
377        )
378    status = schema.TextLine(
379        # XXX: should be 'status' type
380        title = u'Application Status',
381        required = False,
382        )
383    screening_date = schema.Date(
384        title = u'Screening Date',
385        required = False,
386        )
387    screening_type = schema.TextLine(
388        # XXX: schould be choice
389        title = u'Screening Type',
390        required = False,
391        )
392    screening_score = schema.TextLine(
393        title = u'Screening Score',
394        required = False,
395        )
396    screening_venue = schema.TextLine(
397        title = u'Screening Venue',
398        required = False,
399        )
400    total_score = schema.TextLine(
401        title = u'Total Score',
402        required = False,
403        )
404    course_admitted = schema.TextLine(
405        # XXX: should be choice
406        title = u'Admitted Course of Study',
407        required = False,
408        )
409    department = schema.TextLine(
410        # XXX: if we have a course, dept. is not necessary
411        title = u'Department',
412        required = False,
413        )
414    faculty = schema.TextLine(
415        # XXX: if we have a course, faculty is not necessary
416        title = u'Faculty',
417        required = False,
418        )
419    entry_session = schema.TextLine(
420        # XXX: should be choice, should have sensible default: upcoming session
421        title = u'Entry Session',
422        required = False,
423        )
424    notice = schema.Text(
425        title = u'Notice',
426        required = False,
427        )
428    student_id = schema.TextLine(
429        title = u'Student ID',
430        required = False,
431        )
432    import_record_no = schema.TextLine(
433        title = u'Import Record No.',
434        required = False,
435        )
436    imported_by = schema.TextLine(
437        title = u'Imported By',
438        required = False,
439        )
440    import_date = schema.Datetime(
441        title = u'Import Date',
442        required = False,
443        )
444    import_from = schema.TextLine(
445        title = u'Import Source',
446        required = False,
447        )
448    confirm_passport = schema.Bool(
449        title = u"Confirmation that photograph represents applicant ticked.",
450        default = False,
451        required = True,
452        )
453
454   
455class IApplicant(IApplicantBaseData):
456    """An applicant.
457
458    This is basically the applicant base data. Here we repeat the
459    fields from base data only with the `required` attribute of
460    required attributes set to True (which is the default).
461    """
462    locked = schema.Bool(
463        title = u'Form locked',
464        default = False,
465        readonly = True,
466        )
467    access_code = schema.TextLine(
468        title = u'Access Code',
469        )
470    course1 = schema.TextLine(
471        title = u'1st Choice Course of Study',
472        )
473    firstname = schema.TextLine(
474        title = u'First Name',
475        )
476    middlenames = schema.TextLine(
477        title = u'Middle Names',
478        )
479    lastname = schema.TextLine(
480        title = u'Surname/Full Name',
481        )
482    date_of_birth = schema.Date(
483        title = u'Date of Birth',
484        )
485    jamb_state = schema.TextLine(
486        title = u'State (provided by JAMB)',
487        )
488    jamb_lga = schema.TextLine(
489        title = u'LGA (provided by JAMB)',
490        )
491    lga = schema.TextLine(
492        # XXX: should be choice
493        title = u'State/LGA (confirmed by applicant)',
494        )
495    sex = schema.Choice(
496        title = u'Sex',
497        source = GenderSource(),
498        default = u'm',
499        )
500    #passport = schema.Bool(
501    #    title = u'Passport Photograph',
502    #    default = True,
503    #    )
504    passport = ImageFile(
505        title = u'Passport Photograph',
506        default = DEFAULT_PASSPORT_IMAGE_MALE,
507        required = True ,
508        )
509    #
510    # Higher Educational Data
511    #
512
513    #
514    # First sitting data
515    #
516    fst_sit_fname = schema.TextLine(
517        title = u'Full Name',
518        )
519
520    #
521    # Second sitting data
522    #
523    scd_sit_fname = schema.TextLine(
524        title = u'Full Name',
525        )
526    #
527    # JAMB scores
528    #
529   
530    #
531    # Application Data
532    #
533    application_date = schema.Date(
534        title = u'Application Date',
535        )
536    status = schema.TextLine(
537        # XXX: should be 'status' type
538        title = u'Application Status',
539        )
540    screening_date = schema.Date(
541        title = u'Screening Date',
542        )
543    screening_type = schema.TextLine(
544        # XXX: schould be choice
545        title = u'Screening Type',
546        )
547    screening_score = schema.TextLine(
548        title = u'Screening Score',
549        )
550    entry_session = schema.TextLine(
551        # XXX: should be choice
552        # XXX: should have sensible default: upcoming session
553        title = u'Entry Session',
554        )
555    import_record_no = schema.TextLine(
556        title = u'Import Record No.',
557        )
558    imported_by = schema.TextLine(
559        title = u'Imported By',
560        )
561    import_date = schema.Datetime(
562        title = u'Import Date',
563        )
564    import_from = schema.TextLine(
565        title = u'Import Source',
566        )
567
568class IApplicantPDEEditData(IWAeUPObject):
569    """The data set presented to PDE applicants.
570    """
571    locked = schema.Bool(
572        title = u'Form locked',
573        default = False,
574        readonly = True,
575        )
576    reg_no = schema.TextLine(
577        title = u'JAMB Registration Number',
578        readonly = True,
579        )
580    access_code = schema.TextLine(
581        title = u'Access Code',
582        readonly = True,
583        )
584    course1 = schema.TextLine(
585        title = u'1st Choice Course of Study',
586        readonly = True,
587        )
588    course2 = schema.TextLine(
589        title = u'2nd Choice Course of Study',
590        required = False,
591        )
592    course3 = schema.TextLine(
593        title = u'3rd Choice Course of Study',
594        required = False,
595        )
596    lastname = schema.TextLine(
597        title = u'Name',
598        readonly = True,
599        )
600    jamb_age = schema.Int(
601        title = u'Age',
602        readonly = True,
603        )
604    date_of_birth = schema.Date(
605        title = u'Date of Birth',
606        required = True,
607        )
608    jamb_state = schema.TextLine(
609        title = u'State (provided by JAMB)',
610        readonly = True,
611        )
612    jamb_lga = schema.TextLine(
613        title = u'LGA (provided by JAMB)',
614        readonly = True,
615        )
616    lga = schema.TextLine(
617        # XXX: should be choice
618        title = u'State/LGA (confirmed by applicant)',
619        required = False,
620        )
621    email = schema.TextLine(
622        title = u'Email',
623        required = False,
624        )
625    phone = schema.TextLine(
626        title = u'Phone',
627        required = False,
628        )
629    aos = schema.TextLine(
630        # XXX: should be choice
631        title = u'Area of Specialisation',
632        required = False,
633        )
634    subj1 = schema.TextLine(
635        # XXX: should be choice
636        title = u'1st Choice of Study',
637        readonly = True,
638        )
639    subj2 = schema.TextLine(
640        # XXX: should be choice
641        title = u'2nd Choice of Study',
642        required = False,
643        )
644    subj3 = schema.TextLine(
645        # XXX: should be choice
646        title = u'3rd Choice of Study',
647        required = False,
648        )
649   
650    #
651    # Application Data
652    #
653    application_date = schema.Date(
654        title = u'Application Date',
655        readonly = True,
656        )
657    status = schema.TextLine(
658        # XXX: should be 'status' type
659        title = u'Application Status',
660        readonly = True,
661        )
662    screening_date = schema.Date(
663        title = u'Screening Date',
664        readonly = True,
665        )
666    #passport = schema.Bool(
667    #    title = u'Passport Photograph',
668    #    default = True,
669    #    required = False,
670    #    ),
671    #passport = schema.Bytes(
672    #    title = u'Passport Photograph',
673    #    required = True,
674    #    )
675    #passport = schema.Object(
676    #    title = u'Passport Photograph',
677    #    required = True,
678    #    schema = IImage)
679    passport = ImageFile(
680        title = u'Passport Photograph',
681        default = DEFAULT_PASSPORT_IMAGE_MALE,
682        required = True,
683        )
684    confirm_passport = schema.Bool(
685        title = u"Confirmation that photograph represents applicant ticked.",
686        default = False,
687        required = True,
688        )
689
690   
691class IApplicantPrincipalInfo(IPrincipalInfo):
692    """Infos about principals that are applicants.
693    """
694    reg_no = Attribute("The JAMB registration no. of the user")
695
696    access_code = Attribute("The Access Code the user purchased")
697
698class IApplicantPrincipal(IPrincipal):
699    """A principal that is an applicant.
700
701    This interface extends zope.security.interfaces.IPrincipal and
702    requires also an `id` and other attributes defined there.
703    """
704    reg_no = schema.TextLine(
705        title = u'Registration number',
706        description = u'The JAMB registration number',
707        required = True,
708        readonly = True)
709
710    access_code = schema.TextLine(
711        title = u'Access Code',
712        description = u'The access code purchased by the user.',
713        required = True,
714        readonly = True)
715
716class IApplicantsFormChallenger(Interface):
717    """A challenger that uses a browser form to collect applicant
718       credentials.
719    """
720    loginpagename = schema.TextLine(
721        title = u'Loginpagename',
722        description = u"""Name of the login form used by challenger.
723
724        The form must provide an ``access_code`` input field.
725        """)
726
727    accesscode_field = schema.TextLine(
728        title = u'Access code field',
729        description = u'''Field of the login page which is looked up for
730                          access_code''',
731        default = u'access_code',
732        )
733
734
735class IApplicantSessionCredentials(Interface):
736    """Interface for storing and accessing applicant credentials in a
737       session.
738    """
739
740    def __init__(access_code):
741        """Create applicant session credentials."""
742
743    def getAccessCode():
744        """Return the access code."""
745
746
747class IApplicantsContainerProvider(Interface):
748    """A provider for applicants containers.
749
750    Applicants container providers are meant to be looked up as
751    utilities. This way we can find all applicant container types
752    defined somewhere.
753
754    Each applicants container provider registered as utility provides
755    one container type and one should be able to call the `factory`
756    attribute to create an instance of the requested container type.
757
758    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
759   
760    Samples
761    *******
762   
763    Given, you had an IApplicantsContainer implementation somewhere
764    and you would like to make it findable on request, then you would
765    normally create an appropriate provider utility like this::
766
767      import grok
768      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
769
770      class MyContainerProvider(grok.GlobalUtility):
771          grok.implements(IApplicantsContainerProvider)
772          grok.name('MyContainerProvider') # Must be unique
773          factory = MyContainer # A class implementing IApplicantsContainer
774                                # or derivations thereof.
775
776    This utility would be registered on startup and could then be used
777    like this:
778
779      >>> from zope.component import getAllUtilitiesRegisteredFor
780      >>> from waeup.sirp.applicants.interfaces import (
781      ...     IApplicantsContainerProvider)
782      >>> all_providers = getAllUtilitiesRegisteredFor(
783      ...     IApplicantsContainerProvider)
784      >>> all_providers
785      [<MyContainerProvider object at 0x...>]
786
787    You could look up this specific provider by name:
788
789      >>> from zope.component import getUtility
790      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
791      >>> p
792      <MyContainerProvider object at 0x...>
793     
794    An applicants container would then be created like this:
795
796      >>> provider = all_providers[0]
797      >>> container = provider.factory()
798      >>> container
799      <MyContainer object at 0x...>
800
801    """
802    factory = Attribute("A class that can create instances of the "
803                        "requested container type")
Note: See TracBrowser for help on using the repository browser.