Changeset 7263 for main/waeup.sirp/trunk


Ignore:
Timestamp:
4 Dec 2011, 12:59:40 (13 years ago)
Author:
Henrik Bettermann
Message:

Add tests for applicant batch importer.

Make reg_no filed unique.

Two tests still fail because the importer only accepts the application_number as location field and does not yet search for registration numbers.

Location:
main/waeup.sirp/trunk/src/waeup/sirp
Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/batching.py

    r7262 r7263  
    6666        return
    6767
    68 class ApplicantProcessor(BatchProcessor):
     68class ApplicantImporter(BatchProcessor):
    6969    """A batch processor for IApplicant objects.
    7070    """
     
    118118    def delEntry(self, row, site):
    119119        applicant = self.entryExists(row, site)
    120         if applicant:
     120        if applicant is not None:
    121121            parent = self.getParent(row, site)
    122122            del parent[row['application_number']]
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/browser.py

    r7260 r7263  
    931931    form_fields = grok.AutoFields(IApplicantEdit).omit(
    932932        'locked', 'course_admitted', 'student_id',
    933         'screening_score', 'applicant_id'
     933        'screening_score', 'applicant_id', 'reg_no'
    934934        )
    935935    form_fields['date_of_birth'].custom_widget = FriendlyDateWidget('le-year')
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/interfaces.py

    r7262 r7263  
    2222
    2323from zope import schema
    24 from zope.interface import Interface, Attribute
    25 from zope.component import getUtilitiesFor
     24from zope.interface import Interface, Attribute, implements, directlyProvides
     25from zope.component import getUtilitiesFor, queryUtility
     26from zope.catalog.interfaces import ICatalog
     27from zope.schema.interfaces import ValidationError, ISource, IContextSourceBinder
    2628from zc.sourcefactory.basic import BasicSourceFactory
     29from waeup.sirp.schema import TextLineChoice
    2730from waeup.sirp.interfaces import (
    2831    IWAeUPObject, year_range, validate_email, academic_sessions_vocab)
     
    4043MAX_UPLOAD_SIZE = 1024 * 20
    4144
     45class RegNumInSource(ValidationError):
     46    """Registration number exists already
     47    """
     48    # The docstring of ValidationErrors is used as error description
     49    # by zope.formlib.
     50    pass
     51
     52class RegNumberSource(object):
     53    implements(ISource)
     54    cat_name = 'applicants_catalog'
     55    field_name = 'reg_no'
     56    validation_error = RegNumInSource
     57    def __init__(self, context):
     58        self.context = context
     59        return
     60
     61    def __contains__(self, value):
     62        cat = queryUtility(ICatalog, self.cat_name)
     63        if cat is None:
     64            return True
     65        kw = {self.field_name: (value, value)}
     66        results = cat.searchResults(**kw)
     67        for entry in results:
     68            if entry.applicant_id != self.context.applicant_id:
     69                # XXX: sources should simply return False.
     70                #      But then we get some stupid error message in forms
     71                #      when validation fails.
     72                raise self.validation_error(value)
     73                #return False
     74        return True
     75
     76def contextual_reg_num_source(context):
     77    source = RegNumberSource(context)
     78    return source
     79directlyProvides(contextual_reg_num_source, IContextSourceBinder)
     80
    4281class ApplicantContainerProviderSource(BasicSourceFactory):
    4382    """A source offering all available applicants container types.
     
    259298        readonly = False,
    260299        )
    261     reg_no = schema.TextLine(
     300    reg_no = TextLineChoice(
    262301        title = u'JAMB Registration Number',
    263         readonly = True,
    264         required = False,
     302        readonly = False,
     303        required = True,
     304        default = None,
     305        source = contextual_reg_num_source,
    265306        )
    266307    access_code = schema.TextLine(
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_batching.py

    r7193 r7263  
    2323import unittest
    2424from zope.component.hooks import setSite, clearSite
     25from zope.component import createObject
    2526from zope.interface.verify import verifyClass, verifyObject
    2627
    2728from waeup.sirp.app import University
    28 from waeup.sirp.applicants.batching import ApplicantsContainerImporter
     29from waeup.sirp.applicants.batching import (
     30    ApplicantsContainerImporter, ApplicantImporter)
    2931from waeup.sirp.applicants.container import ApplicantsContainer
     32from waeup.sirp.applicants.applicant import Applicant
     33from waeup.sirp.university.faculty import Faculty
     34from waeup.sirp.university.department import Department
    3035from waeup.sirp.testing import FunctionalLayer, FunctionalTestCase
    3136from waeup.sirp.interfaces import IBatchProcessor
     
    3944# The header fields of the above CSV snippet
    4045APPS_CONTAINER_HEADER_FIELDS = APPS_CONTAINER_SAMPLE_DATA.split(
     46    '\n')[0].split(',')
     47
     48# The same for students
     49APPLICANT_SAMPLE_DATA = open(
     50    os.path.join(os.path.dirname(__file__), 'sample_applicant_data.csv'),
     51    'rb').read()
     52
     53APPLICANT_HEADER_FIELDS = APPLICANT_SAMPLE_DATA.split(
     54    '\n')[0].split(',')
     55
     56APPLICANT_SAMPLE_DATA_UPDATE = open(
     57    os.path.join(os.path.dirname(__file__), 'sample_applicant_data_update.csv'),
     58    'rb').read()
     59
     60APPLICANT_HEADER_FIELDS_UPDATE = APPLICANT_SAMPLE_DATA_UPDATE.split(
    4161    '\n')[0].split(',')
    4262
     
    119139        shutil.rmtree(os.path.dirname(fin_file))
    120140
    121 def test_suite():
    122     suite = unittest.TestSuite()
    123     for testcase in [
    124         ApplicantsContainerImporterTest,
    125         ]:
    126         suite.addTest(unittest.TestLoader().loadTestsFromTestCase(
    127                 testcase
    128                 )
    129         )
    130     return suite
     141class ApplicantImporterTest(FunctionalTestCase):
     142
     143    layer = FunctionalLayer
     144
     145    def setUp(self):
     146        super(ApplicantImporterTest, self).setUp()
     147        # Setup a sample site for each test
     148        app = University()
     149        self.dc_root = tempfile.mkdtemp()
     150        app['datacenter'].setStoragePath(self.dc_root)
     151
     152        # Prepopulate the ZODB...
     153        self.getRootFolder()['app'] = app
     154        # we add the site immediately after creation to the
     155        # ZODB. Catalogs and other local utilities are not setup
     156        # before that step.
     157        self.app = self.getRootFolder()['app']
     158        # Set site here. Some of the following setup code might need
     159        # to access grok.getSite() and should get our new app then
     160        setSite(app)
     161
     162        # Add an applicants container
     163        self.container = ApplicantsContainer()
     164        self.container.code = u'dp2011'
     165        self.app['applicants']['dp2011'] = self.container
     166
     167        # Populate university
     168        self.certificate = createObject('waeup.Certificate')
     169        self.certificate.code = 'CERT1'
     170        self.certificate.application_category = 'basic'
     171        self.certificate.start_level = 100
     172        self.certificate.end_level = 500
     173        self.app['faculties']['fac1'] = Faculty()
     174        self.app['faculties']['fac1']['dep1'] = Department()
     175        self.app['faculties']['fac1']['dep1'].certificates.addCertificate(
     176            self.certificate)
     177
     178        # Add applicant with subobjects
     179        applicant = Applicant()
     180        applicant.firstname = u'Anna'
     181        applicant.firstname = u'Tester'
     182        self.app['applicants']['dp2011'].addApplicant(applicant)
     183        self.application_number = applicant.application_number
     184        self.applicant = self.app['applicants']['dp2011'][
     185            self.application_number]
     186        self.importer = ApplicantImporter()
     187        self.workdir = tempfile.mkdtemp()
     188        self.csv_file = os.path.join(self.workdir, 'sample_applicant_data.csv')
     189        self.csv_file_update = os.path.join(
     190            self.workdir, 'sample_applicant_data_update.csv')
     191        open(self.csv_file, 'wb').write(APPLICANT_SAMPLE_DATA)
     192        open(self.csv_file_update, 'wb').write(APPLICANT_SAMPLE_DATA_UPDATE)
     193
     194    def tearDown(self):
     195        super(ApplicantImporterTest, self).tearDown()
     196        shutil.rmtree(self.workdir)
     197        shutil.rmtree(self.dc_root)
     198        clearSite()
     199        return
     200
     201    def test_interface(self):
     202        # Make sure we fulfill the interface contracts.
     203        assert verifyObject(IBatchProcessor, self.importer) is True
     204        assert verifyClass(
     205            IBatchProcessor, ApplicantImporter) is True
     206
     207    def test_entryExists(self):
     208        assert self.importer.entryExists(
     209                dict(container_code='dp2011', application_number='999'),
     210                self.app) is None
     211
     212    def test_getEntry(self):
     213        applicant = self.importer.getEntry(
     214                dict(container_code='dp2011',
     215                     application_number=self.application_number), self.app)
     216        self.assertEqual(applicant.applicant_id, self.applicant.applicant_id)
     217
     218    def test_addEntry(self):
     219        new_applicant = Applicant()
     220        self.importer.addEntry(
     221            new_applicant, dict(container_code='dp2011'), self.app)
     222        assert len(self.app['applicants']['dp2011'].keys()) == 2
     223
     224    def test_delEntry(self):
     225        assert self.application_number in self.app['applicants']['dp2011'].keys()
     226        self.importer.delEntry(
     227            dict(container_code='dp2011',
     228                application_number=self.application_number), self.app)
     229        assert self.application_number not in self.app['applicants']['dp2011'].keys()
     230
     231    def test_import(self):
     232        num, num_warns, fin_file, fail_file = self.importer.doImport(
     233            self.csv_file, APPLICANT_HEADER_FIELDS)
     234        self.assertEqual(num_warns,0)
     235        assert len(self.app['applicants']['dp2011'].keys()) == 4
     236        shutil.rmtree(os.path.dirname(fin_file))
     237
     238    def test_import_update(self):
     239        num, num_warns, fin_file, fail_file = self.importer.doImport(
     240            self.csv_file, APPLICANT_HEADER_FIELDS)
     241        shutil.rmtree(os.path.dirname(fin_file))
     242        num, num_warns, fin_file, fail_file = self.importer.doImport(
     243            self.csv_file_update, APPLICANT_HEADER_FIELDS_UPDATE, 'update')
     244        self.assertEqual(num_warns,0)
     245        shutil.rmtree(os.path.dirname(fin_file))
     246
     247    def test_import_remove(self):
     248        num, num_warns, fin_file, fail_file = self.importer.doImport(
     249            self.csv_file, APPLICANT_HEADER_FIELDS)
     250        shutil.rmtree(os.path.dirname(fin_file))
     251        num, num_warns, fin_file, fail_file = self.importer.doImport(
     252            self.csv_file_update, APPLICANT_HEADER_FIELDS_UPDATE, 'remove')
     253        self.assertEqual(num_warns,0)
     254        shutil.rmtree(os.path.dirname(fin_file))
     255
  • main/waeup.sirp/trunk/src/waeup/sirp/applicants/tests/test_browser.py

    r7260 r7263  
    118118        # Add an applicant
    119119        self.applicant = Applicant()
     120        # reg_no is the only field which has to be preset here
     121        # because managers are allowed to edit this required field
     122        self.applicant.reg_no = u'1234'
    120123        app['applicants']['app2009'].addApplicant(self.applicant)
    121124        IUserAccount(
  • main/waeup.sirp/trunk/src/waeup/sirp/schema/field.py

    r7196 r7263  
    5757    or u'bar'.
    5858
    59     The main adavantage of this modified TextLine field is to support
     59    The main advantage of this modified TextLine field is to support
    6060    contextual sources. That means you can define some
    6161    IContextSourceBinder component that looks up catalogs or something
  • main/waeup.sirp/trunk/src/waeup/sirp/students/batching.py

    r7261 r7263  
    130130    def delEntry(self, row, site):
    131131        student = self.entryExists(row, site)
    132         if student:
     132        if student is not None:
    133133            parent = self.getParent(row, site)
    134134            del parent[student.student_id]
  • main/waeup.sirp/trunk/src/waeup/sirp/students/tests/test_batching.py

    r7256 r7263  
    5454
    5555STUDENT_HEADER_FIELDS_UPDATE2 = STUDENT_SAMPLE_DATA_UPDATE2.split(
    56     '\n')[0].split(',')
    57 
    58 STUDENT_SAMPLE_DATA = open(
    59     os.path.join(os.path.dirname(__file__), 'sample_student_data.csv'),
    60     'rb').read()
    61 
    62 STUDENT_HEADER_FIELDS = STUDENT_SAMPLE_DATA.split(
    6356    '\n')[0].split(',')
    6457
Note: See TracChangeset for help on using the changeset viewer.