[Mpuls-commits] r5321 - base/trunk/mpulsweb/model

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Tue Sep 13 13:22:37 CEST 2011


Author: bh
Date: 2011-09-13 13:22:37 +0200 (Tue, 13 Sep 2011)
New Revision: 5321

Added:
   base/trunk/mpulsweb/model/meta.py
Log:
Port the meta model implementation from WASKU to mpulsweb.

The code was taken from wasku-web revision 391:e20e98117f34.
The agencysettings module is now imported from mpulsweb, though.


Added: base/trunk/mpulsweb/model/meta.py
===================================================================
--- base/trunk/mpulsweb/model/meta.py	2011-09-13 10:01:01 UTC (rev 5320)
+++ base/trunk/mpulsweb/model/meta.py	2011-09-13 11:22:37 UTC (rev 5321)
@@ -0,0 +1,259 @@
+# -*- coding: utf-8 -*-
+# Authors:
+# Torsten Irländer <torsten.irlaender at intevation.de>
+
+"""Classes reperesenting the connection to the meta-case"""
+
+import logging
+import subprocess
+from datetime import datetime
+from xml.etree import ElementTree
+
+import psycopg2.extras
+
+from pylons import config
+
+from formed.instance.backends.postgres import DBFactory as InstanceFactory
+
+from mpulsweb.lib.db import PostgresDBInterface, db
+from mpulsweb.lib.translation import _
+from mpulsweb.lib.base import g, session
+from mpulsweb.lib.security import get_unmapped_db_name
+from mpulsweb.lib.metaclient import MetaClient, MetaException
+
+from mpulsweb.model.agencysettings import Agency
+
+log = logging.getLogger(__name__)
+
+LOAD_META_SQL = """SELECT * from ka_meta_tbl_view where master_id = %s"""
+UPDATE_META_SQL = """\
+UPDATE ka_meta_tbl_view
+SET meta_uuid = %(meta_uuid)s,
+    project_uuid = %(project_uuid)s,
+    sync_permission = %(sync_permission)s,
+    last_download = %(last_download)s,
+    last_upload = %(last_upload)s
+WHERE id = %(id)s"""
+
+
+def get_meta_client():
+    agency = Agency()
+    user = session['USER_AUTHORIZED']
+    return MetaClient(config["mpuls.meta.host"],
+                      int(config["mpuls.meta.port"]), g.ssl_context,
+                      get_unmapped_db_name(),
+                      agency.getMetaUserName(),
+                      agency.getMetaUserPassword(),
+                      user.login)
+
+
+def convert_meta_xml(converter, project_xml):
+    proc = subprocess.Popen(["xsltproc", converter, "-"], stdin=subprocess.PIPE,
+                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    out, err = proc.communicate(project_xml)
+
+    if proc.returncode != 0:
+        log.error("Error running xsltproc subprocess: %r", err)
+        return None
+    if err:
+        log.warning("Unexpected stderr output of xsltproc: %r", err)
+
+    return out
+
+
+class Meta:
+
+    """Model for the client-side information about the meta-case.
+
+    Instances of this class store basic information about the link to
+    the meta case. The link is established using the UUIDs of the meta
+    case and, once it exists, the project part containing the data from
+    the client-side case this instance belongs to. This class also
+    handles operations like setting permissions to sync data with the
+    metacase, link und unlinking and transfer of data.
+    """
+
+    def __init__(self, master_id):
+        self.id = None
+        self.master_id = master_id
+        self._set_default_values()
+
+        # Load meta data
+        self._load()
+
+    #
+    # Low-level interface for the direct manipulation of the case's meta
+    # settings
+    #
+
+    def _save(self):
+        """Save data of meta class in db"""
+        db.execute(UPDATE_META_SQL,
+                   dict(id=self.id,
+                        meta_uuid=self.meta_uuid,
+                        project_uuid=self.project_uuid,
+                        sync_permission=self.sync_permission,
+                        last_download=self.last_download,
+                        last_upload=self.last_upload))
+
+    def _load(self):
+        """Load data of meta class from db"""
+        result = None
+        conn, cur = None, None
+        try:
+            conn = db.getConnection()
+            cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
+            cur.execute(LOAD_META_SQL, (self.master_id,))
+            result = cur.fetchone()
+            # Set values of meta object.
+            self.id = result.get('id')
+            self.meta_uuid = result.get('meta_uuid')
+            self.project_uuid = result.get('project_uuid')
+            self.sync_permission = result.get('sync_permission')
+            self.last_download = result.get('last_download')
+            self.last_upload = result.get('last_upload')
+        except:
+            log.exception('Error on loading meta data for case %s',
+                          self.master_id)
+            raise
+        finally:
+            db.recycleConnection(conn, cur)
+
+    def _set_default_values(self):
+        self.meta_uuid = None
+        self.project_uuid = None
+        self.sync_permission = False
+        self.last_download = None
+        self.last_upload = None
+
+    def reset(self):
+        """Reset the meta information by unlink the case and revoke permission.
+        In fact, all case-specific information stored about the link to
+        the meta-server are set to defaults.
+        """
+        self._set_default_values()
+        self._save()
+
+    # Synchronisation
+    def is_sync_allowed(self):
+        """Returns True if the transfer data to the meta-server is allowed."""
+        return self.sync_permission
+
+    def allow_sync(self):
+        """Set flag to allow synchronisation with meta case."""
+        self.sync_permission = True
+        self._save()
+
+    def disallow_sync(self):
+        """Unset flag to allow synchronisation with meta case.
+        Synchronisation is not allowed anymore afterwards.
+        """
+        self.sync_permission = False
+        self._save()
+
+    # Linking
+    def is_linked(self):
+        """Returns True if a link is set to the meta case, False otherwise."""
+        return self.meta_uuid is not None
+
+    def link(self, uuid, project_uuid=None):
+        """Link the case with a meta case identified with its uuids."""
+        if self.is_sync_allowed():
+            if ((self.meta_uuid is not None and self.meta_uuid != uuid)
+                or (self.project_uuid is not None
+                    and self.project_uuid != project_uuid)):
+                raise MetaException(_('Case is already linked'))
+            self.meta_uuid = uuid
+            self.project_uuid = project_uuid
+            self._save()
+        else:
+            raise MetaException(_('Synchronisation is not allowed for this'
+                                  ' case.'))
+
+    def unlink(self):
+        """Delete link to meta case by setting the UUIDs to None"""
+        if self.meta_uuid is None:
+            raise MetaException(_('Case is not linked'))
+        self.meta_uuid = None
+        self.project_uuid = None
+        self._save()
+
+    def reset_project_uuid(self):
+        self.project_uuid = None
+        self._save()
+
+    #
+    # High-level interface for operations involving the meta-server
+    #
+
+    def delete_projectdata(self):
+        """Delete the project part of the meta-case on the meta-server"""
+        if self.is_linked():
+            if self.project_uuid:
+                get_meta_client().delete_project_data(self.meta_uuid,
+                                                      self.project_uuid)
+        else:
+            raise MetaException(_('Case is not linked with any meta case.'))
+
+    def delete_metacase(self):
+        """Delete the meta-case on the meta-server.
+        More precisely, the meta-case is marked for deletion on the
+        meta-server after deleting the project part corresponding to the
+        client case.
+        """
+        if self.is_linked():
+            get_meta_client().delete_case(self.meta_uuid, self.project_uuid)
+        else:
+            raise MetaException(_('Case is not linked with any meta case.'))
+
+    def get_meta_xml(self):
+        """Return the data to transfer to the meta server as an XML string."""
+        factory = InstanceFactory(g.formedTree, PostgresDBInterface())
+        xmltree = factory.toXML([self.master_id])
+        return convert_meta_xml(g.meta_converter,
+                                ElementTree.tostring(xmltree.getroot()))
+
+    def create(self, hash):
+        """Create a new meta case and link the uuid with this case."""
+        uuids = get_meta_client().upload_new_case(hash, self.get_meta_xml())
+        # no need to explicitly save the last_upload change because it
+        # will be done by self.link anyway.
+        self.last_upload = datetime.today()
+        self.link(*uuids)
+
+    def pull(self):
+        """Return the meta-case rendered as HTML as a string."""
+        if self.is_linked():
+            rendered = get_meta_client().meta_case_as_html(self.meta_uuid)
+            self.last_download = datetime.today()
+            self._save()
+            return rendered
+        else:
+            raise MetaException(_('Case is not linked with any meta case.'))
+
+    def push(self):
+        """Upload the case data to the linked meta case."""
+        if self.is_linked():
+            uuids = get_meta_client().upload_project_data(self.meta_uuid,
+                                                          self.project_uuid,
+                                                          self.get_meta_xml())
+            self.last_upload = datetime.today()
+            self._save()
+            self.link(*uuids)
+        else:
+            raise MetaException(_('Case is not linked with any meta case.'))
+
+    def delete_metacase_and_reset(self):
+        """Delete the meta case and afterwards reset the meta settings."""
+        self.delete_metacase()
+        self.reset()
+
+    def delete_projectdata_and_reset(self):
+        """Delete the project part and afterwards reset the meta settings."""
+        self.delete_projectdata()
+        self.reset()
+
+    def delete_projectdata_and_unlink(self):
+        """Delete the project part and afterwards reset the meta UUIDs."""
+        self.delete_projectdata()
+        self.unlink()


Property changes on: base/trunk/mpulsweb/model/meta.py
___________________________________________________________________
Name: svn:keywords
   + Id Revision
Name: svn:eol-style
   + native



More information about the Mpuls-commits mailing list