Author: Donnal Walter
Revised: 2003-03-02

Warning:As of this writing, Mindwrapper is still in the pre-alpha stage of development. The API as described here is therefore subject to change.

Subsections


Overview

Mindwrapper is an experimental framework for developing custom clinical applications.

Framework architecture. Mindwrapper applications may be characterized as single-tiered and bi-layered, by which we mean that they are partitioned into two distinct, cohesive layers. This section of the documentation describes the abstraction layer (AL). The presentation layer (PL) will be discussed separately. (See also Architecture.)

Framework information model. Mindwrapper applications use structure data documents best described as directed, acyclic, labeled, elaborately-typed graphs. For brevity, we will call such graphs object-trees. The terms domain model, data document, and object-tree are thus synonymous here. The user-developer composes the object-tree by means of a special notation for component naming, and component binding.

An object-tree consists of branching nodes and leaf nodes. Branching nodes are called a branches, appropriately, but leaf nodes are here called cells by analogy both to biological cells and to cells in a spreadsheet. (Biological cells superbly demonstrate encapsulation, instantiation, inheritance, polymorphism and many other object-oriented features.) Complex top-level branches consist of simpler sub-branches, which may consist of still simpler branches nested to any degree of complexity. Ultimately the simplest branches consist of cells. Cells are the atomic or scalar nodes in the object-tree. Branches and cells that make no reference to other branches or cells in the tree are said to be independent. Branches or cells that require access to external branches or cells are designated as dependent.

Framework notation. The Python-based notation is designed to facilitate the definition of the object-tree using a generic text editor. The notation is machine readible, that is to say, executable, as well as readily understood by humans. It is also designed to be a kind of resource-file notation for future experiments with special purpose editors. The notation consists of Python classes (some abstract, some concrete), methods (some to be called, some to be overridden), and keyword parameters for the Add() method. Inheritance and polymorphism are used extensively.

Independent Branches

The object-tree for an application is made up independent and dependent nodes. An independent node is one that is completely encapsulated; i.e., it has no external references. The structure of an independent branch node is determined (defined) at design-time using the following syntax:

class PatientName(Branch):     # subclass derived from abstract Branch
    def Assemble(self):        #   abstract method overridden
        self.Add(              #   concrete method called
            node = Text,       #     node is instance of Text class
            name = 'last')     #     the last name of the patient
        self.Add(              #   concrete method called
            node = Text,       #     another instance of Text class
            name = 'first')    #     the first name of the patient

Every branch is derived from the abstract Branch class, with the Assemble() method overridden to specify the child nodes to be added to this parent node. Each child node is added by calling the self.Add() method with at least a node parameter to specify the class of node to be added. There will usually also be a name parameter to label the node. It is not necessary to put each parameter on a separate line, but (a) with many paramters, this form is possibly more readable, (b) machine generated code will probably use this form, and (c) this form would allow comments following each parameter. Nevertheless, the above could just as well have been written as the example on the left below. For those already familiar with Python, this is roughly equivalent to the traditional Python code on the right.

# Mindwrapper syntax
class PatientName(Branch):
    def Assemble(self):
        self.Add(node=Text, name='last')
        self.Add(node=Text, name='first')
# Roughly equivalent Python
class PatientName(Branch):
    def __init__(self):
        self.last = Text()
        self.first = Text()

The main difference is that the Mindwrapper Assemble/Add syntax also takes care of passing hidden information from the parent node to each child node. What sets the independent branches apart from dependent branches (described below) is that the Assemble() method has only the self parameter and no others. This means that the independent branch cannot reference any nodes (branches or cells) outside of itself.

Independent Cells

The leaf nodes of an object-tree are called cells. As mentioned in the overview, cells demonstrate encapsulation, inheritance and polymorphism much as biological cells do. In addition, they function like cells in a spreadsheet, except that they are labeled with names in a namespace hierarchy rather than by coordinates on a grid.

Three basic cell types are built into Mindwrapper: Text, Number, and Timestamp. Extending Mindwrapper to create new basic cells types is not difficult, but the need for such extensions is expected to be infrequent. On the other hand, defining specialized versions of the basic cell types (a process called elaboration by instance or by class) will be quite common in composing a Mindwrapper application.

abstract cells

Regardless of type, all cells have a number of features in common. Independent cells stand alone; they do not reference any other data objects. This means that when they are instantiated with the Add() method, no ref parameter is provided, and when subclasses are defined (class elaboration), the Assemble() method is not overridden.

