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