← Back to team overview

oship-dev team mailing list archive

[Branch ~oship-dev/oship/devel] Rev 468: The commit merges a simple way to add measurement sources to the trunk repository. It also fixes ...

 

Merge authors:
  Diego Manhães Pinheiro <diego@diego-notebook>
------------------------------------------------------------
revno: 468 [merge]
committer: Diego Manhães Pinheiro <diego@diego-notebook>
branch nick: trunk
timestamp: Tue 2010-08-10 08:03:05 -0300
message:
  The commit merges a simple way to add measurement sources to the trunk repository. It also fixes a problem found on new mlhim demographic module that was breaking some tests.
added:
  src/oship/openehr/rm/support/measurement/tests/measurement.txt
modified:
  src/oship/mlhim/rm/demographic/__init__.py
  src/oship/openehr/rm/support/measurement/__init__.py
  src/oship/openehr/rm/support/measurement/interfaces.py


--
lp:oship
https://code.launchpad.net/~oship-dev/oship/devel

Your team OSHIP Development Team is subscribed to branch lp:oship.
To unsubscribe from this branch go to https://code.launchpad.net/~oship-dev/oship/devel/+edit-subscription
=== modified file 'src/oship/mlhim/rm/demographic/__init__.py'
--- src/oship/mlhim/rm/demographic/__init__.py	2010-08-06 22:33:36 +0000
+++ src/oship/mlhim/rm/demographic/__init__.py	2010-08-10 11:03:05 +0000
@@ -1,337 +0,0 @@
-# -*- coding: utf-8 -*-# -*- coding: utf-8 -*-
-##############################################################################
-# Copyright (c) 2007, Timothy W. Cook and Contributors. All rights reserved.
-# Redistribution and use are governed by the MPL license.
-#
-# Use and/or redistribution of this file assumes you have read and accepted the
-# terms of the license.
-##############################################################################
-
-
-u"""
-
-From the demographic package from openEHR
-Demographic Information Model package Rev. 2.0.1
-
-"""
-
-__author__ = u'Timothy Cook <timothywayne.cook@xxxxxxxxx>'
-__docformat__ = u'plaintext'
-__Contributors__ = u'Roberto Cunha <roliveiracunha@xxxxxxxxxxxx>',
-u'Sergio Miranda Freire <sergio@xxxxxxxxxxxxxxx>',
-u'Eduardo César Pimenta Ribeiro <xcesar at gmail dot com'
-
-import grok
-from grok import index
-from oship.mlhim.rm.common.archetyped import Locatable
-from oship.app import OSHIP
-from interfaces import *
-
-
-class ActorCatalog(grok.Indexes):
-    grok.site(OSHIP)
-    grok.context(ISearchableActor)
-    grok.name('actor_catalog')
-    
-    relationships = index.Set()
-    reverse_relationships = index.Set()
-    legal_identity = index.Text()
-
-
-
-class SearchableActor(grok.Adapter):
-
-    grok.context(IActor)
-    grok.implements(ISearchableActor)
-
-
-    def __init__(self, context):
-        self.context = context
-        rel_references = []
-        for rel in self.context.relationships:
-            rel_references.append(rel.target.uid.value)
-        self.relationships = rel_references
-        rev_references = []
-        for rev_rel in context.reverseRelationships:
-            rev_references.append(rev_rel.source.uid.value)
-        self.reverse_relationships = rev_references
-        identities  = self.context.identities
-        self.legal_identity = ""
-        for identity in identities:
-            if identity.purpose().value == 'legal identity':
-                self.legal_identity = identity.asString()
-                break
-
-
-
-class Party(Locatable):
-    """
-    Ancestor of all party types.
-    """
-
-    grok.implements(IParty,ILocatable)
-
-    def __init__(self,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links):
-        Locatable.__init__(self,uid,archetypeNodeId,name,archetypeDetails,feederAudit,links)
-
-        if self.isArchetypeRoot() == False:
-            raise(ValueError, u"Not an archetype root")
-        # if relationships != None:
-        #     for r in relationships:
-        #         if r.source.id_ != self.uid:
-        #             raise(ValueError,u"Source in relationship is not current party")
-        self.identities=identities
-        self.contacts=contacts
-        self.relationships=relationships
-        self.reverseRelationships=reverseRelationships
-        self.details=details
-
-
-    def type(self):
-        """
-        Return the type of party from the inherited 'name' attribute.
-        """
-        return self.name;
-
-    def __eq__(self, obj):
-        if self is obj:
-            return True
-        if isinstance(obj, Party):
-            return obj.identities == self.identities and obj.contacts == self.contacts and obj.relationships == self.relationships and obj.reverseRelationships == self.reverseRelationships and obj.details == self.details
-        return False
-
-    def __hash__(self):
-        result = 17
-        result += 31 * result + hash(self.identities)
-        result += 31 * result + hash(self.contacts)
-        result += 31 * result + hash(self.relationships)
-        result += 31 * result + hash(self.reverseRelationships)
-        result += 31 * result + hash(self.details)
-        return result
-
-
-class Actor(Party):
-    """
-    Ancestor of al real world types.
-    """
-
-    grok.implements(IActor)
-
-
-    def __init__(self,roles,languages,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links):
-        Party.__init__(self,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links)
-
-        self.roles=roles
-        self.languages=languages
-
-    def hasLegalIdentity(self):
-        """
-        Return True if there is an identity with purpose 'legal identity'
-        """
-        if 'legal identity' in self.identities:
-            return True
-        else:
-            return False
-
-    def __eq__(self, obj):
-        if self is obj:
-            return True
-        if isinstance(obj, Actor):
-            return obj.roles == self.roles and obj.languages == self.languages and Party.__eq__(self, obj)
-        return False
-
-    def __hash__(self):
-        result = 17
-        result += 31 * result + hash(self.roles)
-        result += 31 * result + hash(self.languages)
-        result += 31 * result + hash(Party.__hash__(self))
-        return result
-       
-
-
-class Address(Locatable):
-    """
-    Address of contact as an ItemStructure.
-    """
-
-    grok.implements(IAddress)
-
-    def __init__(self,details,uid,atnodeid,name,atdetails,fdraudit,links):
-        Locatable.__init__(self,uid,atnodeid,name,atdetails,fdraudit,links)
-
-        self.details=details
-
-
-    def type(self):
-        """
-        Return the type of address from 'name'.
-        """
-        return self.name
-
-    def asString(self):
-        """
-        Address in the form of a string.
-        """
-        return ""
-
-
-class Agent(Actor):
-    """
-    Generic concept of of any kind of agent including devices.
-    """
-
-    grok.implements(IAgent)
-
-    def __init__(self,roles,languages,uid,identities,contacts,category,language,relationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links):
-        Actor.__init__(self,roles,languages,uid,identities,contacts,category,language,relationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links)
-
-
-class Capability(Locatable):
-    """
-    Capability of a role such as ehr modifier, healthcare provider, etc.
-    """
-
-    grok.implements(ICapability)
-
-    def __init__(self,credentials,timeValidity,uid,atnodeid,name,atdetails,fdraudit,links):
-        Locatable.__init__(self,uid,atnodeid,name,atdetails,fdraudit,links)
-        self.credentials=credentials
-        self.timeValidity=timeValidity
-
-
-class Contact(Locatable):
-    """
-    Description of a means of contacting a party.
-    """
-    grok.implements(IContact)
-
-    def __init__(self,timeValidity,addresses,uid,atnodeid,name,atdetails,fdraudit,links):
-        Locatable.__init__(self,uid,atnodeid,name,atdetails,fdraudit,links)
-
-
-    def purpose():
-        """
-        Purpose for which this contact is used.
-        Taken from the inherited 'name' attribute.
-        """
-
-
-
-class Group(Actor):
-    """
-    A real world group of parties.
-    """
-
-    grok.implements(IGroup)
-    def __init__(self,roles,languages,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links):
-        Actor.__init__(self,roles,languages,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links)
-
-
-
-
-class Organisation(Actor):
-    """
-    Generic descriptions of organizations.
-    """
-
-    grok.implements(IOrganisation)
-
-    def __init__(self,roles,languages,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links):
-        Actor.__init__(self,roles,languages,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links)
-
-
-class PartyIdentity(Locatable):
-    """
-    An identity owned by a party.
-    """
-
-    grok.implements(IPartyIdentity)
-
-    def __init__(self,details,uid,atnodeid,name,atdetails,fdraudit,links):
-        Locatable.__init__(self,uid,atnodeid,name,atdetails,fdraudit,links)
-        self.details = details
-
-    def asString(self):
-        """
-        Identity in the form of a string.
-        """
-        return ""
-
-    def purpose(self):
-        """
-        Return the purpose of party identity from the inherited 'name' attribute.
-        """
-        return self.name
-
-    def __eq__(self, obj):
-        if obj is self:
-            return True
-        if isinstance(obj, PartyIdentity):
-            return Locatable.__eq__(self, obj) and self.details == obj.details
-        return False
-
-    def __hash__(self):
-        return hash(self.details)
-
-
-
-class PartyRelationship(Locatable):
-    """
-    Generic description of a relationship between parties.
-    """
-
-    grok.implements(IPartyRelationship)
-
-    def __init__(self,uid,details,timeValidity,source,target,atnodeid,name,atdetails,fdraudit,links):
-        Locatable.__init__(self,uid,atnodeid,name,atdetails,fdraudit,links)
-        self.details=details
-        self.timeValidity=timeValidity
-        self.source=source
-        self.target=target
-
-
-    def type(self):
-        """
-        Type of relationship such as employment, authority, etc.
-        """
-        return self.name
-
-
-class Person(Actor):
-    """
-    Generic description of of persons.  Provides a dedicated type to whicih Person archetypes can be targeted."),
-    """
-
-    grok.implements(IPerson)
-
-    def __init__(self,roles,languages,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links):
-        Actor.__init__(self,roles,languages,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links)
-
-
-class Role(Party):
-    """
-    Generic role played by a party.
-    """
-
-    grok.implements(IRole)
-
-    def __init__(self,capabilities,timeValidity,performer,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links):
-        Party.__init__(self,uid,identities,contacts,relationships,reverseRelationships,details,archetypeNodeId,name,archetypeDetails,feederAudit,links)
-        self.capabilities=capabilities
-        self.timeValidity=timeValidity
-        self.performer=performer
-
-    def __eq__(self, obj):
-        if self is obj:
-            return True
-        if isinstance(obj, Role):
-            return obj.capabilities == self.capabilities and obj.timeValidity == self.timeValidity and obj.performer == self.performer and Party.__eq__(self, obj)
-        return False
-
-    def __hash__(self):
-        result = 17
-        result += 31 * result + hash(self.capabilities)
-        result += 31 * result + hash(self.timeValidity)
-        result += 31 * result + hash(self.performer)
-        result += 31 * result + hash(Party.__hash__(self))
-        return result

