# 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)