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

Last change on this file since 5824 was 5820, checked in by uli, 14 years ago

Add interface for applicants container providers.

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