source: TI01-discovery-Ingest/trunk/v4.3.0/ingestAutomation-upgrade/OAIBatch/ExtractISO.py @ 7901

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

Update to bugs involved in handling CEH service records etc

Line 
1
2from xml.etree import ElementTree as ET
3import logging,urllib,os,sys,inspect,string
4from Utilities import IsoIngestListUtilities # so can use those useful getSingleVal and getMultipleVal methods for discerning lists etc (esp useful when using the isoVal to dbColumn methods..)
5import ndg.common.src.lib.utilities as ndgUtilities
6import random
7import ndg.common.src.lib.fileutilities as FileUtilities
8
9
10'''
11ExtractISO handler class designed to deal with NERC DMS series of ISO profile metadata (for NERC Revitilisation work)
12'''
13error=''
14message=''     
15
16class ExtractISO:
17
18        def __init__(self,filenameIP,isoFormat):                               
19                               
20                self.inputXMLfile = filenameIP
21                self.isoFormatToExtract = isoFormat
22               
23                #show actual values extracted from the XML in logging.info
24                self.showXMLvalues = True
25               
26                logging.info("Have initialised ISO XML Extractor")
27               
28                #self.createISOdataStructure()
29               
30               
31        '''
32        Method to control re-writing of ISO xml with NDG redirect URL's
33        '''
34        def generateNDGredirectURL(self, ndgRedirectURL, xmlOutputFile, changeUrl):
35               
36                logging.info("Generating XML copy with URLS converted to NDG redirect URL's")
37               
38                self.redirectBaseURL = ndgRedirectURL
39               
40                rewriteIsoXml = self.changeElementUrlVal(self.isoModel.urlsToChange(), xmlOutputFile, changeUrl)
41                               
42                return rewriteIsoXml
43       
44       
45       
46        '''
47        Method to map ISO variables to Discovery Database columns - use in PostgresDAO.py with getattr!
48       
49        returns a dictionary where key is
50    '''
51        def mapIsoFieldsToDBcolumns(self):
52               
53                return {'originalFormat':'original_format_name', \
54        'originalFormatVersion':'original_format_version',\
55        'datasetAbstract':'dataset_abstract', \
56        'resourceType_text':'resource_type', \
57        'resourceType_tsvector':'resource_type_ts_vector', \
58        'topicCategory_text':'topic_category', \
59        'topicCategory_tsvector':'topic_category_ts_vector', \
60        'lineage_text':'lineage', \
61        'lineage_tsvector':'lineage_ts_vector', \
62        'publicAccessLimitations_text':'limitations_public_access', \
63        'publicAccessLimitations_tsvector':'limitations_public_access_ts_vector', \
64        'dataOriginator_text':'data_originator',\
65        'dataOriginator_tsvector':'data_originator_tsvector',\
66        'metadataUpdateDate':'dataset_metadata_update_date', \
67        'dataFormat':'original_format_name',
68        'createDate':'dataset_metadata_creation_date',
69        'publicationDate':'dataset_metadata_publication_date',
70        'authors_text':'authors','parameters_text':'parameters',
71        'dataFormats_text':'data_formats','dataFormats_ts_vector':'data_formats_tsvector', \
72        'resourceLocatorBool':'resource_locator','inspireThemes_text':'inspire_themes','inspireThemes_tsvector':'inspire_themes_ts_vector', \
73        'verticalExtent_text':'vertical_extent','verticalExtent_tsvector':'vertical_extent_ts_vector'
74        }
75               
76               
77        '''
78                Method to match the columns found in mapIsoFieldsToDBcolumns to the postgres data types
79        '''
80        def mapDBcolumnsToIsoDataTypes(self):
81               
82                return {'original_format_name':'text', \
83        'original_format_version':'text',\
84        'dataset_abstract':'text', \
85        'resource_type':'text', \
86        'resource_type_ts_vector':'tsvector', \
87        'topic_category':'text', \
88        'topic_category_ts_vector':'tsvector', \
89        'lineage':'text', \
90        'limitations_public_access':'text', \
91        'lineage_ts_vector':'tsvector', \
92        'limitations_public_access_ts_vector':'tsvector', \
93        'data_originator':'text',\
94        'data_originator_tsvector':'tsvector',\
95        'dataset_metadata_update_date':'timestamp', \
96        'original_format_name':'text',
97        'dataset_metadata_creation_date':'timestamp',
98        'dataset_metadata_publication_date':'timestamp',
99        'authors':'text','parameters':'text',
100        'data_formats':'text','data_formats_tsvector':'tsvector', \
101        'resource_locator':'boolean','inspire_themes':'text','inspire_themes_ts_vector':'tsvector', \
102        'vertical_extent':'text','vertical_extent_ts_vector':'tsvector'
103        }
104       
105               
106        '''
107        Simple method to allow return information on success of extraction ( __init__ method cannot return anything so use this method)
108        '''
109        def createISOdataStructure(self):
110               
111                logging.info("")
112                logging.info("********************************************************************************************************************************************")
113                logging.info("****************** Creating ISO data structure from " + self.inputXMLfile + "****************** ")
114                logging.info("********************************************************************************************************************************************")
115                logging.info("")
116               
117                #set processing message and processing docs
118                self.processingMsg = ''
119                self.validDoc = True
120               
121                self.root = self.getIsoXML(self.inputXMLfile)
122               
123                self.isoFileLocation = self.inputXMLfile       
124               
125                #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')
126               
127                if self.root is None:
128                        logging.error("Detected possible problem with input xml - cannot parse it!")
129                        self.processingMsg = 'Problem with the XML structure!'
130                        self.validDoc = False
131                        return False
132               
133                #choose the ISO xpath definition class depending on format profile required
134                self.importISOxmlDefinition(self.isoFormatToExtract)
135                                                       
136                #get all the required information from the chosen xpath definition class
137               
138                #first - work out what type of resource we are ingesting .. assume dataset             
139                try:                   
140                        self.resourceType = self.getElementVal(self.isoModel.resourceType())[0][0]                     
141                        self.resourceType_text = self.resourceType
142                        self.resourceType_tsvector = self.resourceType
143                except:
144                        self.resourceType = None
145                        self.resourceType_text = None
146                        self.resourceType_tsvector = None
147               
148                #if dataset is a dataset type get id from usual place, if service use fileIdentifier..
149                self.datasetID = 'None'
150                if self.resourceType == 'dataset' or self.resourceType=='series':               
151                        self.datasetID = self.getElementVal(self.isoModel.datasetID()) 
152                elif self.resourceType == 'service':
153                        logging.info("Ingesting a service metadata record!")
154                        self.datasetID = self.getElementVal(self.isoModel.fileIdentifier())
155               
156                if self.datasetID == 'None':
157                        self.processingMsg = 'No entry for Dataset ID so record not valid and cannot/will not Ingest!'
158                        logging.error(self.processingMsg)
159                       
160                        #to aid proper filename reporting must give it a quasi id as will give "None" filename!
161                        id = [str(random.randrange(1000,20000,3)) + '_ingestIempID'] # remember must be in that double list format!
162                        logging.info("Using random integer: %s for ID to complete reporting process..." %id)
163                        self.datasetID = [id]
164                        self.validDoc = False
165               
166                else:
167                       
168                        #noticed some MEDIN centres allowing space characters in a unique id.. change to single underscore! (remember only 1 allowed!)
169                        self.datasetID[self.findTheListData(self.datasetID)][0] = self.datasetID[self.findTheListData(self.datasetID)][0].replace(" ", "_")
170               
171               
172                self.datasetName = self.getElementVal(self.isoModel.dataSetName())
173                if self.datasetName == 'None':
174                        self.processingMsg = 'No entry for Dataset Name so record not valid and cannot/will not Ingest!'
175                        logging.error(self.processingMsg)
176                        self.validDoc = False
177               
178                self.datasetAbstract = self.getElementVal(self.isoModel.dataSetAbstract())                     
179               
180                self.revisionDate = self.getElementVal(self.isoModel.metadataRevisionDate())
181               
182                #BODC using timestamp, so must isolate whatever value is available             
183                self.singleRevisionDate = self.revisionDate[self.findTheListData(self.revisionDate)][0]
184               
185                self.createDate = self.getElementVal(self.isoModel.metadataCreationDate())
186                               
187                self.datasetName = self.getElementVal(self.isoModel.dataSetName())
188                                               
189                self.boundingDates = self.getElementVal(self.isoModel.boundingDates())
190               
191                self.boundingDatesRange = self.boundingDateRange(self.boundingDates)           
192                                       
193                self.originalFormat = self.getElementVal(self.isoModel.originalFormat())
194                               
195                try:
196                        self.dataFormats = self.getElementVal(self.isoModel.dataFormats())
197                except:
198                        self.dataFormats = 'None'
199                               
200                if self.dataFormats != 'None':
201                       
202                        self.dataFormatsOb = IsoIngestListUtilities(self.dataFormats,True)
203                        self.dataFormats_text = self.dataFormatsOb.getDelimitedStringFromList(self.dataFormatsOb.listVals)
204                       
205                        #will be nothing in there due to way we've caught NoneTypes in Utilities.py - so set to 'None' here so can be nulled in sql
206                        if len(self.dataFormats_text) == 0:
207                                self.dataFormats_text = 'None'
208                       
209                        self.dataFormats_ts_vector = self.dataFormats_text
210                                               
211                else:
212                        self.dataFormats_text = 'None'
213                        self.dataFormats_ts_vector = 'None'
214                       
215                self.authors = self.getElementVal(self.isoModel.authors())
216               
217                if self.authors == 'None':
218                        self.processingMsg = 'No entry for Authors so record not valid and cannot/will not Ingest!'
219                        logging.error(self.processingMsg)
220                        self.validDoc = False
221               
222                else:
223                        #need to provide a list for updating relevant db column - keep distinct from other authors based vars just in case of problems..               
224                        self.authorOb = IsoIngestListUtilities(self.authors,True)               
225                        self.authors_text = self.authorOb.getDelimitedStringFromList(self.authorOb.listVals)           
226                        self.authors_ts_vector = self.authors_text
227                               
228                self.datacentreName = self.getElementVal(self.isoModel.dataCentreName())
229               
230                if self.datacentreName == 'None':
231                        self.processingMsg = 'No entry for Data Centre name so record not valid and cannot/will not Ingest!'
232                        logging.error(self.processingMsg)
233                        self.validDoc = False
234               
235               
236                self.keywords = self.getElementVal(self.isoModel.keywords())
237               
238                self.keywordsList = self.listify(self.keywords)
239               
240               
241                if self.keywords == 'None':
242                        logging.warn("No parameter info for record!")
243                        self.parameters_text = ''
244                        self.parameters_tsvector = ''
245                else:           
246                        self.parametersOb = IsoIngestListUtilities(self.keywords,True)                   
247                        self.parameters_text = self.parametersOb.getDelimitedStringFromList(self.parametersOb.listVals)
248                        self.parameters_tsvector = self.parameters_text
249                                               
250                coords = self.getElementVal(self.isoModel.coordinates())
251                if coords == 'None':                   
252                        self.boundingBoxCoordinates = None
253                else:
254                        self.boundingBoxCoordinates = coords   
255               
256                #These are the MEDIN requested extra fields - make optional
257                try:
258                        self.originalFormatVersion = self.getElementVal(self.isoModel.originalFormatVersion())
259                except:
260                        self.originalFormatVersion = None
261               
262                try:
263                        self.publicationDate = self.getElementVal(self.isoModel.metadataPublicationDate())
264                except:
265                        self.publicationDate = None
266                                       
267                try:
268                       
269                        self.topicCategory = self.getElementVal(self.isoModel.topicCategory())
270                        self.topicCategoryList = self.listify(self.topicCategory)
271                       
272                        if self.topicCategory == 'None':
273                                logging.warn("No topic category keywords available!")
274                                self.topicCategory_text = None
275                                self.topicCategory_tsvector = None
276                        else:
277                                self.topicCategoryOb = IsoIngestListUtilities(self.topicCategory,True)
278                                self.topicCategory_text = self.topicCategoryOb.getDelimitedStringFromList(self.topicCategoryOb.listVals)
279                                self.topicCategory_tsvector = self.topicCategory_text
280                               
281                        #self.topicCategory = self.getElementVal(self.isoModel.topicCategory())
282                        #self.topicCategory_text = self.topicCategory
283                        #self.topicCategory_tsvector = self.topicCategory
284                except:
285                        self.topicCategory = None
286                        self.topicCategory_text = None
287                        self.topicCategory_tsvector = None
288                       
289                #updated for NERC API v4.3.0
290               
291                #resource locator               
292                try:
293                        self.resourceLocator = self.getElementVal(self.isoModel.resourceLocator())
294                        self.resourceLocator_text = self.resourceLocator
295                        self.resourceLocator_tsvector = self.resourceLocator
296                except:
297                        self.resourceLocator = None
298                        self.resourceLocator_text = None
299                        self.resourceLocator_tsvector = None
300               
301                       
302                #resourceLocator in the db is a boolean field - it either has a data resource or it doesnt             
303                if self.resourceLocator == 'None' or self.resourceLocator =='':
304                        self.resourceLocatorBool = False
305                else:
306                        self.resourceLocatorBool = True
307               
308                #INSPIRE themes (sub class of keywords)
309               
310                try:   
311                        self.inspireThemes = self.getElementVal(self.isoModel.INSPIREthemes()) 
312                        self.inspireThemesList = self.listify(self.inspireThemes)
313                except:
314                        self.inspireThemes = None
315               
316                if self.inspireThemes == 'None' or self.inspireThemes is None:
317                        logging.warn("No INSPIRE themes info for record!")
318                        self.inspireThemes_text = '' 
319                        self.inspireThemes_tsvector = ''
320                else:           
321                        self.inspireThemesOb = IsoIngestListUtilities(self.inspireThemes,True)                   
322                        self.inspireThemes_text = self.inspireThemesOb.getDelimitedStringFromList(self.inspireThemesOb.listVals)
323                        self.inspireThemes_tsvector = self.inspireThemes_text
324               
325                #vertical extent (sub class of keywords)        OPTIONAL for NERC DMS                   
326                try:
327                        self.verticalExtent = self.getElementVal(self.isoModel.verticalExtentKeyword()) 
328                        self.verticalExtentList = self.listify(self.verticalExtent)
329                except:
330                        self.verticalExtent = None
331               
332                if self.verticalExtent == 'None' or self.verticalExtent is None:
333                        logging.warn("No Vertical Extent keywords info for record!")
334                        self.verticalExtent_text = ''
335                        self.verticalExtent_tsvector = ''
336                else:           
337                        self.verticalExtentOb = IsoIngestListUtilities(self.verticalExtent,True)                         
338                        self.verticalExtent_text = self.verticalExtentOb.getDelimitedStringFromList(self.verticalExtentOb.listVals)
339                        self.verticalExtent_tsvector = self.verticalExtent_text
340                                               
341                       
342                '''
343                self.keywords = self.getElementVal(self.isoModel.keywords())
344               
345                self.keywordsList = self.listify(self.keywords)
346               
347                if self.keywords == 'None':
348                        logging.warn("No parameter info for record!")
349                        self.parameters_text = ''
350                        self.parameters_tsvector = ''
351                else:           
352                        self.parametersOb = IsoIngestListUtilities(self.keywords,True)                   
353                        self.parameters_text = self.parametersOb.getDelimitedStringFromList(self.parametersOb.listVals)
354                        self.parameters_tsvector = self.parameters_text
355                '''
356               
357                try:
358                        self.lineage = self.getElementVal(self.isoModel.lineage())
359                        self.lineage_text = self.lineage
360                        self.lineage_tsvector = self.lineage
361                except:
362                        self.lineage = None
363                        self.lineage_text = None
364                        self.lineage_tsvector = None
365                '''
366                try:
367                        self.publicAccessLimitations = self.getElementVal(self.isoModel.publicAccessLimitations())
368                        self.publicAccessLimitations_text = self.publicAccessLimitations
369                        self.publicAccessLimitations_tsvector = self.publicAccessLimitations
370                        self.publicAccessLimitationsList = self.listify(self.publicAccessLimitations_text)
371                except:
372                        self.publicAccessLimitations = None
373                        self.publicAccessLimitations_text = None
374                        self.publicAccessLimitations_tsvector = None
375                        self.publicAccessLimitationsList = None
376                '''     
377               
378                try:   
379                        self.publicAccessLimitations= self.getElementVal(self.isoModel.publicAccessLimitations())       
380                        self.publicAccessLimitationsList = self.listify(self.publicAccessLimitations)
381                except:
382                        self.publicAccessLimitations = None
383               
384                if self.publicAccessLimitations == 'None' or self.publicAccessLimitations is None:
385                        logging.warn("No Public access limitation info for record!")
386                        self.publicAccessLimitations_text = '' 
387                        self.publicAccessLimitations_tsvector = ''
388                else:           
389                        self.publicAccessLimitationsOb = IsoIngestListUtilities(self.publicAccessLimitations,True)                       
390                        self.publicAccessLimitations_text = self.publicAccessLimitationsOb.getDelimitedStringFromList(self.publicAccessLimitationsOb.listVals)
391                        self.publicAccessLimitations_tsvector = self.inspireThemes_text         
392                               
393                try:
394                        self.dataOriginator= self.getElementVal(self.isoModel.dataOriginator())
395                                               
396                except:
397                        self.dataOriginator = None
398                        self.dataOriginator_text = None
399                        self.dataOriginator_tsvector = None
400                       
401               
402                #check presence of originator - Mandatory!
403                if self.dataOriginator == 'None' or self.dataOriginator is None:
404                        #self.processingMsg = 'No entry for Data Originator!'
405                        #logging.error(self.processingMsg)
406                        #self.validDoc = False
407                        self.dataOriginator_text = 'null'
408                        self.dataOriginator_tsvector = 'null'
409               
410                else:                   
411                        self.dataOriginatorOb = IsoIngestListUtilities(self.dataOriginator,True)
412                       
413                        #add some extra specifications to deal with MEDIn requirement to have both searchable field (vector) and return as a text field
414                        self.dataOriginator_text = self.dataOriginatorOb.getDelimitedStringFromList(self.dataOriginatorOb.listVals)
415                        self.dataOriginator_tsvector = self.dataOriginator_text
416               
417                try:
418                        self.dataFormat = self.getElementVal(self.isoModel.originalFormat())
419                except:
420                        self.dataFormat = None
421               
422                try:
423                        self.metadataUpdateDate = self.getElementVal(self.isoModel.metadataUpdateDate())
424                except:
425                        self.metadataUpdateDate = None
426                       
427                       
428                try:
429                        self.originalFormatName = self.getElementVal(self.isoModel.originalFormat())
430                except:
431                        self.originalFormatName = None
432                       
433                try:
434                        self.originalFormatVersion = self.getElementVal(self.isoModel.originalFormatVersion())
435                except:
436                        self.originalFormatVersion = None
437                       
438               
439                logging.info("")
440                logging.info(" ****************** Completed rendering ISO xml into data structure! ****************** ")
441                logging.info("")
442               
443                return True
444       
445       
446        '''
447        Method to generate a simple list object from a compound list set
448        '''
449        def listify(self,listObject):
450               
451                list = []
452               
453                for outer in listObject:
454                        for inner in outer:                             
455                                list.append(inner)
456                                               
457                return list
458       
459        '''
460        Method to pad out a date required for proper updating postgres timestamps if only have a stump of a date (i.e. YYYY or YYYY-MM)
461       
462        NOTE: only pads out dates if it needs to! (checkDate checks the values!) - use checkDate for validation- if bad date just returns input but with warning
463       
464        Uses template YYYY-MM-DDTHH:MM:SS if anything other then pad to minimum YYYY-MM-DD
465       
466        '''
467        def padDate(self,inputDate,rangeStart=None):
468               
469                if inputDate is None:
470                        #just pass it on through
471                        return None
472                               
473                if rangeStart is None:
474                        padMax = True
475                else:
476                        padMax = rangeStart
477                       
478                inputDate=inputDate.strip()
479                                       
480                dateStrArr = inputDate.split('-')
481                               
482                #check if long date string or short - if long, will have a T in! (should do..)
483                #just stick with date values now..
484                if 'T' in dateStrArr:
485                        logging.info("*********************************** LONG VALUE DATE STRING ALERT!")
486                        dateStrArr = dateStrArr[:3]
487                        timeStrArr = dateStrArr[4:]
488                                                       
489                if len(dateStrArr)< 3:
490                       
491                        #acceptible to have YYYY, or YYYY-MM as well now...
492                        if len(dateStrArr)==0:
493                                logging.warn("Invalid Date! (%s)" %inputdate)
494                                return inputDate
495                       
496                        elif len(dateStrArr) == 1:
497                                logging.info("looks like just a year supplied..")
498                                yearStr = dateStrArr[0]
499                               
500                                if len(yearStr) != 4:
501                                        logging.warn("Invalid Date! (%s)" %inputDate)
502                                        return inputDate
503                                #temp fudge
504                                logging.info("No Month or day data- pad for postgres!")
505                               
506                                #pad values as appropriate
507                                if padMax:                             
508                                        monthStr = "01"
509                                        dayStr = "01"
510                                else:
511                                        monthStr = "12"
512                                        dayStr = "31"
513                                       
514                                outputDate = "%s-%s-%s" %(yearStr,monthStr,dayStr)
515                                logging.info("Have padded input of %s to %s" %(inputDate,outputDate))
516                               
517                                return outputDate
518                               
519                        elif len(dateStrArr)==2:
520                                msg = "checking what could be a year:%s month:%s" %(dateStrArr[0],dateStrArr[1])
521                                yearStr = dateStrArr[0]
522                                monthStr = dateStrArr[1]
523                               
524                                if len(yearStr) != 4:
525                                        logging.warn("Bad year value...(%s)" %yearStr)
526                                        return False
527                               
528                                #pad values as appropriate
529                                if padMax:     
530                                        dayStr = "01"
531                                else:
532                                        if monthStr in ['01','03','05','07','08','10','12']:
533                                                dayStr = "31"
534                                        elif monthStr in ['04','06','09','11']:
535                                                dayStr = "30"
536                                        else:
537                                                dayStr = '28'
538                                               
539                                outputDate = "%s-%s-%s" %(yearStr,monthStr,dayStr)
540                                logging.info("Have padded input of %s to %s" %(inputDate,outputDate))
541                               
542                                return outputDate
543                               
544                else:
545                        logging.info("No padding required (%s)" %inputDate)
546                        return inputDate
547                       
548       
549        '''
550        Method to check date values to ensure correct insertion into database - if doesnt match minimum YYYY-MM-DD format then is false,
551        so date should be set to None in calling method.  Will adjust self.processingMsg too for return info
552       
553        NOTE: update to just check values - padDate method must be called first to ensure date format is minimum of YYYY-MM-DD
554       
555        '''
556        def checkDate(self,inputDate):
557               
558                #rangeStartOrEnd - to aid in padding out dates - will choose minimum pad values if true, maximum if false
559                #returnValue - if had to pad values, and want a changed value back thgis must be true           
560               
561                #strip any leading or trailing spaces
562                inputDate = inputDate.strip()
563               
564                if 'T' in inputDate:
565                        if len(inputDate.split('T'))==2:
566                                dateStrArr = inputDate.split('T')[0]
567                                timeStrArr = inputDate.split('T')[0]
568                               
569                                dateStrArr = dateStrArr.split('-')
570                                if len(dateStrArr)!=3:
571                                        msg = "Invalid input format date (%s)" %inputDate
572                                        return False
573                       
574                                else:                                   
575                                        yearStr = dateStrArr[0]
576                                        monthStr = dateStrArr[1]
577                                        dayStr = dateStrArr[2]
578                               
579                        else:
580                                #still need to do some basic date structure checking as padDate only fills out where it can, if still bad
581                                #this method should ultimately say whether good or bad date
582                                msg = "Invalid input format date (%s)" %inputDate
583                                return False
584               
585                elif ' ' in inputDate:
586                        if len(inputDate.split(' '))==2:
587                                dateStrArr = inputDate.split(' ')[0]
588                                timeStrArr = inputDate.split(' ')[0]
589                               
590                                dateStrArr = dateStrArr.split('-')
591                                if len(dateStrArr)!=3:
592                                        msg = "Invalid input format date (%s)" %inputDate
593                                        return False
594                       
595                                else:                                   
596                                        yearStr = dateStrArr[0]
597                                        monthStr = dateStrArr[1]
598                                        dayStr = dateStrArr[2]
599                               
600                        else:
601                                #still need to do some basic date structure checking as padDate only fills out where it can, if still bad
602                                #this method should ultimately say whether good or bad date
603                                msg = "Invalid input format date (%s)" %inputDate
604                                return False
605       
606                else:
607                        #should now have YYYY-MM-DD if padDate used properly
608                        dateStrArr = inputDate.split('-')
609                       
610                        if len(dateStrArr)!=3:
611                                        msg = "Invalid input format date (%s)" %inputDate
612                                        return False
613                       
614                        else:                                   
615                                yearStr = dateStrArr[0]
616                                monthStr = dateStrArr[1]
617                                dayStr = dateStrArr[2]
618               
619                #check days, months etc - Years can be anything... just do simple test for now         
620                if int(monthStr) > 12 or int(monthStr) < 1:
621                        msg = "Invalid Month value (%s) \n" %monthStr
622                        logging.warn(msg)
623                        self.processingMsg = self.processingMsg + msg
624                        return False
625                               
626                elif monthStr in ['01','03','05','07','08','10','12']:
627                        if int(dayStr) > 31 or int(dayStr) < 1:
628                                msg = "(31 ck) Invalid Day value (%s) for month (%s)\n" %(dayStr,monthStr)
629                                logging.warn(msg)
630                                self.processingMsg = self.processingMsg + msg
631                                return False
632                        else:
633                                logging.info("(31 ck) Date value seems ok...(%s) " %inputDate)
634                                return True
635                       
636                elif monthStr in ['04','06','09','11']:
637                        if int(dayStr) > 30 or int(dayStr) < 1:
638                                msg = "(30 ck) Invalid Day value (%s) for month (%s)\n" %(dayStr,monthStr)
639                                logging.warn(msg)
640                                self.processingMsg = self.processingMsg + msg
641                                return False
642                        else:
643                                logging.info("(30 ck) Date value seems ok...(%s) " %inputDate)
644                                return True
645                                       
646                elif monthStr == '02':
647                        if int(yearStr) % 4 == 0:
648                                if int(dayStr) > 29 or int(dayStr) < 1:
649                                        msg = "Invalid Day value (%s) for month (%s)\n" %(dayStr,monthStr)
650                                        logging.warn(msg)
651                                        self.processingMsg = self.processingMsg + msg
652                                        return False
653                                else:
654                                        logging.info("(29 ck) Date value seems ok...(%s) " %inputDate)
655                                        return True
656                                                                       
657                        else:                           
658                                if int(dayStr) > 28 or int(dayStr) < 1:
659                                        msg = "Invalid Day value (%s) for month (%s)\n" %(dayStr,monthStr)
660                                        logging.warn(msg)
661                                        self.processingMsg = self.processingMsg + msg
662                                        return False
663                                else:
664                                        logging.info("(28 ck) Date value seems ok...(%s) " %inputDate)
665                                        return True                                                                     
666                               
667                else:
668                        msg = "Invalid month value (%s)" %monthStr
669                        logging.warn(msg)
670                        self.processingMsg = self.processingMsg + msg
671                        return False
672               
673        '''
674        Method to return start and end dates from a sequence of dates such as that found returned using the xpath defn from boundingDates
675        '''
676        def boundingDateRange(self,boundingDatesList):
677               
678                #generate a simple list of all dates
679                allDates = []
680                returnDates = {}
681               
682                #check that there's something in it...
683                if boundingDatesList == 'None':
684                        return 'None'
685                                       
686                #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
687                if len(boundingDatesList) == 1:
688                        logging.info("Only 1 set of start and stop dates, so setting range to this..")
689                        returnDates['start'] = boundingDatesList[0]['start']
690                        returnDates['end'] = boundingDatesList[0]['end']
691                                               
692                        #check date validity & convert None to 'None' as necessary
693                        for date in returnDates.keys():
694                                if date=='start':                       
695                                        if returnDates[date] is not None:
696                                               
697                                                #pad if possible..
698                                                returnDates[date] = self.padDate(returnDates[date],rangeStart=True)
699                                                                                                                       
700                                                if not self.checkDate(returnDates[date]):
701                                                        returnDates[date] = 'None'
702                                        else:
703                                                returnDates[date] = 'None'
704                                                       
705                                elif date=='end':
706                                       
707                                        if returnDates[date] is not None:
708                                               
709                                                #pad if possible..
710                                                returnDates[date] = self.padDate(returnDates[date],rangeStart=False)
711                                                                                                                       
712                                                if not self.checkDate(returnDates[date]):
713                                                        returnDates[date] = 'None'
714                                        else:
715                                                returnDates[date] = 'None'
716                                                                               
717                        return returnDates
718               
719                for outer in boundingDatesList:
720                        for inner in outer.keys():
721                                if outer[inner] is not None:
722                                        if outer[inner] != 'None':
723                                                #some other test here to ensure proper date object
724                                                allDates.append(outer[inner])
725                                               
726                               
727                #min and max seem to work for now (to get it working) - assuming proper dateformat (restrict to YYYY-MM-DD for now)
728                #TODO: use datetime library to do this properly
729               
730                #check correct formatting for dates - if invalid, set to None - use checkDate method           
731                for date in returnDates.keys():                 
732                        if not self.checkDate(returnDates[date]):
733                                returnDates[date] = 'None'
734                                       
735                returnDates['start'] = min(allDates)
736                returnDates['end'] = max(allDates)
737               
738               
739                return returnDates
740       
741       
742               
743        '''
744        Method to import specific ISO xpath definition class depending on the profile used
745        '''
746        def importISOxmlDefinition(self,format):
747               
748                '''
749                ISO Profile version (date? - MEDIN upgrade dev)
750                '''
751               
752                logging.info("Format chosen: " + format)
753               
754                #Dynamically import module of correct profile...
755                import_string = "from " + format + " import " + format + " as iso"
756               
757                try:
758                        exec import_string
759                except:
760                        logging.error("Could not import xpath class for: " + format)
761                        sys.exit()
762               
763                self.isoModel = iso()
764               
765               
766        '''
767        Method to govern the changing of an element value (in the first instance change a url to a NDG redirect URL
768       
769        NOTE: will only change Urls if changeUrl set to True - this method is also used to ready the xml for writing into db
770       
771        '''
772        def changeElementUrlVal(self,keyMethod,outputFile,changeUrl):
773               
774       
775                logging.info("******************************************************************************************")
776                logging.info("Extracting xpath information for data extraction type (to change URL..):" + keyMethod[0])
777                logging.info("******************************************************************************************")
778                               
779               
780                #first need to interpret the contents of the tuple we've been passed everything is a dictionary in the tuple apart from element 0:
781                #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
782                dataStruct = self.interpretTuple(keyMethod)
783                cnt = 0
784               
785                #this is similar to getElementVal - but no interest in ordering or returning the actual value..
786               
787                #if changeUrl set to true, activate changes, just write xml as is if not
788                if changeUrl:           
789                        logging.info("changing urls now...")
790                        for i in dataStruct.keys()[1:]:
791                                               
792                                thisData = keyMethod[i][dataStruct[i]]
793                                                               
794                                #is dicionary a dependant value or straight xpath?
795                                if 'baseXpath' in thisData.keys():
796                                        logging.info("Extracting xml using COMPLEX dependant method for xpath def: " + str(cnt))                       
797                                        self.changeDependantURLElementVal(thisData['baseXpath'],thisData['elValXpath'],thisData['depValXpath'],thisData['depVal'])                             
798                                                               
799                                if 'xpath' in thisData.keys():         
800                                        logging.info("Extracting xml using SIMPLE method for xpath def: " + str(cnt))                                           
801                                        self.changeSimpleURLElementVal(thisData['xpath'])       
802                                       
803                        try:
804                                #rewrite this back into the xml doc                             
805                                self.etree.write(outputFile)
806                                logging.info("Have successfully generated new ISO file with rewritten urls to NDG redirect format!")
807                       
808                                return True
809               
810                        except:
811                                logging.error("Could not rewrite NDG url ISO xml to; " + outputFile)                   
812                                return False
813                                       
814                else:
815                        logging.info("URL's not set to change- therefore copy original file to discovery dir")
816
817                        try:
818                                FileUtilities.copyFileToDir(self.originalXMLfileName,outputFile)
819                                return True
820                        except:
821                                logging.error("Couldnt create a direct copy of original file in discovery dir!")
822                                return False
823               
824               
825        '''
826        Method to change an ISO element val - simple xpath value
827        '''
828        def changeSimpleURLElementVal(self, xpath):
829               
830                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
831                xpathNS = self.appendNameSpace(xpath)           
832               
833                resElementVal = []
834               
835                try:   
836                        rootEl = self.root.findall(xpathNS)
837                       
838                except:
839                        logging.error("Could not find element for this xpath: " + xpath)                       
840                       
841                                               
842                #use a text 'None' rather than a None type                     
843                for elVal in rootEl:
844                        if elVal is None: 
845                                originalUrl = 'None' 
846                        else:
847                                try:                   
848                                        originalUrl = elVal.text       
849                                        redirectURL = self.redirectBaseURL + originalUrl + '&docID=' + urllib.quote(self.datasetID[0][0]) + '&docTitle=' + urllib.quote(self.datasetName[0][0])
850                               
851                                        #reassign...
852                                        elVal.text = redirectURL                                       
853                                        logging.info("Have successfuly rewritten ndg redirect URL: " + redirectURL)
854                                       
855                                except:
856                                        logging.error("Could not change original (simple) url!")
857               
858                       
859                #no need to return anything -if we rewrite the doc, any successful changes will be incorporated, otherwise will be ignored
860       
861        '''
862        Method to change an ISO element val - compound dependant xpath values
863        '''
864        def changeDependantURLElementVal(self,baseXpath,elXpath,depXpath,depValReqd):
865               
866                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
867                baseXpath = self.appendNameSpace(baseXpath)
868                               
869                #for path in baseXpaths:
870                try:                           
871                        rootEl = self.root.findall(baseXpath) #returns an elementree object of elements in a list                               
872                except:
873                        logging.error("Could not find element for this xpath: " + baseXpath)
874                        return 'None'
875                       
876                for el in rootEl:
877                        thisElXpth = self.appendNameSpace(elXpath)
878                                                               
879                        thisEl = self.doFindall(el,thisElXpth)
880                               
881                        #if there's a value for the actual xpath we want, go and get the dependant value
882                        if thisEl != 'None':
883                                       
884                                elVal = thisEl[0]
885                                elValTxt = elVal.text #NOTE take first one as we expect this to be the value we want
886                                                                       
887                                thisEldepXpth = self.appendNameSpace(depXpath)                                 
888                                thisDepEl = self.doFindall(el,thisEldepXpth)                                                           
889                                       
890                                if thisDepEl != 'None':
891                                                                               
892                                        depVal = thisDepEl[0].text
893                                                                       
894                                        if depVal == depValReqd:
895                                               
896                                                try:
897                                                        originalUrl = elValTxt                                         
898                                                        redirectURL = self.redirectBaseURL + originalUrl + '&docID=' + urllib.quote(self.datasetID[0][0]) + '&docTitle=' + urllib.quote(self.datasetName[0][0])
899                                                                                       
900                                                        #reassign...
901                                                        elVal.text = redirectURL                                                                                       
902                                                        logging.info("Have successfuly rewritten ndg redirect URL: " + redirectURL)
903                                                       
904                                                except:
905                                                        logging.error("Could not change original (complex) url!")
906                                       
907                                                                                                                       
908                #no need to return anything -if we rewrite the doc, any successful changes will be incorporated, otherwise will be ignored
909               
910       
911        '''
912        Method to initially interpret the xpath dictionary type passed
913        '''
914        def interpretTuple(self,keyMethod):
915               
916                dataStruct = {}
917                counter=0
918                for i in keyMethod:
919                        if type(i) is dict:
920                               
921                                #loop through the top level (remember its a dictionary of dictionaries!)
922                                for j in i.keys():                             
923                                        dataStruct[counter]=j
924                       
925                        #Only other data type present should be a string
926                        if type(i) is str:
927                                dataStruct[counter] = i
928                               
929                        counter = counter + 1
930                               
931                return dataStruct
932       
933               
934        '''
935        Method to aid passing and extraction info from complex xpath tuples
936       
937        Will work out whether complex or simple xpath extraction required depending on type of xpath method sent
938       
939        If 'order' key present then will return a dictionary of values with the extracted value using the specified key.
940       
941        '''
942        def getElementVal(self,keyMethod):
943               
944                #develop handler method to simplify
945                returnValList=[]                               
946                returnVal = 'None'
947               
948                logging.info("******************************************************************************************")
949                logging.info("Extracting xpath information for data extraction type:"  + keyMethod[0])
950                logging.info("******************************************************************************************")
951               
952                               
953                showValue = self.showXMLvalues
954               
955                #first need to interpret the contents of the tuple we've been passed everything is a dictionary in the tuple apart from element 0:
956                #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
957                dataStruct = self.interpretTuple(keyMethod)
958                '''
959                dataStruct = {}
960                counter=0
961                for i in keyMethod:
962                        if type(i) is dict:
963                               
964                                #loop through the top level (remember its a dictionary of dictionaries!)
965                                for j in i.keys():                             
966                                        dataStruct[counter]=j
967                       
968                        #Only other data type present should be a string
969                        if type(i) is str:
970                                dataStruct[counter] = i
971                               
972                        counter = counter + 1
973                '''
974               
975                #now iterate through the key list in dataStruct but miss First one (as its a name val!)                 
976                valueList = []
977                ordering = False
978               
979                cnt = 1
980               
981                for i in dataStruct.keys()[1:]:
982                                               
983                        thisData = keyMethod[i][dataStruct[i]]
984                                                               
985                        #is dicionary a dependant value or straight xpath?
986                        if 'baseXpath' in thisData.keys():
987                                logging.info("Extracting xml using COMPLEX dependant method for xpath def: " + str(cnt))                       
988                                returnVal = self.returnDependantElementVal(thisData['baseXpath'],thisData['elValXpath'],thisData['depValXpath'],thisData['depVal'],showValue)                           
989                                valueList.append(returnVal)
990                                                               
991                        if 'xpath' in thisData.keys():         
992                                logging.info("Extracting xml using SIMPLE method for xpath def: " + str(cnt))                                           
993                                returnVal =  self.returnSimpleElementVal(thisData['xpath'],showValue)                                                           
994                                valueList.append(returnVal)
995                                                               
996                        #is there any ordering info required - if so, order the return list according to the ordering tuple
997                        if 'order' in keyMethod[i].keys():
998                                logging.info("Returning a dictionary of paired values as ordering requested for xpath (number of xpath defs MUST match ordering vals available")                       
999                                ordering = True                         
1000                                orderingList = thisData
1001                               
1002                        else:
1003                                logging.info("Returning a simple list of values for xpath def: " + str (cnt))
1004                               
1005                        cnt = cnt + 1
1006                       
1007                #check theres any data..
1008                dataPresent=False
1009                for values in valueList:
1010                       
1011                        #as valueList is a list of lists, length of valueList will be > 1 even if no data in sub-lists..
1012                        #..so check through all subLists and if not data at all then can return None
1013                        if len(values) > 0:
1014                                dataPresent = True
1015                               
1016                if not dataPresent:                     
1017                        logging.warning("No data to extract from the xml for %s" %keyMethod[0])
1018                        return 'None'
1019                       
1020                #if ordering is required, create a corresponding dictionary of ordered values                                   
1021                if ordering:
1022                       
1023                        #logging.INFO("Ordering data by specified order information!")
1024                        logging.info("ordering information now!")
1025                       
1026                        '''FIRST need to group information together in correct order (i.e. elementtree groups all starts in one list, then all ends etc
1027                        i.e. [[start1,start2],[end1,end2]] to [[start1,end1],[start2,end2]]
1028                        '''
1029                        #import pdb
1030                        #pdb.set_trace()
1031                        #check that number of number of elements in ordering list is not less than number of items in list to be ordered!
1032                        #if multiple sets to be ordered, check numbers and return as dictionary
1033                        checkCompLnth = len(valueList[0])
1034                        for list in valueList:
1035                                if len(list) != checkCompLnth:
1036                                        logging.error("Sub component lists are of unequal length - CANNOT order!!")
1037                                        return 'None'
1038                               
1039                        #outer loop should be the number of elements in the sublists..
1040                        outer = []
1041                        for localPos in range(0,checkCompLnth):
1042                                inner=[]
1043                                #inner loop should be the number of lists corresponding to repeating subelements                       
1044                                for listPos in range(0,len(valueList)):
1045                                       
1046                                                inner.append(valueList[listPos][localPos])
1047                                #append to outer loop
1048                                outer.append(inner)                                                     
1049                       
1050                        for returnedList in outer:
1051                               
1052                                #create a disctionary for this round (will append it later to the return version)
1053                                orderedValsSub = {}
1054                                                                               
1055                                if len(returnedList) != len(orderingList):
1056                                        logging.error("Ordering List length does not match length of list to be ordered! (NOTE: can only order where lengths match!)")
1057                                        returnValList.append('None')
1058                               
1059                                else:           
1060                                        #go through valueList and extract values in correct position and build dictionary
1061                                        for i in orderingList.keys():
1062                                               
1063                                                orderedValsSub[i] = returnedList[orderingList[i]-1] # remember there's an offset as '0' not used in these vals
1064                               
1065                               
1066                                        returnValList.append(orderedValsSub)
1067                       
1068                        return returnValList
1069                       
1070                else:
1071                        return valueList
1072                       
1073                       
1074        '''
1075        Method to return a list of values of an element value dependant on another element value held within the local tree
1076        i.e. return a specific string val depending on a local code val in ISO
1077       
1078        if element exists and any dependant value present matches the expected value, the element value is returned as part of a list,
1079        otherwise an empty list is returned
1080        '''
1081        def returnDependantElementVal(self,baseXpath,elXpath,depXpath,depValReqd,showValue=False):
1082                       
1083                #Note using single path namespath appender rather than list version.. (keeps it simpler)       
1084                baseXpath = self.appendNameSpace(baseXpath)
1085               
1086               
1087                resDependantVal = []
1088               
1089                #for path in baseXpaths:
1090                try:                           
1091                        rootEl = self.root.findall(baseXpath) #returns an elementree object of elements in a list                               
1092                except:
1093                        logging.error("Could not find element for this xpath: " + baseXpath)
1094                        return 'None'
1095               
1096               
1097                for el in rootEl:
1098                       
1099                        thisElXpth = self.appendNameSpace(elXpath)                                             
1100                        thisEl = self.doFindall(el,thisElXpth)
1101                       
1102                        #if there's a value for the actual xpath we want, go and get the dependant value
1103                        if thisEl != 'None':
1104                               
1105                                #update to handle multiple values dependant on a single element value.. (based on bodc having multiple keywords in a single MD_keywords for Inspire themes                                     
1106                                #elVal = thisEl[0].text #NOTE take first one as we expect this to be the value we want
1107                                                                       
1108                                for elValIt in thisEl:
1109                                       
1110                                        elVal = elValIt.text
1111                               
1112                                        thisEldepXpth = self.appendNameSpace(depXpath)                                 
1113                                        thisDepEl = self.doFindall(el,thisEldepXpth)                                                           
1114                               
1115                                        if thisDepEl != 'None':
1116                                                                                                                       
1117                                                depVal = thisDepEl[0].text # still need a single dependant value
1118                                                                       
1119                                                if depVal == depValReqd:
1120                                               
1121                                                        if showValue is True:
1122                                                                logging.info("")
1123                                                                logging.info("          Element Value: " + elVal)
1124                                                                logging.info("")
1125                                                                                                               
1126                                                        resDependantVal.append(elVal)
1127                                               
1128                                                                                                                                               
1129                return resDependantVal
1130       
1131        '''
1132        Method to extract a value from the doc using a simple xpath.  If no element found or no data present then None is returned
1133        '''
1134        def returnSimpleElementVal(self,xpath,showValue=False):
1135                               
1136                attrib=False
1137               
1138                #are there any attributes required i.e. xpath uses "@" to get attribute name, elementtree uses the "attrib" method
1139                if '@' in xpath:
1140                        logging.info("Requires an attribute value...")
1141                       
1142                        attrib = True
1143                       
1144                        #get the attribute name to find(assume its the last term after the "@")
1145                        attribVal = xpath.split('@')[1:][0]
1146                        attribXpath = xpath.split('@')[0:][0]
1147                       
1148                        #Note using single path namespath appender rather than list version.. (keeps it simpler)       
1149                        xpathNS = self.appendNameSpace(attribXpath)
1150                       
1151                else:
1152                        xpathNS = self.appendNameSpace(xpath)
1153                               
1154                resElementVal = []
1155                               
1156                try:                   
1157                        rootEl = self.root.findall(xpathNS)
1158                       
1159                except:
1160                        logging.error("Could not find element for this xpath: " + xpath)                       
1161                        return ['None']
1162                                               
1163                #use a text 'None' rather than a None type
1164               
1165                for elVal in rootEl:
1166                        if elVal is None:
1167                                resElementVal.append('None')
1168                        else:
1169                                if attrib:
1170                                        value = elVal.attrib[attribVal]
1171                                else:
1172                                        value = elVal.text
1173                                       
1174                                if (showValue is True) and (value is not None):
1175                                        logging.info("")
1176                                        logging.info("          Element Value: " + value)
1177                                        logging.info("")
1178                                       
1179                                        resElementVal.append(value)
1180                '''     
1181                if rootEl[0].text is None:                                     
1182                        resElementVal.append('None')
1183                else:
1184                        for el in rootEl:                                       
1185                                resElementVal.append(el.text)   
1186                               
1187                                       
1188                '''
1189               
1190                return resElementVal
1191                               
1192       
1193        '''
1194        Method to run an elementtree findall on ns appended path in the supplied element and return list
1195        returns None if nothing found
1196        '''
1197        def doFindall(self,el,thisElXpth):
1198                               
1199                try:
1200                        thisElXpthEl = el.findall(thisElXpth)
1201                       
1202                        if len(thisElXpthEl) == 0:
1203                                thisElXpthEl = 'None'
1204                except:
1205                        thisElXpthEl = 'None'
1206               
1207               
1208                return thisElXpthEl
1209               
1210               
1211        '''
1212        Method to extract actual value from xml doc - expects a list of namespace qualified xpaths and will return corresponding list of values
1213        '''
1214        def getXmlVal(self,paths):
1215               
1216                xmlVals = []
1217                               
1218                for path in paths:
1219                        try:
1220                                xmlVals.append(self.root.find(path).text)                       
1221                        except:
1222                                logging.error("Could not extract value for xpath: " + path)
1223                                xmlVals.append('null')
1224                               
1225                return xmlVals                                 
1226               
1227        '''
1228        Method to hold a dictionary linking namespace prefixes to URLs
1229        '''
1230        def isoNameSpaces(self):
1231               
1232                isoNs = {'gmd':'http://www.isotc211.org/2005/gmd',
1233                                'gco':'http://www.isotc211.org/2005/gco',
1234                                'gmx':'http://www.isotc211.org/2005/gmx',
1235                                'gml':'http://www.opengis.net/gml/3.2',
1236                                'none':'','gts':'http://www.isotc211.org/2005/gts',
1237                                'gsr':'http://www.isotc211.org/2005/gsr',
1238                                'gss':'http://www.isotc211.org/2005/gss',
1239                                'srv':'http://www.isotc211.org/2005/srv',
1240                                'geonet':'http://www.fao.org/geonetwork'
1241                                }
1242               
1243                return isoNs
1244       
1245       
1246        '''
1247        Method to return DEFAULT namespace in elementtree format of ISO profile used (i.e. should be gmd)
1248        (to deal with those elements with no prefix)
1249        '''
1250        def defaultIsoNamespace(self):
1251                return 'gmd'
1252               
1253       
1254       
1255        '''
1256        Method to extract root element of XML file specified using elementtree
1257        '''     
1258        def getIsoXML(self,file):
1259               
1260                logging.info("Getting xml root element")
1261               
1262                self.originalXMLfileName = file         
1263               
1264                #parse the xml with elementtree
1265                try:
1266                       
1267                        self.etree=ET.parse(file)
1268                        root=self.etree.getroot() # should be the "gmd:MD_Metadata" element
1269                except:
1270                        logging.error("File %s appears to be bad XML!" %file)
1271                        return None
1272               
1273                #avoid ns0 and ns1 etc etc namespaces when rewriting this ET
1274                logging.info("Sorting out namespaces so can properly rewrite ISO xml..")
1275                for ns in self.isoNameSpaces().keys():         
1276                        if ns != 'None':                                               
1277                                ET._namespace_map[self.isoNameSpaces()[ns]] = ns
1278                               
1279                #check root element is an ISO one - use elementtree appended namespace..
1280                if root.tag != '{http://www.isotc211.org/2005/gmd}MD_Metadata':
1281                        logging.error("XML document does not appear to be ISO (not gmd:MD_Metadata)!!")                 
1282                        return None
1283       
1284                return root
1285               
1286               
1287        '''
1288        Method to convert the given xpath list to a namespace abbreviated version so elementtree can handle it (grrr.). 
1289        Will return a list of corresponding namespace qualified xpaths - if errors a 'null' will be placed.
1290        '''
1291        def appendNameSpaceList(self,paths):
1292               
1293                nameSpaceAppendedPaths = []
1294               
1295               
1296                for path in paths:             
1297                       
1298                        pathElements = path.split('/')
1299                       
1300                        #note due to crappy elementtree have to avoid initial "/"
1301                        count = 0
1302                        for element in pathElements:
1303                               
1304                                try:                                   
1305                                        if ':' in element:                                             
1306                                                splitElement = element.split(':')
1307                                                nsPrefix,nsElement = splitElement[0],splitElement[1]
1308                                        else:
1309                                                #use default namespace                                         
1310                                                nsPrefix = self.defaultIsoNamespace()
1311                                                nsElement = element
1312                                       
1313                                        if count == 0:
1314                                               
1315                                                #appendedPath = self.returnNS() + element
1316                                                appendedPath = '{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
1317                                        else:
1318                                               
1319                                                appendedPath = appendedPath + '/{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
1320                                       
1321                                        count += 1
1322                                except:
1323                                        appendedPath = 'null'
1324                                        logging.error("Could not change to elementtree ns xpath")
1325                                                       
1326                        #clear up any blank namespace prefixes
1327                        appendedPath = appendedPath.replace('{}','')
1328                       
1329                        nameSpaceAppendedPaths.append(appendedPath)
1330
1331                return nameSpaceAppendedPaths
1332       
1333        '''
1334        Method to convert the given xpath to a namespace abbreviated version so elementtree can handle it (grrr.). 
1335        Will return an equivalent namespace qualified xpath - if errors a 'null' will be placed.
1336        '''
1337        def appendNameSpace(self,path):
1338               
1339                nameSpaceAppendedPath = ''
1340               
1341                pathElements = path.split('/')
1342                       
1343                #note due to crappy elementtree have to avoid initial "/"
1344                count = 0
1345                for element in pathElements:
1346                               
1347                        try:                                   
1348                                if ':' in element:                                             
1349                                        splitElement = element.split(':')
1350                                        nsPrefix,nsElement = splitElement[0],splitElement[1]
1351                                else:
1352                                        #use default namespace                                         
1353                                        nsPrefix = self.defaultIsoNamespace()
1354                                        nsElement = element
1355                                       
1356                                if count == 0:
1357                                               
1358                                        #appendedPath = self.returnNS() + element
1359                                        appendedPath = '{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
1360                                else:                                           
1361                                        appendedPath = appendedPath + '/{' + self.isoNameSpaces()[nsPrefix] +'}' + nsElement
1362                                       
1363                                count += 1
1364                        except:
1365                                appendedPath = 'null'
1366                                logging.error("Could not change to elementtree ns xpath")
1367                                                       
1368                #clear up any blank namespace prefixes
1369                nameSpaceAppendedPath = appendedPath.replace('{}','')
1370               
1371                return nameSpaceAppendedPath
1372       
1373       
1374        '''
1375        Method to find which of the lists actually has data in it
1376        '''
1377        def findTheListData(self,xpathExtraction):
1378               
1379                cnt = 0
1380                mapping = []
1381               
1382                       
1383                for subList in xpathExtraction:
1384                        subListLen = len(subList)
1385                        if subListLen > 0:
1386                                #found where the data is, return sublist                               
1387                                mapping.append(cnt)     
1388                        cnt += 1                       
1389                                                               
1390                #how many values are there
1391                if len(mapping) == 1:
1392                        return mapping[0]
1393                else:
1394                        logging.warn("More than 1 value available - for now, have taken the first!")
1395                        return mapping[0]
1396               
1397               
1398       
1399       
1400       
Note: See TracBrowser for help on using the repository browser.