Page tree
Skip to end of metadata
Go to start of metadata

Dynamic Properties

Introduction

Dynamic Properties are one of two types of properties that use Jython scripts for providing special functionality to OpenBIS. To understand the basic concept read about Properties Handled By Scripts.

Defining dynamic properties

To create a dynamic property:

  • Define a property type with appropriate name and data type (Administration->Property Types->New)
  • Define a script which should be used to calculate the value of your dynamic property (Administration->Scripts)
  • Assign the created property type to chosen entity type using the created script (e.g. for samples: Administration->Property Types->Assign to Sample Type):
    1. select Handled By Script checkbox,
    2. select Dynamic Property Evaluator radio option for script type,
    3. choose the appropriate Script (formula)

Creating scripts

To browse and edit existing scripts or add new ones, select Administration->Scripts from the top menu.

The scripts should be written using standard Jython syntax. Unlike custom columns and filters (which also require Jython syntax), dynamic properties can have more than one line of code. If the Script contains only one line, it will be evaluated and used as the value of appropriate dynamic property. If on the other hand a multi line script is needed, the function named "calculate" will be expected and the the result will be used as property value.

To access the entity object from the script, use the following syntax:

entity.<requested method>

Currently available methods that can be called on all kinds of entities include:

  • code()
  • property(propertyTypeCode)
  • propertyValue(propertyTypeCode)
  • propertyRendered(propertyTypeCode)
  • properties()

For more details see IEntityAdaptor (interface implemented by entity) and IEntityPropertyAdaptor (interface implemented by each property).

It is also possible to acces the complete Java Object by calling "entityPE()" method, but this is appropach is not recomended, as the returned value is not a part of a well defined API and may change at any point. You may use it as a workaround, in case some data are not accessible via well defined API, but you should contact OpenBIS helpdesk and comunicate your needs, so the appropriate methods can be added to the official API.

You can test your script on selected entities (samples/experiments/materials/data sets) using the testing environment - part of Script Editor.

 

Simple Examples

  1. Show a value of a Sample property which is named 'Multiplicity'

 

entity.propertyValue('Multiplicity')

 

2. Takes an existing property and multiplies the value by 1.5

float(entity.propertyValue('CONCENTRATION_ORIGINAL_ILLUMINA'))*1.5

 

 

Advanced Examples

  1. Show all entity properties as one dynamic property:

 

def get_properties(e):  
    """Automatically creates entity description"""
    properties = e.properties()
    if properties is None:
        return "No properties defined"
    else:
        result = ""
        for p in properties:
            result = result + "\n" + p.propertyTypeCode() + ": " + p.renderedValue()
        return result
    
def calculate(): 
    """Main script function. The result will be used as the value of appropriate dynamic property."""
    return get_properties(entity) 

 

2. Calculate a new float value based some other values

 

import java.lang.String as String

def calculateValue():  
  nM = 0
  uLDNA = 0
  if entity.propertyValue('CONCENTRATION_PREPARED_ILLUMINA') != '' and \
     entity.propertyValue('FRAGMENT_SIZE_PREPARED_ILLUMINA') != '' :
    nM =  float(entity.propertyValue('CONCENTRATION_PREPARED_ILLUMINA')) /  \
           float(entity.propertyValue('FRAGMENT_SIZE_PREPARED_ILLUMINA')) *  \
           1000000 / 650
    if float(entity.propertyValue('UL_STOCK')) !='' :
      uLDNA =  float(entity.propertyValue('UL_STOCK')) * 2 / nM    
      uLEB = float(entity.propertyValue('UL_STOCK')) - uLDNA
      return String.format("%16.1f", uLEB)
  return 0
 
def calculate(): 
    """Main script function. The result will be used as the value of appropriate dynamic property."""
    return calculateValue()

 

3. Calculate a time difference between two time stamps:

 

from datetime import datetime
def dateTimeSplitter(openbisDate):
  dateAndTime, tz = openbisDate.rsplit(" ", 1)
  pythonDateTime = datetime.strptime(dateAndTime, "%Y-%m-%d %H:%M:%S")  
  return pythonDateTime
