source: TI01-discovery/branches/ingestAutomation-upgrade/OAIBatch/Utilities.py @ 6017

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI01-discovery/branches/ingestAutomation-upgrade/OAIBatch/Utilities.py
Revision 6017, 14.3 KB checked in by sdonegan, 10 years ago (diff)

Updated redirectUrlchanger class so deals with different input formats differently and escapes defined contenttpyes in dif

Line 
1from xml.etree import ElementTree as ET
2import logging,pkg_resources
3from ndg.common.src.lib.ETxmlView import loadET, nsdumb
4from ndg.common.src.models.ndgObject import ndgObject
5from ndg.common.src.lib.ndgresources import ndgResources
6import ndg.common.src.lib.fileutilities as FileUtilities
7
8def idget(xml,dataType='DIF'):
9    ''' Given an xml document (string), parse it using ElementTree and
10    find the identifier within it. Supports dataTypes of 'DIF' and 'MDIP'...
11    '''
12    et=loadET(xml)
13    helper=nsdumb(et)
14    if dataType=='DIF':
15        return helper.getText(et,'Entry_ID')
16    elif dataType == 'MDIP':
17        return helper.getText(self.tree,'DatasetIdentifier')
18    else:
19        raise TypeError,'idget does not support datatype [%s]'%dataType
20 
21import os, sys, logging
22
23'''
24        Class to handle identityTransform and correct UTF handling of parsable URLS for use in the ndg redirect service
25
26        Currently handles either DIF or MDIP input format
27
28        Steve Donegan June 2009 STFC RAL
29'''
30
31from xml.etree import ElementTree as ET
32import logging,urllib
33
34class redirectUrlChanger:
35
36       
37
38        ''' Reads XML vocab structure. '''
39        def __init__(self,filenameIP,outputFilenameIP,formatIP,redirectURLIP):
40               
41                self.format = formatIP
42                self.filename = filenameIP
43                self.outputFilename = outputFilenameIP
44                self.redirectBaseURL = redirectURLIP
45                               
46                #parse the xml with elementtree
47                self.etree=ET.parse(self.filename)
48                self.root=self.etree.getroot() # should be the "vocab" element
49
50                #look for and change relevant portions - fudge it here for now
51                self.difContentType = ["GET SERVICE"]
52       
53       
54                if self.format == 'DIF':
55                        self.changeUrl_dif()
56               
57                elif self.format == 'MDIP':
58                        self.changeUrl_mdip()
59           
60                #rewrite this back into the xml doc     
61                self.etree.write(self.outputFilename)
62
63
64        '''
65        Method to obtain and change selected URLs - for MEDIN vN input
66        '''
67        def changeUrl_medin_vN(self, contentType):
68
69                url = ''
70
71                return url
72
73               
74        '''
75        Method to obtain and change selected URLs - for MDIP input
76        '''
77        def changeUrl_mdip(self):
78
79                try:
80                        docTitle = self.root.find(self.appendNameSpace(self.datasetTitle())[0]).text
81                except:
82                        #doesnt matter too much if this doesnt work!
83                        docTitle = 'NOT_AVAILABLE'
84                       
85                try:
86                       
87                        docID = self.root.find(self.appendNameSpace(self.getIdPath())[0]).text
88                       
89                        self.paths = self.appendNameSpace(self.URLpaths_MDIP())
90                                               
91                       
92                        for path in self.paths:
93                                                                                                       
94                                nodes = self.root.findall(path) ##finds all related_url/url
95                               
96                                for node in nodes:
97                                       
98                                        currentURL = urllib.quote(node.txt)
99                                        redirectURL = self.redirectBaseURL + children[url] + '&docID=' + urllib.quote(docID) + '&docTitle=' + urllib.quote(docTitle)
100                                        node.text = redirectURL
101                       
102                except:
103                        print "Cannot perform identityTransform to rewrite ndg redirect urls on MDIP input format doc"
104                        logging.warn("Cannot perform identityTransform to rewrite ndg redirect urls on MDIP format doc")
105
106
107               
108        '''
109        Method to obtain and change selected URLs - for GCDM DIF input
110        '''
111        def changeUrl_dif(self):
112               
113                #get document id
114                #note as appendNameSPace returns a list, must define which element, only one entry_id so 0 here... (java is so much better at this).
115               
116                problem=False
117               
118                try:
119                        docTitle = self.root.find(self.appendNameSpace(self.datasetTitle())[0]).text
120                except:
121                        #doesnt matter too much if this doesnt work!
122                        docTitle = 'NOT_AVAILABLE'
123                        problem = True
124                               
125                try:
126                        docID = self.root.find(self.appendNameSpace(self.getIdPath())[0]).text
127                except:
128                        docID = 'NOT_AVAILABLE'
129                        problem = True
130               
131                #get available DIF paths
132                self.paths = self.appendNameSpace(self.URLpaths_DIF())
133               
134                for path in self.paths:
135                               
136                        try:
137                       
138                                nodes = self.root.findall(path) ##finds all related_url/url
139                       
140                                for node in nodes:
141                                                               
142                                        #have to handle different paths available for urls in difs differently - related_url is a complex type, and is
143                                        #the one we have to change depending on url_content_type so must do this differently to elsewhere
144                                        if path == self.appendNameSpace(['Related_URL'])[0]:
145                                                                                               
146                                                difChildElements = node.getiterator()
147                                                                                                                                               
148                                                #build dictionary of node name content pairs                                   
149                                                children={}
150                                                for child in difChildElements:
151                                                        children[child.tag] = child.text
152                                                       
153                                                #check for URL_Content_Type - these are the keys of the elements we want
154                                                content = self.appendNameSpace(['URL_Content_Type'])[0]
155                                                url = self.appendNameSpace(['URL'])[0] 
156                                                                                                                                                                                       
157                                                #go over the keys - if url_content_type is in there..           
158                                                if content in children.keys():                                                 
159                                                        urlContentType = children[content]
160                                                                                                               
161                                                        #using the values in contentType, check to see if present
162                                                        for ct in self.difContentType:
163                                                               
164                                                                               
165                                                                #..if a match then we actually WANT the original URL
166                                                                if ct not in urlContentType:                                                                           
167                                                                                                                                               
168                                                                        #if no match, work out redirect url
169                                                                        redirectURL = self.redirectBaseURL + children[url] + '&docID=' + urllib.quote(docID) + '&docTitle=' + urllib.quote(docTitle)
170                                                                       
171                                                                        #if this related_url element is to be changed, access the element directly as a node
172                                                                        urlNode = node.find(self.appendNameSpace(['URL'])[0])
173                                               
174                                                                        #now set the url to the redirected value                                                       
175                                                                        urlNode.text = redirectURL                                                     
176                                                                       
177                                                #if no url_content_type element present, continue to rewrite the url
178                                                else:
179                                                        urlNode = node.find(self.appendNameSpace(['URL'])[0])
180                                                        currentURL = urllib.quote(urlNode.text)
181                                                        redirectURL = self.redirectBaseURL + children[url] + '&docID=' + urllib.quote(docID) + '&docTitle=' + urllib.quote(docTitle)
182                                                        urlNode.text = redirectURL
183                                                       
184                                                                                                                                                                               
185                                        #other paths of non related_url complex type
186                                        else:                                           
187                                                currentURL = urllib.quote(node.text)
188                                                redirectURL = self.redirectBaseURL + children[url] + '&docID=' + urllib.quote(docID) + '&docTitle=' + urllib.quote(docTitle)
189                                                node.text = redirectURL
190                       
191                        except:
192                                print "Cannot perform identityTransform to rewrite ndg redirect urls!"
193                                logging.warn("Cannot perform identityTransform to rewrite ndg redirect urls!")
194
195               
196                        #cannot rewrite doc so just leave doc as original..
197       
198       
199        '''
200        Method to convert the given xpath list to a namespace abbreviated version so elementtree can handle it (grrr.)
201        '''
202        def appendNameSpace(self,paths):
203               
204                nameSpaceAppendedPaths = []
205
206                for path in paths:                     
207                        pathElements = path.split('/')
208                       
209                        #note due to crappy elementtree have to avoid initial "/"
210                        count = 0
211                        for element in pathElements:
212                                if count == 0:
213                                        appendedPath = self.returnNS() + element
214                                else:
215                                        appendedPath = appendedPath + '/' + self.returnNS() + element
216                               
217                                count += 1
218                                                       
219                        nameSpaceAppendedPaths.append(appendedPath)
220
221                return nameSpaceAppendedPaths
222
223       
224        '''method to handle xpath to title for acceptable formats'''
225        def datasetTitle(self):
226                if self.format == 'DIF':
227                        return ['Entry_Title']
228                elif self.format == 'MDIP':
229                        return ['Title']
230
231        '''method to handle xpath for id if for reqd format '''
232        def getIdPath(self):
233                if self.format == 'DIF':
234                        return ['Entry_ID']
235                elif self.format == 'MDIP':
236                        return ['DatasetIdentifier']
237       
238        '''method to handle xpath for expected URLS if for reqd format '''
239        def URLpaths_DIF(self):
240                return ['Related_URL','Data_Center/Data_Center_URL']
241               
242        def URLpaths_MDIP(self):               
243                return ['OnlineResource','Distributor/Web']
244
245        '''method to handle default namespaces for reqd format '''
246        def returnNS(self):
247                if self.format == 'DIF':
248                        return '{http://gcmd.gsfc.nasa.gov/Aboutus/xml/dif/}' #Note that ns has to be encapsulated in {}'s!
249                elif self.format == 'MDIP':
250                        return '{http://www.oceannet.org/mdip/xml}'
251
252'''
253Class operating the identity transform of new ingest docs to change all urls to include a redirect via the ndg redirect service
254so can record all traffic from discovery service elsewhere.  Rewrites back to original filename so can continue with ingest
255'''
256class ndgRedirectTransform:
257
258    def __init__(self, xQueryType,ndgRedirect,dir):
259
260           
261        SAXON_JAR_FILE = 'lib/saxon9.jar'
262
263       
264        _repository_local_id = 'notneeded'
265        _local_id = 'notneeded'
266
267        xqueryLib = ndgResources()       
268        xquery = xqueryLib.createXQuery(xQueryType,dir, _repository_local_id, _local_id)
269        xquery = xquery.replace('INPUT_FILE',dir)
270        xquery = xquery.replace('NDG_REDIRECT_URL',ndgRedirect)
271
272        #generate the actual xqueryFile
273        xqFile = "redirectQuery_"+xQueryType+".xq"
274        FileUtilities.createFile(xqFile, xquery)
275       
276
277        # ensure the jar file is available - NB, this may be running from a different
278        # location - e.g. the OAIInfoEditor.lib.harvester - and this won't have the
279        # saxon file directly on its filesystem
280        jarFile = pkg_resources.resource_filename('OAIBatch','lib/saxon9.jar')
281
282        # Now do the transform
283        os.putenv ('PATH', ':/usr/java/jdk1.5.0_03/bin:/usr/java/jdk1.5.0_03:/usr/java/jdk1.5.0_03/lib/tools.jar:/usr/local/WSClients/OAIBatch:/usr/local/exist-client/bin:/bin:/usr/bin:.')
284        xqCommand = "java -cp %s net.sf.saxon.Query %s !omit-xml-declaration=yes" %(jarFile, xqFile)
285        logging.debug("Running saxon command: " + xqCommand)
286        pipe = os.popen(xqCommand + " 2>&1")
287        output = pipe.read()
288        status = pipe.close()
289                       
290        if status is not None:
291            raise SystemError, 'Failed at running the XQuery'
292        else:
293            #write the output to a file for the rest of the ingest chain
294            FileUtilities.createFile(dir, output)
295       
296            logging.info("Have performed identityTransform on " + dir)
297       
298       
299'''
300Class representing a record to be deleted from the postgres DB.  Mimics the class "PostgresRecord" as passed to PostgresDAO
301This is so we can use the PostgresDAO class for DB manipulation
302
303Merged into Utilities June 2009 SJD
304'''   
305class RecordToDelete:
306   
307    '''
308    Class representing a document to be deleted from the postgres DB
309    @param filename: Name of the original_document_file   
310    '''
311   
312    def __init__(self, filename, discovery_ID):
313       
314       
315        #this relates to the "original_document_filename" column in the original_document table
316        if filename is not None:
317            self.filename = filename
318        else:
319            self.filename = "temporaryFilename"
320           
321        if discovery_ID is not None:
322            self.discovery_id = discovery_ID
323        else:
324            self.discovery_id = "temporaryDiscoveryID"
325       
326        # initialise the various record fields
327        self.db_id = None    # the DB ID of the record, for easy reference when it is created
328       
329
330class DatasetBasicParameters:
331   
332    '''
333    Class representing extracted parameters from the input file
334    @param filename: Name of the original_document_file   
335    '''
336   
337    def __init__(self, filename,format):
338       
339        logging.info("Retrieving identifier for metadata record " + filename + " in format: " + format)
340        xml=file(filename).read()
341               
342        self._datacentre_format = format
343        '''
344        #If ingesting DIF
345        if self._datacentre_format == "DIF":
346            d=DIF(xml)
347            self.datasetID=d.entryID
348            self.datasetName = d.datasetTitle
349            self.datacentreName = d.datacentreName
350            self.metadataCreationDate=d.metadataCreationDate
351           
352        #If ingesting MDIP           
353        elif self._datacentre_format == "MDIP":
354            d=MDIP(xml)           
355            self.datasetID=d.id
356            self.datasetName = d.datasetTitle
357            self.datacentreName = d.datacentreName
358            self.metadataCreationDate=d.metadataCreationDate
359           
360        else:
361            raise TypeError, "Only handles DIF or MDIP here."
362        '''
363       
364        et=loadET(xml)
365        helper=nsdumb(et)
366        if ((self._datacentre_format=='DIF') or (self._datacentre_format=='dif')):
367            #return helper.getText(et,'Entry_ID')
368            self.datasetID=helper.getText(et,'Entry_ID')
369            self.datasetName = helper.getText(et,'Data_Set_Citation/Dataset_Title')
370            self.datacentreName = helper.getText(et,'Data_Center/Data_Center_Name/Short_Name')           
371            self.datasetStartDateNom = helper.getText(et,'Temporal_Coverage/Start_Date')
372            self.datasetEndDateNom = helper.getText(et,'Temporal_Coverage/Stop_Date')
373           
374            #need to make sure that latest date, eother from creation or last revision is present.
375            if helper.getText(et,'Last_DIF_Revision_Date') != '':
376                self.metadataCreationDate=helper.getText(et,'Last_DIF_Revision_Date')               
377            else:               
378                self.metadataCreationDate=helper.getText(et,'DIF_Creation_Date')
379               
380            #Fudge to get around some DC's using "entry_title" and others "dataset_title".  grrr.
381            if self.datasetName == '':
382             self.datasetName == helper.getText(et,'Entry_Title')
383               
384            #TODO amend this - just a fudge to ingest records from crappy badc/neodc whilst pipeline down;..
385            if self.datasetEndDateNom == '':
386             self.datasetEndDateNom = helper.getText(et,'Temporal_Coverage/End_Date')                       
387           
388        elif self._datacentre_format == 'MDIP':
389            #return helper.getText(self.tree,'DatasetIdentifier')
390            self.datasetID=helper.getText(et,'DatasetIdentifier')
391            self.datasetName = helper.getText(et,'DatasetIdentifier')
392            self.datacentreName = helper.getText(et,'Distributor/DistributorName/DistributorNameName')
393            self.metadataCreationDate=helper.getText(et,'DateOfUpdateOfMetadata')
394            self.datasetStartDateNom = helper.getText(et,'Date/DatasetStartDate')
395            self.datasetEndDateNom = helper.getText(et,'Date/DatasetEndDate')
396        else:
397            raise TypeError,'idget does not support datatype [%s]'%dataType
398           
399        #if no values for start or end dates need to set these to NULL!
400        if self.datasetStartDateNom == "":
401            self.datasetStartDateNom = 'NULL'
402               
403        if self.datasetEndDateNom == "":
404            self.datasetEndDateNom = 'NULL'
405
406import unittest
407
408class TestCase(unittest.TestCase):
409    """ Tests as required """
410
411    def testidget(self):
412        self.assertEqual(idget(self.difxml),'NOCSDAT192')
413   
414
415if __name__=="__main__":
416    unittest.main()
417
418
419
Note: See TracBrowser for help on using the repository browser.