diff --git a/scripts/laydi-project/project.py b/scripts/laydi-project/project.py new file mode 100644 index 0000000..5bbaa42 --- /dev/null +++ b/scripts/laydi-project/project.py @@ -0,0 +1,281 @@ +import os, os.path +import sys +import configobj + +from laydi import dataset + +NAME = "laydi-cmd" +VERSION = "0.1.0" +PROJECT_VERSION_STRING = "Laydi project version 1" + +def is_project_directory(dirname): + """Verifies that a directory is a laydi project""" + + if not os.path.isdir(dirname): + return False + + ## Verify that the version is correct. + version_fn = os.path.join(dirname, "VERSION") + if not os.path.exists(version_fn): + return False + fd = open(version_fn) + line = fd.readline() + fd.close() + + if fd.strip() != PROJECT_VERSION_STRING: + return False + + ## Require directories to be present. + if not os.path.isdir(os.path.join(dirname, "annotations")): + return False + if not os.path.isdir(os.path.join(dirname, "data")): + return False + if not os.path.isdir(os.path.join(dirname, "selections")): + return False + if not os.path.isdir(os.path.join(dirname, "exports")): + return False + + ## If no tests failed, return True + return True + + +def make_project_directory(dirname, force=False): + """Creates a project directory + + force: ignore that directory exists and proceed anyway. + """ + if os.path.exists(dirname) and not force: + return False + + rootdir = dirname + anndir = os.path.join(dirname, "annotations") + seldir = os.path.join(dirname, "selections") + datadir = os.path.join(dirname, "data") + exportdir = os.path.join(dirname, "exports") + version_file_path = os.path.join(dirname, "VERSION") + + os.makedirs(rootdir) + for d in [anndir, seldir, datadir, exportdir]: + os.mkdir(d) + + fd = open(version_file_path, "w") + print >> fd, PROJECT_VERSION_STRING + fd.close() + + + +class Universe(object): + """A Universe is a collection of all existing identifiers in a set of datasets""" + + def __init__(self): + self.refcount = {} + + def register_dim(self, dim): + """Increase reference count for identifiers in Dimension object dim""" + d = self.refcount.get(dim.name, None) + if d == None: + d = {} + self.refcount[dim.name] = d + for i in dim: + d[i] = d.get(i, 0) + 1 + + def register_ds(self, ds): + """Increase reference count for identifiers in all Dimensions of dataset ds""" + for dim in ds.dims: + self.register_dim(dim) + + def unregister_dim(self, dim): + """Update reference count for identifiers in Dimension object dim + Update reference count for identifiers in Dimension object dim, and remove all + identifiers with a reference count of 0, as they do not (by definition) exist + any longer. + """ + ids = self.refcount[dim.name] + for i in dim: + refcount = ids[i] + if refcount == 1: + ids.pop(i) + else: + ids[i] -= 1 + if len(ids) == 0: + self.refcount.pop(dim.name) + + + def unregister_ds(self, ds): + """Update reference count for identifiers along Dimensions in Dataset ds. + Update reference count for identifiers along all Dimensions in + Dataset ds, and remove all identifiers with a reference count of 0, + as they do not (by definition) exist any longer. + """ + for dim in ds: + self.register_dim(dim) + + def register(self, obj): + if isinstance(obj, Dataset): + self.register_ds(obj) + else: + self.register_dim(obj) + + def unregister(self, obj): + if isinstance(obj, Dataset): + self.unregister_ds(obj) + else: + self.unregister_dim(obj) + + def __getent___(self, dimname): + return set(self.references[dimname].keys()) + + def __iter__(self): + return self.references.keys().__iter__() + + +class Dimension(object): + """A Dimension represents the set of identifiers an object has along an axis. + """ + def __init__(self, name, ids=[]): + self.name = name + self.idset = set(ids) + self.idlist = list(ids) + + if len(self.idset) != len(self.idlist): + raise Exception("Duplicate identifiers are not allowed") + + def __getitem__(self, element): + return self.idlist[element] + + def __getslice__(self, start, end): + return self.idlist[start:end] + + def __contains__(self, element): + return self.idset.__contains__(element) + + def __str__(self): + return "%s: %s" % (self.name, str(self.idlist)) + + def __len__(self): + return len(self.idlist) + + def __iter__(self): + return iter(self.idlist) + + def intersection(self, dim): + return self.idset.intersection(dim.idset) + + def as_tuple(self): + return (self.name, self.idlist) + + +class DirectoryNotifier(object): + def __init__(self, path): + self.path = path + self.files = {} + self.subdirs = {} + self.timestamp = -1 + self.file_listeners = {} + self.dir_listeners = {} + self.update() + + def update(self): + now = time.time() + for fn in os.listdir(self.path): + if os.getctime(fn) > self.timestamp: + ext = os.path.splitext(fn)[1] + + + def listen_files(self, obj, ext=None): + listeners = self.file_listeners + if listeners.has_key(ext): + listeners[ext].append(obj) + else: + listeners[ext] = [obj] + + + def listen_dirs(self, obj, ext=None): + listeners = self.dir_listeners + if listeners.has_key(ext): + listeners[ext].append(obj) + else: + listeners[ext] = [obj] + + +class DataDirectory(object): + def __init__(self, dirname, recursive=False, universe=None): + self.dirname = dirname + + ## Read datasets, plots and optionally subdirectories + datasets = [] + ds_fn = {} + plots = [] + plot_fn = {} + subdirs = [] + subdir_fn = {} + update_time = 0 + + self.update() + + def update(self): + ## Remember new timestamp. + now = time.time() + + ## Read configuration + ini_fn = os.path.join(dirname, "directory.ini") + if os.path.isfile(ini_fn) and os.getctime(ini_fn) > self.update_time: + self.config = configobj(ini_fn, unrepr=True) + + for fn in os.listdir(self.dirname): + ext = os.path.splitext(fn)[1] + if ext == "ftsv": + ds = dataset.read_ftsv(fn) + if universe is not None: + universe.register_ds(ds) + elif ext == "plot": + plot = configobj(fn, unrepr=True) + plots.append(plot) + elif os.path.isdir(fn) and recursive: + subdirs.append(DataDirectory(fn, recursive=True, universe=universe)) + + ## Set new update time + self.update_time = now + + +def SelectionDirectory(object): + def __init__(self, dirname): + pass + +class Project(object): + def __init__(self, dirname): + """Opens a project directory. The directory must exist and be a valid project.""" + + ## Set path names. + self.rootdir = dirname + self.anndir = os.path.join(dirname, "annotations") + self.seldir = os.path.join(dirname, "selections") + self.datadir = os.path.join(dirname, "data") + self.exportdir = os.path.join(dirname, "exports") + version_file_path = os.path.join(dirname, "VERSION") + + self.universe = Universe() + + self.data = DataDirectory(self.datadir, universe=self.universe, recursive=True) + + def update(self): + for datadir in self.data: + datadir.update() + +## class Dataset +## +## +## class Plot +## +## +## class Selection +## +## +## class Annotation +## +## +## class DataDirectory() +## +## + +