Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/batching.py	(revision 17769)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/batching.py	(revision 17772)
@@ -37,5 +37,5 @@
 from waeup.kofa.applicants.interfaces import (
     IApplicantsContainer, IApplicant, IApplicantUpdateByRegNo,
-    IApplicantOnlinePayment)
+    IApplicantOnlinePayment, IApplicantRefereeReport)
 from waeup.kofa.applicants.workflow import  IMPORTABLE_STATES, CREATED
 
@@ -508,2 +508,64 @@
                 return errs, inv_errs, conv_dict
         return errs, inv_errs, conv_dict
+
+class ApplicantRefereeReportProcessor(BatchProcessor):
+    """The Applicant Referee Report Processor imports applicant referee reports.
+    The reports are located in the applicant container.
+    """
+    grok.implements(IBatchProcessor)
+    grok.provides(IBatchProcessor)
+    grok.context(Interface)
+    util_name = 'applicantrefereereportprocessor'
+    grok.name(util_name)
+
+    name = _('ApplicantRefereeReport Processor')
+    iface = IApplicantRefereeReport
+    factory_name = 'waeup.ApplicantRefereeReport'
+
+    location_fields = ['applicant_id',]
+
+    def parentsExist(self, row, site):
+        return self.getParent(row, site) is not None
+
+    def getParent(self, row, site):
+        applicant_id = row['applicant_id']
+        cat = queryUtility(ICatalog, name='applicants_catalog')
+        results = list(
+            cat.searchResults(applicant_id=(applicant_id, applicant_id)))
+        if results:
+            return results[0]
+        return None
+
+    def getEntry(self, row, site):
+        parent = self.getParent(row, site)
+        if parent is None:
+            return None
+        return parent.get(row['r_id'])
+
+    def entryExists(self, row, site):
+        return self.getEntry(row, site) is not None
+
+    def updateEntry(self, obj, row, site, filename):
+        """Update obj to the values given in row.
+        """
+        items_changed = super(ApplicantRefereeReportProcessor, self).updateEntry(
+            obj, row, site, filename)
+        applicant = self.getParent(row, site)
+        applicant.__parent__.__parent__.logger.info(
+            '%s - %s - %s - updated: %s'
+            % (self.name, filename, applicant.applicant_id, items_changed))
+        return
+
+    def addEntry(self, obj, row, site):
+        parent = self.getParent(row, site)
+        parent[row['r_id']] = obj
+        return
+
+    def delEntry(self, row, site):
+        report = self.getEntry(row, site)
+        parent = self.getParent(row, site)
+        if report is not None:
+            parent.__parent__.__parent__.logger.info(
+                '%s - Referee Report removed' % report.r_id)
+            del parent[report.r_id]
+        return
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/sample_report_data.csv
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/sample_report_data.csv	(revision 17772)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/sample_report_data.csv	(revision 17772)
@@ -0,0 +1,2 @@
+creation_date,email,email_pref,name,phone,r_id,report,applicant_id
+2024-05-12 08:34:46.515874#,xx@xx.xx,aa@aa.aa,John Doe,1234#,r122,Very nice student.,dp2011_1234
Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_batching.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_batching.py	(revision 17769)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/tests/test_batching.py	(revision 17772)
@@ -35,5 +35,6 @@
 from waeup.kofa.applicants.batching import (
     ApplicantsContainerProcessor, ApplicantProcessor,
-    ApplicantOnlinePaymentProcessor)
+    ApplicantOnlinePaymentProcessor,
+    ApplicantRefereeReportProcessor)
 from waeup.kofa.applicants.container import ApplicantsContainer
 from waeup.kofa.applicants.applicant import Applicant
@@ -85,4 +86,12 @@
 
 PAYMENT_HEADER_FIELDS = PAYMENT_SAMPLE_DATA.split(
+    '\n')[0].split(',')
+
+
+REPORT_SAMPLE_DATA = open(
+    os.path.join(os.path.dirname(__file__), 'sample_report_data.csv'),
+    'rb').read()
+
+REPORT_HEADER_FIELDS = REPORT_SAMPLE_DATA.split(
     '\n')[0].split(',')
 
@@ -582,2 +591,86 @@
             'amount_auth=10500.1, p_session=2016, p_state=failed'
             % p_id_failed in logcontent)
