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

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

Clear out problems with the upload of granulite data workflow.

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