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

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

ApplicantsContainerProvider? not ApplicantContainerProvider?

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