Index: /main/waeup.kofa/trunk/CHANGES.txt
===================================================================
--- /main/waeup.kofa/trunk/CHANGES.txt	(revision 16818)
+++ /main/waeup.kofa/trunk/CHANGES.txt	(revision 16819)
@@ -4,4 +4,6 @@
 1.7.2.dev0 (unreleased)
 =======================
+
+* Enable import of student history.
 
 * Don't complain but remove leading and trailing whitespaces 
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 16818)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/batching.py	(revision 16819)
@@ -27,4 +27,5 @@
 import unicodecsv as csv # XXX: csv ops should move to dedicated module.
 from time import time
+from ast import literal_eval
 from datetime import datetime
 from zope.i18n import translate
@@ -92,5 +93,5 @@
         return sorted(list(set(
             ['student_id','reg_number','matric_number',
-            'password', 'state', 'transition'] + fields.keys())))
+            'password', 'state', 'transition', 'history'] + fields.keys())))
 
     def checkHeaders(self, headerfields, mode='create'):
@@ -227,4 +228,14 @@
             row.pop('password')
 
+        # Replace entire history
+        if 'history' in row:
+            new_history = row.get('history', IGNORE_MARKER)
+            if new_history not in (IGNORE_MARKER, ''):
+                history = IObjectHistory(obj)
+                history._annotations[
+                    history.history_key] = literal_eval(new_history)
+                items_changed += ('%s=%s, ' % ('history', new_history))
+            row.pop('history')
+
         # Update registration state
         if 'state' in row:
@@ -233,5 +244,5 @@
                 value = row['state']
                 IWorkflowState(obj).setState(value)
-                msg = _("State '${a}' set", mapping = {'a':value})
+                msg = _("State '${a}' imported", mapping = {'a':value})
                 history = IObjectHistory(obj)
                 history.addMessage(msg)
@@ -302,5 +313,4 @@
                 row['state'] not in (IGNORE_MARKER, ''):
                 errs.append(('workflow','not allowed'))
-                return errs, inv_errs, conv_dict
         if 'transition' in row:
             if row['transition'] not in IMPORTABLE_TRANSITIONS:
@@ -315,4 +325,10 @@
                     # be changed if empty.
                     conv_dict['state'] = IGNORE_MARKER
+        if 'history' in row:
+            if row['history'] not in (IGNORE_MARKER, ''):
+                try:
+                    new_history = literal_eval(row['history'])
+                except:
+                    errs.append(('history','malformed string'))
         try:
             # Correct stud_id counter. As the IConverter for students
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data.csv
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data.csv	(revision 16818)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data.csv	(revision 16819)
@@ -1,13 +1,13 @@
-student_id,firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,state,password
-X666666,Aaren,Pieri,1,1990-01-02,100000,,1234,M,courses validated,test1234
-Y777777,Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,m,courses validated,
-,Susann,Berson,3,1990-01-04,100002,aa@aa.ng,1234,F,courses validated,
-,Else,Mueller,4,1990-01-05,100003,aa@aa.ng,1234,f,school fee paid,
-,,,,,,,,,,
-,,,,  ,,, ,,,
-,,,,<IGNORE>,,,<IGNORE>,,,
-X888888,Alfons,Meier,5,1990-01-02,100004,aa@aa.ng,1234,m,courses validated,
-X999999,Herbert,Mueller,6,1990-01-02,100005,aa@aa.ng,1234,m,courses registered,
-X111111,Fabian,Riester,7,1990-01-02,100006,aa@aa.ng,1234,m,courses validated,
-X222222,Thomas,Kinderman,8,1990-01-02,100007,aa@aa.ng,1234,m,courses validated,
-X333333,Paula,Schall,9,1990-01-02,100008,aa@aa.ng,1234,m,courses registered,
+student_id,firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,sex,state,password,history
+X666666,Aaren,Pieri,1,1990-01-02,100000,,1234,M,courses validated,test1234,"[u'2012-10-23 00:05:39 WAT - Record created by System Admin', u'2012-10-23 00:05:39 WAT - Admitted by System Admin',u""2021-02-21 12:49:12 WAT - State 'graduated' set by System Admin""]"
+Y777777,Claus,Finau,2,1990-01-03,100001,aa@aa.ng,1234,m,courses validated,,
+,Susann,Berson,3,1990-01-04,100002,aa@aa.ng,1234,F,courses validated,,
+,Else,Mueller,4,1990-01-05,100003,aa@aa.ng,1234,f,school fee paid,,
+,,,,,,,,,,,
+,,,,  ,,, ,,,,
+,,,,<IGNORE>,,,<IGNORE>,,,,
+X888888,Alfons,Meier,5,1990-01-02,100004,aa@aa.ng,1234,m,courses validated,,
+X999999,Herbert,Mueller,6,1990-01-02,100005,aa@aa.ng,1234,m,courses registered,,
+X111111,Fabian,Riester,7,1990-01-02,100006,aa@aa.ng,1234,m,courses validated,,
+X222222,Thomas,Kinderman,8,1990-01-02,100007,aa@aa.ng,1234,m,courses validated,,
+X333333,Paula,Schall,9,1990-01-02,100008,aa@aa.ng,1234,m,courses registered,,
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_migration.csv
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_migration.csv	(revision 16818)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/sample_student_data_migration.csv	(revision 16819)
@@ -1,7 +1,8 @@
-student_id,firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,password,sex,state
-A123456,Aaren,Pieri,1,1990-01-02#,100000,aa@aa.ng,1234,mypw1,m,clearance started
-B123456,Aaren,Finau,2,1990-01-03,100001,aa@aa.ng,1234,mypw1,m,cleared
-C123456,Aaren,Berson,3,1990-01-04,100002,aa@aa.ng,1234,{SSHA}JJBeA47pgGah500NFpuM2tVeLAbM8dxj,m,admitted
-D123456,John,Wolter,4,1990-01-05,100003,aa@aa.ng,1234,mypw1,m,nonsense
-E123456,John,Kennedy,5,1990-01-06,100004,aa@aa.ng,1234,,x,
-F123456,Frank,Meyer,6,1990-01-06,100005,aa@aa.ng,1234,,m,
+student_id,firstname,lastname,reg_number,date_of_birth,matric_number,email,phone,password,sex,state,history
+A123456,Aaren,Pieri,1,1990-01-02#,100000,aa@aa.ng,1234,mypw1,m,clearance started,"[u'2012-10-23 00:05:39 WAT - Record created by System Admin', u'2012-10-23 00:05:39 WAT - Admitted by System Admin',u""2021-02-21 12:49:12 WAT - State 'graduated' set by System Admin""]"
+B123456,Aaren,Finau,2,1990-01-03,100001,aa@aa.ng,1234,mypw1,m,cleared,
+C123456,Aaren,Berson,3,1990-01-04,100002,aa@aa.ng,1234,{SSHA}JJBeA47pgGah500NFpuM2tVeLAbM8dxj,m,admitted,
+D123456,John,Wolter,4,1990-01-05,100003,aa@aa.ng,1234,mypw1,m,nonsense,
+E123456,John,Kennedy,5,1990-01-06,100004,aa@aa.ng,1234,,x,,
+F123456,Frank,Meyer,6,1990-01-06,100005,aa@aa.ng,1234,,m,,
+G123456,Ulli,Meyer,7,1990-01-06,100007,aa@aa.ng,1234,,m,,nonsense
Index: /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py
===================================================================
--- /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py	(revision 16818)
+++ /main/waeup.kofa/trunk/src/waeup/kofa/students/tests/test_batching.py	(revision 16819)
@@ -420,4 +420,15 @@
         self.assertEqual(
             self.app['students']['X666666'].state, 'courses validated')