=== modified file 'src/oship/openehr/rm/support/measurement/__init__.py'
--- src/oship/openehr/rm/support/measurement/__init__.py	2010-07-12 21:32:11 +0000
+++ src/oship/openehr/rm/support/measurement/__init__.py	2010-08-09 22:53:05 +0000
@@ -1,25 +1,42 @@
 # -*- coding: utf-8 -*-
 
 import grok
-from interfaces import IMeasurementService
-
-
-class MeasurementService(grok.Model):
+from interfaces import IMeasurementService, IMeasurementSource
+from zope.component import getAllUtilitiesRegisteredFor
+
+
+class MeasurementService(object):
     """Defines an object providing proxy access to a measurement
     information service."""
 
     grok.implements(IMeasurementService)
 
-    def isValidUnitsString(units):
+    def getSources(self):
+        return getAllUtilitiesRegisteredFor(IMeasurementSource)
+
+    def isValidUnitsString(self,units):
         u""" True if the units string 'units' is a valid string
         according to the HL7 UCUM specification.  units is not None
         """
-        pass
-
-    def unitsEquivalent(units1, units2):
+        sources = self.getSources()
+        for source in sources:
+            if source.isValidUnitsString(units):
+                return True
+        return False
+
+
+    def unitsEquivalent(self, units1, units2):
         u""" True if two units strings correspond to the same measured
         property.  isValidUnitsString(units1) and
         isValidUnitsString(units2)
         """
