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

Last change on this file since 5886 was 5866, checked in by uli, 14 years ago

Remove the addApplicantsContainer method from IApplicantsRoot.

This method is not used yet in real implementations (and might never)
and confuses people.

IApplicantRoot now is also derived from grokcore.content.IContainer
indicating, that IApplicantRoot is some container type.

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