source: lliurex-abies2pmb/trunk/fuentes/lliurex-abies2pmb.install/usr/share/lliurex-abies2pmb/abies2pmb.py @ 4240

Last change on this file since 4240 was 4240, checked in by jrpelegrina, 2 years ago

Add suport to default values in migration process

  • Property svn:executable set to *
File size: 18.3 KB
Line 
1#!/usr/bin/python3
2import os
3import datetime
4import subprocess
5import sys
6import tempfile
7# -*- coding: utf-8 -*-
8class abies2Pmb():
9        def __init__(self):
10                self.dbg=0
11               
12                #Dict with relations between pmb and abies
13                self.pmb_abies={}
14                self.pmb_abies["empr_categ"]=["TiposLector"]
15                self.pmb_abies["docs_location"]=["Ubicaciones"]
16                self.pmb_abies["authors"]=["Autores"]
17                self.pmb_abies["exemplaires"]=["Ejemplares"]
18                self.pmb_abies["notices"]=["Fondos"]
19                self.pmb_abies["collections"]=["Series"]
20                self.pmb_abies["notices_langues"]=["Fondos_Idiomas"]
21                self.pmb_abies["publishers"]=["Editoriales"]
22                self.pmb_abies["groupe"]=["Cursos"]
23                self.pmb_abies["indexint"]=["CDUs"]
24                self.pmb_abies["notices_tmp"]=["Fondos_all"]
25
26                #Dict with forced column data type
27                self.formatPmbColumns={}
28                self.formatPmbColumns['indexint']={1:'string'}
29
30                #Dict with default values to column
31                self.defaultPmbValues={}
32                self.defaultPmbValues['docs_location']={5:'1'}
33
34                self.defaultPmbValues['notices']={29:'"m"'}
35                self.defaultPmbValues['notices'].update({38:'1'})
36
37                #Blacklist pmb_abies tables.
38                self.blacklist=['notices_tmp']
39
40                #Dict with abies source fields for pmb
41                #Numbers are the indexes of the source data table
42                self.pmb_tables={}
43                self.pmb_tables["docs_location"]=[0,1,'','','','','','','','','','','','','','','','','','','','','','','']
44                self.pmb_tables["empr_categ"]=[0,1,'','','','']
45                self.pmb_tables["authors"]=[0,1,2,'','','','','','','','','','','','']
46                self.pmb_tables["publishers"]=[0,1,'','','','','','','','']
47                self.pmb_tables["groupe"]=[0,1,'','','','']
48                self.pmb_tables["exemplaires"]=[0,4,1,'','',13,'','',6,'','','','','','','','',9,'','','','','','','','','']
49                self.pmb_tables["collections"]=[0,1,'','','','','','']
50                self.pmb_tables["indexint"]=[0,1,'','','']
51                self.pmb_tables["notices_tmp"]=[0,1,2]
52                self.pmb_tables["notices"]=[0,1,14,15,16,13,'','',19,'',24,'',20,17,25,11,21,22,23,'','','','','','','',26,'','','','','','','','','','','','','',6,'','','','','','','','','','','','','']
53                self.pmb_tables["notices_langues"]=[0,'',1,'']
54                #dict with needed virtual tables
55                #Only load one column using a 'get', any other column must be charged with formatTables or formatFields
56                self.virtual_tables={}
57                self.virtual_tables["CDUs"]={}
58                self.virtual_tables["CDUs"].update({'index 0':''})
59                self.virtual_tables["CDUs"].update({'index 1':'get Fondos_CDUs.1'})
60                self.virtual_tables['Series']={}
61                self.virtual_tables['Series'].update({'index 0':''})
62                self.virtual_tables['Series'].update({'index 1':'get Fondos.24'})
63                self.virtual_tables['Fondos_all']={'index 0':'get Fondos.0'}
64                self.virtual_tables['Fondos_all'].update({'index 1':''})
65                self.virtual_tables['Fondos_all'].update({'index 2':''})
66                self.virtual_tables['Fondos_Idiomas']={}
67                self.virtual_tables['Fondos_Idiomas']={'index 0':'get Fondos.0'}
68                self.virtual_tables['Fondos_Idiomas'].update({'index 1':''})
69
70                #Dict whith field transformations
71                self.formatFields={}
72                self.formatFields['TiposLector']={'index 0':'add index 0 7'}
73                self.formatFields['Autores']={'index 1':'value 100=70,value 110=71,value 120=72,value 700=70'}
74                self.formatFields['Ejemplares']={'index 13':'concat 13,14,15'}
75                self.formatFields['Ejemplares'].update({'index 9':'date %m/%d/%y %H:%M:%S %Y-%m-%d %H:%M:%S'})
76                self.formatFields['CDUs']={'index 0':'inc 1 1000'}
77                self.formatFields['Series']={'index 0':'inc 1 1000'}
78                self.formatFields['Fondos']={'index 1':'value 1="a",value 2="g",value 3="m",value4="j",value 5="k",value 6="a",value 7="l"'}
79                self.formatFields['Fondos'].update({'index 13':'from Autores.2 on 13=0'})
80                self.formatFields['Fondos'].update({'index 24':'from Series.0 on 24=0'})
81                self.formatFields['Fondos'].update({'index 26':'from Fondos_all.2 on 0=1'})
82                self.formatFields['Fondos_all']={'index 2':'from CDUs.0 on 0=1'}
83                self.formatFields['Fondos_Idiomas']={'index 1':'from Fondos.9 on 0=0'}
84
85                #Dict whith special columns
86                self.formatTables={}
87                self.formatTables['CDUs']={'index 1':'uniq'}
88                self.formatTables['Series']={'index 1':'uniq'}
89                self.formatTables['Fondos_all']={'index 1':'from Fondos_CDUs.1 on 0=0'}
90
91
92                #Dict with errors
93                self.error={}
94                #self.error['status']=True
95
96                #Array with process order (including virtual tables)
97                self.executionOrder=['Ubicaciones','TiposLector','Autores','Editoriales','Cursos','Ejemplares','Series','CDUs','Fondos_all','Fondos','Fondos_Idiomas']
98                #Tmp variables
99                self.workDir='/tmp/.abiesToPmb/'
100                self.tableValuesDict={}
101                self.tmpInc=0
102
103                #Work files
104                self.mdb=''
105                self.sql=''
106                self.sqlFiles=[]
107        #def _init
108
109        def _debug(self,msg):
110                if self.dbg:
111                        print('Migrator: '+str(msg))
112        #def _debug
113
114        def _error(self,code,index=None):
115                #Error Codes
116                #10 -> Table not found
117                #11 -> Error ocurred while importing mdb file
118                #12 -> Error exporting mdb to csv
119                #13 -> Error generating sql file
120                #14 -> Error concatenating sql files
121                #15 -> No source table for virtual table
122                #16 -> Couldn't fetch data from table
123                self.error['status']=False
124                self.error['code']=code
125#               if index:
126#                       if index in self.error.keys():
127#                               self.error[index].append(e)
128#                       else:
129#                               self.error[index]=e
130#               else:
131#                       if 'ERR' in self.error.keys():
132#                               self.error['ERR'].append(e)
133#                       else:
134#                               self.error['ERR']=e
135        #def _error
136
137        ###
138        #Process array executionOrder
139        ###
140        def beginMigration(self,mdbFile,sqlFile):
141
142                self.error['status']=True
143                self.error['code']=""
144                self.mdb=mdbFile
145                self.sql=sqlFile
146                if self.exportMdb():
147                        self.sqlFiles=[]
148                        for abiesTable in self.executionOrder:
149                                self.tmpInc=0
150                                self._debug("Processing "+abiesTable)
151                                self._loadTable(abiesTable)
152                                if not self.error['status']:
153                                        break
154                                pmbTable=self._getTableEquivalence(abiesTable)
155                                if not self.error['status']:
156                                        break
157                                self._generateSql(pmbTable,abiesTable)
158                                if not self.error['status']:
159                                        break
160                        if self.error['status']:
161                                self._debug("Concatenating sql files in an unique file")
162                                self._concatFiles()
163                        else:
164                                self._debug("Errors ocurred")
165                                self._debug(self.error)
166                        self._debug("Files generated for tables "+str(self.executionOrder))
167                else:
168                        self._error(11)
169
170                self._debug("ErrorDict:")
171                self._debug(self.error)
172                return self.error
173        #def beginMigration
174
175        def exportMdb(self):
176                returnValue=None
177                self._debug(self.mdb)
178                if os.path.exists(self.mdb):
179                        try:
180                                cmdOut=subprocess.check_output(['mdb-tables',self.mdb],universal_newlines=True)
181                                if cmdOut:
182                                        self.workDir=tempfile.mkdtemp()+"/"
183                                        tables=cmdOut.strip("\n")
184                                        listTables=tables.split(' ')
185                                        for table in listTables:
186                                                if table!='':
187                                                        cmdOut=subprocess.check_output(['mdb-export','-H','-d|||',self.mdb,table],universal_newlines=True)
188                                                        if cmdOut:
189                                                                f=open (self.workDir+table+".csv",'w')
190                                                                f.writelines(cmdOut)
191                                                                f.close()
192                                        returnValue=True
193                        except Exception as e:
194                                self._error(12)
195                                returnValue=False
196                return returnValue
197        #def exportMdb
198
199        ###
200        #Generates the sql
201        #Input: pmb tableName
202        #Output: Array of sql sentences
203        ###
204        def _generateSql(self,destTable,abiesTable):
205                transformDict=self._checkTransformField(abiesTable)
206                origData=self.tableValuesDict[abiesTable]
207                sqlFile=self.workDir+destTable+".sql"
208                fileName=destTable+".sql"
209                try:
210                        if destTable not in self.blacklist:
211                                f=open (sqlFile,'w')
212                        for line in origData:
213                                pmbColumn=0
214                                strInsertValues=''
215                                for index in self.pmb_tables[destTable]:
216                                        if index!='':
217                                                if transformDict:
218                                                        if index in transformDict.keys():
219                                                                strTransform=transformDict[index]
220        #                                                       self._debug("Transforming value "+str(line[index])+" index "+str(index)+" with "+strTransform)
221                                                                line[index]=eval(strTransform)
222                                                tmp=str(line[index]).strip("\n")
223                                                #Clean tmp field
224                                                tmp=tmp.replace('"',' ')
225                                                chkFormat=True
226                                                if (destTable in self.formatPmbColumns):
227                                                        if index in self.formatPmbColumns[destTable]:
228                                                                chkFormat=False
229                                                                if self.formatPmbColumns[destTable][index]=='string':
230                                                                        tmp=str(tmp)
231                                                                        tmp=tmp.lstrip(' ')
232                                                                        tmp=tmp.rstrip(' ')
233                                                                        tmp='"'+tmp+'"'
234                                                                elif self.formatPmbColumns[destTable][index]=='int':
235                                                                        tmp=int(tmp)
236
237                                                if chkFormat:
238                                                        try:
239                                                                int(tmp)
240                                                        except:
241                                                                tmp=tmp.lstrip(' ')
242                                                                tmp=tmp.rstrip(' ')
243                                                                tmp='"'+tmp+'"'
244                                                                pass
245                                        else:
246                                                if (destTable in self.defaultPmbValues):
247                                                        if pmbColumn in self.defaultPmbValues[destTable]:
248                                                                tmp=self.defaultPmbValues[destTable][pmbColumn]
249                                                               
250                                                        else:
251                                                                tmp='""'       
252                                                else:
253                                                        tmp='""'
254                                               
255                                        strInsertValues=strInsertValues+tmp+','
256                                        pmbColumn=pmbColumn+1
257                                strInsertValues=strInsertValues[:-1]
258                                if destTable not in self.blacklist:
259                                        f.write("Insert into "+destTable+" values ("+strInsertValues+");\n")
260                        if destTable not in self.blacklist:
261                                f.close()
262                                self.sqlFiles.append(sqlFile)
263                except Exception as e:
264                        self._error(13)
265
266        #def _generateSql
267
268        ###
269        #Concats all the generated files
270        ###
271        def _concatFiles(self):
272                self._debug("Generating "+self.sql)
273                self._debug("Files list:")
274                self._debug(self.sqlFiles)
275                try:
276                        f=open(self.sql,'w')
277                        f.write("DELETE from docs_location;\n")
278                        f.write("DELETE from indexint;\n")
279                        for sqlFile in self.sqlFiles:
280                                self._debug("Opening "+sqlFile)
281                                a=open(sqlFile,'r')
282                                aContent=a.readlines()
283                                a.close()
284                                f.writelines(aContent)
285                        f.close()
286                except Exception as e:
287                        self._error(14)
288                       
289        #def _concatFile
290
291
292        ###
293        #Loads a table from a file or a virtual table from a bunch of tables
294        #Input: Table name
295        ###
296        def _loadTable(self,tableName):
297                returnValue=False
298                fileName=self.workDir+tableName+'.csv'
299                data=[]
300                if os.path.exists(fileName):
301                        try:
302                                f=open(fileName,'r')
303                                lines=f.readlines()
304                                f.close()
305                                for line in lines:
306                                        array=line.split('|||')
307                                        data.append(array)
308                                returnValue=True
309                        except Exception as e:
310                                self._error(10)
311                else: #Check in virtual dict
312                        if tableName in self.virtual_tables.keys():
313                                data=self._loadVirtualTable(tableName)
314                                returnValue=True
315                        else:
316                                self._error(10)
317
318                self.tableValuesDict[tableName]=data
319                if tableName in self.formatTables.keys():
320                        self.tableValuesDict[tableName]=self._checkTransformTables(tableName)
321                return (returnValue)
322        #def _loadTable
323
324        ###
325        #Loads a virtual table as described by virtual tables dict
326        #Input: Table name
327        #Output: Dataset (array of arrays with the virtual table)
328        ###
329        def _loadVirtualTable(self,tableName):
330                self._debug("Loading virtual table "+tableName)
331                data=[]
332                dataDict={}
333                if tableName in self.virtual_tables.keys():
334                        proceed=False
335                        for key,line in self.virtual_tables[tableName].items():
336#                               print (s)ourceData)
337#                               for key,line in sourceData.items():
338                                        if line.startswith('get'):
339                                                proceed=True
340                                                sourceIndex=key.split(' ')[1]
341                                                getFrom=line.split(' ')[1]
342                                                sourceIndex=getFrom.split('.')[1]
343                                                sourceTable=getFrom.split('.')[0]
344                                                dataDict[key]=self._funcGetValueFromTable(sourceTable,sourceIndex)
345                        if proceed:
346                                #Data is charged on the dataDict, time to populate data array
347                                maxLen=0
348                                for key in dataDict.keys():
349                                        columnLen=len(dataDict[key])
350                                        if columnLen>maxLen:
351                                                maxLen=columnLen
352                                index=0
353                                while index<maxLen:
354                                        dataLine=[]
355                                        for key in self.virtual_tables[tableName].keys():
356                                                arrayIndex=int(key.split(' ')[-1])
357                                                if key in dataDict.keys():
358                                                        dataLine.insert(arrayIndex,dataDict[key][index])
359                                                else:
360                                                        dataLine.insert(arrayIndex,'')
361                                        data.append(dataLine)
362                                        index=index+1
363                else:
364                        self._error(15)
365                return(data)
366
367        ###
368        #Finds the pmb table related with an abies table
369        #Input: Table name in abies
370        #Output: pmb table name
371        ###
372        def _getTableEquivalence(self,tableName):
373                pmbTable=None
374                for key in self.pmb_abies.keys():
375                        if tableName in self.pmb_abies[key]:
376                                pmbTable=key
377                                break
378                return pmbTable
379        #def _getTableEquivalence
380
381        ###
382        #Check if there's any transformation to apply to the fields of a table
383        #Input: Abies table name
384        #Output: Dict with field indexes as keys and transformations to apply as values
385        def     _checkTransformField(self,abiesTable):
386                transformDict={}
387                if abiesTable in self.formatFields.keys():
388                        transform=self.formatFields[abiesTable]
389                        self._debug("Transforming fields "+str(transform)+" in "+abiesTable)
390                        for key,line in transform.items():
391                                index=int(key.split(' ')[-1])
392                                if line.startswith('add'):
393                                        transformDict[index]=self._transformAdd(line,index)
394                                elif line.startswith('value'):
395                                        transformDict[index]=self._transformValue(line,index)
396                                elif line.startswith('concat'):
397                                        transformDict[index]=self._transformConcat(line,index)
398                                elif line.startswith('date'):
399                                        transformDict[index]=self._transformDate(line,index)
400                                elif line.startswith('inc'):
401                                        transformDict[index]=self._transformInc(line,index)
402                                elif line.startswith('get'):
403                                        transformDict[index]=self._transformGet(line,index)
404                                elif line.startswith('from'):
405                                        transformDict[index]=self._transformQuery(line,index,abiesTable)
406
407                self._debug("TransformDict: "+str(transformDict))
408                return transformDict
409        #def _checkTransformField
410
411        ###
412        #Transform routines, self-explaining
413        ###
414        def _transformAdd(self,line,index):
415                sourceIndex=line.split(' ')[1]
416                increment=line.split(' ')[-1]
417                returnValue='int(line['+sourceIndex+'])+'+increment
418                return(returnValue)
419
420        def _transformValue(self,line,index):
421                values=line.split(',')
422                tmpStr=''
423                for value in values:
424                        relation=value.split('=')
425                        sourceValue=relation[0].split(' ')[-1]
426                        destValue=relation[-1]
427                        tmpStr=tmpStr+str(destValue)+' if line['+str(index)+']=="'+str(sourceValue)+'" else '
428                try:
429                        int(destValue)
430                        returnValue='('+tmpStr+ '"'+str(destValue)+'")'
431                except:
432                        returnValue='('+tmpStr+ ''+str(destValue)+')'
433                return(returnValue)
434
435        def _transformConcat(self,line,index):
436                concat=line.split(' ')[-1]
437                concatArray=concat.split(',')
438                tmpStr=''
439                for concatField in concatArray:
440                        tmpStr=tmpStr+'line['+str(concatField)+']+'
441                returnValue=tmpStr[:-1]
442                return(returnValue)
443
444        def _transformDate(self,line,index):
445                dates=line.split(' ')
446                sourceFormat=dates[1]+' '+dates[2]
447                destFormat=dates[3]+' '+dates[4]
448                returnValue='self._funcDateTransform(line['+str(index)+'],"'+sourceFormat+'","'+destFormat+'")'
449                return(returnValue)
450
451        def _funcDateTransform(self,date,origFormat,destFormat):
452                date=date.replace('"','')
453                destFormat=destFormat.replace('"','')
454                returnValue=(datetime.datetime.strptime(date, origFormat).strftime(destFormat))
455                return(returnValue)
456               
457        def _transformInc(self,line,index):
458                increment=line.split(' ')
459                inc=increment[1]
460                base=0
461                if len(increment)==3:
462                        base=int(increment[2])
463                if self.tmpInc<base:
464                        self.tmpInc=base
465                returnValue='self._funcIncrementField('+inc+')'
466                return(returnValue)
467        ###
468        #Some values need a special transformation that requires a helper function.
469        #Those helper functions are the _func...
470        ###
471        def _funcIncrementField(self,inc):
472                self.tmpInc=self.tmpInc+int(inc)
473                returnValue=self.tmpInc
474                return(returnValue)
475
476        def _transformGet(self,line,index):
477                getFrom=line.split(' ')[-1]
478                getFromTable=getFrom.split('.')[0]
479                getFromIndex=getFrom.split('.')[-1]
480                returnValue='self._funcGetValueFromTable('+getFromTable+','+getFromIndex+')'
481                return(returnValue)
482
483        def _funcGetValueFromTable(self,table,index):
484                fileName=self.workDir+table+'.csv'
485                data=[]
486                index=int(index)
487                if os.path.exists(fileName):
488                        try:
489                                f=open(fileName,'r')
490                                lines=f.readlines()
491                                f.close()
492                                for line in lines:
493                                        array=line.split('|||')
494                                        data.append(array[index])
495                        except Exception as e:
496                                self._error(16)
497                else:
498                        self._error(16)
499                return(data)
500
501        def _transformQuery(self,line,index,tableName):
502                self._debug("Join field "+line)
503                transformDict=[]
504                arrayLine=line.split(' ')
505                sourceTableField=arrayLine[1]
506                joinFromTo=arrayLine[-1]
507                sourceTable=sourceTableField.split('.')[0]
508                sourceField=int(sourceTableField.split('.')[1])
509                joinFrom=joinFromTo.split('=')[0]
510                joinTo=joinFromTo.split('=')[1]
511                returnValue='self._funcGetValueFromQuery("'+tableName+'","'+sourceTable+'",line['+str(joinFrom)+'],'+str(joinTo)+','+str(sourceField)+')'
512                return(returnValue)
513
514        def _funcGetValueFromQuery(self,abiesTable,sourceTable,valueFrom,fieldTo,reqFieldIndex):
515                returnValue=''
516                valueFrom=valueFrom.strip("\n")
517                if sourceTable not in self.tableValuesDict:
518                        self._loadTable(sourceTable)
519                for row in self.tableValuesDict[sourceTable]:
520                                #                       print ("Comparing "+str(valueFrom)+" with "+str(row[fieldTo]))
521                        if valueFrom==str(row[fieldTo]).strip("\n"):
522                                returnValue=row[reqFieldIndex]
523                                break
524                return returnValue
525        #Transform fields functions
526
527        ###
528        #Check if there's any Transform to apply to a whole table
529        #Input: abies table
530        #Output: Dataset with the table
531        ###
532        def _checkTransformTables(self,tableName):
533                transformDict={}
534                if tableName in self.formatTables.keys():
535                        transform=self.formatTables[tableName]
536                        for key,line in transform.items():
537                                index=int(key.split(' ')[-1])
538                                if line.startswith('uniq'):
539                                        transformDict=self._transformTableUniq(tableName,index)
540                                if line.startswith('from'):
541                                        transformDict=self._transformTableJoin(tableName,index,line)
542                return transformDict
543        #def _checkTransformTamble
544
545        ###
546        #Function helpers for table transformation. Self-explaining
547        ###
548        def _transformTableUniq(self,tableName,index):
549                self._debug("Uniq items from index "+str(index))
550                transformDict=[]
551                for item in self.tableValuesDict[tableName]:
552                        if item not in transformDict:
553                                transformDict.append(item)
554                return transformDict
555
556        def _transformTableJoin(self,tableName,index,line):
557                self._debug("Join "+line)
558                transformDict=[]
559                arrayLine=line.split(' ')
560                sourceTableField=arrayLine[1]
561                joinFromTo=arrayLine[-1]
562                sourceTable=sourceTableField.split('.')[0]
563                sourceField=int(sourceTableField.split('.')[1])
564                joinFrom=joinFromTo.split('=')[0]
565                joinTo=joinFromTo.split('=')[1]
566                if sourceTable not in self.tableValuesDict:
567                        self._loadTable(sourceTable)
568                #Generate a dict with joinTo as index and required field as value
569                tmpDict={}
570                tmpIndex=int(joinTo)
571                for row in self.tableValuesDict[sourceTable]:
572                        rowIndex=row[tmpIndex]
573                        rowValue=row[sourceField]
574                        tmpDict.update({rowIndex:rowValue})
575
576                #Walk through origin table and insert values from sourceTableDict
577                tmpIndex=int(joinFrom)
578                for row in self.tableValuesDict[tableName]:
579                        rowIndex=row[tmpIndex]
580                        if rowIndex in tmpDict:
581                                row.insert(tmpIndex,tmpDict[rowIndex])
582                        else:
583                                row.insert(tmpIndex,'')
584                        transformDict.append(row)
585                return transformDict
586        #Table transformation function helpers
587
588### MAIN PROGRAM ###
589
590#migration=abies2Pmb()
591#result=migration.beginMigration(sys.argv[1],sys.argv[2])
592#print(result)
Note: See TracBrowser for help on using the repository browser.