oship-dev team mailing list archive
-
oship-dev team
-
Mailing list archive
-
Message #01356
[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