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

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

Updated handling of some elements where no data present

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