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

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

fixed bug in getting latest revision date & distinguishing from creation date

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