[Mpuls-commits] r5336 - base/trunk/mpulsweb/controllers
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Wed Sep 14 20:05:45 CEST 2011
Author: bh
Date: 2011-09-14 20:05:45 +0200 (Wed, 14 Sep 2011)
New Revision: 5336
Added:
base/trunk/mpulsweb/controllers/meta.py
Log:
Port the meta controller from WASKU to mpulsweb.
The controller was copied from WASKU web 415:9215fe60dbe6. It was copied
almost unchanged. All the changes involve imports:
- The MetaSearchForm validator is now imported from mpulsweb
- The agencysettings are also now imported from mpulsweb.
Importing the agencysettings from mpulsweb instead of waskuweb could be
a problem if there is code called from the meta controller that tries to
access waskuweb specific settings. This does not currently seem to be
the case.
Added: base/trunk/mpulsweb/controllers/meta.py
===================================================================
--- base/trunk/mpulsweb/controllers/meta.py 2011-09-14 17:55:27 UTC (rev 5335)
+++ base/trunk/mpulsweb/controllers/meta.py 2011-09-14 18:05:45 UTC (rev 5336)
@@ -0,0 +1,454 @@
+# -*- coding: utf-8 -*-
+# Authors:
+# Torsten Irländer <torsten.irlaender at intevation.de>
+
+'''Meta specfic logic'''
+
+import logging
+import formencode
+import hashlib
+
+from pylons.controllers.util import abort
+
+from mpulsweb.lib.translation import _, N_
+from mpulsweb.lib.base import BaseController, c, render, request, session
+from mpulsweb.lib.dialogs import confirm, success, error
+from mpulsweb.lib.helpers import url_for, format_date, escape
+from mpulsweb.lib.metaclient import MetaUnauthorized, UnknownMetaCase, \
+ UnknownProjectPart, MetaCaseAnonymized, MetaCasePending
+from mpulsweb.lib.validators import MetaSearchForm
+from mpulsweb.model.meta import MetaException, get_meta_client
+from mpulsweb.model.agencysettings import Agency
+
+
+log = logging.getLogger(__name__)
+
+
+def generate_hash(birthname, firstname, birthdate, gender):
+ '''Return hash build from birthname, firstname, birthdate and gender'''
+ hashsrc = ":".join([str(len(birthname)), str(len(firstname)),
+ birthdate.strftime('%Y-%m-%d'), str(gender)])
+ return hashlib.sha256(hashsrc.encode('utf-8')).hexdigest()
+
+
+def show_newmetcasedata(id):
+ return (_("<br>\n<br>\n"
+ "Birthname: %(birthname)s<br>\n"
+ "Firstname: %(firstname)s<br>\n"
+ "Birthdate: %(birthdate)s<br>\n"
+ "Gender: %(gender)s<br>\n"
+ "<br>")
+ % dict(birthname=escape(session['meta_birthname']),
+ firstname=escape(session['meta_firstname']),
+ birthdate=escape(format_date(session['meta_birthdate'])),
+ gender=escape(_("male") if session['meta_gender'] == "1"
+ else _("female"))))
+
+
+uuid_characters = set("0123456789abcdefABCDEF-")
+
+def validate_uuid(uuid):
+ return set(uuid).issubset(uuid_characters)
+
+
+class MetaController(BaseController):
+
+ '''Controller with meta specific actions.'''
+
+ def index(self, id):
+ '''Return a overview page for the meta object. Shows status on
+ permission or link. Provides access to varios actions for the meta
+ case.'''
+ if id != session.get('meta_caseid'):
+ session['meta_caseid'] = id
+ session['meta_discretion_statement'] = False
+ session['meta_acceptance_statement'] = False
+ session.save()
+ c.case = self._loadCase(id)
+ c.meta = c.case.getMeta()
+ c.agency = Agency()
+ return render('/meta/index.mako')
+
+ # Allow/Disallow sync
+ def print_statement1(self, id):
+ '''Returns a rendered statement of the agreement statement to
+ sychronize data with the meta case'''
+ c.case = self._loadCase(id)
+ c.agency = Agency()
+ return render('/meta/statement1.mako')
+
+ @confirm(header=N_('Set acceptance statement?'),
+ text=N_('Do you really want to mark the acceptance statement as set. This requires a printable and signed acceptance statement from the client.'))
+ def set_statement1(self, id):
+ '''Returns a rendered statement of the agreement statement to
+ sychronize data with the meta case'''
+ session['meta_acceptance_statement'] = True
+ session.save()
+ if (session['meta_acceptance_statement']
+ and session['meta_discretion_statement']):
+ return self._allow_sync(id)
+ else:
+ return self.index(id)
+
+ def print_statement2(self, id):
+ '''Returns a rendered statement of the agreement statement to
+ sychronize data with the meta case'''
+ c.case = self._loadCase(id)
+ c.agency = Agency()
+ return render('/meta/statement2.mako')
+
+ @confirm(header=N_('Set discretion statement?'),
+ text=N_('Do you really want to mark the discretion statement as set. This requires a printable and signed discretion statement from the client.'))
+ def set_statement2(self, id):
+ '''Returns a rendered statement of the agreement statement to
+ sychronize data with the meta case'''
+ session['meta_discretion_statement'] = True
+ session.save()
+ if (session['meta_acceptance_statement']
+ and session['meta_discretion_statement']):
+ return self._allow_sync(id)
+ else:
+ return self.index(id)
+ return self.index(id)
+
+
+ @confirm(header=N_('Allow synchronisation with meta?'),
+ text=N_('Sychronisation of data with the meta case requires the'
+ ' agreement of the subject.'))
+ def allow_sync(self, id):
+ return self._allow_sync(id)
+
+ def _allow_sync(self, id):
+ case = self._loadCase(id)
+ meta = case.getMeta()
+ try:
+ meta.allow_sync()
+ return success(header=_('Synchronisation allowed.'),
+ text=_('The sychronisation of data with the meta'
+ ' case has succsessfully been allowed.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=id))
+ except MetaException, ex:
+ log.exception('Can not allow sync for case %s', id)
+ return error(header=_('Error! Synchronisation not allowed.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=id),
+ text=ex.message)
+
+ def disallow_sync(self, id):
+ '''Return overview page with options for withdrawing the agreement.'''
+ return render('/meta/disallow.mako')
+
+ @confirm(header=N_('Withdraw agremment for synchronisation with meta?'),
+ text=N_('Please note that disallowing the synchronisation with'
+ ' the meta case leads to unlinking this case with its'
+ ' meta case and deleting already transfered data in the'
+ ' meta case. After that no sync is possible anymore!'))
+ def withdraw_delete_projectdata(self, id):
+ '''Will set flag to disallow the synchronisation and delete metadata
+ and the link to the metacase.'''
+ case = self._loadCase(id)
+ meta = case.getMeta()
+ try:
+ meta.delete_projectdata_and_reset()
+ session['meta_discretion_statement'] = False
+ session['meta_acceptance_statement'] = False
+ session.save()
+ return success(header=_('Agreement withdrawn.'),
+ text=_('The agremment for sychronisation of data'
+ ' with the meta case has succsessfully been'
+ ' withdrawn. Already transfered data has'
+ ' been deleted.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=id))
+ except MetaException, exc:
+ return self._handle_meta_exception(meta, exc,
+ "delete_projectdata_and_reset",
+ _("Error! Agreement for"
+ " synchronisation not"
+ " withdrawn."))
+
+ @confirm(header=N_('Withdraw agremment globally for synchronisation with'
+ ' meta?'),
+ text=N_('Please note that disallowing the synchronisation globally'
+ ' with the meta case leads to the deletion of the whole'
+ ' meta-case on the meta server. The data of the meta case'
+ ' will not be available to anybody anymore.'))
+ def withdraw_delete_metacase(self, id):
+ '''Will delete delete the projectdata in the metacase, deletes the link
+ and marks the metacase for deleteion.'''
+ case = self._loadCase(id)
+ meta = case.getMeta()
+ try:
+ meta.delete_metacase_and_reset()
+ session['meta_discretion_statement'] = False
+ session['meta_acceptance_statement'] = False
+ session.save()
+ return success(header=_('Agreement globally withdrawn.'),
+ text=_('The agremment for sychronisation of data'
+ ' with the meta case has succsessfully been'
+ ' withdrawn. Already transfered data has been'
+ ' deleted. Meta case has been marked for'
+ ' deletion.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=id))
+ except MetaException, exc:
+ return self._handle_meta_exception(meta, exc,
+ "delete_metacase_and_reset",
+ _("Error! Agreement for"
+ " synchronisation not"
+ " withdrawn."))
+
+ # Searching and linking meta cases
+ def search(self, id=None):
+ if id is None:
+ id = session.get('case').id
+ c.case = self._loadCase(id)
+ form_defaults = c.case.getHashFields()
+ form_defaults['birthname'] = form_defaults.get('birthname')
+ form_defaults['birthdate'] = format_date(form_defaults.get('birthdate'))
+ form = render('meta/search.mako')
+ c.fields = []
+ return formencode.htmlfill.render(form, defaults=form_defaults,
+ auto_insert_errors=False,
+ errors={})
+
+ def searchAction(self):
+ form_defaults = {}
+ form_errors = {}
+ validator = MetaSearchForm()
+ c.case = self._loadCase(session.get('case').id)
+ try:
+ form_result = validator.to_python(request.params)
+ birthname = form_result.get('birthname')
+ firstname = form_result.get('firstname')
+ birthdate = form_result.get('birthdate')
+ gender = form_result.get('gender')
+ hash = generate_hash(birthname, firstname, birthdate, gender)
+
+ # Save values in session because the values may be used to create a
+ # new meta-case
+ session['meta_birthname'] = form_result.get('birthname')
+ session['meta_firstname'] = form_result.get('firstname')
+ session['meta_birthdate'] = form_result.get('birthdate')
+ session['meta_gender'] = form_result.get('gender')
+ session.save()
+
+ form_defaults = validator.from_python(form_result)
+
+ c.result = get_meta_client().search_cases_by_hash(hash)
+ c.show_create = True
+ except formencode.Invalid, exc:
+ form_defaults = exc.value
+ form_errors = exc.error_dict or {}
+ c.show_create = False
+ except MetaException, exc:
+ log.exception('Exception while trying to search for case')
+ return error(header=_("Error while trying to search case"),
+ text=exc.message,
+ url_ok=url_for(controller="/meta", action="search"))
+
+ form = render('meta/search.mako')
+ return formencode.htmlfill.render(form, defaults=form_defaults,
+ auto_insert_errors=False,
+ errors=form_errors)
+
+ @confirm(header=N_('Create new meta?'),
+ text=N_('The new meta case will be created with the following'
+ ' data: %s'
+ ' Do you really want to create a new meta case for this'
+ ' case?'),
+ url_abort = url_for(controller="/meta", action="search"),
+ dynfunc=show_newmetcasedata)
+ def create(self, id):
+ case = self._loadCase(id)
+ meta = case.getMeta()
+ hash = generate_hash(session.get('meta_birthname'),
+ session.get('meta_firstname'),
+ session.get('meta_birthdate'),
+ session.get('meta_gender'))
+
+ try:
+ meta.create(hash)
+ del session['meta_birthname']
+ del session['meta_firstname']
+ del session['meta_birthdate']
+ del session['meta_gender']
+ session.save()
+ return success(header=_('Meta case created.'),
+ text=_('The meta case was sucessfully created and'
+ ' linked with this case.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=id))
+ except MetaException, ex:
+ log.exception('Can not create a meta case for case %s', id)
+ return error(header=_('Error! Meta case not created.'),
+ text=ex.message,
+ url_ok=url_for(controller="/meta", action="index",
+ id=id))
+
+ @confirm(header=N_('Link with meta?'),
+ text=N_('Do you really want to link this the case with the'
+ ' selected meta case?'),
+ url_abort=url_for(controller="/meta", action="search"))
+ def link(self, id):
+ if not validate_uuid(id):
+ # The uuid is not valid. Abort with HTTP response 400 Bad Request.
+ log.error("Invalid uuid %r. Aborting with 400 response", id)
+ abort(400)
+ case_id = session.get('case').id
+ case = self._loadCase(case_id)
+ meta = case.getMeta()
+ try:
+ meta.link(id)
+ return success(header=_('Case linked.'),
+ text=_('The case was sucessfully link with the'
+ ' meta case.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=case_id))
+ except MetaException, ex:
+ log.exception('Can not link case with meta %s', id)
+ return error(header=_('Error! Case not linked.'),
+ text=ex.message,
+ url_ok=url_for(controller="/meta", action="index",
+ id=case_id))
+
+ def unlink(self, id):
+ '''Returns a overview page with different option to unlink the case'''
+ return render('/meta/unlink.mako')
+
+ @confirm(header=N_('Unlink from meta?'),
+ text=N_('Do you really want to unlink this the case from the meta'
+ ' case and delete already stored data from this agency in'
+ ' the meta case?'))
+ def unlink_delete_projectdata(self, id):
+ '''Deletes transferred data in meta case and removed link'''
+ case = self._loadCase(id)
+ meta = case.getMeta()
+ try:
+ meta.delete_projectdata_and_unlink()
+ return success(header=_('Case unlinked.'),
+ text=_('The case was sucessfully unlinked from the'
+ ' meta case. Already transfered data has'
+ ' been deleted in meta case.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=id))
+ except MetaException, exc:
+ return self._handle_meta_exception(meta, exc,
+ "delete_projectdata_and_unlink",
+ _('Error! Case not unlinked.'))
+
+ def upload(self, id):
+ meta = self._loadCase(id).getMeta()
+ try:
+ meta.push()
+ return success(header=_('Data uploaded.'),
+ text=_('The data was sucessfully uploaded to the'
+ ' meta case.'),
+ url_ok=url_for(controller="/meta", action="index",
+ id=id))
+ except MetaException, exc:
+ return self._handle_meta_exception(meta, exc, "push",
+ _('Error! Data not uploaded.'))
+
+ def strip_meta(self, meta, sneedle, eneedle):
+ '''Strips specific part from meta case html. Start and End of part is
+ defined by the search of sneedle and eneedle'''
+ try:
+ raw = meta.pull()
+ start = raw.find(sneedle)
+ end = raw.find(eneedle) + len(eneedle)
+ c.metacase = unicode(raw[start:end], 'utf-8')
+ return render(url_for('/meta/case.mako'))
+ except MetaException, exc:
+ return self._handle_meta_exception(meta, exc, "pull",
+ _('Error! Data not downloaded.'))
+
+ def digest(self, id):
+ '''Will return the digest part of the meta case html'''
+ meta = self._loadCase(id).getMeta()
+ sneedle = "<!-- START CASE DIGEST -->"
+ eneedle = "<!-- END CASE DIGEST -->"
+ return self.strip_meta(meta, sneedle, eneedle)
+
+ def download(self, id):
+ '''Will return the body the full meta case html'''
+ meta = self._loadCase(id).getMeta()
+ sneedle = "<!-- START CASE BODY -->"
+ eneedle = "<!-- END CASE BODY -->"
+ return self.strip_meta(meta, sneedle, eneedle)
+
+ def _handle_meta_exception(self, meta, exc, action_name, title):
+ log.exception("Exception during action %(action)r for"
+ " client case %(master_id)s",
+ dict(action=action_name, master_id=meta.master_id))
+
+ text = exc.message
+ if isinstance(exc, MetaUnauthorized):
+ log.error("Meta server refused request with 401 Unauthorized")
+ text = _("The meta server did not accept the request"
+ " because of missing or incorrect username or"
+ " password. Please check whether the"
+ " meta-server is configured correctly.")
+
+ elif isinstance(exc, UnknownMetaCase):
+ log.error("Meta case for %s seems to have been deleted."
+ " Unlinking the meta case and revoking link permission.",
+ meta.master_id)
+ meta.reset()
+ text = _("The meta case doesn't seem to exist (anymore)"
+ " on the meta server. The most likely reason"
+ " for this is that the permission to store"
+ " data on the meta server has been revoked,"
+ " therefore the link to the meta-case has been"
+ " removed and the revocation of the permission"
+ " has been recorded in WASKU as well.")
+
+ elif isinstance(exc, MetaCaseAnonymized):
+ log.error("The meta case for %s has been anonymized."
+ " Resetting the meta information",
+ meta.master_id)
+ meta.reset()
+ text = _("The meta-case has been anonymized."
+ " Please check whether the case has to be"
+ " anonymized as well."
+ " The information about the meta-case has"
+ " been deleted and the permission to sync has"
+ " been revoked.")
+
+ elif isinstance(exc, MetaCasePending):
+ log.error("The meta case for %d has been marked for deletion"
+ " or anonymization", meta.master_id)
+ text = _("The meta-case has been marked for deletion"
+ " or anonymization."
+ " It is possible that the case may be available"
+ " for editing later, so you might be able to"
+ " complete your task later.")
+
+ elif isinstance(exc, UnknownProjectPart):
+ log.error("The project part of the meta case for %d seems to be"
+ " missing. Deleting project_uuid from the meta table.",
+ meta.master_id)
+ meta.reset_project_uuid()
+ text = _("The project part of the meta-case seems to be"
+ " missing. Since the information about the"
+ " meta case seems to be outdated, the link to"
+ " the project part has been removed, the link"
+ " to the meta-case as a whole is still present,"
+ " however. Please upload the data again, if"
+ " you want it to be on the meta-server.")
+
+ elif isinstance(exc, MetaException):
+ # the error parameters should be set already
+ pass
+
+ else:
+ raise RuntimeError("_handle_meta_exception not called with"
+ " MetaException instance")
+
+ return error(header=title, text=text,
+ url_ok=url_for(controller="/meta", action="index",
+ id=meta.master_id))
+
+
+# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:
Property changes on: base/trunk/mpulsweb/controllers/meta.py
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ native
More information about the Mpuls-commits
mailing list