Source code for core.project.manager

# -*- python -*-
#
#       OpenAlea.OALab: Multi-Paradigm GUI
#
#       Copyright 2014-2015 INRIA - CIRAD - INRA
#
#       File author(s): Julien Coste <julien.coste@inria.fr>
#
#       File contributor(s): Guillaume Baty <guillaume.baty@inria.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
#
###############################################################################

from openalea.core.manager import GenericManager, UnknownItemError


from ConfigParser import NoSectionError, NoOptionError
import os
import sys

from openalea.core import settings
from openalea.core.control.manager import ControlManager
from openalea.core.path import path as Path
from openalea.core.project.project import Project
from openalea.core.service.ipython import interpreter
from openalea.core.service.plugin import plugins
from openalea.core.settings import get_openalea_tmp_dir


[docs]def get_criteria(project): criteria = {} for criterion in Project.DEFAULT_METADATA.keys() + ['path']: criteria[criterion] = getattr(project, criterion) return criteria
[docs]class ProjectManager(GenericManager): def __init__(self, items=None, item_proxy=None, autoload=['entry_points']): # GenericManager GenericManager.__init__(self, items, item_proxy, autoload) self.default_group = "local" # ProjectManager self.tmpdir = Path(get_openalea_tmp_dir()) self._cproject = None self._cwd = Path('.').abspath() self.old_syspath = sys.path self.cm = ControlManager() self.repositories = self.search_path() self.previous_project = "temp" self.shell = interpreter() self.cproject = None
[docs] def generate_item_id(self, item): return str(item.path)
[docs] def load_items(self, group=None): raise NotImplementedError
[docs] def instantiate(self, item): return item
[docs] def patch_item(self, item): if not hasattr(item, "label"): if hasattr(item, "alias"): item.label = item.alias else: item.label = item.name.replace('_', ' ').capitalize() if not hasattr(item, "criteria"): item.__class__.criteria = property(fget=get_criteria) GenericManager.patch_item(self, item)
[docs] def discover(self, group=None, config_name='oaproject.cfg'): """ Discover projects from your disk and put them in self.projects. Projects are not loaded, only metadata are. :use: >>> project_manager.discover() >>> list_of_projects = project_manager.projects To discover new projects, you can add path into *self.repositories* .. code-block:: python project_manager.repositories.append('path/to/search/projects') project_manager.discover() """ for _path in self.repositories: _path = Path(_path) if not _path.exists(): continue for p in _path.walkfiles(config_name): project = Project(p.parent) self.add(project, self.default_group)
@staticmethod
[docs] def search_path(): """ Return a list of all path containing projects """ repositories = set() # 1. Add default user project dir repositories.add(Path(settings.get_project_dir())) # 2. Add project repositories defined by packages for plugin in plugins('oalab.plugin', criteria=dict(implement="ProjectRepositoryList")): for repository in plugin(): repositories.add(repository) # 3. Read repositories defined by users and saved in config config = settings.Settings() lst = list(repositories) try: s = config.get("ProjectManager", "Path") lst = eval(s, {"path": Path}) except NoSectionError: config.add_section("ProjectManager") config.add_option("ProjectManager", "Path", str([str(path) for path in lst])) except NoOptionError: config.add_option("ProjectManager", "Path", str([str(path) for path in lst])) for repo in lst: repositories.add(repo) # Remove all paths to directories that don't exist final_list = set() for p in repositories: p = Path(p).abspath() if not p.isdir(): continue final_list.add(p) return list(final_list)
@property
[docs] def defaultdir(self): return Path(settings.get_project_dir())
[docs] def load_settings(self): config = settings.Settings() try: self.previous_project = config.get("ProjectManager", "Last Project") except (settings.NoSectionError, settings.NoOptionError): pass
[docs] def write_settings(self): """ Add a new path to the settings. """ lst = list(set(self.repositories)) lst = map(str, lst) config = settings.Settings() config.set("ProjectManager", "Path", str(lst)) try: config.set("ProjectManager", "Last Project", str(self.previous_project)) except settings.NoSectionError as e: config.add_section("ProjectManager") config.add_option("ProjectManager", "Last Project", str(self.previous_project)) except settings.NoOptionError as e: config.add_option("ProjectManager", "Last Project", str(self.previous_project)) config.write() ########################################################################### # TO CLEAN ###########################################################################
[docs] def default(self): """ :return: a default empty project """ # TODO: should define a generic "create" method that returns a patched item. # for the moment, patch it manually _path = self.defaultdir proj = Project(_path / "temp") proj.label = "Temporary Project" if not proj.path.exists(): txt = '''""" OpenAlea Lab editor This temporary script is saved in temporary project in %s You can rename/move this project thanks to the button "Save As" in menu. """''' % str(proj.path) proj.add("model", filename="model.py", content=txt) proj.save() return proj
[docs] def create(self, name, projectdir=None, **kwargs): """ Create new project and return it. :use: >>> project1 = project_manager.create('project1') >>> project2 = project_manager.create('project2', '/path/to/project') :param name: name of project to create (str) :param path: path where project will be saved. By default, path is the user path of all projects ($HOME/.openalea/projects/). :return: Project """ if projectdir is None: projectdir = self.defaultdir else: projectdir = Path(projectdir).abspath() if projectdir not in self.repositories: self.repositories.append(projectdir) project = Project(projectdir / name, **kwargs) self.patch_item(project) return project
[docs] def load_default(self): self.discover() self.load_settings() try: project = self.item(self.previous_project) except UnknownItemError: project = self.default() return project
[docs] def load(self, name, projectdir=None, **kwargs): """ Load existing project :use: >>> project1 = project_manager.load('project1') >>> project2 = project_manager.load('project2', '/path/to/project') # doctest: +SKIP :param name: name of project to load. Must be a string. :param projectdir: path of project to load. Must be a path (see module path.py). By default, try to guess with name only. If there are various projects with the same name, return the first. :return: Project """ project = None if 'proj_path' in kwargs: projectdir = kwargs['proj_path'] elif 'path' in kwargs: projectdir = kwargs['path'] if not projectdir: try: project = self.item(name) except UnknownItemError: pass else: full_path = Path(projectdir) / name if full_path.exists(): project = Project(full_path) else: print 'Project %s in repository %s does not exist' % (name, projectdir) if project: self.cproject = project return self.cproject
[docs] def close(self, name=None, proj_path=None): """ Close current project. :TODO: not yet implemented """ # TODO: cleaner! self.cproject = None
def __getitem__(self, name): try: return self.item(name) except UnknownItemError: return None
[docs] def clear(self): """ Clear the list of projects. """ GenericManager.clear(self) self.cproject = self.default()
[docs] def notify(self, sender, event=None): signal, data = event if signal == 'project_changed': self.notify_listeners(('project_updated', self)) self.update_namespace(self.shell)
def _close_project(self): os.chdir(self._cwd) if self._cproject: self.notify_listeners(('close_project', self)) self._cproject.unregister_listener(self) self._cproject.stop() self.clear_namespace(self.shell, self._cproject) del self._cproject self._cproject = None self.notify_listeners(('project_closed', self)) @property def cproject(self): return self._cproject @cproject.setter
[docs] def cproject(self, project): if project is self._cproject: if project and not project.started: self.notify_listeners(('start_project', self)) project.start(shell=self.shell) self.notify_listeners(('project_started', self)) self.update_namespace(self.shell) return if project is None: self._close_project() else: self._close_project() if project.path.isdir(): os.chdir(project.path) self._cproject = project if not project.started: project.start(shell=self.shell) project.register_listener(self) self.update_namespace(self.shell) self.notify_listeners(('project_changed', self))
[docs] def clear_namespace(self, interpreter, project): if project is None: return sys.path = self.old_syspath for k in project.ns.keys() + ['world', 'data', 'project']: if k in interpreter.user_ns: del interpreter.user_ns[k] from openalea.core.world import World world = World() world.clear()
[docs] def update_namespace(self, interpreter): """ Definition: Update namespace """ if self._cproject: if self._cproject.path.exists(): os.chdir(self._cproject.path) sys.path.insert(0, str(self._cproject.path / 'lib')) else: os.chdir(self.tmpdir) sys.path.insert(0, str(self.tmpdir / 'lib')) interpreter.user_ns.update(self._cproject.ns) interpreter.user_ns['project'] = self._cproject interpreter.user_ns['data'] = self._cproject.path / 'data' from openalea.core.world import World world = World() world.update_namespace(interpreter)
[docs]def main(): import sys from openalea.vpltk.qt import QtGui from openalea.core.service.ipython import interpreter from openalea.oalab.shell import ShellWidget # Create Window with IPython shell app = QtGui.QApplication(sys.argv) interp = interpreter() shellwdgt = ShellWidget(interp) mainWindow = QtGui.QMainWindow() mainWindow.setCentralWidget(shellwdgt) mainWindow.show() # Create Project Manager pm = ProjectManager() # Create or load project name = "project_test" pm.load(name) app.exec_()
if (__name__ == "__main__"): main()