source: MILK/trunk/milk_server/milk_server/controllers/browse/retrieve.py @ 4625

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/MILK/trunk/milk_server/milk_server/controllers/browse/retrieve.py@4625
Revision 4625, 11.3 KB checked in by cbyrom, 11 years ago (diff)

Improve error handling in the atom controllers + refactor ndgInterface
to remove code from constructor to allow dependency injection - to
improve flexibility and testability. Also, change ambiguous naming
references in classes to minimise codeword overaps/confusion.

Line 
1
2from milk_server.lib.base import *
3from ndgUtils import ndgObject
4from paste.request import parse_querystring
5from ndgUtils.models import stubB, DIF
6from milk_server.lib import mailer
7from milk_server.lib.ndgInterface import ndgInterface
8from ndgUtils.models.DIF import DIF
9from xml.parsers.expat import ExpatError
10import logging
11from ndgUtils.models.Atom import Atom
12from ndgUtils.vocabtermdata import VocabTermData as VTD
13from ndgUtils import DocumentRetrieve
14
15class RetrieveController(BaseController):
16    ''' Provides the pylons controller for retrieving NDG documents. The simple model
17    is now that an attempt to retrieve/uri will parse the uri, read the config file,
18    and if the local server name is not the same as the uri server name. '''
19   
20    def __setup(self,uri):
21        ''' Common setup stuff for all the actions on this controller '''
22        logging.info("Setting up RetrieveController")
23        self.cf=request.environ['ndgConfig']
24        try:
25            self.uri=ndgObject(uri, config=self.cf)
26        except ValueError,e:
27            return e
28
29        self.inputs=dict(parse_querystring(request.environ))
30
31        # NB: we could have two types of query string argument:
32        #   format= (raw,html) and
33        #   outputSchema=(original, someSchema)
34        self.format=''
35        if 'format' in self.inputs: 
36            self.format=self.inputs['format']
37        self.outputSchema=''
38        if 'outputSchema' in self.inputs: 
39            self.outputSchema=self.inputs['outputSchema']
40
41        # Add selected granules to context
42        #c.selectedGranules = [g_uri for g_uri, g_name in session.get('selection', [])]
43                             
44        logging.info("RetrieveController set up")
45        return 0
46       
47    def index(self,uri):
48        ''' Returns the document unadorned in anyway, i.e. the raw xml'''
49        status=self.__setup(uri)
50        if status:
51            c.xml='<p>%s</p>'%status
52            response.status_code = 400
53            return render('error')
54       
55        interface = ndgInterface()
56        status,x=interface.GetXML(uri,outputSchema=self.outputSchema)
57        if status:
58            #(the return object x is an xmlHandler object)
59            c.xml=x.xmls
60            response.headers['Content-Type'] = 'application/xml'
61            response.write(c.xml)
62        else:
63            e=404
64            if x.startswith('<p> Access'): e=401
65            response.status_code = e
66            return render('error')
67
68    def viewAssociatedData(self, type, uri):
69        '''
70        Get the specified data associated with the uri
71        @param type: type of associated data to lookup - currently VTD.DEPLOYMENT_TERM
72        or VTD.DE_TERM
73        @param uri: valid ndg uri
74        @return rendered genshi deployments template
75        '''
76        logging.info("Getting %s data for '%s'" %(type, uri))
77        status=self.__setup(uri)
78        if status:
79            c.xml='<p>%s</p>'%status
80            response.status_code = 400
81            return render('error')
82
83        try:
84            # NB, we should have stored the current atom data - so look this up now
85            atom = session.get('currentAtom')
86            if atom:
87                c.atom = atom
88            else:
89                interface = ndgInterface()
90                status,x=interface.GetXML(uri,outputSchema=self.outputSchema, useCache=False)
91                c.xmlh=x
92                c.atom = Atom(xmlString=str(x), ndgObject = self.uri)
93           
94            # NB, there are two routes through here - if we're viewing a data entity,
95            # the deployment links are available directly from the atom; if not we
96            # need to look them up
97            lookupIndirectReferences = True
98            if c.atom.isDE():
99                lookupIndirectReferences = False
100           
101            dr = DocumentRetrieve(self.cf.get('NDG_EXIST','local'),
102                      pwfile=self.cf.get('NDG_EXIST','passwordFile'))
103           
104            c.atom.lookupAssociatedData(type, dr, lookupIndirectReferences = \
105                                             lookupIndirectReferences)
106
107            template = 'atom_editor/deployments_data'
108            if type == VTD.DE_TERM:
109                template = 'atom_editor/data_entities_data'
110
111            # store the atom to retain the looked up info
112            session['currentAtom'] = c.atom
113            session.save()
114               
115            logging.info("- %s data retrieved - now rendering" %type)
116            return render('genshi', template)
117       
118        except ExpatError, e:
119            c.xml='XML content is not well formed'
120            c.doc=str(x)
121            response.status_code = 400
122            logging.error("Error retrieving [%s] - XML content: %s" % (uri, e))
123            return render('error')
124
125        except Exception, e:
126            #we may be showing an xml document ... but it could go wrong if
127            #we have crap content ...
128            c.xml='Unexpected error [%s] viewing [%s]'%(str(e), uri)
129            c.doc=''
130            response.status_code = 400
131            logging.error(c.xml)
132            return render('error')
133
134       
135       
136    def view(self,uri):
137        ''' Returns either an html marked up version of the xml, or a properly laid
138        out version of the document '''
139        c.doc=None
140        # NB, this sets up self.uri as an ndgObject
141        status=self.__setup(uri)
142        if status:
143            c.xml='<p>%s</p>'%status
144            response.status_code = 400
145            return render('error')
146
147        logging.info("Retrieving document to view")
148        useCache = True
149        viewFormat=self.uri.schema
150        # NB, don't use cache if we're looking up atoms as these can change quickly
151        # when using the atom editor
152        if viewFormat == ndgObject.ATOM_DOC_TYPE or \
153            viewFormat ==  ndgObject.ATOM_BACKUP_DOC_TYPE:
154            useCache = False
155           
156        interface = ndgInterface()
157        status,x=interface.GetXML(uri,outputSchema=self.outputSchema, useCache=useCache)
158        c.title='Viewing [%s]'%self.uri
159       
160        # NB, legacy code uses kid templates - newer stuff should use genshi
161        templateType = "kid"
162        if status:
163            logging.debug("Document retrieved ok - now processing results")
164            #(the return object x is an xmlHandler object)
165            if self.outputSchema!='': 
166                viewFormat=self.outputSchema
167
168            logging.debug("Doc format: %s, View format: %s" \
169                           %(self.format, viewFormat))
170
171            name=str(self.uri)
172            if self.format=='xml':
173                c.xml=x.tohtml()
174                renderTemplate = 'content'
175                c.tab='Details'
176
177            elif self.format=='raw':
178                c.xml=x.xmls
179                response.headers['Content-Type'] = 'application/xml'
180                return response.write(c.xml)
181               
182            elif viewFormat == ndgObject.ATOM_DOC_TYPE or \
183                viewFormat ==  ndgObject.ATOM_BACKUP_DOC_TYPE:
184                renderTemplate = 'atom_editor/atom_editor'
185                c.xmlh=x
186                c.atom = Atom(xmlString=str(x), ndgObject = self.uri)
187                c.title = 'Viewing [%s]'%self.uri
188                c.deploymentsURL = h.url_for(controller='browse/retrieve', \
189                                             action='viewAssociatedData', \
190                                             type = VTD.DEPLOYMENT_TERM, \
191                                             uri = self.uri)
192                c.dataEntitiesURL = h.url_for(controller='browse/retrieve', \
193                                             action='viewAssociatedData', \
194                                             type = VTD.DE_TERM, \
195                                             uri = self.uri)
196                templateType = "genshi"
197                # store the atom for easy re-use by asynch calls
198                # - primarily for the viewAssociatedData() method here
199                session['currentAtom'] = c.atom
200                session.save()
201
202            elif viewFormat == ndgObject.MOLES_DOC_TYPE:
203                renderTemplate = 'browse/stubB'
204                c.doc=stubB.stubB(x.tree,self.cf)
205                name=c.doc.abbreviation
206               
207            elif viewFormat == ndgObject.NDGA0_DOC_TYPE:
208                renderTemplate = 'content'
209                name=self.uri.localID
210                #for now we'll handle as pretty print xml
211                c.xml=x.tohtml()
212                c.tab='Details'
213               
214            elif viewFormat == ndgObject.DIF_DOC_TYPE:
215                renderTemplate = 'browse/dif'
216                c.doc=DIF.DIF(x.tree,et=1,ndgObj=self.uri)
217                name=c.doc.name
218               
219            elif viewFormat == ndgObject.NUMSIM_DOC_TYPE:
220                renderTemplate = 'browse/numsim'
221                c.xmlh=x
222                name=x.getText('NS_Name')
223                c.name=name
224               
225            else:
226                c.xml=x.tohtml()
227                renderTemplate = 'content'
228           
229            needed=0
230            if 'lastViewed' not in session: 
231                needed=1
232               
233            session['lastViewed']=h.current_url()
234            if needed: 
235                c.pageTabs.append(('Details',session['lastViewed']))
236           
237            session.save()
238            code=200
239        else:
240            renderTemplate = 'error'
241            if x.startswith('<p> Access Denied'):
242                code=401
243            else: code=400
244            c.xml='%s'%x
245            logging.error(x)
246           
247        response.status_code = code
248        try:
249            return render(templateType, renderTemplate)
250       
251        except ExpatError, e:
252            c.xml='XML content is not well formed'
253            c.doc=str(x)
254            response.status_code = 400
255            logging.error("Error retrieving [%s] - XML content: %s" % (uri, e))
256            return render('error')
257
258        except Exception, e:
259            #we may be showing an xml document ... but it could go wrong if
260            #we have crap content ...
261            c.xml='Unexpected error [%s] viewing [%s]'%(str(e), uri)
262            c.doc=''
263            response.status_code = 400
264            logging.error(c.xml)
265            return render('error')
266   
267    def askCorrect(self,uri):
268        ''' Provide a form (ajaxaciously) so that a user can correct a metadata record'''
269        status=self.__setup(uri)
270        c.uri=self.uri
271        if status: return response.write(status)
272        return render('correct',fragment=True)
273
274   
275    def correct(self,uri):
276        ''' Receive a correction form to update a record and post it to the holder '''
277        status=self.__setup(uri)
278        if 'ndgSec' not in session:
279            return response.write('Not Logged In')
280        body='Metadata Change Request for %s'%uri
281        payload='<Change><uri>%s</uri><from>%s</from><Contents>%s</Contents></Change>'%(
282            self.inputs['Identifier'],self.inputs['Submitter'],self.inputs['Comment'])
283        name='ChangeRequestFor_%s.xml'%uri
284        status,message=mailer.mailHandler([self.cf.get('DEFAULT','metadataMaintainer'),],
285                       body,body,xmlAttachments=[(payload,name),],
286                       server=self.cf.get('DEFAULT','mailserver'))
287        if status:
288            c.xml='<p>Success: Mail sent to metadata maintainer</p>'
289        else:
290            c.xml=message
291        return render('content')
Note: See TracBrowser for help on using the repository browser.