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

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

Extend granulite to allow command line operation - with input options
to specify logging level and 'replace atom' mode - which command line
inputs to ask users if they want to replace duplicated data.

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