-        pass
+
+        sources = self.getSources()
+        for source in sources:
+            units1_valid = source.isValidUnitsString(units1)
+            units2_valid = source.isValidUnitsString(units2)
+            if units1_valid and units2_valid:
+                return source.unitsEquivalent(units1, units2)
+        return False
+
 

=== modified file 'src/oship/openehr/rm/support/measurement/interfaces.py'
--- src/oship/openehr/rm/support/measurement/interfaces.py	2010-07-12 21:32:11 +0000
+++ src/oship/openehr/rm/support/measurement/interfaces.py	2010-08-09 22:53:05 +0000
@@ -9,13 +9,19 @@
     information service."""
 
     def isValidUnitsString(units):
-        u""" True if the units string 'units' is a valid string
+        """ True if the units string 'units' is a valid string
         according to the HL7 UCUM specification.  units is not None
         """
+         
+
 
     def unitsEquivalent(units1, units2):
-        u""" True if two units strings correspond to the same measured
+        """ True if two units strings correspond to the same measured
         property.  isValidUnitsString(units1) and
         isValidUnitsString(units2)
         """
 
+class IMeasurementSource(IMeasurementService):
+    """
+      Marker interface that defines an interaction with measurement sources. 
+    """

=== added file 'src/oship/openehr/rm/support/measurement/tests/measurement.txt'
--- src/oship/openehr/rm/support/measurement/tests/measurement.txt	1970-01-01 00:00:00 +0000
+++ src/oship/openehr/rm/support/measurement/tests/measurement.txt	2010-08-09 22:53:05 +0000
@@ -0,0 +1,129 @@
+:Test-Layer: functional
+
+Measurement Service
+===================
+
+A measurement service should provide ways to verify if
+a unit is valid and if two units use the same measurement unit.
+To make this concept clear: Gb(Gigabit) and Mb(Megabit) use 
+the same property unit. The Measurement Service provides useful 
+functions to other modules of the OpenEHR Reference Model, mainly 
+to the `oship.openehr.rm.datatypes.quantity` module.
+The MeasurementService class provide access to operations that enable
+to do that. It can validate units which can come from a 
+variety of sources, such as:
+
+* CEN ENV 12435, Medical Informatics - Expression of results of measurements
+in health sciences .
+
+* the Unified Code for Units of Measure (UCUM), developed by Gunther Schadow 
+and Clement J. McDonald of The Regenstrief Institute .
+
+So, Every time a unit is verified, a MeasurementService object acts like a proxy
+to these measurement sources, verifying which one can answer if units are
+valid units::
+
+
+>>> from __future__ import unicode_literals
+>>> import grok
+>>> from oship.openehr.rm.support.measurement import MeasurementService
+>>> ms = MeasurementService()
+>>> ms.isValidUnitsString("meter")
+False
+
+At the moment, the measurement service don't have a measurement source yet. 
+It means that a measurement service cannot verify any unit. All verifications 
+will be False. Let's create a measurement source::
+
+>>> from oship.openehr.rm.support.measurement.interfaces import IMeasurementSource
+>>> class SimpleMeasurementSource(grok.GlobalUtility):
+...   grok.name('simple')
+...   grok.implements(IMeasurementSource)
+...   def __init__(self):
+...     self.units = ['milimeter','kilometer','centimeter',"meter"]
+...   def isValidUnitsString(self, unit):
+...      if unit in self.units:
+...        return True
+...      return False
+...   def unitsEquivalent(self, units1, units2):
+...      units1_valid = self.isValidUnitsString(units1)
+...      units2_valid = self.isValidUnitsString(units2)
+...      if units1_valid and units2_valid:
+...        return True
+...      return False
+>>> from grok.testing import grok_component
+>>> grok_component("SimpleMeasurementSource",SimpleMeasurementSource)
+True
+
+All measurement system must be created using the grok utility pattern.
+This approach was used because it allows the service have lots of
+sources with low code maintenance and high extensibility.
+Now we can verify a unit::
+
+>>> ms = MeasurementService()
+>>> ms.isValidUnitsString("meter")
+True
+>>> ms.isValidUnitsString("pascal")
+False
+>>> ms.isValidUnitsString("centimeter")
+True
+>>> ms.unitsEquivalent("meter","centimeter")
+True
+>>> ms.unitsEquivalent("kilometer","milimeter")
+True
+
+
+Using this mechanism you can add different measurement sources, covering
+different types of measurement units::
+
+
+>>> class AnotherMeasurementSource(SimpleMeasurementSource,grok.GlobalUtility):
+...   grok.name('other')
+...   grok.implements(IMeasurementSource)
+...   def __init__(self):
+...     self.units = ['gram','kilogram']
+>>> grok_component("AnotherMeasurementSource",AnotherMeasurementSource)
+True
+>>> ms = MeasurementService()
+>>> ms.isValidUnitsString("gram")
+True
+>>> ms.isValidUnitsString("milimeter")
+True
+>>> ms.unitsEquivalent("kilogram","gram")
+True
+>>> ms.unitsEquivalent("kilometer","centimeter")
+True
+>>> ms.unitsEquivalent("kilogram","meter")
+False
+
+
+Using this mechanism, new measurement sources can be included. It's important
+to notice that two sources don't have relation between them. For example, let's
+consider two sources: a source has the only the 'gram' unit and a second source 
+have just the 'kilogram' unit provided by themselves. In the real world, they are 
+considered equivalent but this cannot be considered true here because they are
+related to different sources. In other hand, they are considered valid 
+because each source validate those units::
+
+
+>>> class LastMeasurementSource(SimpleMeasurementSource, grok.GlobalUtility):
+...   grok.name('another')
+...   grok.implements(IMeasurementSource)
+...   def __init__(self):
+...     self.units = ['decimeter','centigram', 'kilogram']
+>>> grok_component('LastMeasurementSource',LastMeasurementSource)
+True
+>>> ms = MeasurementService()
+>>> ms.isValidUnitsString("decimeter")
+True
+>>> ms.isValidUnitsString("centigram")
+True
+>>> ms.unitsEquivalent('meter','decimeter')
+False
+>>> ms.unitsEquivalent('centigram','kilogram')
+True
+>>> ms.unitsEquivalent('kilogram','gram')
+True
+>>> ms.unitsEquivalent('centigram','gram')
+False
+


Follow ups