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

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

roundup updates - ingestion working well

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                                thisEldepXpth = self.appendNameSpace(depXpath)                                 
632                                thisDepEl = self.doFindall(el,thisEldepXpth)                                                           
633                                       
634                                if thisDepEl != 'None':
635                                                                               
636                                        depVal = thisDepEl[0].text
637                                                                       
638                                        if depVal == depValReqd:
639                                               
640                                                if showValue is True:
641                                                        logging.info("")
642                                                        logging.info("          Element Value: " + elVal)
643                                                        logging.info("")
644                                               
645                                                resDependantVal.append(elVal)
646                                                                                                                                                       
647                return resDependantVal
648       
649        '''
650        Method to extract a value from the doc using a simple xpath.  If no element found or no data present then None is returned
651        '''
652        def returnSimpleElementVal(self,xpath,showValue=False):
653               
654                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
655                xpathNS = self.appendNameSpace(xpath)           
656               
657                resElementVal = []
658               
659                try:   
660                        rootEl = self.root.findall(xpathNS)
661                       
662                except:
663                        logging.error("Could not find element for this xpath: " + xpath)                       
664                        return ['None']
665                                               
666                #use a text 'None' rather than a None type
667               
668                for elVal in rootEl:
669                        if elVal is None:
670                                resElementVal.append('None')
671                        else:
672                                value = elVal.text
673                                if (showValue is True) and (value is not None):
674                                        logging.info("")
675                                        logging.info("          Element Value: " + value)
676                                        logging.info("")
677                                       
678                                resElementVal.append(value)
679                '''     
680                if rootEl[0].text is None:                                     
681                        resElementVal.append('None')
682                else:
683                        for el in rootEl:                                       
684                                resElementVal.append(el.text)   
685                               
686                                       
687                '''
688               
689                return resElementVal
690                               
691       
692        '''
693        Method to run an elementtree findall on ns appended path in the supplied element and return list
694        returns None if nothing found
695        '''
696        def doFindall(self,el,thisElXpth):
697                               
698                try:
699                        thisElXpthEl = el.findall(thisElXpth)
700                       
701                        if len(thisElXpthEl) == 0:
702                                thisElXpthEl = 'None'
703                except:
704                        thisElXpthEl = 'None'
705               
706               
707                return thisElXpthEl
708               
709               
710        '''
711        Method to extract actual value from xml doc - expects a list of namespace qualified xpaths and will return corresponding list of values
712        '''
713        def getXmlVal(self,paths):
714               
715                xmlVals = []
716                               
717                for path in paths:
718                        try:
719                                xmlVals.append(self.root.find(path).text)                       
720                        except:
721                                logging.error("Could not extract value for xpath: " + path)
722                                xmlVals.append('null')
723                               
724                return xmlVals                                 
725               
726        '''
727        Method to hold a dictionary linking namespace prefixes to URLs
728        '''
729        def isoNameSpaces(self):
730               
731                isoNs = {'gmd':'http://www.isotc211.org/2005/gmd','gco':'http://www.isotc211.org/2005/gco',
732                                'gmx':'http://www.isotc211.org/2005/gmx','gml':'http://www.opengis.net/gml/3.2',
733                                'none':'','gts':'http://www.isotc211.org/2005/gts','gsr':'http://www.isotc211.org/2005/gsr',
734                                'gss':'http://www.isotc211.org/2005/gss'
735                                }
736               
737                return isoNs
738       
739       
740        '''
741        Method to return DEFAULT namespace in elementtree format of ISO profile used (i.e. should be gmd)
742        (to deal with those elements with no prefix)
743        '''
744        def defaultIsoNamespace(self):
745                return 'gmd'
746               
747       
748       
749        '''
750        Method to extract root element of XML file specified using elementtree
751        '''     
752        def getIsoXML(self,file):
753               
754                logging.info("Getting xml root element")
755               
756                #parse the xml with elementtree
757                self.etree=ET.parse(file)
758                root=self.etree.getroot() # should be the "gmd:MD_Metadata" element
759               
760                #avoid ns0 and ns1 etc etc namespaces when rewriting this ET
761                logging.info("Sorting out namespaces so can properly rewrite ISO xml..")
762                for ns in self.isoNameSpaces().keys():         
763                        if ns != 'None':                                               
764                                ET._namespace_map[self.isoNameSpaces()[ns]] = ns
765               
766                               
767                #check root element is an ISO one - use elementtree appended namespace..
768                if root.tag != '{http://www.isotc211.org/2005/gmd}MD_Metadata':
769                        logging.error("XML document does not appear to be ISO (not gmd:MD_Metadata)!!")                 
770                        return None
771       
772                return root
773               
774               
775        '''
776        Method to convert the given xpath list to a namespace abbreviated version so elementtree can handle it (grrr.). 
777        Will return a list of corresponding namespace qualified xpaths - if errors a 'null' will be placed.
778        '''
779        def appendNameSpaceList(self,paths):
780               
781                nameSpaceAppendedPaths = []
782               
783               
784                for path in paths:             
785                       
786                        pathElements = path.split('/')
787                       
788                        #note due to crappy elementtree have to avoid initial "/"
789                        count = 0
790                        for element in pathElements:
791                               
792                                try:                                   
793                                        if ':' in element:                                             
794                                                splitElement = element.split(':')
795                                                nsPrefix,nsElement = splitElement[0],splitElement[1]
796                                        else:
797                                                #use default namespace                                         
798                                                nsPrefix = self.defaultIsoNamespace()
799                                                nsElement = element
800                                       
801                                        if count == 0:
802                                               
803                                                #appendedPath = self.returnNS() + element
804                                                appendedPath = '{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
805                                        else:
806                                               
807                                                appendedPath = appendedPath + '/{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
808                                       
809                                        count += 1
810                                except:
811                                        appendedPath = 'null'
812                                        logging.error("Could not change to elementtree ns xpath")
813                                                       
814                        #clear up any blank namespace prefixes
815                        appendedPath = appendedPath.replace('{}','')
816                       
817                        nameSpaceAppendedPaths.append(appendedPath)
818
819                return nameSpaceAppendedPaths
820       
821        '''
822        Method to convert the given xpath to a namespace abbreviated version so elementtree can handle it (grrr.). 
823        Will return an equivalent namespace qualified xpath - if errors a 'null' will be placed.
824        '''
825        def appendNameSpace(self,path):
826               
827                nameSpaceAppendedPath = ''
828               
829                pathElements = path.split('/')
830                       
831                #note due to crappy elementtree have to avoid initial "/"
832                count = 0
833                for element in pathElements:
834                               
835                        try:                                   
836                                if ':' in element:                                             
837                                        splitElement = element.split(':')
838                                        nsPrefix,nsElement = splitElement[0],splitElement[1]
839                                else:
840                                        #use default namespace                                         
841                                        nsPrefix = self.defaultIsoNamespace()
842                                        nsElement = element
843                                       
844                                if count == 0:
845                                               
846                                        #appendedPath = self.returnNS() + element
847                                        appendedPath = '{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
848                                else:                                           
849                                        appendedPath = appendedPath + '/{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
850                                       
851                                count += 1
852                        except:
853                                appendedPath = 'null'
854                                logging.error("Could not change to elementtree ns xpath")
855                                                       
856                #clear up any blank namespace prefixes
857                nameSpaceAppendedPath = appendedPath.replace('{}','')
858               
859                return nameSpaceAppendedPath
Note: See TracBrowser for help on using the repository browser.