# This file is part of ReportTool # ReportTool (Felicity) is copyright 2004-8 Steve Butterfill. # # ReportTool is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # ReportTool is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with ReportTool. If not, see . # # If you want to use ReportTool under a difference licence, email # s.butterfill@warwick.ac.uk. # imports data from old version of (java) reporttool database # assumes that all reports have the warwick.philosophy xml format # (c) Steve Butterfill 2007 # use subject to licence. see licence.txt # Not thoroughly tested, potentially buggy (e.g. linking ids correctly). import sqlobject from sqlobject import (SQLObject, UnicodeCol, PickleCol, BoolCol, ForeignKey, AND, IN, NOT, ) from model import (ReportFormat, Student, Dept, Degree, Staff, User, Group, Module, Year, Term, SitsSprLine, OmrLine, Report, ) import re #define anything to skip SKIP_MODULES = False SKIP_SDA = True #define connections to various databases #conn_rt is the reporttool database conn_rt = sqlobject.connectionForURI("postgres://steve:hope@localhost/reporttool") #felicity's database conn_felicity = sqlobject.connectionForURI("postgres://steve:hope@localhost/felicity") #temporary database for use while this script is running #conn_temp = sqlobject.connectionForURI("sqlite:///:memory:") print "you may need to update path to sqlite db" conn_temp = sqlobject.connectionForURI("sqlite:///home/steve/python/turbogears_projects/felicity/import_rt.db") #connect felicity objects to database for obj in (ReportFormat, Student, Dept, Degree, Report, Staff, User, Group, Module, Year, Term, SitsSprLine, OmrLine, Report, ): obj._connection = conn_felicity #define objects for connecting to reporttool database #each of these objects handles the connection to one table class RtStudent(SQLObject): class sqlmeta: table = "students" idName = "student_id" fromDatabase=True deleted_h = property(lambda self: self.deletedH) def __str__(self): return self.firstname+" "+self.lastname _connection = conn_rt class RtDept(SQLObject): class sqlmeta: table = "depts" idName = "dept_id" fromDatabase=True _connection = conn_rt class RtStaff(SQLObject): class sqlmeta: table = "staff" idName = "staff_id" fromDatabase=True _connection = conn_rt class RtDegree(SQLObject): class sqlmeta: table = "degrees" idName = "degree_id" fromDatabase=True _connection = conn_rt class RtModule(SQLObject): class sqlmeta: table = "modules" idName = "module_id" fromDatabase = True _connection = conn_rt class RtYear(SQLObject): class sqlmeta: table = "academic_years" idName="academicyear_id" fromDatabase = True _connection = conn_rt class RtTerm(SQLObject): class sqlmeta: table = "terms" idName= "term_id" fromDatabase = True _connection = conn_rt class RtTermDate(SQLObject): class sqlmeta: table = "termdates" idName = "termdate_id" fromDatabase = True _connection = conn_rt class RtStudentDegree(SQLObject): class sqlmeta: table = "student_degree_assignments" idName = "student_degree_assignment_id" fromDatabase = True _connection = conn_rt class RtStudentModule(SQLObject): class sqlmeta: table = "student_module_assignments" idName = "student_module_assignment_id" fromDatabase = True _connection = conn_rt class RtReport(SQLObject): class sqlmeta: table = "reports" idName = "report_id" fromDatabase = True _connection = conn_rt #necessary but won't work because multiple column primary key #class RtDeptDegree(SQLObject): # class sqlmeta: # table = "dept_degrees" # fromDatabase=True # _connection = conn_rt #define link objects for use in this class #each link object stores the key of a reporttool object and #the corresponding felicity key class LinkStudent(SQLObject): """Some reporttool students may link to one student in Felicity because of the way student codes are handled""" rt_stud = ForeignKey('RtStudent', unique=True) fel_stud = ForeignKey('Student', notNone=True) imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_student_id): """given a RT student id, returns the corresponding Student in felicity""" studs = list(LinkStudent.select(LinkStudent.q.rt_studID==rt_student_id)) if len(studs) != 1: return None else: fel_id= studs[0].fel_studID return Student.get(fel_id) class LinkDept(SQLObject): rt_dept = ForeignKey('RtDept', unique=True) fel_dept = ForeignKey('Dept', unique=True) imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_dept_id): "gives the Dept in felicity of an RtDept" depts = list(LinkDept.select(LinkDept.q.rt_deptID==rt_dept_id)) if len(depts) != 1: return None return Dept.get(depts[0].fel_deptID) class LinkDegree(SQLObject): rt_degree = ForeignKey('RtDegree', unique=True) fel_degree = ForeignKey('Degree', unique=True) imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_degree_id): degrees = list(LinkDegree.select(LinkDegree.q.rt_degreeID==rt_degree_id)) if len(degrees) != 1: return None #may be hiding programming error else: return Degree.get(degrees[0].fel_degreeID) class LinkStaff(SQLObject): rt_staff = ForeignKey('RtStaff', unique=True) fel_staff = ForeignKey('Staff', notNone=True) imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_staff_id): staff = list(LinkStaff.select(LinkStaff.q.rt_staffID==rt_staff_id)) if len(staff) != 1: return None #may be hiding programming error else: return Staff.get(staff[0].fel_staffID) class LinkModule(SQLObject): rt_module = ForeignKey('RtModule', unique=True) fel_module = ForeignKey('Module', unique=True) imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_module_id): modules = list(LinkModule.select(LinkModule.q.rt_moduleID==rt_module_id)) if len(modules) != 1: return None else: return Module.get(modules[0].fel_moduleID) class LinkYear(SQLObject): rt_year = ForeignKey('RtYear', unique=True) fel_year = ForeignKey('Year', unique=True) imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_year_id): link = list(LinkYear.select(LinkYear.q.rt_yearID==rt_year_id))[0] return Year.get(link.fel_yearID) class LinkTerm(SQLObject): rt_term = ForeignKey('RtTerm', unique=True) fel_term = ForeignKey('Term', unique=True) imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_term_id): link = list(LinkTerm.select(LinkTerm.q.rt_termID==rt_term_id))[0] return Term.get(link.fel_termID) class LinkTermDate(SQLObject): rt_termdate = ForeignKey('RtTermDate', unique=True) fel_term = ForeignKey('Term', default=None) fel_year = ForeignKey('Year', default=None) imported = BoolCol(default=None) #true if this item was imported from reporttool def _fel_termdate_r(self): return (self.fel_term, self.fel_year) def _fel_termdate_w(self, (fel_term, fel_year)): self.fel_term = fel_term self.fel_year = fel_year fel_termdate = property(_fel_termdate_r, _fel_termdate_w) @staticmethod def from_rt(rt_termdate_id): """Returns fel term, year pair or None, None if not found""" res = list(LinkTermDate.select(LinkTermDate.q.rt_termdateID==rt_termdate_id)) if len(res) !=1: return None,None return Term.get(res[0].fel_termID), Year.get(res[0].fel_yearID) class LinkStudentDegree(SQLObject): """NB: funny names in this class""" rt_student_degree = ForeignKey('RtStudentDegree', unique=True) fel_student_degree = ForeignKey('SitsSprLine', notNone=True) imported = BoolCol(default=None) #true if this item was imported from reporttool class LinkStudentModule(SQLObject): """funny names again""" rt_student_module=ForeignKey('RtStudentModule', unique=True) fel_student_module=ForeignKey('OmrLine') imported = BoolCol(default=None) #true if this item was imported from reporttool @staticmethod def from_rt(rt_id): """Returns fel omr_line id or None if not fuond""" res = list(LinkStudentModule.select(LinkStudentModule.q.rt_student_moduleID==rt_id)) if len(res) !=1: return None return OmrLine.get(res[0].fel_student_moduleID) class LinkReport(SQLObject): rt_report = ForeignKey('RtReport', unique=True) fel_report = ForeignKey('Report', notNone=False) #we don't import blank reports imported = BoolCol(default=None) #true if this item was imported from reporttool #link working objects to working database, creating tables for obj in (LinkStudent,LinkDept, LinkStaff, LinkDegree, LinkModule, LinkYear, LinkTerm, LinkTermDate, LinkStudentDegree, LinkStudentModule, LinkReport): obj._connection = conn_temp if not conn_temp.tableExists(obj.sqlmeta.table): print "creating new table for %s" % obj.__name__ #obj.dropTable() obj.createTable() #---- # main section -- first match students from two databases #--- # create any missing students def convert_rt_stud_code(rt_code): idx = rt_code.index(r'/') return rt_code[0:idx] def stud_from_rt(rt_stud): "creates a Student given an rt_student" if rt_stud.deleted_h is not None: raise Exception, "rt_stud %s is deleted!" % rt_stud code = convert_rt_stud_code(rt_stud.code) return Student(code=code, lastname=rt_stud.lastname, firstname=rt_stud.firstname,) if __name__=='__main__': new_studs = 0 #find matches for student objects, record results in LinkStudent print "students" already_linked = [x.rt_studID for x in list(LinkStudent.select())] print "%d are already linked" % len(already_linked) for rt_stud in list(RtStudent.select()): rt_code = convert_rt_stud_code(rt_stud.code) #strip away trailing degree part stud = Student.from_code(rt_code) if rt_stud.deleted_h is not None: continue #ignore if deleted if rt_stud.id in already_linked: continue if stud is None: #create new student stud = stud_from_rt(rt_stud) new_studs +=1 else: #print "rt code %s found match, rt.id=%s, fel.id=%s" % (rt_code, rt_stud.id, stud.id) pass LinkStudent(rt_stud=rt_stud, fel_stud=stud) print "new students", new_studs print "total student links", LinkStudent.select().count() #--- # generic functions for matching objects, creating links and creating new objects as needed def default_match(rt_item, fel_class): """match function accepts item from ReportTool database and class of object to match against. It returns the item matching rt_item from Felicity or None. This default function attempts to match items by their code attributes""" return fel_class.from_code(rt_item.code) def link_n_create(fel_class, rt_class, create_fn, link_class, match_fn=default_match): """creates links between RT and Felicity object, creates new Felicity objects where needed. Generalised version of the above code for students. Uses fel_class' name to infer attributes of link_class; can pass in a string instead if necessary.""" new_items = 0 #use the name of the felicity class to work out some attribute names later if type(fel_class)==str: item_name=fel_class else: item_name = fel_class.__name__.lower() print "---" print "importing %ss." % item_name #filter already linked items attr_name = "rt_"+item_name+"ID" already_linked = [getattr(x,attr_name) for x in list(link_class.select())] print "%d are already linked" % len(already_linked) #get all items to match or import, but ignore deleted items in reporttool clause = rt_class.q.deletedH==None if len(already_linked) >0 : clause = AND(clause,NOT(IN(rt_class.q.id, already_linked))) rt_items = list(rt_class.select(clause)) print "nof unlinked %ss: %d" % (item_name, len(rt_items)) for rt_item in rt_items: #attempt to match the reporttool object in felicity fel_item=match_fn(rt_item, fel_class) imported = False #True if new item created for Felicity #doesn't exist in felicity so muct create if fel_item is None: #create the item in felicity fel_item = create_fn(rt_item) imported = True new_items+=1 #finally, create new instance of the link class to record connection dic = {}; dic["rt_"+item_name]=rt_item; dic["fel_"+item_name]=fel_item dic['imported'] = imported link_class(**dic) print "new %ss: %d" %(item_name, new_items) print "total %s linked: %d" % (item_name, link_class.select().count()) #finally, report on deteled items nof_del_items = rt_class.select(rt_class.q.deletedH!=None).count() print "ignored %d deleted %ss" %(nof_del_items, item_name) #--- #match and create depts def dept_from_rt(rt_dept): "creates a Dept given an rt_dept" if rt_dept.deletedH is not None: raise Exception, "rt_dept %s is deleted!" % rt_dept return Dept(code=rt_dept.code, name=rt_dept.name) if __name__=='__main__': link_n_create(Dept, RtDept, dept_from_rt, LinkDept) #--- # match and create degrees def degree_from_rt(rt_degree): #issue--felicity's degrees all have dept info, have to recover this from RtDeptDegree return Degree(code=rt_degree.code, name=rt_degree.name) if __name__=='__main__': link_n_create(Degree, RtDegree, degree_from_rt, LinkDegree) #--- # match and create staff (plus Users) #first undelete any staff that appear on reports def undelete_reporting_staff(): """undelete any staff that appear on reports""" raw_results = conn_rt.queryAll("SELECT DISTINCT author_id FROM REPORTS WHERE deleted_h IS NULL") for r in raw_results: author_id = r[0] staff = RtStaff.get(author_id) if staff.deletedH is not None: #undelete staff to avoid errors later staff.deletedH = None def staff_match(rt_item, fel_class): #NB: the strip() is because we strip before create res = list(fel_class.select(fel_class.q.lastname==rt_item.lastname.strip())) if len(res)!=1: return None return res[0] def staff_from_rt(rt_staff): "also creates a User" if rt_staff.deletedH is not None: raise Exception, "rt_staff %s is deleted!" % rt_staff dept = LinkDept.from_rt(rt_staff.deptID) #issue: rt_staff.code may be null. Will use lastnames for now! code =rt_staff.code if code is None: code=rt_staff.firstname.strip()+rt_staff.lastname.strip() staff = Staff(firstname=rt_staff.firstname.strip(), lastname=rt_staff.lastname.strip(), code=code, dept=dept) #issue--TG requires email_address be specified user = User(user_name=rt_staff.username, password=rt_staff.password, display_name = str(staff), email_address=rt_staff.username) #issue--not sure which permissions--some are TAs, some lecturers. #will make all lecturers lecturer = Group.by_group_name("lecturer") user.addGroup(lecturer) staff.user=user return staff def do_staff(): undelete_reporting_staff() link_n_create(Staff, RtStaff, staff_from_rt, LinkStaff, staff_match) if __name__=='__main__': do_staff() #--- #modules # complication: module-dept links stored in table with multiple-col primary key def module_dept_links(): """gets module_dept links from reporttool database""" conn = RtModule._connection #use reporttool database list_of_pairs = conn.queryAll("SELECT module_id, dept_id FROM dept_modules ;" ) moduleDegree = {} for module_id, dept_id in list_of_pairs: moduleDegree[module_id] = dept_id return moduleDegree moduleDegree = module_dept_links() def module_from_rt(rt_module): """create a felicity module give rt_module as the reporttool Module object""" if rt_module.deletedH is not None: raise Exception, "rt_module %s is deleted!" % rt_module rt_code = rt_module.code if rt_code is None or rt_code=="": raise Exception, "RtModule %s with blank code" % rt_module.id rt_name=rt_module.name if rt_name is None: raise Exception, "RtModule %s with name=None" % rt_module.id #first match dept if any rt_module_id= rt_module.id if not moduleDegree.has_key(rt_module_id): dept = None else: rt_dept_id = moduleDegree[rt_module_id] dept=LinkDept.from_rt(rt_dept_id) #finally, create module module = Module(code = rt_code, name = rt_name) if dept is not None: module._dept_key=dept return module if __name__=='__main__': if not SKIP_MODULES: link_n_create(Module, RtModule, module_from_rt, LinkModule) #---- # years def match_year(rt_year, fel_year_class): """given the rt_year, returns the corresponding Year in felicity or thows exception if None. (This import program is no prepared to create Years)""" #match depends on year name #typical rt year name: 2007-8 #typical fel year name: 07/08 rt_name = rt_year.name[2:] left, right = rt_name.split("-") fel_name = "%02d/%02d" % (int(left), int(right)) year = Year.from_name(fel_name) if year is None: raise Exception, """Year %s (which in felicity would be %s) does not exist in felicity. This import script requires all years to exist in felicity.""" % (rt_name, fel_name) return year if __name__=='__main__': link_n_create(Year, RtYear, None, LinkYear, match_year) #test links created correctly def test_year_link(rt_name, fel_name): rt_year = list(RtYear.select(RtYear.q.name==rt_name))[0] fel_year = list(Year.select(Year.q.name==fel_name))[0] linked_year = LinkYear.from_rt(rt_year.id) if fel_year != linked_year: print rt_name, " is linked to ", linked_year, "!!!" raise Exception, "Error in linking years %s=%s" % (rt_name, fel_name) if __name__=='__main__': test_year_link("2006-7", "06/07") test_year_link("2005-6", "05/06") #----------- # terms def match_term(rt_term, fel_term_class): """given the rt_term, returns the corresponding Year in felicity or thows exception if None. (This import program is no prepared to create Terms)""" #match depends on term name #typical rt term name: Autumn Term #typical fel term name: Autumn fel_name = rt_term.name[:-5] term = Term.from_name(fel_name) if term is None: raise Exception, """Term %s (which in felicity would be %s) does not exist in felicity. This import script requires all terms to exist in felicity.""" % (rt_term.name, fel_name) return term if __name__=='__main__': link_n_create(Term, RtTerm, None, LinkTerm, match_term) #--- # term dates # felicity: year + term only # reporttool: each term date is an object def match_termdate(rt_termdate, nothing): """given the rt_termdate, returns the corresponding (Term, Year) pair in felicity or thows exception if None. (This import program is no prepared to create Terms or Years)""" fel_term = LinkTerm.from_rt(rt_termdate.termID) fel_year = LinkYear.from_rt(rt_termdate.academicyearID) if fel_term is None or fel_year is None: raise Exception, """Missing year or term in Felicity! This script doesn't create them.""" return (fel_term, fel_year) class TermDate(object): "link_n_create needs this class' name to infer names of attributes" pass if __name__=='__main__': link_n_create(TermDate, RtTermDate, None, LinkTermDate, match_termdate) #test import works q = LinkTermDate.select() for ltd in list(q): #print "q.rt_termdate", ltd.rt_termdate #print "q.fel_term", ltd.fel_term #print "q.fel_year", ltd.fel_year assert ltd.fel_termID is not None assert ltd.fel_yearID is not None #---- #student_degree_assignments def match_student_degree(rt_student_degree, nothing): """returns corresponding SitsSprLine object or None. (This import programme will not create SitsSprLines, missing degree assignments will be silently ignored.)""" rt_stud_id = rt_student_degree.studentID rt_degree_id = rt_student_degree.degreeID fel_stud = LinkStudent.from_rt(rt_stud_id) if fel_stud is None: raise Exception, "No student found for rt id %d" % rt_stud_id fel_degree = LinkDegree.from_rt(rt_degree_id) if fel_degree is None: raise Exception, "No degree found for rt id %s" % rt_degree_id sits_spr_lines = list(SitsSprLine.select(AND( SitsSprLine.q.studentID==fel_stud.id, SitsSprLine.q.route_code==fel_degree.code ))) if len(sits_spr_lines) != 1: #fallback to trying for a unique degree for this student sits_spr_lines = list(SitsSprLine.select(SitsSprLine.q.studentID==fel_stud.id)) if len(sits_spr_lines) != 1: print " >ignoring: Found %d matches for %s on %s" % (len(sits_spr_lines), fel_stud, fel_degree) return None return sits_spr_lines[0] def dummy_create(*args, **kw): return None if __name__=='__main__': if not SKIP_SDA: link_n_create("student_degree", RtStudentDegree, dummy_create, LinkStudentDegree, match_student_degree) #---- #student_module_assignments def match_student_module(rt_student_module, nothing): """returns corresponding OmrLine object or None""" rt_stud_id = rt_student_module.studentID rt_module_id = rt_student_module.moduleID rt_termdate_id = rt_student_module.termdateID fel_stud = LinkStudent.from_rt(rt_stud_id) if fel_stud is None: raise Exception, "No student found for rt id %d" % rt_stud_id fel_module = LinkModule.from_rt(rt_module_id) if fel_module is None: raise Exception, "No module found for rt id %d" % rt_module_id fel_term, fel_year = LinkTermDate.from_rt(rt_termdate_id) omr_lines_q = OmrLine.select(AND(OmrLine.q.studentID==fel_stud.id, OmrLine.q.moduleID==fel_module.id, OmrLine.q.yearID==fel_year.id)) if omr_lines_q.count()==1: return list(omr_lines_q)[0] return None def create_omr_line(rt_student_module): """creates an OmrLine given a Rt StudentModuleAssignment""" rt_stud_id = rt_student_module.studentID rt_module_id = rt_student_module.moduleID rt_termdate_id = rt_student_module.termdateID fel_stud = LinkStudent.from_rt(rt_stud_id) if fel_stud is None: raise Exception, "No student found for rt id %d" % rt_stud_id fel_module = LinkModule.from_rt(rt_module_id) if fel_module is None: raise Exception, "No module found for rt id %d" % rt_module_id fel_term, fel_year = LinkTermDate.from_rt(rt_termdate_id) student_code = fel_stud.code #strip degree info from code spr_code = RtStudent.get(rt_stud_id).code #find degree code rt_sda = RtStudentDegree.get(rt_student_module.studentDegreeAssignmentID) rt_deg = RtDegree.get(rt_sda.degreeID) degree_code = rt_deg.code new = OmrLine(student=fel_stud, student_code=student_code, spr_code=spr_code, firstname=fel_stud.firstname, lastname=fel_stud.lastname, year_f = fel_year.name, year = fel_year, module=fel_module, cats = rt_student_module.cats, assessment_code = rt_student_module.omrAssessmentgroup, year_of_study = str(rt_student_module.omrYearofstudy), degree_code = degree_code, status=None ) return new if __name__=='__main__': link_n_create("student_module", RtStudentModule, create_omr_line, LinkStudentModule, match_student_module) #--- # reports # NB reports in reporttool which are not submitted are ignored (a LinkReport entry contains None for them) def match_report(rt_report, not_used): """attempts to match reports using student, module and time submitted""" student = LinkStudent.from_rt(rt_report.studentID) if student is None: print "rt_report id %d has no student!" % rt_report.id module = LinkModule.from_rt(rt_report.moduleID) if module is None: print "rt_report id %d has no module!" % rt_report.id submitted = rt_report.submittedH reports = list(Report.select(AND(Report.q.studentID==student.id, Report.q.moduleID==module.id, Report.q.submitted==submitted))) if len(reports) != 1: return None return reports[0] def transform_report_content(old_content): """transforms reporttool xml content into felicity python object content. Assumes that the reportformat being used in felicity is 'warwick.philosophy'. And only works for one reportformat used in reporttool. Returns None if there is no content to the report (i.e. no values in the fields) a reporttool content looks like this: 5/8 Improved as the course went on. Very good by the end. 68 3 a felicity report content looks liKe this: [u'80%', u'100%', u'good', u'excellent', u'should do well', u'1'] The reportformat used is configured so that the recommended action numbers match """ #create rs, a list of pairs of regex for parsing content of report rs = [] #a list of pairs of regex, first matches '' style blank fields #NB order must match the report format we're using!!! for name in ('"Attendance at lectures"','"Attendance at seminars"', '"Contribution to seminars"', '"Written work"', '"Other comments"', '"recommended action"'): rb = re.compile(name+'\s*/>') #matches field if blank r = re.compile(name+'\s*>'+'(.*?)'+'', re.DOTALL) #matches content of field if not blank rs.append((rb, r)) content = [] for rb, r in rs: field = "" if rb.search(old_content) is None: #field is not empty field = r.search(old_content).group(1).strip() content.append(field) #check newly created content has at least one non empty field empty = True #flag to indicate no content has been added for field in content: if len(field)>0: empty = False if not empty: return content else: return None def create_report(rt_report): """create a felicity report given a RtReport""" #don't create a report if not submitted if rt_report.submittedH is None: return None #ignore unsubmitted reports if rt_report.content is None or len(rt_report.content) < 1: return None #ignore reports without content #following line replaced with query because doesn't work (reason unknown) #rf = list(ReportFormat.select(ReportFormat.q.name=='Warwick.philosophy'))[0] rf = conn_felicity.queryAll("SELECT ID FROM REPORT_FORMAT WHERE NAME='Warwick.philosophy'")[0][0] created = rt_report.created student = LinkStudent.from_rt(rt_report.studentID) module = LinkModule.from_rt(rt_report.moduleID) submitted = rt_report.submittedH term, year = LinkTermDate.from_rt(rt_report.termdateID) author = LinkStaff.from_rt(rt_report.authorID) module_code_f = rt_report.modulecodeF firstname_f = rt_report.studentfirstnameF lastname_f = rt_report.studentlastnameF author_firstname_f = rt_report.staffwriterfirstnameF author_lastname_f = rt_report.staffwriterlastnameF omr_line = LinkStudentModule.from_rt(rt_report.studentModuleAssignmentID) degree = omr_line.degree #convert content try: content = transform_report_content(rt_report.content) except Exception, e: print "exception transforming content of report id %d" % rt_report.id print "content:" print rt_report.content raise Exception, e if content is None: #ignore reports that have no content return None result = Report(format=rf, content=content, created=created, student=student, module=module, submitted=submitted, term=term, year=year, degree=degree, author=author, module_code_f=module_code_f, firstname_f=firstname_f, lastname_f=lastname_f, author_firstname_f=author_firstname_f, author_lastname_f=author_lastname_f, omr_line=omr_line) return result if __name__=='__main__': print "If you get errors here it may be because deleted staff have written reports" link_n_create(Report, RtReport, create_report, LinkReport, match_report)