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

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

Add workflow in controllers and templates to allow users to select
whether a CSML/granulite ingest, which will create an atom with an ID
that already exists, should continue.

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