+
+class RefereeReportProcessorTest(ApplicantImportExportSetup):
+
+    def setUp(self):
+        super(RefereeReportProcessorTest, self).setUp()
+
+        applicant = Applicant()
+        applicant.firstname = u'Anna2'
+        applicant.lastname = u'Tester'
+        applicant.applicant_id = u'dp2011_1234'
+        self.app['applicants']['dp2011'].addApplicant(applicant)
+        report = createObject(u'waeup.ApplicantRefereeReport')
+        report.r_id = 'r120'
+        report.p_session = 2012
+        report.p_category = 'application'
+        report.p_state = 'paid'
+        applicant['r120'] = report
+        self.applicant2 = applicant
+        self.processor = ApplicantRefereeReportProcessor()
+        self.csv_file = os.path.join(
+            self.workdir, 'sample_report_data.csv')
+        open(self.csv_file, 'wb').write(REPORT_SAMPLE_DATA)
+
+    def test_interface(self):
+        # Make sure we fulfill the interface contracts.
+        assert verifyObject(IBatchProcessor, self.processor) is True
+        assert verifyClass(
+            IBatchProcessor, ApplicantRefereeReportProcessor) is True
+
+    def test_getEntry(self):
+        assert self.processor.getEntry(
+            dict(applicant_id='ID_NONE', r_id='nonsense'), self.app) is None
+        assert self.processor.getEntry(
+            dict(applicant_id=self.applicant2.applicant_id, r_id='r120'),
+            self.app) is self.applicant2['r120']
+
+    def test_delEntry(self):
+        assert self.processor.getEntry(
+            dict(applicant_id=self.applicant2.applicant_id, r_id='r120'),
+            self.app) is self.applicant2['r120']
+        self.assertEqual(len(self.applicant2.keys()),1)
+        self.processor.delEntry(
+            dict(applicant_id=self.applicant2.applicant_id, r_id='r120'),
+            self.app)
+        assert self.processor.getEntry(
+            dict(applicant_id=self.applicant2.applicant_id, r_id='r120'),
+            self.app) is None
+        self.assertEqual(len(self.applicant.keys()),0)
+
+    def test_addEntry(self):
+        self.assertEqual(len(self.applicant2.keys()),1)
+        self.processor.delEntry(
+            dict(applicant_id=self.applicant2.applicant_id, r_id='r120'),
+            self.app)
+        self.assertEqual(len(self.applicant2.keys()),0)
+        report1 = createObject(u'waeup.ApplicantRefereeReport')
+        report1.r_id = 'r234'
+        self.processor.addEntry(
+            report1, dict(applicant_id=self.applicant2.applicant_id, r_id='r234'),
+            self.app)
+        self.assertEqual(len(self.applicant2.keys()),1)
+        self.assertEqual(self.applicant2['r234'].r_id, 'r234')
+
+    def test_import(self):
+        num, num_warns, fin_file, fail_file = self.processor.doImport(
+            self.csv_file, REPORT_HEADER_FIELDS,'create')
+        #fail_contents = open(fail_file, 'rb').read()
+        self.assertEqual(num_warns,0)
+        report = self.processor.getEntry(dict(applicant_id='dp2011_1234',
+            r_id='r122'), self.app)
+        self.assertEqual(report.r_id, 'r122')
+        cdate = report.creation_date.strftime("%Y-%m-%d %H:%M:%S")
+        self.assertEqual(cdate, '2024-05-12 08:34:46')
+        self.assertEqual(str(report.creation_date.tzinfo),'UTC')
+        # ... the other one with generated p_id.
+        shutil.rmtree(os.path.dirname(fin_file))
+        logcontent = open(self.logfile).read()
+        # Logging message from updateEntry
+        self.assertTrue(
+            'INFO - system - ApplicantRefereeReport Processor - '
+            'sample_report_data - dp2011_1234 - updated: name=John Doe, '
+            'email_pref=aa@aa.aa, creation_date=2024-05-12 08:34:46.515874+00:00, '
+            'phone=--1234, r_id=r122, report=Very nice student., '
+            'email=xx@xx.xx' in logcontent)
Index: main/waeup.kofa/trunk/src/waeup/kofa/doctests/batchprocessing_browser.txt
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/doctests/batchprocessing_browser.txt	(revision 17769)
+++ main/waeup.kofa/trunk/src/waeup/kofa/doctests/batchprocessing_browser.txt	(revision 17772)
@@ -100,4 +100,5 @@
     'ApplicantOnlinePayment Processor',
     'Applicant Processor',
+    'ApplicantRefereeReport Processor',
     'ApplicantsContainer Processor',
     'Bed Processor (update only)',
