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

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

Updated to handle lower case dif format spec from info editor..

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
51                self.changeUrl()
52               
53                #rewrite this back into the xml doc     
54                self.etree.write(self.outputFilename)
55
56               
57        '''
58        Method to obtain and change selected URLs
59        '''
60        def changeUrl(self):
61               
62                #get document id
63                #note as appendNameSPace returns a list, must define which element, only one entry_id so 0 here... (java is so much better at this).
64               
65               
66                try:
67                        docTitle = self.root.find(self.appendNameSpace(self.datasetTitle())[0]).text
68                except:
69                        #doesnt matter too much if this doesnt work!
70                        docTitle = 'NOT_AVAILABLE'
71               
72                try:
73                       
74                        docID = self.root.find(self.appendNameSpace(self.getIdPath())[0]).text
75                       
76                        self.paths = self.appendNameSpace(self.URLpaths())
77                       
78                        for path in self.paths:
79                               
80                                nodes = self.root.findall(path)
81                               
82                                for node in nodes:
83                                       
84                                        currentURL = urllib.quote(node.text)
85                                       
86                                        redirectURL = self.redirectBaseURL + currentURL + '&docID=' + urllib.quote(docID) + '&docTitle=' + urllib.quote(docTitle)               
87                                       
88                                        node.text = redirectURL
89                                       
90                except:
91                       
92                        logging.warn("Cannot perform identityTransform to rewrite ndg redirect urls!")
93
94               
95                        #cannot rewrite doc so just leave doc as original..
96       
97       
98        '''
99        Method to convert the given xpath list to a namespace abbreviated version so elementtree can handle it (grrr.)
100        '''
101        def appendNameSpace(self,paths):
102               
103                nameSpaceAppendedPaths = []
104
105                for path in paths:                     
106                        pathElements = path.split('/')
107                       
108                        #note due to crappy elementtree have to avoid initial "/"
109                        count = 0
110                        for element in pathElements:
111                                if count == 0:
112                                        appendedPath = self.returnNS() + element
113                                else:
114                                        appendedPath = appendedPath + '/' + self.returnNS() + element
115                               
116                                count += 1
117                                                       
118                        nameSpaceAppendedPaths.append(appendedPath)
119
120                return nameSpaceAppendedPaths
121
122       
123        '''method to handle xpath to title for acceptable formats'''
124        def datasetTitle(self):
125                if (self.format == 'DIF') | (self.format == 'dif'):
126                        return ['Entry_Title']
127                elif self.format == 'MDIP':
128                        return ['Title']
129
130        '''method to handle xpath for id if for reqd format '''
131        def getIdPath(self):
132                if (self.format == 'DIF') | (self.format == 'dif'):
133                        return ['Entry_ID']
134                elif self.format == 'MDIP':
135                        return ['DatasetIdentifier']
136
137        '''method to handle xpath for expected URLS if for reqd format '''
138        def URLpaths(self):
139                if (self.format == 'DIF') | (self.format == 'dif'):
140                        return ['Related_URL/URL','Data_Center/Data_Center_URL', 'Data_Set_Citation/Online_Resource']
141                elif self.format == 'MDIP':
142                        return ['OnlineResource','Distributor/Web']
143
144        '''method to handle default namespaces for reqd format '''
145        def returnNS(self):
146                if (self.format == 'DIF') | (self.format == 'dif'):
147                        return '{http://gcmd.gsfc.nasa.gov/Aboutus/xml/dif/}' #Note that ns has to be encapsulated in {}'s!
148                elif self.format == 'MDIP':
149                        return '{http://www.oceannet.org/mdip/xml}'
150
151'''
152Class operating the identity transform of new ingest docs to change all urls to include a redirect via the ndg redirect service
153so can record all traffic from discovery service elsewhere.  Rewrites back to original filename so can continue with ingest
154'''
155class ndgRedirectTransform:
156
157    def __init__(self, xQueryType,ndgRedirect,dir):
158
159           
160        SAXON_JAR_FILE = 'lib/saxon9.jar'
161
162       
163        _repository_local_id = 'notneeded'
164        _local_id = 'notneeded'
165
166        xqueryLib = ndgResources()       
167        xquery = xqueryLib.createXQuery(xQueryType,dir, _repository_local_id, _local_id)
168        xquery = xquery.replace('INPUT_FILE',dir)
169        xquery = xquery.replace('NDG_REDIRECT_URL',ndgRedirect)
170
171        #generate the actual xqueryFile
172        xqFile = "redirectQuery_"+xQueryType+".xq"
173        FileUtilities.createFile(xqFile, xquery)
174       
175
176        # ensure the jar file is available - NB, this may be running from a different
177        # location - e.g. the OAIInfoEditor.lib.harvester - and this won't have the
178        # saxon file directly on its filesystem
179        jarFile = pkg_resources.resource_filename('OAIBatch','lib/saxon9.jar')
180
181        # Now do the transform
182        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:.')
183        xqCommand = "java -cp %s net.sf.saxon.Query %s !omit-xml-declaration=yes" %(jarFile, xqFile)
184        logging.debug("Running saxon command: " + xqCommand)
185        pipe = os.popen(xqCommand + " 2>&1")
186        output = pipe.read()
187        status = pipe.close()
188                       
189        if status is not None:
190            raise SystemError, 'Failed at running the XQuery'
191        else:
192            #write the output to a file for the rest of the ingest chain
193            FileUtilities.createFile(dir, output)
194       
195            logging.info("Have performed identityTransform on " + dir)
196       
197       
198'''
199Class representing a record to be deleted from the postgres DB.  Mimics the class "PostgresRecord" as passed to PostgresDAO
200This is so we can use the PostgresDAO class for DB manipulation
201
202Merged into Utilities June 2009 SJD
203'''   
204class RecordToDelete:
205   
206    '''
207    Class representing a document to be deleted from the postgres DB
208    @param filename: Name of the original_document_file   
209    '''
210   
211    def __init__(self, filename, discovery_ID):
212       
213       
214        #this relates to the "original_document_filename" column in the original_document table
215        if filename is not None:
216            self.filename = filename
217        else:
218            self.filename = "temporaryFilename"
219           
220        if discovery_ID is not None:
221            self.discovery_id = discovery_ID
222        else:
223            self.discovery_id = "temporaryDiscoveryID"
224       
225        # initialise the various record fields
226        self.db_id = None    # the DB ID of the record, for easy reference when it is created
227       
228
229class DatasetBasicParameters:
230   
231    '''
232    Class representing extracted parameters from the input file
233    @param filename: Name of the original_document_file   
234    '''
235   
236    def __init__(self, filename,format):
237       
238        logging.info("Retrieving identifier for metadata record " + filename + " in format: " + format)
239        xml=file(filename).read()
240               
241        self._datacentre_format = format
242        '''
243        #If ingesting DIF
244        if self._datacentre_format == "DIF":
245            d=DIF(xml)
246            self.datasetID=d.entryID
247            self.datasetName = d.datasetTitle
248            self.datacentreName = d.datacentreName
249            self.metadataCreationDate=d.metadataCreationDate
250           
251        #If ingesting MDIP           
252        elif self._datacentre_format == "MDIP":
253            d=MDIP(xml)           
254            self.datasetID=d.id
255            self.datasetName = d.datasetTitle
256            self.datacentreName = d.datacentreName
257            self.metadataCreationDate=d.metadataCreationDate
258           
259        else:
260            raise TypeError, "Only handles DIF or MDIP here."
261        '''
262       
263        et=loadET(xml)
264        helper=nsdumb(et)
265        if ((self._datacentre_format=='DIF') or (self._datacentre_format=='dif')):
266            #return helper.getText(et,'Entry_ID')
267            self.datasetID=helper.getText(et,'Entry_ID')
268            self.datasetName = helper.getText(et,'Data_Set_Citation/Dataset_Title')
269            self.datacentreName = helper.getText(et,'Data_Center/Data_Center_Name/Short_Name')
270            self.metadataCreationDate=helper.getText(et,'DIF_Creation_Date')
271            self.datasetStartDateNom = helper.getText(et,'Temporal_Coverage/Start_Date')
272            self.datasetEndDateNom = helper.getText(et,'Temporal_Coverage/Stop_Date')
273           
274            #Fudge to get around some DC's using "entry_title" and others "dataset_title".  grrr.
275            if self.datasetName == '':             
276             self.datasetName = helper.getText(et,'Entry_Title')
277               
278            #TODO amend this - just a fudge to ingest records from crappy badc/neodc whilst pipeline down;..
279            if self.datasetEndDateNom == '':
280             self.datasetEndDateNom = helper.getText(et,'Temporal_Coverage/End_Date')                       
281           
282        elif self._datacentre_format == 'MDIP':
283            #return helper.getText(self.tree,'DatasetIdentifier')
284            self.datasetID=helper.getText(et,'DatasetIdentifier')
285            self.datasetName = helper.getText(et,'DatasetIdentifier')
286            self.datacentreName = helper.getText(et,'Distributor/DistributorName/DistributorNameName')
287            self.metadataCreationDate=helper.getText(et,'DateOfUpdateOfMetadata')
288            self.datasetStartDateNom = helper.getText(et,'Date/DatasetStartDate')
289            self.datasetEndDateNom = helper.getText(et,'Date/DatasetEndDate')
290        else:
291            raise TypeError,'idget does not support datatype [%s]'%dataType
292           
293        #if no values for start or end dates need to set these to NULL!
294        if self.datasetStartDateNom == "":
295            self.datasetStartDateNom = 'NULL'
296               
297        if self.datasetEndDateNom == "":
298            self.datasetEndDateNom = 'NULL'
299
300import unittest
301
302class TestCase(unittest.TestCase):
303    """ Tests as required """
304
305    def testidget(self):
306        self.assertEqual(idget(self.difxml),'NOCSDAT192')
307   
308
309if __name__=="__main__":
310    unittest.main()
311
312
313
Note: See TracBrowser for help on using the repository browser.