Source code for openalea.rsml.metadata

"""
Manage rsml metadata attributes

The metadata element in rsml contains some mandatory content and optional ones.
In the IO operation from openalea.rsml file to&from mtg, these metadata are looked for
in the 'metadata' graph property of the mtg::

    mtg.graph_properties()['metadata']


Mandatory metadata:
 - version: set 1.0 
 - unit:    default is 'pixel'
 - resolution: default is 1
 - software:   default is 'openalea'
 - user:       default is ''
 - file-key:   default is a random unique identifier (uuid4)
 
Optional metadata:
 - property_definition: ....
 - function_definition: ....
 - time_sequence: ...
 
`set_metadata` can be used to retrieve the metadata dictionary. This
function also fill missing items, folowing the specified behavior describe in
the function documentation.
"""

# ordered list of metadata attribute name
flat_metadata = ['version','unit','resolution','software','user',
                   'last-modified','file-key']
metadata_names = flat_metadata + ['image', 'property-definitions',
                                  'function-definitions', 'time-sequence',
                                  'observation-hours',
                                  'private']

# default values
default = {'version':1.0,
           'unit':'pixel',
           'resolution':1,
           'software':'openalea',
           'user':'',
           'file-key':''}
#           'image':''}
#           'last-modified':'',


[docs] def set_metadata(g): """ Set the rsml 'metadata' element from the graph-properties of mtg `g` The rsml metadata is a dictionary equivalent to the metadata xml element of rsml. Its main purpose is to be used by `rsml.io.Dumper`. This dictionary is constructed from the 'metadata' item of `g.graph_properties()`, if it exist. Then it adds missing items. In particular: - it set the 'last-modified' item to current time - if missing, it set the 'file-key' item to a random unique id (uuid4) - if the 'image' item is a string, convert it to a dict with item 'name' - if 'image.captured' is missing, try to set it to the file 'image.name' creation time, if the file exists. - if 'image.sha256' is missing, try to set it to file 'image.name' hash This function update given mtg `g` in-place and returns its updated 'metadata' graph properties. """ from datetime import datetime # metadata from graph properties and default values metadata = default.copy() metadata.update(g.graph_properties().get('metadata',{})) # image metadata if 'image' in metadata: if isinstance(metadata['image'],str): metadata['image'] = dict(name=metadata['image']) image = metadata['image'] if 'sha256' not in image: try: import hashlib with open(image['name']) as f: sha256 = hashlib.sha256(f.read()) image['sha256'] = sha256.hexdigest() except IOError: # no such file pass if 'captured' not in image: try: from os.path import getctime creation = getctime(image['name']) image['captured'] = datetime.fromtimestamp(creation).isoformat() except KeyError: # not defined pass except OSError: # no such file pass if 'observation-hours' in metadata: # table of observation times obs = metadata['observation-hours'] if isinstance(obs, str): import ast metadata['observation-hours'] = list(ast.literal_eval(obs)) # convert string to list if metadata['file-key']!=default['file-key']: import uuid metadata['file-key'] = uuid.uuid4() # time stamp metadata['last-modified'] = datetime.now().isoformat() g.graph_properties()['metadata'] = metadata return metadata
[docs] def add_property_definition(g, label, type, unit=None, default=None): """ add a rsml property definition to mtg `g` :Inputs: - `label`: The label of the property - `type`: Either a string of the rsml type, or a python type object for which the suitable rsml type is selected using the `rsml_type` function. """ prop = dict(type=type if isinstance(type,str) else rsml_type(type)) if unit is not None: prop['unit'] = unit if default is not None: prop['default'] = default gmeta = g.graph_properties().setdefault('metadata',{}) prop_def = gmeta.setdefault('property-definitions', {}) prop_def[label] = prop
[docs] def rsml_type(python_type): """ Automatically select rsml type for the given `python_type` """ if issubclass(python_type, bool): return 'boolean' elif issubclass(python_type, int): return 'integer' elif issubclass(python_type, int): return 'integer' elif issubclass(python_type, float): return 'real' else: from datetime import datetime if issubclass(python_type, datetime): return 'datetime' else: return 'string'
_literal_types = set([type(None),bool,int,float,int,complex,str,str])
[docs] def filter_literal(obj, default=None): """ return given `obj` with only "literal" types The output can be safely converted to string and evaluated using `ast.literal_eval`:: import ast safe_obj = filter_literal(obj) assert ast.literal_eval(repr(save_obj))==safe_obj literal types are strings, number, tuples, lists, dicts, booleans and None For non-literal content: - object with an `iteritems` attribute are parsed and converted to dict - object with an `__iter__` attribute are parsed and converted to list - the rest is replaced by given `default` """ if type(obj) in _literal_types: return obj elif isinstance(obj,tuple): return tuple([filter_literal(v,default) for v in obj]) elif hasattr(obj,'iteritems'): return dict((k,filter_literal(v,default)) for k,v in obj.items()) elif hasattr(obj,'__iter__'): return [filter_literal(v,default) for v in obj] else: return filter_literal(default)