[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