Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/batching.py	(revision 12868)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/batching.py	(revision 12869)
@@ -35,5 +35,7 @@
 
 class ApplicantsContainerProcessor(BatchProcessor):
-    """A processor for applicants containers.
+    """The Applicants Container Processor imports containers for applicants.
+    It does not import their content. There is nothing special about this
+    processor.
     """
     grok.implements(IBatchProcessor)
@@ -76,14 +78,15 @@
 
 class ApplicantProcessor(BatchProcessor):
-    """A batch processor for IApplicant objects.
-
-    In create mode container_code is required. If application_number is given
-    an applicant with this number is created in the designated container.
-    If application_number is not given a random application_number is assigned.
-    applicant_id is being determined by the system and can't be imported.
-
-    In update or remove mode container_code and application_number columns
-    must not exist. The applicant object is solely searched by its applicant_id
-    or reg_number.
+    """The Applicant Processor imports application records (applicants).
+
+    In create mode `container_code` is required. If `application_number` is
+    given, an applicant with this number is created in the designated container.
+    If `application_number` is not given, a random `application_number` is
+    assigned. `applicant_id` is being determined by the system and can't be
+    imported.
+
+    In update or remove mode `container_code` and `application_number` columns
+    must not exist. The applicant object is solely localized by
+    `applicant_id` or `reg_number`.
     """
     grok.implements(IBatchProcessor)
@@ -95,5 +98,4 @@
     iface = IApplicant
     iface_byregnumber = IApplicantUpdateByRegNo
-    location_fields = ['']
     factory_name = 'waeup.Applicant'
 
@@ -303,5 +305,6 @@
         if cert is not None and (mode in ('create', 'update')):
             # course1 application category must match container's.
-            parent = self.getParent(row, self.site)
+            site = grok.getSite()
+            parent = self.getParent(row, site)
             if parent is None:
                 errs.append(('container', 'not found'))
@@ -332,11 +335,2 @@
             return 'Applicant is blocked.'
         return None
-
-    def doImport(self, *args, **kw):
-        # XXX: Not thread-safe.  Parallel applicant imports into
-        # different sites could mean a mess.  Luckily this is not a
-        # typical use-case. On the other hand it spares thousands of
-        # site lookups during large imports.
-        # XXX: Maybe this should go into Importer base.
-        self.site = grok.getSite() # needed by checkConversion()
-        return super(ApplicantProcessor, self).doImport(*args, **kw)
Index: main/waeup.kofa/trunk/src/waeup/kofa/authentication.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/authentication.py	(revision 12868)
+++ main/waeup.kofa/trunk/src/waeup/kofa/authentication.py	(revision 12869)
@@ -449,5 +449,12 @@
 
 class UserProcessor(BatchProcessor):
