'''
Created on 1 oct. 2011
@author: abderrazek boufahja
'''
from xml.dom import minidom
from difflib import ndiff
import re
import os
import psycopg2
SENDMAIL = '/usr/sbin/sendmail' # sendmail location
NOTIFICATION_BY_MAIL = False # True or False
CALCULATE_DIFFERENCE = False # True or False
LIST_ADMIN_MAIL = ['abderrazek.boufahja@irisa.fr', 'abderrazek.boufahja@gmail.com']
SENDER = 'epsos-code-updater@irisa.fr'
DB_TSAM_NAME = 'tsam'
DB_USER = 'gazelle'
FILE_TO_UPDATE = '/Users/aboufahj/Documents/workspace/XMLGEN/doc/res.xml'
##
#
# these are list of DTO Class used on this script
#
##
class CodeSystem:
def __init__(self):
self.id = 0
self.oid = ''
self.name = ''
self.description = ''
self.listValueSet = []
self.listCodeSystemVersion = []
def __str__(self):
return '(' + str(self.id) + ',' + self.oid + ',' + self.name + ',' + self.description + ')'
def _getAsXML(self):
ss = '\n'
for valset in self.listValueSet:
ss = ss + valset._getAsXML()
ss = ss + '\n'
return ss
class CodeSystemConcept:
def __init__(self):
self.id = 0;
self.code = ''
self.status = ''
self.statusDate = ''
self.definition = ''
self.codeSystemVersion = None
self.listDesignation = []
class CodeSystemVersion:
def __init__(self):
self.id = 0
self.fullName = ''
self.localName = ''
self.previousVersionId = 0
self.effectiveDate = ''
self.releaseDate = ''
self.status = ''
self.statusDate = ''
self.description = ''
self.copyright = ''
self.source = ''
self.codeSystem = None
self.listCodeSystemConcept = []
class Designation:
def __init__(self):
self.id = 0
self.designation = ''
self.languageCode = ''
self.type = ''
self.isPreffered = False
self.status = ''
self.statusDate = ''
self.codeSystemConcept = None
def _getAsXML(self):
ss = '\n'
return ss
class TranscodingAssociation:
def __init__(self):
self.id = 0
self.targetConcept = None
self.sourceConcept = None
self.quality = ''
self.status = ''
self.statusDate =''
class ValueSet:
def __init__(self):
self.id = 0
self.oid = ''
self.epsosName = ''
self.description = ''
self.parentCodeSystem = None
self.listValueSetVersion = []
def _getAsXML(self):
ss = '\n'
for valsetVersion in self.listValueSetVersion:
for csc in valsetVersion.listCodeSystemConcept:
for des in csc.listDesignation:
ss = ss + des._getAsXML()
ss = ss + '\n'
return ss
class ValueSetVersion:
def __init__(self):
self.id = 0
self.versionName = ''
self.effectiveDate = ''
self.releaseDate = ''
self.status = ''
self.statusDate = ''
self.description = ''
self.previousVersion = None
self.valueSet = None
self.listCodeSystemConcept = []
##
#
# XMLDAO class used for XML parsing of the original epsos-code.xml file
#
##
class XMLDAO:
def __init__(self):
pass
def getListCodeSystem(self, epsosFilePath):
self.dom = minidom.parse(epsosFilePath)
self.root = self._getNode(self.dom,'Codes')
result = self.root.getElementsByTagName('CodeSystem')
return result
def getListValueSetFromCodeSystem(self, codeSystemNode):
return codeSystemNode.getElementsByTagName('ValueSet')
def _getNode(self, parent_node, name):
try:
return parent_node.getElementsByTagName(name)[0]
except:
return None
##
#
# DBDAO used to access to tsam database and load objects from this db
#
##
class DBDAO:
def __init__(self):
pass
def connect(self):
self.conn = psycopg2.connect('dbname=' + DB_TSAM_NAME + ' user=' + DB_USER)
self.cur = self.conn.cursor()
def loadListCodeSystem(self):
self.connect()
self.cur.execute("SELECT DISTINCT * FROM code_system;")
x=self.cur.fetchone()
res = []
while x:
toAdd = CodeSystem()
toAdd.id = x[0]
toAdd.oid = x[1]
toAdd.name = x[2]
toAdd.description = x[3]
res.append(toAdd)
toAdd.listValueSet = self.loadListValueSetForCodeSystem(toAdd)
x=self.cur.fetchone()
res = sorted(res, key=lambda cs: cs.oid)
return res
def loadListValueSetForCodeSystem(self, toAdd):
res = []
if toAdd.id != 0:
conn = psycopg2.connect('dbname=' + DB_TSAM_NAME +' user=' + DB_USER)
cur = conn.cursor()
cur.execute('SELECT DISTINCT * FROM value_set WHERE parent_code_system_id=' + str(toAdd.id) + ';')
x=cur.fetchone()
while x:
valset = ValueSet()
valset.id = x[0]
valset.oid = x[1]
valset.epsosName = x[2]
valset.description = x[3]
if valset.oid != None:
valset.parentCodeSystem = toAdd
res.append(valset)
valset.listValueSetVersion = self.loadListValueSetVersionForValueSet(valset)
x=cur.fetchone()
res = sorted(res, key=lambda cs: cs.oid)
return res
def loadListValueSetVersionForValueSet(self, valset):
res = []
if (valset.id != 0):
conn = psycopg2.connect('dbname=' + DB_TSAM_NAME +' user=' + DB_USER)
cur = conn.cursor()
cur.execute('SELECT DISTINCT * FROM value_set_version WHERE value_set_id=' + str(valset.id) + ' AND value_set_version.status=\'Current\';')
x=cur.fetchone()
while x:
valsetver = ValueSetVersion()
valsetver.id = x[0]
valsetver.versionName = x[1]
valsetver.effectiveDate = x[2]
valsetver.status = x[4]
valsetver.valueSet = valset
valsetver.listCodeSystemConcept = self.loadListCodeSystemConceptForValueSetVersion(valsetver)
res.append(valsetver)
x=cur.fetchone()
return res
def loadListCodeSystemConceptForValueSetVersion(self, valsetver):
res = []
if (valsetver.id != 0):
conn = psycopg2.connect('dbname=' + DB_TSAM_NAME +' user=' + DB_USER)
cur = conn.cursor()
# cur.execute('SELECT DISTINCT * FROM code_system_concept JOIN code_system_version ON code_system_concept.code_system_version_id=code_system_version.id WHERE code_system_concept.id IN (SELECT code_system_concept_id from x_concept_value_set WHERE x_concept_value_set.value_set_version_id=' + str(valsetver.id) +
# ') AND code_system_concept.status=\'Current\'AND code_system_version.status=\'Current\' AND code_system_version.code_system_id=' +
# str(valsetver.valueSet.parentCodeSystem.id) +' ;')
cur.execute('SELECT DISTINCT * FROM code_system_concept WHERE code_system_concept.id IN (SELECT code_system_concept_id from x_concept_value_set WHERE x_concept_value_set.value_set_version_id=' + str(valsetver.id) +
') AND code_system_concept.status=\'Current\' ;')
x=cur.fetchone()
while x:
csc = CodeSystemConcept()
csc.id = x[0]
csc.code = x[1]
csc.status = x[2]
csc.statusDate = x[3]
csc.definition = x[4]
csc.listDesignation = self.loadListDesignationForCodeSystemConcept(csc)
res.append(csc)
x=cur.fetchone()
return sorted(res, key=lambda csc: csc.code)
def loadListDesignationForCodeSystemConcept(self, csc):
res = []
if csc.id != 0:
conn = psycopg2.connect('dbname=' + DB_TSAM_NAME +' user=' + DB_USER)
cur = conn.cursor()
cur.execute('SELECT DISTINCT * FROM designation WHERE (code_system_concept_id=' + str(csc.id) + ' and designation.status=\'Current\' and designation.language_code=\'en\') ;')
x=cur.fetchone()
while x:
des = Designation()
des.id = x[0]
des.designation = x[1]
des.languageCode = x[2]
des.type = x[3]
des.isPreffered = x[4]
des.status = x[5]
des.statusDate = x[6]
des.codeSystemConcept = csc
res.append(des)
x=cur.fetchone()
return res
def getListCodeSystemAsFullXML(self, listCodeSystem):
ss = '\n'
ss = ss + ' '
for cs in listCodeSystem:
ss = ss + cs._getAsXML()
ss = ss + self._getLanguageStringCodeAsXML()
ss = ss + ''
#dom = minidom.parseString(ss.encode('utf-8'))
dom = minidom.parseString(ss)
res = dom.toprettyxml()
return res
def _getLanguageStringCodeAsXML(self):
return '''
'''
def generateNewEpSOSPivotXML(self, filePath):
notif = ''
xmlcomp = XMLComparator()
fio = FileIOExtra()
news = False
diffString = ''
# try:
listCS = self.loadListCodeSystem()
print '11'
xml = self.getListCodeSystemAsFullXML(listCS)
if CALCULATE_DIFFERENCE:
oldFich = fio.getOriginalFile(filePath)
diffToOld = xmlcomp.getDifferenceBetweenXML(oldFich, xml)
if diffToOld != '':
diffString = diffString + 'There are new modification on the XML pivot-codes : \n'
diffString = diffString + diffToOld
news = True
else :
diffString = diffString + 'No modification of the XML was observed. \n'
print '22'
fio.saveXMLFile(xml, filePath)
print '33'
notif = 'The pivot codes file : ' + filePath + ' was updated using the database ' + DB_TSAM_NAME + '\n'
notif = notif + diffString
#except Exception as e:
# notif = 'Error when trying to update the pivot codes file : ' + filePath + ' using the database ' + DB_TSAM_NAME + '\n'
# notif = notif + 'The Error is : ' + str(e)
#print notif
if NOTIFICATION_BY_MAIL:
mn = MailNotification()
for reseaver in LIST_ADMIN_MAIL:
ss = ''
if news:
ss = '(new modifications)'
mn.sendMail(reseaver, 'Update pivot codes notification' + ss, notif)
##
#
# FileIOExtra Class is used to read and write from a text file
#
##
class FileIOExtra:
def __init__(self):
pass
def saveXMLFile(self, content, fileName):
fich = open(fileName,'w')
print content
fich.write(content.encode('utf-8'))
fich.close()
def getOriginalFile(self, fileName):
res = ''
if os.path.exists(fileName):
fich = open(fileName,'r')
res = fich.read()
fich.close()
return res
##
#
# MailNotification Class is used to send mail using sendmail tools
#
##
class MailNotification:
def __init__(self):
pass
def sendMail(self, to, subject, content):
p = os.popen("%s -t" % SENDMAIL, "w")
p.write('From: ' + SENDER + '\n')
p.write('To: ' + to + '\n')
p.write('Subject: ' + subject + '\n')
p.write('\n')
p.write(content)
sts = p.close()
#if sts != 0:
# print 'Sendmail exit status', sts
##
#
# XMLComparator Class used to compare file and return the difference beetween them
#
##
class XMLComparator:
def __init__(self):
pass
def getDifferenceBetweenXML(self, xmlString1, xmlString2):
res = ''
listres = list(ndiff(xmlString1.splitlines(1), xmlString2.splitlines(1)))
for rr in listres:
m = re.search('^[\+-]+.*', rr)
if m:
res = res + rr
return res
if __name__ == '__main__':
db = DBDAO()
db.generateNewEpSOSPivotXML(FILE_TO_UPDATE)