source: MILK/trunk/milk_server/milk_server/controllers/atom_editor/editatom.py @ 4697

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

Add delete function to atom editor to delete granule atoms - together
with warning messages beforehand and result pop up messages afterwards.
Adjust granulite to expose the delete functions publicly.

Line 
1'''
2 Class representing pylons controller for the creation and editing of atom
3 data
4 
5 @author: C Byrom, Tessella Sep 2008
6'''
7import logging, traceback, sys, cgi, xmlrpclib
8from paste.request import parse_querystring
9from xml.parsers.expat import ExpatError
10from formencode import Invalid
11from genshi.filters import HTMLFormFiller
12from genshi import HTML
13from milk_server.lib.base import *
14from milk_server.models.form import *
15from milk_server.lib import mailer
16from milk_server.lib.ndgInterface import ndgInterface
17import milk_server.lib.htmlUtilities as utils
18from milk_server.lib.atomutilities import savePageAndRender
19from ndgUtils import ndgObject
20from ndgUtils.models.Atom import Atom, Person, Link, Category
21from ndgUtils.eXistConnector import eXistConnector
22from ndgUtils.lib.atomvalidator import ValidationError
23import ndgUtils.lib.existdbclient as edc
24from ndgUtils.models.MolesEntity import MolesEntity as ME
25from ndgUtils.lib.utilities import escapeSpecialCharacters
26from ndgUtils.models.vocabtermdata import VocabTermData as VTD
27from ndgUtils.lib.granulite import granulite
28from editorconstants import *
29
30class EditatomController(BaseController):
31    '''
32    Provides the pylons controller for editing NDG Atom documents.
33    '''
34    ADD_ASSOCIATIONS = 3
35    REMOVE_ASSOCIATIONS = 4
36   
37    def __setup(self,uri=None):
38        ''' Common setup stuff for all the actions on this controller '''
39        logging.info("Setting up EditatomController")
40        self.cf=request.environ['ndgConfig']
41
42        if uri:
43            try:
44                self.ndgObject = ndgObject(uri, config=self.cf)
45            except ValueError,e:
46                return e
47
48        self.inputs=dict(parse_querystring(request.environ))
49
50        logging.info("EditatomController set up")
51        return 0
52
53
54    def __unpackErrors(self, e):
55        '''
56        Add exception errors to the common error structures - for use
57        in templates
58        @param e: Exception to add
59        '''
60        errorMessage = e.message
61        if g.debugAtomEditor:
62            errorMessage = traceback.format_exc()
63
64        c.xml = escapeSpecialCharacters('Unexpected error loading page [%s]' \
65                                        %str(errorMessage))
66        c.doc = ''
67       
68        # unpack errors, if possible - NB, the c.errors value is used by the template
69        # function, displayErrors, which is used by most of the editor templates
70        if isinstance(e, xmlrpclib.Fault):
71            # strip out the exception type - NB, this is usually native library code
72            # and is of no real interest - and will just confuse viewers
73            c.errors['Unexpected error'] = e.faultString.split(':')[-1] 
74        if hasattr(e, 'unpack_errors'):
75            c.errors.update(e.unpack_errors())
76           
77        else:
78            c.errors['Unexpected error'] = [c.xml]
79
80        # tidy up errors - escaping any xml tags, if necessary
81        for key, errors in c.errors.items():
82            newErrors = []
83            for error in errors:
84                for err in error.split('<br/>'):
85                    newErrors.append(escapeSpecialCharacters(err))
86            c.errors[key] = newErrors
87
88
89    def __handleError(self, e, template='atom_editor/error'):
90        '''
91        Handle exceptions thrown; if debug mode on, display full stack trace
92        in output, otherwise just show basic error message in error template
93        @param e: Exception to process
94        @keyword template: template to render - 'error' is the default - NB, if
95        an alternative is specified it should have a div with class set to 'error'
96        containing the variable, c.xml to display properly
97        '''
98        self.__unpackErrors(e)
99        logging.error(c.xml)
100        response.status_code = 400
101        return render("genshi", template)
102
103   
104    def upload(self, uri):
105        '''
106        Upload a CSML, CDML or granulite file and store it in the session variable
107        NB, if the uri is specified, we're already dealing with an atom
108        (which this refers to) - so the file is not a granulite - since
109        this is used to create an atom from scratch
110        '''
111        logging.info("Uploading file...")
112        self.__setup(uri=uri)
113        inputs = self.__getTidyInputs()
114       
115        granFile = request.POST.get('upload_granulite')
116        csmlOrCdmlFile = request.POST.get('CSMLOrCDML')
117       
118        # check whether we can replace existing atoms
119        replaceAtom = inputs.get('replaceAtom')
120       
121        # NB, need to turn from string to boolean - there doesn't seem a reliable
122        # way of doing this using built in methods - so just do simple check
123        if replaceAtom == 'True':
124            replaceAtom = True
125        else:
126            replaceAtom = False
127       
128        # if this is true, then re-extract the inputs from the session data
129        if replaceAtom:
130            if session.get(GRAN_FILE_VALUE):
131                granFile = cgi.FieldStorage()
132                granFile.value = session.get(GRAN_FILE_VALUE)
133                granFile.filename = session.get(GRAN_FILE_NAME)
134                del session[GRAN_FILE_VALUE]
135                del session[GRAN_FILE_NAME]
136               
137            if session.get(CSML_OR_CDML_FILE_VALUE):
138                csmlOrCdmlFile = cgi.FieldStorage()
139                csmlOrCdmlFile.value = session.get(CSML_OR_CDML_FILE_VALUE)
140                csmlOrCdmlFile.filename = session.get(CSML_OR_CDML_FILE_NAME)
141                del session[CSML_OR_CDML_FILE_VALUE]
142                del session[CSML_OR_CDML_FILE_NAME]
143         
144        c.errors = {}
145        try:
146            logging.info("Validating inputs")
147            validator = LoadGranuliteFormSchema()
148            validator.to_python(inputs)
149            logging.info("- inputs valid")
150           
151            useCSMLID = True
152            c.atom = None
153            if uri:
154                self.prepareDataModel(uri)
155                useCSMLID = False
156
157            if (granFile == '' or granFile == None) and \
158                (csmlOrCdmlFile == '' or csmlOrCdmlFile == None):
159                errorMessage = "Error: could not load file - please try again"
160                logging.error(errorMessage)
161                raise IOError(errorMessage)
162            else:
163                # Prepare the basic data model
164                # NB, if loading a granulite, this will create the displayed atom
165                # with the ID taken from the CSML file, if specified
166                fileContents = None
167                if (granFile is not None and granFile != ''):
168                    fileContents = granFile.value
169                   
170                # use the granulite helper class to add either the full granulite
171                # data or just the CSML/CDML data
172                # NB, we'll be creating the atom in the default local eXist
173                eXistClient = self.__getExistClient('local')
174                gran = granulite(fileContents, granuleAtom = c.atom, \
175                                 eXistClient = eXistClient, \
176                                 csmlOrCdmlFile = csmlOrCdmlFile, \
177                                 timeAxis = inputs.get('timeAxis'), \
178                                 datasetID = inputs.get('datasetID'), \
179                                 useCSMLID = useCSMLID, \
180                                 replaceAtom = replaceAtom)
181
182                # now process the input file and add any extra required data
183                if uri:
184                    c.atom = gran.processCSMLOrCDMLFile()
185
186                    # save new data - NB, for granulites, this is done as part of the
187                    # processing steps
188                    self.saveAtomToExist(c.atom)
189                else:
190                    try:
191                        c.atom = gran.processGranulite()
192
193                        # Now set up the ndgObject with the created atom's vals
194                        self.__setup(uri=c.atom.ndgURI)
195                        c.atom.ndgObject = self.ndgObject
196                    except edc.DuplicateError, e:
197                        # we've found an existing atom with the same ID
198                        # - give the users the choice of replacing the contents of this atom
199                        # or just exiting
200                        # - NB, do this via a session variable to act as a flag
201                        # for a javascript command
202                        session[OVERWRITE_GRANULE_FLAG] = e.message
203                       
204                        # store the inputs data for easy retrieval
205                        # - NB, file fields don't behave as text fields - for security
206                        # purposes - so need to store their data as session variables
207                        # for easy retrieval
208                        # - Also, cannot pickle the cgi.FieldStorage object so extract
209                        # picklable data and recreate on the return run
210                        if granFile != '':
211                            session[GRAN_FILE_VALUE] = granFile.value
212                            session[GRAN_FILE_NAME] = granFile.filename
213                        if csmlOrCdmlFile != '':
214                            session[CSML_OR_CDML_FILE_VALUE] = csmlOrCdmlFile.value
215                            session[CSML_OR_CDML_FILE_NAME] = csmlOrCdmlFile.filename
216                       
217                        # need to return to original screen - so clear out variables
218                        c.atom = None
219                        uri = None
220                           
221                # now do redirection - NB, this ensures that current atom contents are
222                # reloaded and displayed
223                logging.info("File data loaded and extracted to atom")
224        except Invalid, e:
225            logging.info(" - inputs invalid")
226            c.errors = e.unpack_errors()
227        except Exception, e:
228            c.errors['WARNING'] = ['Error loading data: the displayed data will not be saved - please fix problem and retry']
229            self.__unpackErrors(e)
230        except SystemExit, ee:
231            # NB, some of the CSML libraries just sys.exit on problems - catch errors here
232            c.errors['ERROR'] = ['Problem encountered whilst transforming the CDML data into CSML']
233            self.__unpackErrors(ee)
234
235        if c.atom and hasattr(c.atom, 'ndgURI'):
236            self.pathInfo = self.pathInfo.replace('upload', 'editAtom')
237            return self.edit(c.atom.ndgURI)
238        elif uri:
239            # something has gone wrong here...
240            return render("genshi", 'atom_editor/error')
241        else:
242            return self.createGranule(**inputs)
243
244
245    def __getTidyInputs(self):
246        '''
247        The inputs can be used generically to specify atom attributes - however
248        some inputs should not be set against the Atom object since they use slots
249        so cannot be pickled - which means saving the atom in the session with fail.
250        This method clears out any unwanted inputs
251        '''
252        logging.debug("Getting pickleable input data")
253        inputs = request.params
254        tidyInputs = {}
255        for key, val in inputs.items():
256            if not isinstance(val, cgi.FieldStorage):
257                tidyInputs[key] = val
258        logging.debug("Pickleable data extracted")
259        return tidyInputs
260
261   
262    def saveAtom(self, uri, saveLevel=0):
263        '''
264        Save the atom contents - NB, validation is done by method decoration
265        - if this fails, the action is reran as a GET with htmlfill auto-populating
266        the fields to keep them as they were before the submission
267        '''
268        logging.info("Saving input atom data")
269        c.errors = {}
270        try:
271            self.prepareDataModel(uri)
272        except SystemError, e:
273            return self.__handleError(e)
274       
275        inputs = request.params
276
277        # save atom association changes
278        if int(saveLevel) == self.ADD_ASSOCIATIONS:
279            atomLinks = self.extractAtomAssociations(inputs)
280            c.atom.addUniqueRelatedLinks(atomLinks)
281        elif int(saveLevel) == self.REMOVE_ASSOCIATIONS:
282            atomLinks = self.extractAtomAssociations(inputs)
283            c.atom.removeRelatedLinks(atomLinks)
284        else:
285            authors = self.extractAuthorDetails(inputs)
286            c.atom.addAuthors(authors)
287   
288            onlineRefs = self.extractOnlineReferenceDetails(inputs)
289            c.atom.addOnlineReferences(onlineRefs)
290
291            params = self.extractParameterDetails(inputs)
292            # NB, the atom type and subtype are added to the categories when the
293            # atom is exported to XML - so don't need to worry about overwriting
294            # them now
295            c.atom.parameters = params
296           
297            if inputs.get('subtype'):
298                c.atom.subtype = self.getLatestTermURLFromDropDownInput( \
299                        inputs.get('subtype'))
300                c.atom.subtypeID = c.atom.subtype.split('/')[-1]
301
302        logging.info("Validating input")
303        try:
304            g.validator.setAtom(c.atom)
305            g.validator.validateAtom()
306            logging.info("- input valid")
307
308            self.saveAtomToExist(c.atom)
309        except Exception, e:
310            self.__unpackErrors(e)
311            logging.info("- input invalid")
312            return self.edit(uri)
313                   
314        # now do redirection - NB, this ensures that current atom contents are
315        # reloaded and displayed
316        h.redirect_to(controller = 'atom_editor/editatom', action='edit', \
317                        uri = c.atom.ndgURI)
318
319
320    def prepareDataModel(self, uri):
321        '''
322        Set up the underlying atom data model - loading the bulk from eXist
323        then updating any input fields appropriately
324        '''
325        logging.info("Preparing underlying data model")
326        status=self.__setup(uri=uri)
327        if status:
328            c.xml='<p>%s</p>'%status
329            response.status_code = 400
330            raise SystemError('Problem experienced setting up data model')
331
332        logging.info("Retrieving document to edit")
333        # NB, don't use the cache as docs are likely to change
334        # quite a lot during editing; ensure you've always got
335        # the latest updates loaded
336        interface = ndgInterface()
337        status,x = interface.GetXML(uri, useCache=False)
338
339        if not status:
340            code=400
341            if x.startswith('<p> Access Denied'):
342                code=401
343
344            c.xml='%s'%x
345            response.status_code = code
346            raise SystemError('Problem experienced retrieving atom doc from eXist')
347
348        # NB, passing in the inputs will overwrite any original values with the
349        # user input ones
350        inputs = self.__getTidyInputs()
351       
352        c.atom = Atom(xmlString=str(x), ndgObject = self.ndgObject, **dict(inputs))
353       
354        # save the current atom - to avoid this needing be recreated by the
355        # asynch viewAssociatedData call
356        session['currentAtom'] = c.atom
357        session.save()
358        logging.info("Data model set up")
359
360
361    def prepareEditForm(self, uri):
362        '''
363        Get everything set up for displaying the edit form
364        @param uri: ndg url for the atom to load in edit mode
365        '''
366        if not c.errors:
367            c.errors = {}
368
369        # NB, can get here directly from saveAtom - if there have been errors
370        # - in this case keep original data
371        if not c.atom:
372            self.prepareDataModel(uri)
373           
374        c.title= EDIT_TITLE %c.atom.ndgURI
375        c.uri = c.atom.ndgURI
376       
377        c.saveLink = h.url_for(controller='atom_editor/editatom',action='saveAtom', \
378                               saveLevel='1',  uri = c.atom.ndgURI)
379        c.saveLink2 = h.url_for(controller='atom_editor/editatom',action='saveAtom', saveLevel='2')
380        c.saveAssoc = h.url_for(controller='atom_editor/editatom',action='saveAtom', \
381                                 saveLevel = self.REMOVE_ASSOCIATIONS)
382        c.deploymentsURL = h.url_for(controller='browse/retrieve', \
383                                     action='viewAssociatedData', \
384                                     type = VTD.DEPLOYMENT_TERM, \
385                                     uri = c.atom.ndgURI)
386        c.dataEntitiesURL = h.url_for(controller='browse/retrieve', \
387                                     action='viewAssociatedData', \
388                                     type = VTD.DE_TERM, \
389                                     uri = c.atom.ndgURI)
390       
391        # adjust atom type to cope with activity deployment exception
392        atomType = c.atom.atomTypeID
393        if atomType == g.vtd.ACTIVITY_TERM and \
394            c.atom.subtypeID == g.vtd.DEPLOYMENT_TERM:
395            atomType = g.vtd.DEPLOYMENT_TERM
396       
397        c.addEntityLink = h.url_for(controller='atom_editor/listatom',action='list', searchData = '0', \
398                               associatedAtomID = c.atom.ndgURI, \
399                               associatedAtomType = atomType, 
400                               associationType = utils.ENTITY_ASSOCIATION)
401           
402        c.addGranuleLink = h.url_for(controller='atom_editor/listatom',action='list', searchData = '0', \
403                               associatedAtomID = c.atom.ndgURI, \
404                               associatedAtomType = atomType, 
405                               associationType = utils.GRANULE_ASSOCIATION)
406           
407        c.addDeploymentLink = h.url_for(controller='atom_editor/listatom',action='list', searchData = '0', \
408                               associatedAtomID = c.atom.ndgURI, \
409                               associatedAtomType = atomType, 
410                               associationType = utils.DEPLOYMENT_ASSOCIATION)
411
412        # account for special case where we're dealing with deployments
413        listVals = g.vtd.getValidSubTypes(c.atom.atomTypeID)
414        if c.atom.isDeployment():
415            listVals = [g.vtd.TERM_DATA[g.vtd.DEPLOYMENT_TERM]]
416
417        c.subTypes = utils.getVocabTermDataDropdown(listVals, \
418                                        selected=c.atom.subtype)
419       
420        self.addRelatedLinksDropDowns()
421
422
423    def delete(self, uri):
424        '''
425        Delete the atom associated with the specified uri - and return
426        user to the atom home page.  NB, only granule atoms can be deleted
427        at the moment.
428        '''
429        if uri:
430            try:
431                logging.info("Deleting atom, '%s'" %uri)
432                self.prepareDataModel(uri)
433                eXistClient = self.__getExistClient('local')
434                gran = granulite(None, granuleAtom = c.atom, \
435                                 eXistClient = eXistClient, \
436                                 deleteMode = True)
437   
438                gran.deleteGranuleAndDEReferences()
439                raise Exception('blah')
440                c.deleteResult = "Atom deleted successfully."
441                logging.info("- atom deleted")
442            except Exception, e:
443                logging.error("Problem occured whilst deleting atom: '%s'" %e.message)
444                c.deleteResult = "Warning: a problem occured whilst deleting the atom - this " + \
445                    "may have left the system in an unstable state - please check if the atom, or " + \
446                    "references to the atom still exist"
447
448        return render("genshi", "atom_editor/atom_home")
449       
450   
451    def edit(self, uri):
452        '''
453        Edit the specified uri
454        '''
455        logging.info("Setting up atom edit template")
456        try:
457            self.prepareEditForm(uri)
458            return savePageAndRender(self.pathInfo, "atom_editor/atom_editor")
459       
460        except ExpatError, e:
461            c.xml='XML content is not well formed'
462            c.doc=str(x)
463            logging.error("Error retrieving [%s] - XML content: %s" % (uri, e))
464        except SystemError, e:
465            return self.__handleError(e)
466        except Exception, e:
467            errorMessage = traceback.format_exc()
468            c.xml='Unexpected error [%s] viewing [%s]'%(str(e), uri)
469            c.doc=''
470            logging.error(c.xml)
471       
472        response.status_code = 400
473        return render("genshi", 'atom_editor/error')
474
475
476    def addRelatedLinksDropDowns(self):
477        '''
478        Set up the drop down lists required for the selection of online ref links
479        '''
480        # at the very least, we need a simple drop down list with no preselected
481        # values
482        logging.debug("Setting up drop down lists for related links")
483        c.relatedLinkTerms = utils.getVocabTermDataDropdown(\
484                g.vtd.getValidTypes(g.vtd.ONLINE_REF_CATEGORY))
485       
486        c.relatedLinkSelectedLists = {}
487        for link in c.atom.relatedLinks:
488            logging.debug("Adding dropdown for related link, '%s'" %(str(link)))
489            c.relatedLinkSelectedLists[str(link)] = \
490                utils.getVocabTermDataDropdown(g.vtd.getValidTypes(g.vtd.ONLINE_REF_CATEGORY), \
491                                                     selected=link.rel)
492
493        logging.debug("Finished setting up drop down lists")
494
495
496    def extractAuthorDetails(self, inputs):
497        '''
498        Retrieve author data from inputs and set appropriately on Atom, if any
499        found
500        @return: list of Person objects with the author data
501        '''
502        logging.info("Extracting author data from inputs")
503        processedAuthors = []
504        authors = []
505        for key in inputs:
506            keyBits = key.split('.')
507            if len(keyBits) == 3 and keyBits[1] not in processedAuthors:
508               
509                authorType = -1
510                if key.lower().startswith('author'):
511                    authorType = Person.AUTHOR_TYPE
512                elif key.lower().startswith('contributor'):
513                    authorType = Person.CONTRIBUTOR_TYPE
514                elif key.lower().startswith('responsible'):
515                    authorType = Person.RESPONSIBLE_PARTY_TYPE
516                else:
517                    continue
518
519                # NB, adding an empty object here is valid as it will clear out
520                # existing entries, potentially
521                author = Person(personType = authorType)
522                # check if the remove checkbox has been set
523                keyStem = ".".join(keyBits[0:2])
524                if inputs.get(keyStem + ".remove"):
525                    logging.info("Removing author data")
526                else:
527                    author.name = inputs.get(keyStem + '.name') or ""
528                    author.uri = inputs.get(keyStem + '.uri') or ""
529                    author.role = inputs.get(keyStem + '.role') or ""
530                   
531                    logging.info("Adding new author info")
532                    logging.debug("Extracted author (type:'%s', name:'%s', uri:'%s', role:'%s')" \
533                                  %(author.type, author.name, author.uri, author.role))
534                authors.append(author)
535                processedAuthors.append(keyBits[1])
536
537        logging.info("Finished extracting author data")
538        return authors
539
540
541    def extractOnlineReferenceDetails(self, inputs):
542        '''
543        Retrieve online reference data from inputs and set appropriately on Atom, if any
544        found
545        @return: list of Link objects containing the extracted data
546        '''
547        logging.info("Extracting related links data from inputs")
548        processedLinks = []
549        links = []
550
551        for key in inputs:
552            keyBits = key.split('.')
553            if len(keyBits) == 3 and keyBits[1] not in processedLinks:
554               
555                if key.lower().startswith(Atom.ONLINE_REF_LABEL):
556                    link = Link()
557                    keyStem = ".".join(keyBits[0:2])
558                   
559                    if inputs.get(keyStem + ".remove"):
560                        logging.info("Removing online reference data")
561                    else:
562                        # NB, this is in the format vocabURL--termID, so requires further
563                        # processing
564                        link.rel = self.getLatestTermURLFromDropDownInput(inputs.get(keyStem + '.rel'))
565                        link.href = inputs.get(keyStem + '.href') or ""
566                        link.title = inputs.get(keyStem + '.title') or ""
567                       
568                        if not link.hasValue():
569                            continue
570                           
571                        logging.info("Adding new online reference info")
572                        logging.debug("Extracted online reference (href:'%s', title:'%s', rel:'%s')" \
573                                      %(link.href, link.title, link.rel))
574                        links.append(link)
575
576                    processedLinks.append(keyBits[1])
577                else:
578                    continue
579
580        logging.info("Finished extracting links data")
581        return links
582
583
584    def extractParameterDetails(self, inputs):
585        '''
586        Retrieve parameters data from inputs and set appropriately on Atom, if any
587        found
588        @return: list of Category objects containing the extracted data
589        '''
590        logging.info("Extracting parameters data from inputs")
591        processedParameters = []
592        parameters = []
593
594        for key in inputs:
595            keyBits = key.split('.')
596            if len(keyBits) == 3 and keyBits[1] not in processedParameters:
597               
598                if key.lower().startswith(Atom.PARAMETER_LABEL):
599                    parameter = Category()
600                    keyStem = ".".join(keyBits[0:2])
601                   
602                    if inputs.get(keyStem + ".remove"):
603                        logging.info("Removing parameters data")
604                    else:
605                        parameter.term = inputs.get(keyStem + '.term') or ""
606                        parameter.scheme = inputs.get(keyStem + '.scheme') or ""
607                        parameter.label = inputs.get(keyStem + '.label') or ""
608                           
609                        logging.info("Adding new parameter info")
610                        logging.debug("Extracted parameter (vocabURL:'%s', label:'%s', term:'%s')" \
611                                      %(parameter.scheme, parameter.label, parameter.term))
612                        parameters.append(parameter)
613
614                    processedParameters.append(keyBits[1])
615                else:
616                    continue
617
618        logging.info("Finished extracting parameters data")
619        return parameters
620
621
622    def extractAtomAssociations(self, inputs):
623        '''
624        Retrieve atom data from inputs and create related links pointing to
625        this data
626        @return: list of Links representing the related atoms
627        '''
628        logging.info("Extracting related atom ID data from inputs")
629        atoms = []
630        processedAtoms = []
631
632        for key in inputs:
633            if key.lower().startswith(Atom.ATOM_REF_LABEL):
634                (x, href, title, rel) = key.split(Atom.DELIMITER)
635                # NB, we handle removes by effectively ignoring them later on
636                if href not in processedAtoms:
637                    processedAtoms.append(href)
638
639                    link = Link()
640                    link.href = href or ""
641                    link.title = title or ""
642                    link.rel = rel or ""
643                   
644                    # adjust href to point to the view, not the edit version
645                    link.href = link.href.replace('editAtom', 'view')
646                   
647                    logging.debug("Extracted atom info (href:'%s', title:'%s', rel:'%s')" \
648                                  %(link.href, link.title, link.rel))
649                    atoms.append(link)
650            else:
651                continue
652
653        logging.info("Finished extracting atoms data")
654        return atoms
655               
656
657    def getLatestTermURLFromDropDownInput(self, inputVal):
658        '''
659        Term ID and vocabURL are specified in the drop down menus
660        - using the input from this, return the lastest full href to the
661        term ID
662        '''
663        termData = inputVal.split('--')
664        return g.vtd.getCurrentVocabURI(termData[0]) + \
665                        "/" + termData[1]
666
667
668    def saveAtomToExist(self, atom):
669        '''
670        Save the specified atom in eXist
671        @param atom: atom object to save to eXist
672        @return atom: atom object saved in eXist
673        '''
674        logging.info("Saving changes to eXist")
675        eXist = self.__getExistClient(atom.ME.providerID)
676        createdAtom = eXist.createAtomInExist(atom)
677        logging.info("Changes successfully saved to eXist")
678        return createdAtom
679
680
681    def __getExistClient(self, providerID):
682        '''
683        Use the config data to set up and return an eXist client
684        '''
685        logging.info("Setting up eXist client to provider, '%s'" %providerID)
686        # firstly check if there is a current connection available
687        eXistClient = g.eXistDBCons.get(providerID)
688        if eXistClient:
689            logging.info("- found existing client to provider - returning this")
690            return eXistClient
691       
692        # lookup the eXist DB to use according to the provider ID for the
693        # data - NB, this is specified in the milk.config file
694        existHost = self.cf.get('NDG_EXIST', providerID)
695        configFile = self.cf.get('NDG_EXIST','passwordFile')
696        eXistClient = edc.eXistDBClient(eXistDBHostname = existHost, \
697                                       configFile = configFile)
698        # add the client to the global variables for re-use
699        g.eXistDBCons[providerID] = eXistClient
700        logging.info("Returning eXist client")
701        return eXistClient
702
703   
704    def create(self, saveData = None, **inputs):
705        '''
706        Create a new atom
707        '''
708        if saveData:
709            logging.info("Validating input")
710            try:
711                inputs = self.__getTidyInputs()
712                validator = CreateAtomFormSchema()
713                validator.to_python(inputs)
714                logging.info("- input valid")
715               
716                logging.info("Creating basic atom")
717                self.__setup()
718                atomTypeID = inputs.get('atomTypeID').split('--')[1]
719                inputs['atomTypeID'] = atomTypeID
720   
721                # activity deployments should have subtype deployment specified automatically
722                if atomTypeID == g.vtd.ACTIVITY_DEPLOYMENT_TERM:
723                    inputs['subtypeID'] = g.vtd.DEPLOYMENT_TERM
724                    inputs['atomTypeID'] = g.vtd.ACTIVITY_TERM
725                   
726                inputs['providerID'] = inputs.get('providerID').split('--')[1]
727                atom = self.saveAtomToExist(Atom(**dict(inputs)))
728               
729                h.redirect_to(controller = 'atom_editor/editatom', action='edit',
730                               uri = atom.ndgURI)
731            except Invalid, e:
732                c.errors = e.unpack_errors()
733               
734           
735        logging.info("Setting up atom create template")
736        c.title = CREATE_ATOM_TITLE
737       
738        # set up the drop down content - NB, add special case, 'deployment activity'
739        # - this is just a specialised activity - i.e. with subtype preset
740        c.atomTypes = utils.getVocabTermDataDropdown(g.vtd.getValidTypes(g.vtd.ATOM_CATEGORY),
741                                               selected = inputs.get('atomTypeID'))
742        c.providerIDs = utils.getVocabTermDataDropdown(
743                            g.vtd.getValidTypes(g.vtd.PROVIDER_CATEGORY), 
744                            selected = inputs.get('providerID'))
745
746        try:
747            return savePageAndRender(self.pathInfo, 'atom_editor/atom_creator', **inputs)
748
749        except Exception, e:
750            return self.__handleError(e)
751
752   
753    def createGranule(self, **inputs):
754        '''
755        Create a new atom from a granulite file
756        '''
757        logging.info("Setting up new atom from granulite template")
758        c.title='Create new data granule atom - from a granulite file'
759        c.errors = {}
760        try:
761            return savePageAndRender(self.pathInfo, 'atom_editor/atom_granulator', **inputs)
762
763        except Exception, e:
764            return self.__handleError(e)
Note: See TracBrowser for help on using the repository browser.