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

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

Add new workflow, when an atom title changes, to ensure that any
references to that atom, from other atoms, are updated appropriately

  • NB, references are done via link objects and these include the atom

title.

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