All cells are observable. When the value is changed the cell notifies all objects that have registered themselves as observers of that cell. Observers may be other (dependent) cells in the AL or they may be presenters in the PL. Registration as an observer takes place automatically (at a high level) when a dependent cell or presenter is created.

Text cells

The value of a Text cell is a Python string. Typically the value is set or changed by a presenter in the PL or by an observable in the AL. The user-developer will rarely have reason to access the value of the Text cell except through these built-in mechanisms, but when necessary, the Python property text provides such access.

        tempDx = self.problem.diagnosis.text
        self.currentDx.text = tempDx

The constraints for Text cells include choices and more, as well as the generic constraints (see below). The choices contraint creates a list of options suggested for a Text cell. The more constraint simply adds to that list. The choices are used by associated presenters in the PL but not enforced by the cell itself.

Number cells

The constraints for a Number cell include scale, digits, and limits. The value of a Number cell is a floating-point type if the scale is set to 0 and an integer if the scale is 1. When the scale is between 0.00001 and 1000 (except for 1), the value is a scaled integer (fixed-point decimal) number. The digits constraint determines the number of decimal digits to the right of the decimal place when the number is converted to a text string. The limits constraint provides a range of acceptable values. This range can be nested one level with the first pair of limits being the outer range and the second set the inner range. Typically the inner range would be used for 'normal' values, while the outer range would be used for 'feasible' values. The limits are used by associated presenters in the PL but not enforced by the cell itself.

As with Text cells, it is usually unnecessary to access value of a Number cell except through the built-in observerable mechanisms, but should direct access be necessary, the val property can be used (or the text property for string representation).

Timestamp cells

...

Constraints

Instance constraints

...

    def Assemble(self):
        self.Add(
            node = Number,
            name = myWeight
            scale = 0.001,
            digits = 3,
            limits = [0.3, 5.0, 0.45, 10.0])

Class constraints

Not only is is possible to set constraints on invidual cells, it is also possible to define constraints for classes of cells.

class Weight(Number):
    scale = 0.001
    digits = 3
    limits = [0.3, 5.0, 0.45, 10.0]

Dependent Cells

...

class VolDist(Number):
    """Volume of distribution for multiple dosing."""

    scale = 0     # value will be floating-point
    digits = 2    # show two decimal digits

    def Assemble(self, kE, dose, cPk, tDose, tInf, tPost):
        """(elimination constant, dose, peak concentration, dosing interval,
        infusion time, post-infusion time to peak)"""
        d = kE * tInf
        a = exp(-d)
        b = exp(-kE * tPost)
        c = exp(-kE * tDose)
        x = ((1 - a) * b) / ((1 - c) * d)  # adjustment for timing
        vol = dose / cPk                   # volume of distribution
        return vol * x                     # adjusted volume of distribution

...

        self.Add(
            node = VolDist,
            name = 'volDist',
            ref = [
                self.kElim,
                self.dose,
                self.peak,
                self.time.hrDosing,
                self.time.hrInf,
                self.time.hrPostInf])

Dependent Branches

A branch with one or more cells that make reference to cells external to that branch is said to be dependent.

    def Assemble(self, aBranch):
        self.Add(
            node = Number,
            name = 'aResult',
            ref = [aBranch.inpA, aBranch.inpB])
    def Assemble(self, cellA, cellB):
        self.Add(
            node = Number,
            name = 'aResult',
            ref = [cellA, cellB])

Lists of Branches

...

Lists of independent branches

...

    def Assemble(self):
        self.AddList(
            node = MedicationOrder,
            name = 'myMedList')

Lists of dependent branches

...

    def Assemble(self):
        self.AddList(
            node = MedicationOrder,
            name = 'myMedList',
            ref = [self.weight])

Lists with shared branches

...

Snapshots

...

    def Assemble(self, pt):
        self.AddCopy(
            node = Number,
            name = 'currentWt',
            ref = [pt.weight])

Persistence

Mindwrapper applications are typically data-centric without being data-intensive. In other words they require a mechanism for storing and retrieving data, but not access to huge data sets or large numbers of records at a given time. The unit of data persistence in Mindwrapper is the structured data document. In this sense, Mindwrapper applications are much like a spreadsheet templates except that the defined data structures are hierarchical rather than tabular (as are the user interfaces).

Document

...

Folder

...

Cabinet

Directory. ...

Database file. ...


API

Classes:

Methods:

Parameters:

Properties:


SourceForge Logo
XHTML 1.0 © 2003 Donnal C. Walter