source: TI01-discovery-Ingest/trunk/v4n_MEDIN/ingestAutomation-upgrade/OAIBatch/ExtractISO.py @ 6660

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI01-discovery-Ingest/trunk/v4n_MEDIN/ingestAutomation-upgrade/OAIBatch/ExtractISO.py@6660
Revision 6660, 27.3 KB checked in by sdonegan, 11 years ago (diff)

Update includes latest changes to allow full MEDIN xml and DIF xml ingest - now updates stubISO as proper MEDIN format in transformed docs table

Line 
1
2from xml.etree import ElementTree as ET
3import logging,urllib,os,sys,inspect
4
5class ExtractISO:
6
7        def __init__(self,filenameIP,isoFormat):                               
8                               
9                self.inputXMLfile = filenameIP
10                self.isoFormatToExtract = isoFormat
11               
12                #show actual values extracted from the XML in logging.info
13                self.showXMLvalues = True
14               
15                logging.info("Have initialised ISO XML Extractor")
16               
17                #self.createISOdataStructure()
18               
19               
20        '''
21        Method to control re-writing of ISO xml with NDG redirect URL's
22        '''
23        def generateNDGredirectURL(self, ndgRedirectURL, xmlOutputFile):
24               
25                logging.info("Generating XML copy with URLS converted to NDG redirect URL's")
26               
27                self.redirectBaseURL = ndgRedirectURL
28               
29                rewriteIsoXml = self.changeElementUrlVal(self.isoModel.urlsToChange(), xmlOutputFile)
30               
31               
32                #sys.exit()
33               
34                return rewriteIsoXml
35       
36       
37       
38        '''
39        Method to map ISO variables to Discovery Database columns - use in PostgresDAO.py with getattr!
40       
41        returns a dictionary where key is
42    '''
43        def mapIsoFieldsToDBcolumns(self):
44               
45                return {'originalFormat':'original_format_name', \
46        'originalFormatVersion':'original_format_version',\
47        'datasetAbstract':'dataset_abstract', \
48        'resourceType_text':'resource_type', \
49        'resourceType_tsvector':'resource_type_ts_vector', \
50        'topicCategory_text':'topic_category', \
51        'topicCategory_tsvector':'topic_category_ts_vector', \
52        'lineage_text':'lineage', \
53        'lineage_tsvector':'lineage_ts_vector', \
54        'publicAccessLimitations_text':'limitations_public_access', \
55        'publicAccessLimitations_tsvector':'limitations_public_access_ts_vector', \
56        'dataOriginator_text':'data_originator',\
57        'dataOriginator_tsvector':'data_originator_tsvector',\
58        'metadataUpdateDate':'dataset_metadata_update_date', \
59        'dataFormat':'original_format_name',
60        'createDate':'dataset_metadata_creation_date',
61        'publicationDate':'dataset_metadata_publication_date',
62        'authors_text':'authors','parameters_text':'parameters'}
63               
64               
65        '''
66                Method to match the columns found in mapIsoFieldsToDBcolumns to the postgres data types
67        '''
68        def mapDBcolumnsToIsoDataTypes(self):
69               
70                return {'original_format_name':'text', \
71        'original_format_version':'text',\
72        'dataset_abstract':'text', \
73        'resource_type':'text', \
74        'resource_type_ts_vector':'tsvector', \
75        'topic_category':'text', \
76        'topic_category_ts_vector':'tsvector', \
77        'lineage':'text', \
78        'limitations_public_access':'text', \
79        'lineage_ts_vector':'tsvector', \
80        'limitations_public_access_ts_vector':'tsvector', \
81        'data_originator':'text',\
82        'data_originator_tsvector':'tsvector',\
83        'dataset_metadata_update_date':'timestamp', \
84        'original_format_name':'text',
85        'dataset_metadata_creation_date':'timestamp',
86        'dataset_metadata_publication_date':'timestamp',
87        'authors':'text','parameters':'text'}
88       
89               
90        '''
91        Simple method to allow return information on success of extraction ( __init__ method cannot return anything so use this method)
92        '''
93        def createISOdataStructure(self):
94               
95                logging.info("")
96                logging.info("********************************************************************************************************************************************")
97                logging.info("****************** Creating ISO data structure from " + self.inputXMLfile + "****************** ")
98                logging.info("********************************************************************************************************************************************")
99                logging.info("")
100               
101               
102                self.root = self.getIsoXML(self.inputXMLfile)
103               
104                self.isoFileLocation = self.inputXMLfile       
105               
106                #self.root = self.getIsoXML('/misc/glue1/sdonegan/NDG3_workspace/buildouts/oai_document_ingester_MEDIN/ingestAutomation-upgrade/OAIBatch//data/NEODC/stub_iso/neodc.nerc.ac.uk__stubISO__dataent_11658383444211836_DIF.xml')
107               
108                if self.root is None:
109                        logging.error("Detected possible problem with input xml - cannot parse it!")
110                        return False
111               
112                #choose the ISO xpath definition class depending on format profile required
113                self.importISOxmlDefinition(self.isoFormatToExtract)
114                                                       
115                #get all the required information from the chosen xpath definition class
116                self.datasetID = self.getElementVal(self.isoModel.datasetID())
117               
118                self.datasetName = self.getElementVal(self.isoModel.dataSetName())
119               
120                self.datasetAbstract = self.getElementVal(self.isoModel.dataSetAbstract())
121               
122                self.datasetID = self.getElementVal(self.isoModel.datasetID())
123               
124                self.revisionDate = self.getElementVal(self.isoModel.metadataRevisionDate())
125               
126                self.createDate = self.getElementVal(self.isoModel.metadataCreationDate())
127                               
128                self.datasetName = self.getElementVal(self.isoModel.dataSetName())             
129               
130                self.boundingDates = self.getElementVal(self.isoModel.boundingDates())
131               
132                self.boundingDatesRange = self.boundingDateRange(self.boundingDates)   
133               
134                self.originalFormat = self.getElementVal(self.isoModel.originalFormat())
135               
136                self.authors = self.getElementVal(self.isoModel.authors())
137                self.authors_text = self.authors
138                self.authors_ts_vector = self.authors
139                               
140                self.datacentreName = self.getElementVal(self.isoModel.dataCentreName())
141               
142                self.parameters = self.getElementVal(self.isoModel.parameters())
143                self.parameters_text = self.parameters
144                self.parameters_tsvector = self.parameters
145               
146                self.keywords = self.getElementVal(self.isoModel.keywords())
147               
148                self.keywordsList = self.listify(self.keywords)
149               
150                self.boundingBoxCoordinates = self.getElementVal(self.isoModel.coordinates())                           
151               
152                #These are the MEDIN requested extra fields - make optional
153                try:
154                        self.originalFormatVersion = self.getElementVal(self.isoModel.originalFormatVersion())
155                except:
156                        self.originalFormatVersion = None
157               
158                try:
159                        self.publicationDate = self.getElementVal(self.isoModel.metadataPublicationDate())
160                except:
161                        self.publicationDate = None
162                       
163                try:
164                        self.resourceType = self.getElementVal(self.isoModel.resourceType())
165                        self.resourceType_text = self.resourceType
166                        self.resourceType_tsvector = self.resourceType
167                except:
168                        self.resourceType = None
169                        self.resourceType_text = None
170                        self.resourceType_tsvector = None
171               
172                try:
173                        self.topicCategory = self.getElementVal(self.isoModel.topicCategory())
174                        self.topicCategory_text = self.topicCategory
175                        self.topicCategory_tsvector = self.topicCategory
176                except:
177                        self.topicCategory = None
178                        self.topicCategory_text = None
179                        self.topicCategory_tsvector = None
180               
181                try:
182                        self.lineage = self.getElementVal(self.isoModel.lineage())
183                        self.lineage_text = self.lineage
184                        self.lineage_tsvector = self.lineage
185                except:
186                        self.lineage = None
187                        self.lineage_text = None
188                        self.lineage_tsvector = None
189               
190                try:
191                        self.publicAccessLimitations = self.getElementVal(self.isoModel.publicAccessLimitations())
192                        self.publicAccessLimitations_text = self.publicAccessLimitations
193                        self.publicAccessLimitations_tsvector = self.publicAccessLimitations
194                except:
195                        self.publicAccessLimitations = None
196                        self.publicAccessLimitations_text = None
197                        self.publicAccessLimitations_tsvector = None
198                       
199                try:
200                        self.dataOriginator= self.getElementVal(self.isoModel.dataOriginator())
201                       
202                        #add some extra specifications to deal with MEDIn requirement to have both searchable field (vector) and return as a text field
203                        self.dataOriginator_text = self.dataOriginator
204                        self.dataOriginator_tsvector = self.dataOriginator
205                       
206                except:
207                        self.dataOriginator = None
208                        self.dataOriginator_text = None
209                        self.dataOriginator_tsvector = None
210               
211                try:
212                        self.dataFormat = self.getElementVal(self.isoModel.originalFormat())
213                except:
214                        self.dataFormat = None
215               
216                try:
217                        self.metadataUpdateDate = self.getElementVal(self.isoModel.metadataUpdateDate())
218                except:
219                        self.metadataUpdateDate = None
220                       
221                       
222                try:
223                        self.originalFormatName = self.getElementVal(self.isoModel.originalFormat())
224                except:
225                        self.originalFormatName = None
226                       
227                try:
228                        self.originalFormatVersion = self.getElementVal(self.isoModel.originalFormatVersion())
229                except:
230                        self.originalFormatVersion = None
231                       
232               
233                logging.info("")
234                logging.info(" ****************** Completed rendering ISO xml into data structure! ****************** ")
235                logging.info("")
236               
237                return True
238       
239       
240        '''
241        Method to generate a simple list object from a compound list set
242        '''
243        def listify(self,listObject):
244               
245                list = []
246               
247                for outer in listObject:
248                        for inner in outer:                             
249                                list.append(inner)
250                                               
251                return list
252               
253        '''
254        Method to return start and end dates from a sequence of dates such as that found returned using the xpath defn from boundingDates
255        '''
256        def boundingDateRange(self,boundingDatesList):
257               
258                #generate a simple list of all dates
259                allDates = []
260                returnDates = {}
261               
262                #remember, need to sort out whether there was a start and end date in the first place, or just one so can copy values over
263                if len(boundingDatesList) == 1:
264                        logging.info("Only 1 set of start and stop dates, so setting range to this..")
265                        returnDates['start'] = boundingDatesList[0]['start']
266                        returnDates['end'] = boundingDatesList[0]['end']
267                       
268                        #convert None to 'None'
269                        for date in returnDates.keys():
270                                if returnDates[date] is None:
271                                        returnDates[date] = 'None'
272                       
273                        return returnDates
274               
275                for outer in boundingDatesList:
276                        for inner in outer.keys():
277                                if outer[inner] is not None:
278                                        if outer[inner] != 'None':
279                                                #some other test here to ensure proper date object
280                                                allDates.append(outer[inner])
281                                               
282                               
283                #min and max seem to work for now (to get it working) - assuming proper dateformat (restrict to YYYY-MM-DD for now)
284                #TODO: use datetime library to do this properly         
285                returnDates['start'] = min(allDates)
286                returnDates['end'] = max(allDates)
287               
288                return returnDates
289       
290       
291               
292        '''
293        Method to import specific ISO xpath definition class depending on the profile used
294        '''
295        def importISOxmlDefinition(self,format):
296               
297                '''
298                MEDIN Profile v2.3 (date? - MEDIN upgrade dev)
299                '''
300               
301                logging.info("Format chosen: " + format)
302               
303               
304               
305                #Dynamically import module of correct profile...
306                import_string = "from " + format + " import " + format + " as iso"
307               
308                try:
309                        exec import_string
310                except:
311                        logging.error("Could not import xpath class for: " + format)
312                        sys.exit()
313               
314                self.isoModel = iso()
315               
316               
317        '''
318        Method to govern the changing of an element value (in the first instance change a url to a NDG redirect URL
319        '''
320        def changeElementUrlVal(self,keyMethod,outputFile):
321               
322       
323                logging.info("******************************************************************************************")
324                logging.info("Extracting xpath information for data extraction type (to change URL..):" + keyMethod[0])
325                logging.info("******************************************************************************************")
326                               
327               
328                #first need to interpret the contents of the tuple we've been passed everything is a dictionary in the tuple apart from element 0:
329                #build a map of it - do this as a dictionary with the key as the position in the tuple and the value as the element val, be it str or dictionary key
330                dataStruct = self.interpretTuple(keyMethod)
331                cnt = 0
332               
333                #this is similar to getElementVal - but no interest in ordering or returning the actual value..
334                for i in dataStruct.keys()[1:]:
335                                               
336                        thisData = keyMethod[i][dataStruct[i]]
337                                                               
338                        #is dicionary a dependant value or straight xpath?
339                        if 'baseXpath' in thisData.keys():
340                                logging.info("Extracting xml using COMPLEX dependant method for xpath def: " + str(cnt))                       
341                                self.changeDependantURLElementVal(thisData['baseXpath'],thisData['elValXpath'],thisData['depValXpath'],thisData['depVal'])                             
342                                                               
343                        if 'xpath' in thisData.keys():         
344                                logging.info("Extracting xml using SIMPLE method for xpath def: " + str(cnt))                                           
345                                self.changeSimpleURLElementVal(thisData['xpath'])       
346                                                                               
347                #having returned the completed xml tree... write it to the OP file dictated..
348                try:
349                        #rewrite this back into the xml doc                             
350                        self.etree.write(outputFile)
351                        logging.info("Have successfully generated new ISO file with rewritten urls to NDG redirect format!")
352                       
353                        return True
354               
355                except:
356                        logging.error("Could not rewrite NDG url ISO xml to; " + outputFile)
357                       
358                        return False
359               
360               
361        '''
362        Method to change an ISO element val - simple xpath value
363        '''
364        def changeSimpleURLElementVal(self, xpath):
365               
366                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
367                xpathNS = self.appendNameSpace(xpath)           
368               
369                resElementVal = []
370               
371                try:   
372                        rootEl = self.root.findall(xpathNS)
373                       
374                except:
375                        logging.error("Could not find element for this xpath: " + xpath)                       
376                       
377                                               
378                #use a text 'None' rather than a None type                     
379                for elVal in rootEl:
380                        if elVal is None: 
381                                originalUrl = 'None' 
382                        else:
383                                try:                   
384                                        originalUrl = elVal.text       
385                                        redirectURL = self.redirectBaseURL + originalUrl + '&docID=' + urllib.quote(self.datasetID[0][0]) + '&docTitle=' + urllib.quote(self.datasetName[0][0])
386                               
387                                        #reassign...
388                                        elVal.text = redirectURL                                       
389                                        logging.info("Have successfuly rewritten ndg redirect URL: " + redirectURL)
390                                       
391                                except:
392                                        logging.error("Could not change original (simple) url!")
393               
394                       
395                #no need to return anything -if we rewrite the doc, any successful changes will be incorporated, otherwise will be ignored
396       
397        '''
398        Method to change an ISO element val - compound dependant xpath values
399        '''
400        def changeDependantURLElementVal(self,baseXpath,elXpath,depXpath,depValReqd):
401               
402                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
403                baseXpath = self.appendNameSpace(baseXpath)
404                               
405                #for path in baseXpaths:
406                try:                           
407                        rootEl = self.root.findall(baseXpath) #returns an elementree object of elements in a list                               
408                except:
409                        logging.error("Could not find element for this xpath: " + baseXpath)
410                        return 'None'
411                       
412                for el in rootEl:
413                        thisElXpth = self.appendNameSpace(elXpath)
414                                                               
415                        thisEl = self.doFindall(el,thisElXpth)
416                               
417                        #if there's a value for the actual xpath we want, go and get the dependant value
418                        if thisEl != 'None':
419                                       
420                                elVal = thisEl[0]
421                                elValTxt = elVal.text #NOTE take first one as we expect this to be the value we want
422                                                                       
423                                thisEldepXpth = self.appendNameSpace(depXpath)                                 
424                                thisDepEl = self.doFindall(el,thisEldepXpth)                                                           
425                                       
426                                if thisDepEl != 'None':
427                                                                               
428                                        depVal = thisDepEl[0].text
429                                                                       
430                                        if depVal == depValReqd:
431                                               
432                                                try:
433                                                        originalUrl = elValTxt                                         
434                                                        redirectURL = self.redirectBaseURL + originalUrl + '&docID=' + urllib.quote(self.datasetID[0][0]) + '&docTitle=' + urllib.quote(self.datasetName[0][0])
435                                                                                       
436                                                        #reassign...
437                                                        elVal.text = redirectURL                                                                                       
438                                                        logging.info("Have successfuly rewritten ndg redirect URL: " + redirectURL)
439                                                       
440                                                except:
441                                                        logging.error("Could not change original (complex) url!")
442                                       
443                                                                                                                       
444                #no need to return anything -if we rewrite the doc, any successful changes will be incorporated, otherwise will be ignored
445               
446       
447        '''
448        Method to initially interpret the xpath dictionary type passed
449        '''
450        def interpretTuple(self,keyMethod):
451               
452                dataStruct = {}
453                counter=0
454                for i in keyMethod:
455                        if type(i) is dict:
456                               
457                                #loop through the top level (remember its a dictionary of dictionaries!)
458                                for j in i.keys():                             
459                                        dataStruct[counter]=j
460                       
461                        #Only other data type present should be a string
462                        if type(i) is str:
463                                dataStruct[counter] = i
464                               
465                        counter = counter + 1
466                               
467                return dataStruct
468       
469               
470        '''
471        Method to aid passing and extraction info from complex xpath tuples
472       
473        Will work out whether complex or simple xpath extraction required depending on type of xpath method sent
474       
475        If 'order' key present then will return a dictionary of values with the extracted value using the specified key.
476       
477        '''
478        def getElementVal(self,keyMethod):
479               
480                #develop handler method to simplify
481                returnValList=[]                               
482                returnVal = 'None'
483               
484                logging.info("******************************************************************************************")
485                logging.info("Extracting xpath information for data extraction type:" + keyMethod[0])
486                logging.info("******************************************************************************************")
487                               
488                showValue = self.showXMLvalues
489               
490                #first need to interpret the contents of the tuple we've been passed everything is a dictionary in the tuple apart from element 0:
491                #build a map of it - do this as a dictionary with the key as the position in the tuple and the value as the element val, be it str or dictionary key
492                dataStruct = self.interpretTuple(keyMethod)
493                '''
494                dataStruct = {}
495                counter=0
496                for i in keyMethod:
497                        if type(i) is dict:
498                               
499                                #loop through the top level (remember its a dictionary of dictionaries!)
500                                for j in i.keys():                             
501                                        dataStruct[counter]=j
502                       
503                        #Only other data type present should be a string
504                        if type(i) is str:
505                                dataStruct[counter] = i
506                               
507                        counter = counter + 1
508                '''
509               
510                #now iterate through the key list in dataStruct but miss First one (as its a name val!)                 
511                valueList = []
512                ordering = False
513               
514                cnt = 1
515               
516                for i in dataStruct.keys()[1:]:
517                                               
518                        thisData = keyMethod[i][dataStruct[i]]
519                                                               
520                        #is dicionary a dependant value or straight xpath?
521                        if 'baseXpath' in thisData.keys():
522                                logging.info("Extracting xml using COMPLEX dependant method for xpath def: " + str(cnt))                       
523                                returnVal = self.returnDependantElementVal(thisData['baseXpath'],thisData['elValXpath'],thisData['depValXpath'],thisData['depVal'],showValue)                           
524                                valueList.append(returnVal)
525                                                               
526                        if 'xpath' in thisData.keys():         
527                                logging.info("Extracting xml using SIMPLE method for xpath def: " + str(cnt))                                           
528                                returnVal =  self.returnSimpleElementVal(thisData['xpath'],showValue)                                                           
529                                valueList.append(returnVal)
530                                                               
531                        #is there any ordering info required - if so, order the return list according to the ordering tuple
532                        if 'order' in keyMethod[i].keys():
533                                logging.info("Returning a dictionary of paired values as ordering requested for xpath (number of xpath defs MUST match ordering vals available")                       
534                                ordering = True                         
535                                orderingList = thisData
536                               
537                        else:
538                                logging.info("Returning a simple list of values for xpath def: " + str (cnt))
539                               
540                        cnt = cnt + 1
541                       
542                #if ordering is required, create a corresponding dictionary of ordered values                                   
543                if ordering:
544                       
545                        #logging.INFO("Ordering data by specified order information!")
546                        logging.info("ordering information now!")
547                       
548                        '''FIRST need to group information together in correct order (i.e. elementtree groups all starts in one list, then all ends etc
549                        i.e. [[start1,start2],[end1,end2]] to [[start1,end1],[start2,end2]]
550                        '''
551                        #import pdb
552                        #pdb.set_trace()
553                        #check that number of number of elements in ordering list is not less than number of items in list to be ordered!
554                        #if multiple sets to be ordered, check numbers and return as dictionary
555                        checkCompLnth = len(valueList[0])
556                        for list in valueList:
557                                if len(list) != checkCompLnth:
558                                        logging.error("Sub component lists are of unequal length - CANNOT order!!")
559                                        return 'None'
560                               
561                        #outer loop should be the number of elements in the sublists..
562                        outer = []
563                        for localPos in range(0,checkCompLnth):
564                                inner=[]
565                                #inner loop should be the number of lists corresponding to repeating subelements                       
566                                for listPos in range(0,len(valueList)):
567                                       
568                                                inner.append(valueList[listPos][localPos])
569                                #append to outer loop
570                                outer.append(inner)                                                     
571                       
572                        for returnedList in outer:
573                               
574                                #create a disctionary for this round (will append it later to the return version)
575                                orderedValsSub = {}
576                                                                               
577                                if len(returnedList) != len(orderingList):
578                                        logging.error("Ordering List length does not match length of list to be ordered! (NOTE: can only order where lengths match!)")
579                                        returnValList.append('None')
580                               
581                                else:           
582                                        #go through valueList and extract values in correct position and build dictionary
583                                        for i in orderingList.keys():
584                                               
585                                                orderedValsSub[i] = returnedList[orderingList[i]-1] # remember there's an offset as '0' not used in these vals
586                               
587                               
588                                        returnValList.append(orderedValsSub)
589                       
590                        return returnValList
591                       
592                else:
593                        return valueList
594                       
595                       
596        '''
597        Method to return a list of values of an element value dependant on another element value held within the local tree
598        i.e. return a specific string val depending on a local code val in ISO
599       
600        if element exists and any dependant value present matches the expected value, the element value is returned as part of a list,
601        otherwise an empty list is returned
602        '''
603        def returnDependantElementVal(self,baseXpath,elXpath,depXpath,depValReqd,showValue=False):
604                       
605                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
606                baseXpath = self.appendNameSpace(baseXpath)
607               
608                resDependantVal = []
609               
610                #for path in baseXpaths:
611                try:                           
612                        rootEl = self.root.findall(baseXpath) #returns an elementree object of elements in a list                               
613                except:
614                        logging.error("Could not find element for this xpath: " + baseXpath)
615                        return 'None'
616                       
617                for el in rootEl:
618                        thisElXpth = self.appendNameSpace(elXpath)
619                                                               
620                        thisEl = self.doFindall(el,thisElXpth)
621                               
622                        #if there's a value for the actual xpath we want, go and get the dependant value
623                        if thisEl != 'None':
624                                       
625                                elVal = thisEl[0].text #NOTE take first one as we expect this to be the value we want
626                                                                       
627                                thisEldepXpth = self.appendNameSpace(depXpath)                                 
628                                thisDepEl = self.doFindall(el,thisEldepXpth)                                                           
629                                       
630                                if thisDepEl != 'None':
631                                                                               
632                                        depVal = thisDepEl[0].text
633                                                                       
634                                        if depVal == depValReqd:
635                                               
636                                                if showValue is True:
637                                                        logging.info("")
638                                                        logging.info("          Element Value: " + elVal)
639                                                        logging.info("")
640                                               
641                                                resDependantVal.append(elVal)
642                                                                                                                                                       
643                return resDependantVal
644       
645        '''
646        Method to extract a value from the doc using a simple xpath.  If no element found or no data present then None is returned
647        '''
648        def returnSimpleElementVal(self,xpath,showValue=False):
649               
650                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
651                xpathNS = self.appendNameSpace(xpath)           
652               
653                resElementVal = []
654               
655                try:   
656                        rootEl = self.root.findall(xpathNS)
657                       
658                except:
659                        logging.error("Could not find element for this xpath: " + xpath)                       
660                        return ['None']
661                                               
662                #use a text 'None' rather than a None type
663               
664                for elVal in rootEl:
665                        if elVal is None:
666                                resElementVal.append('None')
667                        else:
668                                value = elVal.text
669                                if (showValue is True) and (value is not None):
670                                        logging.info("")
671                                        logging.info("          Element Value: " + value)
672                                        logging.info("")
673                                       
674                                resElementVal.append(value)
675                '''     
676                if rootEl[0].text is None:                                     
677                        resElementVal.append('None')
678                else:
679                        for el in rootEl:                                       
680                                resElementVal.append(el.text)   
681                               
682                                       
683                '''
684               
685                return resElementVal
686                               
687       
688        '''
689        Method to run an elementtree findall on ns appended path in the supplied element and return list
690        returns None if nothing found
691        '''
692        def doFindall(self,el,thisElXpth):
693                               
694                try:
695                        thisElXpthEl = el.findall(thisElXpth)
696                       
697                        if len(thisElXpthEl) == 0:
698                                thisElXpthEl = 'None'
699                except:
700                        thisElXpthEl = 'None'
701               
702               
703                return thisElXpthEl
704               
705               
706        '''
707        Method to extract actual value from xml doc - expects a list of namespace qualified xpaths and will return corresponding list of values
708        '''
709        def getXmlVal(self,paths):
710               
711                xmlVals = []
712                               
713                for path in paths:
714                        try:
715                                xmlVals.append(self.root.find(path).text)                       
716                        except:
717                                logging.error("Could not extract value for xpath: " + path)
718                                xmlVals.append('null')
719                               
720                return xmlVals                                 
721               
722        '''
723        Method to hold a dictionary linking namespace prefixes to URLs
724        '''
725        def isoNameSpaces(self):
726               
727                isoNs = {'gmd':'http://www.isotc211.org/2005/gmd','gco':'http://www.isotc211.org/2005/gco',
728                                'gmx':'http://www.isotc211.org/2005/gmx','gml':'http://www.opengis.net/gml/3.2',
729                                'none':'','gts':'http://www.isotc211.org/2005/gts','gsr':'http://www.isotc211.org/2005/gsr',
730                                'gss':'http://www.isotc211.org/2005/gss'
731                                }
732               
733                return isoNs
734       
735       
736        '''
737        Method to return DEFAULT namespace in elementtree format of ISO profile used (i.e. should be gmd)
738        (to deal with those elements with no prefix)
739        '''
740        def defaultIsoNamespace(self):
741                return 'gmd'
742               
743       
744       
745        '''
746        Method to extract root element of XML file specified using elementtree
747        '''     
748        def getIsoXML(self,file):
749               
750                logging.info("Getting xml root element")
751               
752                #parse the xml with elementtree
753                self.etree=ET.parse(file)
754                root=self.etree.getroot() # should be the "gmd:MD_Metadata" element
755               
756                #avoid ns0 and ns1 etc etc namespaces when rewriting this ET
757                logging.info("Sorting out namespaces so can properly rewrite ISO xml..")
758                for ns in self.isoNameSpaces().keys():         
759                        if ns != 'None':                                               
760                                ET._namespace_map[self.isoNameSpaces()[ns]] = ns
761               
762                               
763                #check root element is an ISO one - use elementtree appended namespace..
764                if root.tag != '{http://www.isotc211.org/2005/gmd}MD_Metadata':
765                        logging.error("XML document does not appear to be ISO (not gmd:MD_Metadata)!!")                 
766                        return None
767       
768                return root
769               
770               
771        '''
772        Method to convert the given xpath list to a namespace abbreviated version so elementtree can handle it (grrr.). 
773        Will return a list of corresponding namespace qualified xpaths - if errors a 'null' will be placed.
774        '''
775        def appendNameSpaceList(self,paths):
776               
777                nameSpaceAppendedPaths = []
778               
779               
780                for path in paths:             
781                       
782                        pathElements = path.split('/')
783                       
784                        #note due to crappy elementtree have to avoid initial "/"
785                        count = 0
786                        for element in pathElements:
787                               
788                                try:                                   
789                                        if ':' in element:                                             
790                                                splitElement = element.split(':')
791                                                nsPrefix,nsElement = splitElement[0],splitElement[1]
792                                        else:
793                                                #use default namespace                                         
794                                                nsPrefix = self.defaultIsoNamespace()
795                                                nsElement = element
796                                       
797                                        if count == 0:
798                                               
799                                                #appendedPath = self.returnNS() + element
800                                                appendedPath = '{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
801                                        else:
802                                               
803                                                appendedPath = appendedPath + '/{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
804                                       
805                                        count += 1
806                                except:
807                                        appendedPath = 'null'
808                                        logging.error("Could not change to elementtree ns xpath")
809                                                       
810                        #clear up any blank namespace prefixes
811                        appendedPath = appendedPath.replace('{}','')
812                       
813                        nameSpaceAppendedPaths.append(appendedPath)
814
815                return nameSpaceAppendedPaths
816       
817        '''
818        Method to convert the given xpath to a namespace abbreviated version so elementtree can handle it (grrr.). 
819        Will return an equivalent namespace qualified xpath - if errors a 'null' will be placed.
820        '''
821        def appendNameSpace(self,path):
822               
823                nameSpaceAppendedPath = ''
824               
825                pathElements = path.split('/')
826                       
827                #note due to crappy elementtree have to avoid initial "/"
828                count = 0
829                for element in pathElements:
830                               
831                        try:                                   
832                                if ':' in element:                                             
833                                        splitElement = element.split(':')
834                                        nsPrefix,nsElement = splitElement[0],splitElement[1]
835                                else:
836                                        #use default namespace                                         
837                                        nsPrefix = self.defaultIsoNamespace()
838                                        nsElement = element
839                                       
840                                if count == 0:
841                                               
842                                        #appendedPath = self.returnNS() + element
843                                        appendedPath = '{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
844                                else:                                           
845                                        appendedPath = appendedPath + '/{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
846                                       
847                                count += 1
848                        except:
849                                appendedPath = 'null'
850                                logging.error("Could not change to elementtree ns xpath")
851                                                       
852                #clear up any blank namespace prefixes
853                nameSpaceAppendedPath = appendedPath.replace('{}','')
854               
855                return nameSpaceAppendedPath
Note: See TracBrowser for help on using the repository browser.