Overview of Python API
All data are stored in a series of objects. The objects have attributes (strings, floats, booleans, dates, etc.) and links (other objects from the model). Attributes have data types (we have implemented the typing in typeless Python), links have the class of the object(s) at the other end, both have cardinality (maximum and minimum number present for validity), and an isChangeable flag. Singlevalued attributes and links (0..1 optional, or 1..1 mandatory), are implemented as simple attributes, whereas multivalued attributes and links (n..m where n <= m and m > 1) are implemented as lists internally and exported as tuples.
Classes have create functions (python __init__) and delete functions. The create functions take parameters corresponding to attributes and links of the objects, create objects and sets up the attributes and links. Delete does not delete the object as such (that is impossible in Python), but cuts all links to the object from other objects in the model. It also deletes any other object that becomes invalid when the first is deleted, giving a cascading delete.
The internal representation of attributes and links is private, and all access is through functions. This is crucial, as the various functions check the validity of the input. It should be impossible to create invalid data, unless you go out of your way to bypass the API. The Python obj.attr syntax works normally, but we have set up the classes (with 'properties') so that attribute access actually executes the appropriate get or set function. This is a nice feature but not a requirement for other languages, which (unlike Python) might not be allow it. We also allow attributes not defined in the model to be added to individual objects, another feature which will probably be restricted to Python.
The functions available are of the types 'get', 'set', 'add', 'remove', 'findFirst', 'findAll', and 'new' described in more detail below. Functions are not defined where they are not relevant - there is e.g. no 'set' command for parent-child links, as these are set at object creation time and cannot be modified. Certain 'set' functions can only be called by the implementation, either for object creation or when loading data from files.
A singlevalued attribute, say 'task', has functions getTask and setTask. If the attribute has not been set, getTask returns None. Attributes can not be deleted, but can be unset by setting them to None.
A multivalued attribute, say 'keywords', has functions getKeywords, setKeywords, addKeyword, removeKeyword, and pickKeyword (note the use of singular and plural). setKeyword takes a sequence (list or tuple) of keywords as input and (re)sets the keywords list. Again, you can clear the keywords by setting them to 'empty list'. getKeywords returns a copy of the internal list (in actual fact a tuple) so that the internal list is not available for modification. addKeyword appends a new keyword to the end to the list, removeKeyword(value) removes the first occurence of 'value' from the list, and pickKeyword (index) returns the keyword at position 'index' of the list. Note that the order of values in multivalued attribute is defne by the order in which the individual values have been added.
What is said above for attributes also goes for links, but links have additional complications. With a few exceptions, links are two-way. Imagine a link between Sample and Holder, where a Holder can have any number of Samples, and a Sample may optionally have one Holder. Here mySample.setHolder(myHolder) and myHolder.addSample(mySample) will have exactly the same effect - the API checks and sets both ends of the link. mySample.setHolder(None) and myHolder.removeSample(mySample) will also have the same effect, and deleting myHolder will cause all links to it to be reset to None.
Multivalued links have two additional functions, e.g. findFirstSample and findAllSamples (note singular and plural). Both take a series of keyword=value arguments and use them to filter the objects in the link. For example myHolder.findAllSamples(name='test', pH=7.0) would return a list (or rather tuple) of all samples in myHolder.samples that had the name 'test' and pH set to 7.0.
Parents, keys, and factory functions
Every non-abstract class in the model must have a link to another class that serves as the 'parent'. These links are marked with a filled diamond at one end in the class diagrams. The parent-child links form a tree, rooted in Implementation.Project (which has no parent). The parent must be given at object creation time, and remains the same for the lifetime of an object. Every non-abstract class must also have a (set of) attributes and links that serve as a key. The keys must be unique for all objects of the same type that are children of the same parent. The combination of parent links and keys serve to give each object a unique identifier.Every class that has child links will have a set of factory functions that serve to create the child objects. The factory functions to create e.g. a new Sample is named newSample(). The parameters for factory functions is the same as the one for creators, except that the first parameter, the parent object, is omitted.
The Api checks on setting that the type of attributes and the class of links is correct, and that the cardinality is not violated. Objects are validity checked after creation, and mandatory attributes and links must be passed to the object creation function, unless the model has defined a default value. Objects and links may be 'unchangeable', which means they can be set only at objects creation time and cannot be modifed later, and this, too, is checked by the API. The model defines its own data types, which for instance covers enumeration, and these checks are also done.
Links have special problems. When you set or modify a link the API checks that both objects are valid. If you reset a link, i.e. you do obj1.setALink(obj3) when obj1.aLink was already set to obj2, the API checks that all three objects remain valid (including obj2, that will change becaue it loses the link to obj1) and throws an error if the operation can not be done in a valid manner.
Finally, there is a facility for adding ad-hoc constraints, that will also be checked by the API during runtime.
The API does some additional things. There is the notifier system that allows you to set a specified function to be called e.g. every time a Sample is created or that a Holder.name is modified. There is the system for keeping track of which files (or databases) the date are stored in, loading them automatically when they are needed, and marking them as modified (and therefore in need of saving). And there is the series of 'parent-child' links between objects and the definition of a key for each object to allow unique identification of objects, and the way it is used in saving references to objects in different files. There is also a system of 'derived' attributes and links that behave as normal attributes and links, but are effectively convenience functions that calculate useful results not stored explicitly in the data model. As seen from the outside, derived attributes and links behave exactly like normal ones (they may even be settable in some cases), and so allow values to be either stored or calculated on the fly without it making any difference to client code. Finally, every object has a link to ApplicationData objects. These contain an aplication name, a keyword, and a value, and are intended to allow individual applications to store data that are not defined in the model. ApplicationData should not be modified (or indeed accessed) by any application other than the one that wrote them. They will not be be deleted by other applications either (as long as the object that owns them remains alive).
Minimum requirements for other language APIs
Ultimately all APIs should support the full functionality provided it can be done in practice. Not all the aspects here are equally necessary for a minimum API, however. You would definitely need create and delete, set and get, add and remove, whereas findFirst, findAll, and pick could be postponed. The correct management of links is crucial, and the validity checking (except possibly for ad-hoc constraints) is important too. There would have to be a system for file storage and inter-file links, but one could maybe do with a more primitive one to start with. Notifiers and derived links could maybe wait.
Work done by the CCPN team.
Last modified: Tue May 25 18:57:32 CEST 2004