Index: /waeup/branches/ulif-importers/src/waeup/utils/batching.py
===================================================================
--- /waeup/branches/ulif-importers/src/waeup/utils/batching.py	(revision 4831)
+++ /waeup/branches/ulif-importers/src/waeup/utils/batching.py	(revision 4832)
@@ -15,5 +15,6 @@
 from waeup.interfaces import (IBatchProcessor, IFacultyContainer, IFaculty,
                               ICourse, IDepartment, ICertificate,
-                              ISchemaTypeConverter, FatalCSVError)
+                              ICertificateCourse, ISchemaTypeConverter,
+                              FatalCSVError)
 
 class BatchProcessor(grok.GlobalUtility):
@@ -108,12 +109,17 @@
         return result
         
-    def getMapping(self, path, headerfields):
+    def getMapping(self, path, headerfields, mode):
         """Get a mapping from CSV file headerfields to actually used
            fieldnames.
         """
+        result = dict()
         reader = csv.reader(open(path, 'rb'))
         raw_header = reader.next()
-        return dict([(x, headerfields[raw_header.index(x)])
-                     for x in raw_header])
+        for num, field in enumerate(headerfields):
+            if field not in self.location_fields and mode == 'remove':
+                # Ignore non-location fields when removing...
+                field = '--IGNORE--'
+            result[raw_header[num]] = field
+        return result
 
     def getFieldConverters(self, fieldnames):
@@ -176,7 +182,10 @@
         for key, value in row.items():
             setattr(obj, key, value)
-        return
-
-    def createLogfile(self, path, fail_path, num, warnings, mode, user):
+        parent = self.getParent(row, site)
+        parent[row['name']] = obj
+        return
+
+    def createLogfile(self, path, fail_path, num, warnings, mode, user,
+                      timedelta):
         """Write a log file.
         """
@@ -188,4 +197,6 @@
         fd.write("User: %s\n" % user)
         fd.write("Failed datasets: %s\n" % fail_path)
+        fd.write("Processing time: %0.3f s (%0.4f s/item)\n" % (
+                timedelta, timedelta/(num or 1)))
         fd.write("Processed: %s lines (%s successful/ %s failed)\n" % (
                 num, num - len(warnings), len(warnings)
@@ -202,6 +213,7 @@
         """Perform actual import.
         """
+        time_start = time.time()
         self.checkHeaders(headerfields, mode)
-        mapping = self.getMapping(path, headerfields)
+        mapping = self.getMapping(path, headerfields, mode)
         converters = self.getFieldConverters(headerfields)
         reader = csv.DictReader(open(path, 'rb'))
@@ -253,9 +265,13 @@
                 self.updateEntry(obj, row, site)
 
+        time_end = time.time()
+        timedelta = time_end - time_start
+        
         if path.endswith('.pending'):
             os.unlink(path)
             os.rename(failed_path, path)
             failed_path = path
-        self.createLogfile(path, failed_path, num, warnings, mode, user)
+        self.createLogfile(path, failed_path, num, warnings, mode, user,
+                           timedelta)
         if len(warnings) == 0:
             del failed_writer
@@ -441,2 +457,56 @@
         del parent[row['code']]
         return
+
+class CertificateCourseProcessor(BatchProcessor):
+    """A batch processor for ICertificateCourse objects.
+    """
+    grok.provides(IBatchProcessor)
+    grok.context(Interface)
+    util_name = 'certificatecourseimporter'
+    grok.name(util_name)
+
+    name = u'CertificateCourse importer'
+    iface = ICertificateCourse
+
+    location_fields = ['course', 'level', 'faculty_code', 'department_code',
+                       'certificate_code',]
+    factory_name = 'waeup.CertificateCourse'
+
+    def parentsExist(self, row, site):
+        if not 'faculties' in site.keys():
+            return False
+        if not row['faculty_code'] in site['faculties'].keys():
+            return False
+        faculty = site['faculties'][row['faculty_code']]
+        if not row['department_code'] in faculty.keys():
+            return False
+        dept = faculty[row['department_code']]
+        return row['certificate_code'] in dept.certificates.keys()
+
+    def entryExists(self, row, site):
+        if not self.parentsExist(row, site):
+            return False
+        parent = self.getParent(row, site)
+        code = "%s_%s" % (row['course'].code, row['level'])
+        return code in parent.keys()
+
+    def getParent(self, row, site):
+        dept = site['faculties'][row['faculty_code']][row['department_code']]
+        return dept.certificates[row['certificate_code']]
+
+    def getEntry(self, row, site):
+        if not self.entryExists(row, site):
+            return None
+        parent = self.getParent(row, site)
+        return parent.get(row['course'])
+
+    def addEntry(self, obj, row, site):
+        parent = self.getParent(row, site)
+        parent.addCourseRef(row['course'],
+                            row['level'], row['core_or_elective'])
+        return
+
+    def delEntry(self, row, site):
+        parent = self.getParent(row, site)
+        parent.delCourseRef(row['course'].code, row['level'])
+        return