-    """A batch processor for IUserAccount objects.
+    """The User Processor processes user accounts, i.e. `Account` objects in the
+    ``users`` container.
+
+    The `roles` columns must contain Python list
+    expressions like ``['waeup.PortalManager', 'waeup.ImportManager']``.
+
+    The processor does not import local roles. These can be imported
+    by means of batch processors in the academic section.
     """
     grok.implements(IBatchProcessor)
Index: main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 12868)
+++ main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 12869)
@@ -64,5 +64,4 @@
     iface_bymatricnumber = IStudentUpdateByMatricNo
 
-    location_fields = []
     factory_name = 'waeup.Student'
 
@@ -312,6 +311,6 @@
     additional_fields = []
 
-    #: header fields additionally required
-    additional_headers = []
+    # additional required fields (subset of additional_fields)
+    additional_fields_required = []
 
     @property
@@ -328,5 +327,5 @@
                 "Need at least columns student_id " +
                 "or reg_number or matric_number for import!")
-        for name in self.additional_headers:
+        for name in self.additional_fields_required:
             if not name in headerfields:
                 raise FatalCSVError(
@@ -410,7 +409,4 @@
     iface_transfer = IStudentStudyCourseTransfer
     factory_name = 'waeup.StudentStudyCourse'
-
-    location_fields = []
-    additional_fields = []
 
     def getParent(self, row, site):
@@ -510,8 +506,6 @@
     factory_name = 'waeup.StudentStudyLevel'
 
-    location_fields = []
-
     additional_fields = ['level']
-    additional_headers = ['level']
+    additional_fields_required = additional_fields
 
     @property
@@ -591,7 +585,6 @@
     factory_name = 'waeup.CourseTicket'
 
-    location_fields = []
     additional_fields = ['level', 'code']
-    additional_headers = ['level', 'code']
+    additional_fields_required = additional_fields
 
     @property
@@ -695,7 +688,5 @@
     factory_name = 'waeup.StudentOnlinePayment'
 
-    location_fields = []
     additional_fields = ['p_id']
-    additional_headers = []
 
     def checkHeaders(self, headerfields, mode='ignore'):
Index: main/waeup.kofa/trunk/src/waeup/kofa/university/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/university/batching.py	(revision 12868)
+++ main/waeup.kofa/trunk/src/waeup/kofa/university/batching.py	(revision 12869)
@@ -44,5 +44,12 @@
 
 class FacultyProcessor(BatchProcessor):
-    """A batch processor for IFaculty objects.
+    """The Faculty Processor processes faculties in the `faculties` container.
+    The `FacultyProcessor` class also serves as a baseclass for all other 
+    batch processors in the academic section.
+
+    The processor makes some efforts to set local roles.
+    If new roles are provided, the `updateEntry` method first removes
+    all existing roles and then sets the new roles as given in the import
+    file. That means the entire set of local roles is replaced.
     """
     grok.implements(IBatchProcessor)
@@ -103,5 +110,6 @@
             role_map = IPrincipalRoleMap(obj)
             # Remove all existing local roles.
