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

Last change on this file since 6052 was 6039, checked in by uli, 14 years ago

Go on removing JAMB stuff.

File size: 20.9 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 grokcore.content.interfaces import IContainer
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.image.image import WAeUPImageFile
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 = WAeUPImageFile(
41    'passport.jpg',
42    open(os.path.join(IMAGE_PATH, 'placeholder_m.jpg')).read(),
43    )
44DEFAULT_PASSPORT_IMAGE_FEMALE = WAeUPImageFile(
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'Title 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    date_of_birth = schema.Date(
207        title = u'Date of Birth',
208        required = False,
209        )
210    lga = schema.TextLine(
211        # XXX: should be choice
212        title = u'State/LGA (confirmed by applicant)',
213        required = False,
214        )
215    sex = schema.Choice(
216        title = u'Sex',
217        source = GenderSource(),
218        default = u'm',
219        required = False,
220        )
221    email = schema.TextLine(
222        title = u'Email',
223        required = False,
224        )
225    phone = schema.TextLine(
226        title = u'Phone',
227        required = False,
228        )
229    passport = ImageFile(
230        title = u'Passport Photograph',
231        default = DEFAULT_PASSPORT_IMAGE_MALE,
232        required = True,
233        )
234    aos = schema.TextLine(
235        # XXX: should be choice
236        title = u'Area of Specialisation',
237        required = False,
238        )
239    subj1 = schema.TextLine(
240        # XXX: should be choice
241        title = u'1st Choice of Study',
242        required = False,
243        )
244    subj2 = schema.TextLine(
245        # XXX: should be choice
246        title = u'2nd Choice of Study',
247        required = False,
248        )
249    subj3 = schema.TextLine(
250        # XXX: should be choice
251        title = u'3rd Choice of Study',
252        required = False,
253        )
254    #
255    # Higher Educational Data
256    #
257    hq_matric_no = schema.TextLine(
258        title = u'Former Matric Number',
259        required = False,
260        )
261    hq_type = schema.TextLine(
262        title = u'Higher Qualification',
263        required = False,
264        )
265    hq_grade = schema.TextLine(
266        title = u'Higher Qualification Grade',
267        required = False,
268        )
269    hq_school = schema.TextLine(
270        title = u'School Attended',
271        required = False,
272        )
273    hq_session = schema.TextLine(
274        title = u'Session Obtained',
275        required = False,
276        )
277    hq_disc = schema.TextLine(
278        title = u'Discipline',
279        required = False,
280        )
281    #
282    # First sitting data
283    #
284    fst_sit_fname = schema.TextLine(
285        title = u'Full Name',
286        required = False,
287        )
288    fst_sit_no = schema.TextLine(
289        title = u'Exam Number',
290        required = False,
291        )
292    fst_sit_date = schema.Date(
293        title = u'Exam Date (dd/mm/yyyy)',
294        required = False,
295        )
296    fst_sit_type = schema.TextLine(
297        # XXX: Should be choice
298        title = u'Exam Type',
299        required = False,
300        )
301    fst_sit_results = schema.List(
302        title = u'Results',
303        required = False,
304        value_type = schema.Object(
305            title = u'Entries',
306            schema = IResultEntry,
307            required = False,
308            )
309        )
310    scd_sit_fname = schema.TextLine(
311        title = u'Full Name',
312        required = False,
313        )
314    scd_sit_no = schema.TextLine(
315        title = u'Exam Number',
316        required = False,
317        )
318    scd_sit_date = schema.Date(
319        title = u'Exam Date (dd/mm/yyyy)',
320        required = False,
321        )
322    scd_sit_type = schema.TextLine(
323        # XXX: Should be choice
324        title = u'Exam Type',
325        required = False,
326        )
327    scd_sit_results = schema.TextLine(
328        # XXX: Should be nested list of choices
329        title = u'Results',
330        required = False,
331        )
332    #
333    # JAMB scores
334    #
335    eng_score = schema.TextLine(
336        title = u"'English' score",
337        required = False,
338        )
339    subj1score = schema.TextLine(
340        title = u'1st Choice of Study Score',
341        required = False,
342        )
343    subj2score = schema.TextLine(
344        title = u'2nd Choice of Study Score',
345        required = False,
346        )
347    subj3score = schema.TextLine(
348        title = u'3rd Choice of Study Score',
349        required = False,
350        )
351    # XXX: Total score???
352   
353    #
354    # Application Data
355    #
356    application_date = schema.Date(
357        title = u'Application Date',
358        required = False,
359        )
360    status = schema.TextLine(
361        # XXX: should be 'status' type
362        title = u'Application Status',
363        required = False,
364        )
365    screening_date = schema.Date(
366        title = u'Screening Date',
367        required = False,
368        )
369    screening_type = schema.TextLine(
370        # XXX: schould be choice
371        title = u'Screening Type',
372        required = False,
373        )
374    screening_score = schema.TextLine(
375        title = u'Screening Score',
376        required = False,
377        )
378    screening_venue = schema.TextLine(
379        title = u'Screening Venue',
380        required = False,
381        )
382    total_score = schema.TextLine(
383        title = u'Total Score',
384        required = False,
385        )
386    course_admitted = schema.TextLine(
387        # XXX: should be choice
388        title = u'Admitted Course of Study',
389        required = False,
390        )
391    department = schema.TextLine(
392        # XXX: if we have a course, dept. is not necessary
393        title = u'Department',
394        required = False,
395        )
396    faculty = schema.TextLine(
397        # XXX: if we have a course, faculty is not necessary
398        title = u'Faculty',
399        required = False,
400        )
401    entry_session = schema.TextLine(
402        # XXX: should be choice, should have sensible default: upcoming session
403        title = u'Entry Session',
404        required = False,
405        )
406    notice = schema.Text(
407        title = u'Notice',
408        required = False,
409        )
410    student_id = schema.TextLine(
411        title = u'Student ID',
412        required = False,
413        )
414    import_record_no = schema.TextLine(
415        title = u'Import Record No.',
416        required = False,
417        )
418    imported_by = schema.TextLine(
419        title = u'Imported By',
420        required = False,
421        )
422    import_date = schema.Datetime(
423        title = u'Import Date',
424        required = False,
425        )
426    import_from = schema.TextLine(
427        title = u'Import Source',
428        required = False,
429        )
430    confirm_passport = schema.Bool(
431        title = u"Confirmation that photograph represents applicant ticked.",
432        default = False,
433        required = True,
434        )
435
436   
437class IApplicant(IApplicantBaseData):
438    """An applicant.
439
440    This is basically the applicant base data. Here we repeat the
441    fields from base data only with the `required` attribute of
442    required attributes set to True (which is the default).
443    """
444    locked = schema.Bool(
445        title = u'Form locked',
446        default = False,
447        readonly = True,
448        )
449    access_code = schema.TextLine(
450        title = u'Access Code',
451        )
452    course1 = schema.TextLine(
453        title = u'1st Choice Course of Study',
454        )
455    firstname = schema.TextLine(
456        title = u'First Name',
457        )
458    middlenames = schema.TextLine(
459        title = u'Middle Names',
460        )
461    lastname = schema.TextLine(
462        title = u'Surname/Full Name',
463        )
464    date_of_birth = schema.Date(
465        title = u'Date of Birth',
466        )
467    jamb_state = schema.TextLine(
468        title = u'State (provided by JAMB)',
469        )
470    jamb_lga = schema.TextLine(
471        title = u'LGA (provided by JAMB)',
472        )
473    lga = schema.TextLine(
474        # XXX: should be choice
475        title = u'State/LGA (confirmed by applicant)',
476        )
477    sex = schema.Choice(
478        title = u'Sex',
479        source = GenderSource(),
480        default = u'm',
481        )
482    #passport = schema.Bool(
483    #    title = u'Passport Photograph',
484    #    default = True,
485    #    )
486    passport = ImageFile(
487        title = u'Passport Photograph',
488        default = DEFAULT_PASSPORT_IMAGE_MALE,
489        required = True ,
490        )
491    #
492    # Higher Educational Data
493    #
494
495    #
496    # First sitting data
497    #
498    fst_sit_fname = schema.TextLine(
499        title = u'Full Name',
500        )
501
502    #
503    # Second sitting data
504    #
505    scd_sit_fname = schema.TextLine(
506        title = u'Full Name',
507        )
508    #
509    # Application Data
510    #
511    application_date = schema.Date(
512        title = u'Application Date',
513        )
514    status = schema.TextLine(
515        # XXX: should be 'status' type
516        title = u'Application Status',
517        )
518    screening_date = schema.Date(
519        title = u'Screening Date',
520        )
521    screening_type = schema.TextLine(
522        # XXX: schould be choice
523        title = u'Screening Type',
524        )
525    screening_score = schema.TextLine(
526        title = u'Screening Score',
527        )
528    entry_session = schema.TextLine(
529        # XXX: should be choice
530        # XXX: should have sensible default: upcoming session
531        title = u'Entry Session',
532        )
533    import_record_no = schema.TextLine(
534        title = u'Import Record No.',
535        )
536    imported_by = schema.TextLine(
537        title = u'Imported By',
538        )
539    import_date = schema.Datetime(
540        title = u'Import Date',
541        )
542    import_from = schema.TextLine(
543        title = u'Import Source',
544        )
545
546class IApplicantPDEEditData(IWAeUPObject):
547    """The data set presented to PDE applicants.
548    """
549    locked = schema.Bool(
550        title = u'Form locked',
551        default = False,
552        readonly = True,
553        )
554    access_code = schema.TextLine(
555        title = u'Access Code',
556        readonly = True,
557        )
558    course1 = schema.TextLine(
559        title = u'1st Choice Course of Study',
560        readonly = True,
561        )
562    course2 = schema.TextLine(
563        title = u'2nd Choice Course of Study',
564        required = False,
565        )
566    course3 = schema.TextLine(
567        title = u'3rd Choice Course of Study',
568        required = False,
569        )
570    lastname = schema.TextLine(
571        title = u'Name',
572        readonly = True,
573        )
574    date_of_birth = schema.Date(
575        title = u'Date of Birth',
576        required = True,
577        )
578    lga = schema.TextLine(
579        # XXX: should be choice
580        title = u'State/LGA (confirmed by applicant)',
581        required = False,
582        )
583    email = schema.TextLine(
584        title = u'Email',
585        required = False,
586        )
587    phone = schema.TextLine(
588        title = u'Phone',
589        required = False,
590        )
591    aos = schema.TextLine(
592        # XXX: should be choice
593        title = u'Area of Specialisation',
594        required = False,
595        )
596    subj1 = schema.TextLine(
597        # XXX: should be choice
598        title = u'1st Choice of Study',
599        readonly = True,
600        )
601    subj2 = schema.TextLine(
602        # XXX: should be choice
603        title = u'2nd Choice of Study',
604        required = False,
605        )
606    subj3 = schema.TextLine(
607        # XXX: should be choice
608        title = u'3rd Choice of Study',
609        required = False,
610        )
611   
612    #
613    # Application Data
614    #
615    application_date = schema.Date(
616        title = u'Application Date',
617        readonly = True,
618        )
619    status = schema.TextLine(
620        # XXX: should be 'status' type
621        title = u'Application Status',
622        readonly = True,
623        )
624    screening_date = schema.Date(
625        title = u'Screening Date',
626        readonly = True,
627        )
628    passport = ImageFile(
629        title = u'Passport Photograph',
630        default = DEFAULT_PASSPORT_IMAGE_MALE,
631        required = True,
632        )
633    confirm_passport = schema.Bool(
634        title = u"Confirmation that photograph represents applicant ticked.",
635        default = False,
636        required = True,
637        )
638
639   
640class IApplicantPrincipalInfo(IPrincipalInfo):
641    """Infos about principals that are applicants.
642    """
643    access_code = Attribute("The Access Code the user purchased")
644
645class IApplicantPrincipal(IPrincipal):
646    """A principal that is an applicant.
647
648    This interface extends zope.security.interfaces.IPrincipal and
649    requires also an `id` and other attributes defined there.
650    """
651    access_code = schema.TextLine(
652        title = u'Access Code',
653        description = u'The access code purchased by the user.',
654        required = True,
655        readonly = True)
656
657class IApplicantsFormChallenger(Interface):
658    """A challenger that uses a browser form to collect applicant
659       credentials.
660    """
661    loginpagename = schema.TextLine(
662        title = u'Loginpagename',
663        description = u"""Name of the login form used by challenger.
664
665        The form must provide an ``access_code`` input field.
666        """)
667
668    accesscode_field = schema.TextLine(
669        title = u'Access code field',
670        description = u'''Field of the login page which is looked up for
671                          access_code''',
672        default = u'access_code',
673        )
674
675
676class IApplicantSessionCredentials(Interface):
677    """Interface for storing and accessing applicant credentials in a
678       session.
679    """
680
681    def __init__(access_code):
682        """Create applicant session credentials."""
683
684    def getAccessCode():
685        """Return the access code."""
686
687
688class IApplicantsContainerProvider(Interface):
689    """A provider for applicants containers.
690
691    Applicants container providers are meant to be looked up as
692    utilities. This way we can find all applicant container types
693    defined somewhere.
694
695    Each applicants container provider registered as utility provides
696    one container type and one should be able to call the `factory`
697    attribute to create an instance of the requested container type.
698
699    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
700   
701    Samples
702    *******
703   
704    Given, you had an IApplicantsContainer implementation somewhere
705    and you would like to make it findable on request, then you would
706    normally create an appropriate provider utility like this::
707
708      import grok
709      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
710
711      class MyContainerProvider(grok.GlobalUtility):
712          grok.implements(IApplicantsContainerProvider)
713          grok.name('MyContainerProvider') # Must be unique
714          factory = MyContainer # A class implementing IApplicantsContainer
715                                # or derivations thereof.
716
717    This utility would be registered on startup and could then be used
718    like this:
719
720      >>> from zope.component import getAllUtilitiesRegisteredFor
721      >>> from waeup.sirp.applicants.interfaces import (
722      ...     IApplicantsContainerProvider)
723      >>> all_providers = getAllUtilitiesRegisteredFor(
724      ...     IApplicantsContainerProvider)
725      >>> all_providers
726      [<MyContainerProvider object at 0x...>]
727
728    You could look up this specific provider by name:
729
730      >>> from zope.component import getUtility
731      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
732      >>> p
733      <MyContainerProvider object at 0x...>
734     
735    An applicants container would then be created like this:
736
737      >>> provider = all_providers[0]
738      >>> container = provider.factory()
739      >>> container
740      <MyContainer object at 0x...>
741
742    """
743    factory = Attribute("A class that can create instances of the "
744                        "requested container type")
Note: See TracBrowser for help on using the repository browser.