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

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

Fix various whitespaces.

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