-            for local_role, user_name, setting in role_map.getPrincipalsAndRoles():
+            for local_role, user_name, setting in \
+                role_map.getPrincipalsAndRoles():
                 role_manager.unsetRoleForPrincipal(local_role, user_name)
                 notify(LocalRoleSetEvent(
@@ -114,7 +122,8 @@
                     local_role = rolemap['local_role']
                     role_manager.assignRoleToPrincipal(local_role, user)
-                    notify(LocalRoleSetEvent(obj, local_role, user, granted=True))
-                    items_changed += (
-                        '%s=%s, ' % ('local_roles', '%s|%s' % (user,local_role)))
+                    notify(LocalRoleSetEvent(
+                        obj, local_role, user, granted=True))
+                    items_changed += ('%s=%s, '
+                        % ('local_roles', '%s|%s' % (user,local_role)))
             row.pop('local_roles')
 
@@ -151,5 +160,6 @@
                 if not 'user_name' in rolemap.keys() or not \
                     'local_role' in rolemap.keys():
-                    errs.append(('local_roles','user_name or local_role missing'))
+                    errs.append((
+                        'local_roles','user_name or local_role missing'))
                     return errs, inv_errs, conv_dict
                 local_role = rolemap['local_role']
@@ -165,5 +175,7 @@
 
 class DepartmentProcessor(FacultyProcessor):
-    """A batch processor for IDepartment objects.
+    """The Department Processor works in the same way as the Faculty
+    Processor. Since department codes are not necessarily unique, it needs the
+    `faculty_code` to create and update objects.
     """
     grok.implements(IBatchProcessor)
@@ -213,5 +225,10 @@
 
 class CertificateProcessor(FacultyProcessor):
-    """A batch processor for ICertificate objects.
+    """The Certificate Processor gets the parent object (the
+    `certificates` attribute of the department container) in two ways.
+    If both faculty and department codes are provided, `getPartents` uses
+    these to locate the certificate. If department code or
+    faculty code are missing, it use the certificates catalog to find the
+    certificate.
     """
     grok.implements(IBatchProcessor)
@@ -270,6 +287,5 @@
             return dept.certificates
         # If department code or faculty code is missing,
-        # use catalog to get parent. Makes only sense in update mode but
-        # does also work in create mode.
+        # use catalog to get parent. 
         cat = queryUtility(ICatalog, name='certificates_catalog')
         results = list(
@@ -296,5 +312,7 @@
 
 class CourseProcessor(CertificateProcessor):
-    """A batch processor for ICourse objects.
+    """The Course Processor works exactly in the same way as the
+    Certificate Processor. It uses the courses catalog instead of the
+    certificates catalog.
     """
     grok.implements(IBatchProcessor)
@@ -343,5 +361,10 @@
 
 class CertificateCourseProcessor(FacultyProcessor):
-    """A batch processor for ICertificateCourse objects.
+    """The Certificate Course Processor needs more location fields.
+    Certificate courses are stored inside the certificate container.
+    Thus, `faculty_code`, `department_code` and the
+    `certificate_code` are necessary to find the parent container.
+    It furthermore needs the `course` and the `level` field to locate
+    existing objects as they are part of the object id (code).
     """
     grok.implements(IBatchProcessor)
Index: main/waeup.kofa/trunk/src/waeup/kofa/university/export.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/university/export.py	(revision 12868)
+++ main/waeup.kofa/trunk/src/waeup/kofa/university/export.py	(revision 12869)
@@ -26,5 +26,5 @@
 
 class FacultyExporter(grok.GlobalUtility, ExporterBase):
-    """The Faculty Exporter exports all faculties in the 'faculties'
+    """The Faculty Exporter exports all faculties in the `faculties`
     container. This is the only place where faculties are stored.
     """
Index: main/waeup.kofa/trunk/src/waeup/kofa/utils/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/utils/batching.py	(revision 12868)
+++ main/waeup.kofa/trunk/src/waeup/kofa/utils/batching.py	(revision 12869)
@@ -56,5 +56,5 @@
 
     # Internal name...
-    util_name = 'baseprocessor'
+    util_name = ''
 
     # Items for this processor need an interface with zope.schema fields.
@@ -66,8 +66,8 @@
 
     # Headers needed to locate items...
-    location_fields = ['code', 'faculty_code']
+    location_fields = []
 
     # A factory with this name must be registered...
-    factory_name = 'waeup.Department'
+    factory_name = ''
 
     @property
@@ -128,5 +128,4 @@
     def applyMapping(self, row, mapping):
         """Apply mapping to a row of CSV data.
-
         """
         result = dict()
@@ -284,5 +283,5 @@
     def doImport(self, path, headerfields, mode='create', user='Unknown',
                  logger=None, ignore_empty=True):
-        """In contrast to most other methods, ``doImport`` is not supposed to
+        """In contrast to most other methods, `doImport` is not supposed to
         be customized, neither in custom packages nor in derived batch
         processor classes. Therefore, this is the only place where we
@@ -317,7 +316,7 @@
            record is stored in the pending data file.
 
-           Now ``doImport`` tries to add the new object with the data 
+           Now `doImport` tries to add the new object with the data
            from the conversion dictionary. In some cases this
-           may fail and a DuplicationError is raised. For example, a new
+           may fail and a `DuplicationError` is raised. For example, a new
            payment ticket is created but the same payment for same session
            has already been made. In this case the object id is unique, no
@@ -339,5 +338,5 @@
            transitions or states. 
 
-           Finally, ``doImport`` updates the existing object with the data
+           Finally, `doImport` updates the existing object with the data
            from the conversion dictionary.
 
@@ -348,5 +347,5 @@
            stored in the pending data file.
 
-           Finally, ``doImport`` removes the existing object.
+           Finally, `doImport` removes the existing object.
 
         """
