Source code for core.vlab

# -*- python -*-
#
#       OpenAlea.Core
#
#       Copyright 2006-2009 INRIA - CIRAD - INRA
#
#       File author(s): Christophe Pradal <christophe pradal at cirad fr>
#
#       Distributed under the Cecill-C License.
#       See accompanying file LICENSE.txt or copy at
#           http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
#       OpenAlea WebSite : http://openalea.gforge.inria.fr
#
############################################################################
"""doc todo"""

__license__ = "Cecill-C"
__revision__ = " $Id$ "


import os
import sys
import re
from random import randint
from openalea.core.path import path


[docs]def vlab_object(directory, pkgmanager): """ Create an openalea package from a vlab object. First, read the specification file and parse it. Create the list of data, the list of editors, and the list of programs. Build the graph of dependencies. Compute the layout of the graph. Create a package with data and a composite node. """ directory = path(directory) obj = VlabObject2(directory, pkgmanager) return obj # Factories to build nodes from vlab components. # A component may be a data, an editor or a program. # The difference between an editor and a program is that # an editor have the same output value than its input, # and a program may have many inputs but unknow outputs.
[docs]class VlabFile(object): def __init__(self, name): self.name = name self.deps = [] self.editors = {}
[docs]class VlabObject(object): """ A vlab object is a directory containing a specification file, and a set of data. """ text = ['TEXT', 'HINTS'] editors = ['MAP', 'loadmap', 'savemap', 'EDIT', 'SURFACE', 'bezieredit', 'PALETTE', 'palette', 'MEDIT', 'medit', 'GALLERY', 'gallery', 'EDIT', 'funcedit', 'panel', 'CHAR']#panel programs = ['cpfg', 'lpfg'] ignore = [';', '#'] name2program = {'MAP': 'medit', 'SURFACE': 'bezieredit', 'PALETTE': 'palette', 'MEDIT': 'medit', 'GALLERY': 'gallery', 'EDIT': 'edit', 'CHAR': 'edit', 'loadmap': 'palette'} def __init__(self, directory, pkgmanager): self.dir = directory print "Import into OpenAlea the %s directory"%self.dir.basename() self._programs = [] self._files = {} self._text = {} self._editors = {} self.pm = pkgmanager self.sg = None self.sgfactory = None self.factories = [] self._package = None
[docs] def pkgname(self): names = [] def search_name(d): name= d.name if name == 'ext': search_name(d.dirname()) elif (d/'.id').isfile() or (d/'specifications').isfile(): names.insert(0, name) search_name(d.dirname()) else: return d = self.dir search_name(d) _pkgname = 'vlab.'+'.'.join(names) print _pkgname return 'vlab.'+'.'.join(names)
[docs] def get_package(self): if not self._package: self.build_package() self._package.write() return self._package
[docs] def build_package(self): from openalea.core.package import UserPackage # Build MetaData metainfo = dict( version = '', license = '', authors = '', institutes = '', description = '', url = '', ) icons = self.dir.glob('icon.*') if len(icons) > 0: metainfo['icon'] = icons[0].basename() name = self.pkgname() self._package = UserPackage(name, metainfo, str(self.dir)) if not self.sgfactory: self.read_specification() # Add factorie of the dataflow self._package.add_factory(self.sgfactory) # Add data factories there #for f in self.factories: # self._package.add_factory(f)
[docs] def read_specification(self): spec = self.dir / 'specifications' from openalea.core.compositenode import CompositeNodeFactory from openalea.core.compositenode import CompositeNode f = spec.open() self.sg = CompositeNode() #name = self.dir.basename().split('-')[-1] name = self.dir.basename() self.sgfactory = CompositeNodeFactory(name) self.read_files(f) self.read_commands(f) self.process_files() self.build_graph() f.close()
[docs] def read_files(self, f): pattern ='\w+\.\w+' for l in f: if 'ignore:' in l or l is '*': break fn = l.strip() if ':' not in fn and re.match(pattern, fn): self._files[fn]=[]
[docs] def read_commands(self, f): pattern = '\t*[a-zA-Z0-9_ \\-]+:\\s*$' menus = [] for l in f: if re.match(pattern, l): level = l.rstrip().count('\t') menu = l.strip()[:-1] if len(menus) <= level: menus.append(menu) else: menus[level] = menu menus = menus[:level+1] continue # skip ignore files : logic no menus have been created. if not menus: continue cmd = l.strip() if cmd: self.process_command('.'.join(menus), cmd)
[docs] def process_command(self, name, cmd): command = cmd.split() prog = command[0] command[0] = self.name2program.get(prog, prog) if prog in self.programs: self.process_program(name, command) elif prog in self.editors: self.process_editor(name, command) elif prog in self.text: self.process_text(name, command) else: print 'Do not know how to process this command: %s' % cmd
[docs] def process_program(self, name, command): """ Build a process node from the command. """ node = self.pm.get_node("vlab.bin", "process") node.set_input(1, ' '.join(command)) prog_node = self.sg.add_node(node) self._programs.append(prog_node)
[docs] def process_editor(self, name, command): """ Find the file on which the editor works on. """ cmd = ' '.join(command) fn = '' if len(command) > 1: fn = command[-1] if fn not in self._files.keys(): print "WARNING: the file %s used by the editor %s in not in the specification file." %(fn, cmd) return #self._files[fn] = [] prog = command[0] if prog != 'EDIT': node = self.pm.get_node("vlab.bin", "editor") node.set_input(1, cmd) node.set_input(2, str(self.dir)) else: node = self.pm.get_node("vlab.bin", "text editor") filename = self.dir/fn node.set_input(0, str(filename)) edit_node = self.sg.add_node(node) self._editors.setdefault(fn, []).append(edit_node)
[docs] def process_text(self, name, command): node = self.pm.get_node('catalog.file', 'viewfile') text_node = self.sg.add_node(node) filename = command[-1] self._text.setdefault(filename, []).append(text_node)
[docs] def process_files(self): from openalea.core.data import DataFactory deps = self._files files = deps.keys() for f in files: fn = self.dir/f if fn.ext in ['.map', '.txt', '.s', '.e', '.rgb']: continue #binary file or other deps[f] = search(fn, files) self._filenodes = {} for f in files: factory = DataFactory(f) factory.package = self._package self.factories.append(factory) node = self.pm.get_node("vlab.bin", "vlab file stamp") node.set_input(1, str(self.dir/f)) fnode = self.sg.add_node(node) self._filenodes[f] = fnode
[docs] def build_graph(self): """ Specify connections between nodes. """ prog_deps = [] files = self._files.keys() for p in self._programs: cmd = self.sg.node(p).inputs[1].split() fdeps = [f for f in files if f in cmd] for f in fdeps: fnode = self._filenodes[f] self.sg.connect(fnode, 0, p, 0) for f in files: for fdep in self._files[f]: depnode = self._filenodes[fdep] node = self._filenodes[f] self.sg.connect(depnode, 0, node, 0) for f, nodes in self._editors.iteritems(): if not f: # an editor can act withouot a file continue fnode = self._filenodes[f] for node in nodes: self.sg.connect(node, 0, fnode, 0) for f, nodes in self._text.iteritems(): fnode = self._filenodes[f] for node in nodes: self.sg.connect(fnode, 0, node, 0) layout(self) self.sg.to_factory(self.sgfactory)
[docs]def random_layout(obj): sg = obj.sg size = 600 for vid in sg: x, y = randint(0, size), randint(0, size) data = sg.node(vid).internal_data data['posx'] = x data['posy'] = y
min_dx = 100 size=(800, 250)
[docs]def layout(obj): # compute a layout of the graph size = 500 sg= obj.sg dy = 80 y = 250 progs = obj._programs n = len(progs)+1 dx = x = size / n dx = max(min_dx, dx) for vid in progs: data = sg.node(vid).internal_data data['posx'] = x data['posy'] = y x+= dx size = size/n for vid in obj._programs: l = list(sg.in_neighbors(vid)) if not l: continue n1 = sg.node(vid) x0, y0 = n1.internal_data['posx'], n1.internal_data['posy'] dx1 = max(min_dx, size/(2*len(l)+1)) y1 = y0 - dy x1 = x0 - size/2 for node_id in l: data = sg.node(node_id).internal_data data['posx'] = x1 data['posy'] = y1 x1 += dx1 compute_layout(sg, node_id, x1, dx1, y1, dy) # compute layout for nodes which are not connected to a program x = 60 y = 40 for vid in obj._filenodes.values(): data = sg.node(vid).internal_data if not data.get('posx'): data['posx'], data['posy'] = x, y x+= min_dx compute_layout(sg, vid, x, 0, y, dy) # add editor
[docs]def compute_layout(sg, vid, x, dx, y, dy): l = list(sg.in_neighbors(vid)) if not l: return x = x - dx/2 dx /= len(l) dx = max(min_dx, dx) y -= dy for node_id in l: data = sg.node(node_id).internal_data if 'posx' in data: return data['posx'] = x data['posy'] = y x += dx compute_layout(sg, node_id, x, dx, y, dy) #-------------------------------------------------------------------- # new implementation # add files as data with editors inside.
[docs]class VlabObject2(VlabObject): def __init__(self, *args, **kwds): VlabObject.__init__(self, *args, **kwds)
[docs] def read_files(self, f): pattern ='\w+\.\w+' for l in f: if 'ignore:' in l or l is '*': break fn = l.strip() if re.match(pattern, fn) and fn[-1] != ':': self._files[fn] = VlabFile(fn)
[docs] def process_editor(self, name, command): """ Find the file on which the editor works on. """ cmd = ' '.join(command) fn = '' if len(command) > 1: fn = command[-1] if fn not in self._files.keys(): print "WARNING: the file %s used by the editor %s in not in the specification file." %(fn, cmd) prog = command[0] if prog.lower() != 'edit': if fn and fn in self._files: vlabfile = self._files.get(fn) command[-1]="%s" vlabfile.editors[name]=' '.join(command)
[docs] def process_text(self, name, command): pass
[docs] def process_files(self): from openalea.core.data import DataFactory deps = self._files files = deps.keys() for f, vf in deps.iteritems(): assert f[-1] != ':' fn = self.dir/f if fn.ext in ['.map', '.txt', '.s', '.e', '.rgb']: continue #binary file or other vf.deps = search(fn, files) # create the data here # Create vlab data rather than simple data self._filenodes = {} for vf in deps.itervalues(): factory = DataFactory(vf.name, editors=vf.editors) self._package.add_factory(factory) self.factories.append(factory) # TODO: Create data rather than files node = factory.instantiate() #self.pm.get_node("vlab.bin", "vlab file stamp") #node.set_input(1,str(self.dir/f)) node = self.sg.add_node(node) self._filenodes[vf.name] = node vf.node = node
[docs] def build_graph(self): """ Specify connections between nodes. """ prog_deps = [] files = self._files.keys() for p in self._programs: cmd = self.sg.node(p).inputs[1].split() fdeps = [f for f in files if f in cmd] for f in fdeps: fnode = self._filenodes[f] self.sg.connect(fnode, 0, p, 0) for f in files: for fdep in self._files[f].deps: depnode = self._filenodes[fdep] node = self._filenodes[f] self.sg.connect(depnode, 0, node, 2) #for f, nodes in self._editors.iteritems(): # if not f: # an editor can act withouot a file # continue # fnode = self._filenodes[f] # for node in nodes: # self.sg.connect(node,0,fnode,0) #for f, nodes in self._text.iteritems(): # fnode = self._filenodes[f] # for node in nodes: # self.sg.connect(fnode, 0, node, 0) layout(self) self.sg.to_factory(self.sgfactory) # TESTS
[docs]def test1(directory): from openalea.core.pkgmanager import PackageManager pm = PackageManager() pm.init(verbose=False) obj = vlab_object(directory, pm) pkg = obj.get_package() pkg.write()