+        # history was successfully impotrted
+        history = self.app['students']['X666666'].history.messages
+        self.assertEqual(history[0],
+            '2012-10-23 00:05:39 WAT - Record created by System Admin')
+        self.assertTrue(
+            "State 'courses validated' imported by system" in history[3])
+        # history import has been logged
+        logcontent = open(self.logfile).read()
+        self.assertTrue(
+            "history=[u'2012-10-23 00:05:39 WAT - Record created by System Admin'"
+            in logcontent)
         # Two new student_ids have been created.
         self.assertEqual(self.app['students']._curr_stud_id, 1000003)
@@ -514,11 +525,12 @@
             self.csv_file_migration, STUDENT_HEADER_FIELDS_MIGRATION)
         content = open(fail_file).read()
-        self.assertEqual(num_warns,2)
+        self.assertEqual(num_warns,3)
         assert len(self.app['students'].keys()) == 5
         self.assertEqual(
             content,
-            'reg_number,firstname,student_id,sex,email,phone,state,date_of_birth,lastname,password,matric_number,--ERRORS--\r\n'
-            '4,John,D123456,m,aa@aa.ng,1234,nonsense,1990-01-05,Wolter,mypw1,100003,state: not allowed\r\n'
-            '5,John,E123456,x,aa@aa.ng,1234,<IGNORE>,1990-01-06,Kennedy,<IGNORE>,100004,sex: Invalid value\r\n'
+            'reg_number,firstname,student_id,sex,email,phone,state,date_of_birth,lastname,password,matric_number,history,--ERRORS--\r\n'
+            '4,John,D123456,m,aa@aa.ng,1234,nonsense,1990-01-05,Wolter,mypw1,100003,<IGNORE>,state: not allowed\r\n'
+            '5,John,E123456,x,aa@aa.ng,1234,<IGNORE>,1990-01-06,Kennedy,<IGNORE>,100004,<IGNORE>,sex: Invalid value\r\n'
+            '7,Ulli,G123456,m,aa@aa.ng,1234,<IGNORE>,1990-01-06,Meyer,<IGNORE>,100007,nonsense,history: malformed string\r\n'
             )
         students = self.app['students']
@@ -532,7 +544,9 @@
                          datetime.date(1990, 1, 3))
         self.assertTrue(students['B123456'].clearance_locked)
-        history = ' '.join(students['A123456'].history.messages)
+        history = students['A123456'].history.messages
+        self.assertEqual(history[0],
+            '2012-10-23 00:05:39 WAT - Record created by System Admin')
         self.assertTrue(
-            "State 'clearance started' set by system" in history)
+            "State 'clearance started' imported by system" in history[3])
         # state was empty and student is thus in state created
         self.assertEqual(students['F123456'].state,'created')
@@ -723,5 +737,5 @@
         self.assertEqual(self.student.current_mode, 'ug_pt')
         shutil.rmtree(os.path.dirname(fin_file))
-        # Transer has bee logged.
+        # Transfer has bee logged.
         logcontent = open(self.logfile).read()
         self.assertTrue(
