Ignore:
Timestamp:
3 Oct 2011, 08:11:11 (13 years ago)
Author:
Henrik Bettermann
Message:

Searching for reg_numbers or matric_numbers makes batch importing more difficult. Field validation must be skipped for reg_numbers and matric_numbers respectively if these fields are used for seeking students. After quite a lot of experiments I came to the conclusion that we need dedicated interfaces to skip the regular validation.

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

Legend:

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

    r6846 r6849  
    88"""
    99import grok
     10import csv
     11import copy
    1012from zope.interface import Interface
    1113from zope.schema import getFields
    1214from zope.component import queryUtility
    1315from zope.catalog.interfaces import ICatalog
    14 from waeup.sirp.interfaces import IBatchProcessor, FatalCSVError
     16from waeup.sirp.interfaces import (
     17    IBatchProcessor, FatalCSVError, IObjectConverter)
    1518from waeup.sirp.students.interfaces import (
    16     IStudent, IStudentStudyCourse, IStudentStudyCourseImport)
     19    IStudent, IStudentStudyCourse, IStudentStudyCourseImport,
     20    IStudentUpdateByRegNo, IStudentUpdateByMatricNo)
    1721from waeup.sirp.utils.batching import BatchProcessor
    1822
     
    2933    iface = IStudent
    3034
    31     location_fields = ['student_id',]
     35    location_fields = []
    3236    factory_name = 'waeup.Student'
    3337
     
    3539
    3640    @property
    37     def req(self):
    38         result = dict(
    39             create = self.required_fields,
    40             update = self.location_fields,
    41             remove = self.location_fields,
    42         )
    43         return result
     41    def available_fields(self):
     42        result = []
     43        return sorted(list(set(
     44            ['student_id','reg_number','matric_number'] + getFields(
     45                self.iface).keys())))
     46
     47    def checkHeaders(self, headerfields, mode='create'):
     48        if not 'reg_number' in headerfields and not 'student_id' in headerfields and not 'matric_number' in headerfields:
     49            raise FatalCSVError(
     50                "Need at least columns student_id or reg_number or matric_number for import!")
     51        if mode == 'create':
     52            for field in self.required_fields:
     53                if not field in headerfields:
     54                    raise FatalCSVError(
     55                        "Need at least columns %s for import!" %
     56                        ', '.join(["'%s'" % x for x in self.required_fields]))
     57        # Check for fields to be ignored...
     58        not_ignored_fields = [x for x in headerfields
     59                              if not x.startswith('--')]
     60        if len(set(not_ignored_fields)) < len(not_ignored_fields):
     61            raise FatalCSVError(
     62                "Double headers: each column name may only appear once.")
     63        return True
    4464
    4565    def parentsExist(self, row, site):
    4666        return 'students' in site.keys()
     67
     68    def getLocator(self, row):
     69        if 'student_id' in row.keys() and row['student_id']:
     70            return 'student_id'
     71        elif 'reg_number' in row.keys() and row['reg_number']:
     72            return 'reg_number'
     73        elif 'matric_number' in row.keys() and row['matric_number']:
     74            return 'matric_number'
     75        else:
     76            return None
    4777
    4878    # The entry never exists in create mode.
    4979    def entryExists(self, row, site):
    5080        if not 'students' in site.keys():
    51             return False
    52         if 'student_id' in row.keys() and row['student_id']:
     81            return None
     82        if self.getLocator(row) == 'student_id':
    5383            if row['student_id'] in site['students']:
    5484                student = site['students'][row['student_id']]
    5585                return student
    56         elif 'reg_number' in row.keys() and row['reg_number']:
     86        elif self.getLocator(row) == 'reg_number':
    5787            reg_number = row['reg_number']
    5888            cat = queryUtility(ICatalog, name='students_catalog')
     
    6191            if results:
    6292                return results[0]
    63         elif 'matric_number' in row.keys() and row['matric_number']:
    64             #import pdb; pdb.set_trace()
     93        elif self.getLocator(row) == 'matric_number':
    6594            matric_number = row['matric_number']
    6695            cat = queryUtility(ICatalog, name='students_catalog')
     
    6998            if results:
    7099                return results[0]
    71         return False
     100        return None
    72101
    73102    def getParent(self, row, site):
     
    88117            del parent[student.student_id]
    89118        pass
     119
     120    def getMapping(self, path, headerfields, mode):
     121        """Get a mapping from CSV file headerfields to actually used fieldnames.
     122        """
     123        result = dict()
     124        reader = csv.reader(open(path, 'rb'))
     125        raw_header = reader.next()
     126        for num, field in enumerate(headerfields):
     127            if field not in ['student_id', 'reg_number', 'matric_number'] and mode == 'remove':
     128                continue
     129            if field == u'--IGNORE--':
     130                # Skip ignored columns in failed and finished data files.
     131                continue
     132            result[raw_header[num]] = field
     133        return result
     134
     135    def checkConversion(self, row, mode='create'):
     136        """Validates all values in row.
     137        """
     138        if mode in ['update', 'remove']:
     139            if self.getLocator(row) == 'reg_number':
     140                iface = IStudentUpdateByRegNo
     141            elif self.getLocator(row) == 'matric_number':
     142                iface = IStudentUpdateByMatricNo
     143        else:
     144            iface = self.iface
     145        converter = IObjectConverter(iface)
     146        errs, inv_errs, conv_dict =  converter.fromStringDict(
     147            row, self.factory_name)
     148        return errs, inv_errs, conv_dict
    90149
    91150class StudentStudyCourseProcessor(BatchProcessor):
     
    102161    factory_name = 'waeup.StudentStudyCourse'
    103162
     163    location_fields = []
     164
    104165    mode = None
    105166
     
    125186    def parentsExist(self, row, site):
    126187        if not 'students' in site.keys():
    127             return False
     188            return None
    128189        if 'student_id' in row.keys() and row['student_id']:
    129190            if row['student_id'] in site['students']:
     
    132193        elif 'reg_number' in row.keys() and row['reg_number']:
    133194            reg_number = row['reg_number']
     195            #import pdb; pdb.set_trace()
    134196            cat = queryUtility(ICatalog, name='students_catalog')
    135197            results = list(
     
    144206            if results:
    145207                return results[0]
    146         return False
     208        return None
    147209
    148210    def entryExists(self, row, site):
    149211        student = self.parentsExist(row, site)
    150212        if not student:
    151             return False
     213            return None
    152214        if 'studycourse' in student:
    153215            return student
    154         return False
     216        return None
    155217
    156218    def getEntry(self, row, site):
  • main/waeup.sirp/trunk/src/waeup/sirp/students/interfaces.py

    r6825 r6849  
    100100    student_id = schema.TextLine(
    101101        title = u'Student ID',
    102         required = True,
     102        required = False,
    103103        )
    104104
     
    165165    """Representation of a student.
    166166    """
     167
     168class IStudentUpdateByRegNo(IStudent):
     169    """Representation of a student. Skip regular reg_number validation.
     170    """
     171
     172    reg_number = schema.TextLine(
     173        title = u'Registration Number',
     174        default = None,
     175        required = False,
     176        )
     177
     178class IStudentUpdateByMatricNo(IStudent):
     179    """Representation of a student. Skip regular matric_number validation.
     180    """
     181
     182    matric_number = schema.TextLine(
     183        title = u'Matriculation Number',
     184        default = None,
     185        required = False,
     186        )
    167187
    168188class IStudentStudyCourse(IWAeUPObject):
Note: See TracChangeset for help on using the changeset viewer.