def calculate():
  
  try:
    start = entity.propertyValue('FLOW_CELL_SEQUENCED_ON')
    end = entity.propertyValue('SEQUENCER_FINISHED')
    s = dateTimeSplitter(start)
    e = dateTimeSplitter(end)
    diffTime = e-s
    return str(diffTime)
  except:
    return "N/A"

 

4. Illumina NGS Low Plexity Pooling Checker: checks if the complexity of a pooled sample is good enough for a successful run:

def checkBarcodes():
  '''
  'parents' are a HashSet of SamplePropertyPE
  ''' 
  VOCABULARY_INDEX1 = 'BARCODE'
  VOCABULARY_INDEX2 = 'INDEX2'
  RED = set(['A','C'])
  GREEN = set(['T', 'G'])
  SUCCESS_MESSAGE="OK"
  NO_INDEX = "No Index"
  
  listofIndices = []
  boolList = []
  positionList = []
  returnString = " "
  for e in entity.entityPE().parents:
    for s in e.properties:
      if s.entityTypePropertyType.propertyType.simpleCode == VOCABULARY_INDEX1:
        index = s.getVocabularyTerm().code
        if len(listofIndices) > 0:
          for n in range(0,len(index)-1):
            listofIndices[n].append(index[n])
        else:
          for n in range(0,len(index)-1):
            listofIndices.append([index[n]])
       
      # remove any duplicates   
      setofIndices=[set(list) for list in listofIndices]    
 
      # Test whether every element in the set 's' is in the RED set
      boolList=[setofNuc.issubset(RED) for setofNuc in setofIndices]
  if boolList:
    for b in boolList:
      if b:
        positionList.append(boolList.index(b)+1)
        # set the value to False, because 'index' returns only the first occurrence
        boolList[boolList.index(b)]=False
  else:
   return NO_INDEX
    #  if s.entityTypePropertyType.propertyType.simpleCode == VOCABULARY_INDEX2:
    #   pass 
  if positionList:
    for pos in positionList:
      returnString += "WARNING! Base position " + str(pos) + " of " + \
                         VOCABULARY_INDEX1 + \
                         " does not contain both color channels" + \
                         "\n" 
  else:
    returnString = SUCCESS_MESSAGE
  return returnString
def calculate(): 
    """Main script function. The result will be used as the value of appropriate dynamic property."""
    return checkBarcodes() 

 

 

Data Types

Any data type that can be used by openBIS properties is supported by dynamic properties. The script always returns just a string representation of a property value. The value is then validated and in special cases converted before being saved. The string formats and validation rules are the same as in batch import/update of samples/experiments/data sets/materials.

The non-trivial cases are properties with data type:

  • CONTROLLED VOCABULARY - use code of vocabulary term as string representation,
  • MATERIAL - use function material(code, typeCode) to create the string representation.

Creating and Deploying Java Plugins

To create valid Java plugin for Dynamic Properties, one should create a class that is implementing ch.systemsx.cisd.openbis.generic.server.dataaccess.dynamic_property.calculator.api.IDynamicPropertyCalculatorHotDeployPlugin interface. The class should be annotated with  ch.ethz.cisd.hotdeploy.PluginInfo annotation specifying the name of the plugin, and ch.systemsx.cisd.openbis.generic.server.dataaccess.dynamic_property.calculator.api.IDynamicPropertyCalculatorHotDeployPlugin class as a plugin type.

Such a plugin should be exported to a jar file and put into <<openBIS installation directory>>/servers/entity-related-plugins/dynamic-properties directory. The plugin will be detected automatically and will be automatically available to openBIS. No restart is needed.

Dynamic properties evaluator

Evaluation of dynamic properties may be very time consuming, therefore it is not done automatically after each metadata update. To make sure that the potential inconsistencies are repaired, the maintenance task can be defined (service.properties), that runs in specified intervals:

maintenance-plugins = dynamic-property-evaluator

dynamic-property-evaluator.class = ch.systemsx.cisd.openbis.generic.server.task.DynamicPropertyEvaluationMaintenanceTask
# run daily at midnight  
dynamic-property-evaluator.interval = 86400
dynamic-property-evaluator.start = 00:00

If the value of a dynamic property has not yet been calculated, it will be shown as (pending evaluation).

  • No labels