This repository has been archived on 2024-07-04. You can view files and clone it, but cannot push or open issues or pull requests.
laydi/scripts/laydi-project/project.py

282 lines
7.6 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
##
##