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

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

Introduce locked-attribute for applicants to control workflow a bit.

File size: 22.0 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    locked = schema.Bool(
457        title = u'Form locked',
458        default = False,
459        readonly = True,
460        )
461    access_code = schema.TextLine(
462        title = u'Access Code',
463        )
464    course1 = schema.TextLine(
465        title = u'1st Choice Course of Study',
466        )
467    firstname = schema.TextLine(
468        title = u'First Name',
469        )
470    middlenames = schema.TextLine(
471        title = u'Middle Names',
472        )
473    lastname = schema.TextLine(
474        title = u'Surname/Full Name',
475        )
476    date_of_birth = schema.Date(
477        title = u'Date of Birth',
478        )
479    jamb_state = schema.TextLine(
480        title = u'State (provided by JAMB)',
481        )
482    jamb_lga = schema.TextLine(
483        title = u'LGA (provided by JAMB)',
484        )
485    lga = schema.TextLine(
486        # XXX: should be choice
487        title = u'State/LGA (confirmed by applicant)',
488        )
489    sex = schema.Choice(
490        title = u'Sex',
491        source = GenderSource(),
492        default = u'm',
493        )
494    #passport = schema.Bool(
495    #    title = u'Passport Photograph',
496    #    default = True,
497    #    )
498    passport = ImageFile(
499        title = u'Passport Photograph',
500        default = DEFAULT_PASSPORT_IMAGE_MALE,
501        required = True,
502        )
503    #
504    # Higher Educational Data
505    #
506
507    #
508    # First sitting data
509    #
510    fst_sit_fname = schema.TextLine(
511        title = u'Full Name',
512        )
513
514    #
515    # Second sitting data
516    #
517    scd_sit_fname = schema.TextLine(
518        title = u'Full Name',
519        )
520    #
521    # JAMB scores
522    #
523   
524    #
525    # Application Data
526    #
527    application_date = schema.Date(
528        title = u'Application Date',
529        )
530    status = schema.TextLine(
531        # XXX: should be 'status' type
532        title = u'Application Status',
533        )
534    screening_date = schema.Date(
535        title = u'Screening Date',
536        )
537    screening_type = schema.TextLine(
538        # XXX: schould be choice
539        title = u'Screening Type',
540        )
541    screening_score = schema.TextLine(
542        title = u'Screening Score',
543        )
544    entry_session = schema.TextLine(
545        # XXX: should be choice
546        # XXX: should have sensible default: upcoming session
547        title = u'Entry Session',
548        )
549    import_record_no = schema.TextLine(
550        title = u'Import Record No.',
551        )
552    imported_by = schema.TextLine(
553        title = u'Imported By',
554        )
555    import_date = schema.Datetime(
556        title = u'Import Date',
557        )
558    import_from = schema.TextLine(
559        title = u'Import Source',
560        )
561
562class IApplicantPDEEditData(IWAeUPObject):
563    """The data set presented to PDE applicants.
564    """
565    locked = schema.Bool(
566        title = u'Form locked',
567        default = False,
568        readonly = True,
569        )
570    reg_no = schema.TextLine(
571        title = u'JAMB Registration Number',
572        readonly = True,
573        )
574    access_code = schema.TextLine(
575        title = u'Access Code',
576        readonly = True,
577        )
578    course1 = schema.TextLine(
579        title = u'1st Choice Course of Study',
580        readonly = True,
581        )
582    course2 = schema.TextLine(
583        title = u'2nd Choice Course of Study',
584        required = False,
585        )
586    course3 = schema.TextLine(
587        title = u'3rd Choice Course of Study',
588        required = False,
589        )
590    lastname = schema.TextLine(
591        title = u'Name',
592        readonly = True,
593        )
594    jamb_age = schema.Int(
595        title = u'Age',
596        readonly = True,
597        )
598    date_of_birth = schema.Date(
599        title = u'Date of Birth',
600        required = True,
601        )
602    jamb_state = schema.TextLine(
603        title = u'State (provided by JAMB)',
604        readonly = True,
605        )
606    jamb_lga = schema.TextLine(
607        title = u'LGA (provided by JAMB)',
608        readonly = True,
609        )
610    lga = schema.TextLine(
611        # XXX: should be choice
612        title = u'State/LGA (confirmed by applicant)',
613        required = False,
614        )
615    email = schema.TextLine(
616        title = u'Email',
617        required = False,
618        )
619    phone = schema.TextLine(
620        title = u'Phone',
621        required = False,
622        )
623    aos = schema.TextLine(
624        # XXX: should be choice
625        title = u'Area of Specialisation',
626        required = False,
627        )
628    subj1 = schema.TextLine(
629        # XXX: should be choice
630        title = u'1st Choice of Study',
631        readonly = True,
632        )
633    subj2 = schema.TextLine(
634        # XXX: should be choice
635        title = u'2nd Choice of Study',
636        required = False,
637        )
638    subj3 = schema.TextLine(
639        # XXX: should be choice
640        title = u'3rd Choice of Study',
641        required = False,
642        )
643   
644    #
645    # Application Data
646    #
647    application_date = schema.Date(
648        title = u'Application Date',
649        readonly = True,
650        )
651    status = schema.TextLine(
652        # XXX: should be 'status' type
653        title = u'Application Status',
654        readonly = True,
655        )
656    screening_date = schema.Date(
657        title = u'Screening Date',
658        readonly = True,
659        )
660    #passport = schema.Bool(
661    #    title = u'Passport Photograph',
662    #    default = True,
663    #    required = False,
664    #    ),
665    #passport = schema.Bytes(
666    #    title = u'Passport Photograph',
667    #    required = True,
668    #    )
669    #passport = schema.Object(
670    #    title = u'Passport Photograph',
671    #    required = True,
672    #    schema = IImage)
673    passport = ImageFile(
674        title = u'Passport Photograph',
675        default = DEFAULT_PASSPORT_IMAGE_MALE,
676        required = True,
677        )
678
679class IApplicantPrincipalInfo(IPrincipalInfo):
680    """Infos about principals that are applicants.
681    """
682    reg_no = Attribute("The JAMB registration no. of the user")
683
684    access_code = Attribute("The Access Code the user purchased")
685
686class IApplicantPrincipal(IPrincipal):
687    """A principal that is an applicant.
688
689    This interface extends zope.security.interfaces.IPrincipal and
690    requires also an `id` and other attributes defined there.
691    """
692    reg_no = schema.TextLine(
693        title = u'Registration number',
694        description = u'The JAMB registration number',
695        required = True,
696        readonly = True)
697
698    access_code = schema.TextLine(
699        title = u'Access Code',
700        description = u'The access code purchased by the user.',
701        required = True,
702        readonly = True)
703
704class IApplicantsFormChallenger(Interface):
705    """A challenger that uses a browser form to collect applicant
706       credentials.
707    """
708    loginpagename = schema.TextLine(
709        title = u'Loginpagename',
710        description = u"""Name of the login form used by challenger.
711
712        The form must provide an ``access_code`` input field.
713        """)
714
715    accesscode_field = schema.TextLine(
716        title = u'Access code field',
717        description = u'''Field of the login page which is looked up for
718                          access_code''',
719        default = u'access_code',
720        )
721
722
723class IApplicantSessionCredentials(Interface):
724    """Interface for storing and accessing applicant credentials in a
725       session.
726    """
727
728    def __init__(access_code):
729        """Create applicant session credentials."""
730
731    def getAccessCode():
732        """Return the access code."""
733
734
735class IApplicantsContainerProvider(Interface):
736    """A provider for applicants containers.
737
738    Applicants container providers are meant to be looked up as
739    utilities. This way we can find all applicant container types
740    defined somewhere.
741
742    Each applicants container provider registered as utility provides
743    one container type and one should be able to call the `factory`
744    attribute to create an instance of the requested container type.
745
746    .. THE FOLLOWING SHOULD GO INTO SPHINX DOCS (and be tested)
747   
748    Samples
749    *******
750   
751    Given, you had an IApplicantsContainer implementation somewhere
752    and you would like to make it findable on request, then you would
753    normally create an appropriate provider utility like this::
754
755      import grok
756      from waeup.sirp.applicants.interfaces import IApplicantsContainerProvider
757
758      class MyContainerProvider(grok.GlobalUtility):
759          grok.implements(IApplicantsContainerProvider)
760          grok.name('MyContainerProvider') # Must be unique
761          factory = MyContainer # A class implementing IApplicantsContainer
762                                # or derivations thereof.
763
764    This utility would be registered on startup and could then be used
765    like this:
766
767      >>> from zope.component import getAllUtilitiesRegisteredFor
768      >>> from waeup.sirp.applicants.interfaces import (
769      ...     IApplicantsContainerProvider)
770      >>> all_providers = getAllUtilitiesRegisteredFor(
771      ...     IApplicantsContainerProvider)
772      >>> all_providers
773      [<MyContainerProvider object at 0x...>]
774
775    You could look up this specific provider by name:
776
777      >>> from zope.component import getUtility
778      >>> p = getUtility(IApplicantsContainerProvider, name='MyProvider')
779      >>> p
780      <MyContainerProvider object at 0x...>
781     
782    An applicants container would then be created like this:
783
784      >>> provider = all_providers[0]
785      >>> container = provider.factory()
786      >>> container
787      <MyContainer object at 0x...>
788
789    """
790    factory = Attribute("A class that can create instances of the "
791                        "requested container type")
Note: See TracBrowser for help on using the repository browser.