mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 10:27:09 +00:00
The Dougal database will no longer store physical file paths but rather logical ones, relative to (config.yaml).imports.paths. These functions translate between physical and logical paths.
161 lines
5.1 KiB
Python
161 lines
5.1 KiB
Python
import os
|
|
import pathlib
|
|
from glob import glob
|
|
from yaml import full_load as _load
|
|
|
|
"""
|
|
Interface to the instance configuration.
|
|
|
|
The instance configuration is expected to be found under
|
|
$HOME/etc/config.yaml and the configuration for the individual
|
|
surveys should be under $HOME/etc/surveys/*.yaml. In both cases,
|
|
$HOME is the home directory of the user running this script.
|
|
"""
|
|
|
|
prefix = os.environ.get("DOUGAL_ROOT", os.environ.get("HOME", ".")+"/software")
|
|
|
|
DOUGAL_ROOT = os.environ.get("DOUGAL_ROOT", os.environ.get("HOME", ".")+"/software")
|
|
VARDIR = os.environ.get("VARDIR", DOUGAL_ROOT+"/var")
|
|
LOCKFILE = os.environ.get("LOCKFILE", VARDIR+"/runner.lock")
|
|
|
|
def vars ():
|
|
return {
|
|
"DOUGAL_ROOT": DOUGAL_ROOT,
|
|
"VARDIR": VARDIR,
|
|
"LOCKFILE": LOCKFILE
|
|
}
|
|
|
|
def read (file = None):
|
|
if file is None:
|
|
file = prefix+"/etc/config.yaml"
|
|
with open(file, "r") as fd:
|
|
cfg = _load(fd)
|
|
|
|
if "db" in cfg and "connection_string" in cfg["db"] and os.environ.get("PGDATABASE"):
|
|
# This should cause the PostgreSQL library to use the environment variables
|
|
cfg["db"]["connection_string"] = ""
|
|
print("Using environment variables for database connection")
|
|
|
|
return cfg
|
|
|
|
def files (globspec = None, include_archived = False):
|
|
"""
|
|
Read and parse survey configuration files.
|
|
|
|
Arguments:
|
|
|
|
globspec (string): a glob spec matching the selection of files to read.
|
|
If not provided, a default value will be used.
|
|
|
|
include_archived (bool): whether to include surveys marked as archived or
|
|
not. Defaults to ignoring archived surveys.
|
|
|
|
Note that file names starting with `.` or `_` (dot or underscore) will
|
|
never be read (and there is no option to override this). Intended for
|
|
quickly and temporarily “disabling” a survey configuration by renaming
|
|
the relevant file.
|
|
"""
|
|
tuples = []
|
|
|
|
if globspec is None:
|
|
globspec = prefix+'/etc/surveys/*.yaml'
|
|
|
|
for filepath in glob(globspec):
|
|
filepath = os.path.abspath(filepath)
|
|
if os.path.basename(filepath).startswith("_") or os.path.basename(filepath).startswith("."):
|
|
continue
|
|
|
|
survey = read(filepath)
|
|
|
|
if not include_archived and "archived" in survey and survey["archived"] is not False:
|
|
continue
|
|
|
|
tuples.append((filepath, survey))
|
|
|
|
return tuples
|
|
|
|
def surveys (globspec = None, include_archived = False):
|
|
return [i[1] for i in files(globspec, include_archived)]
|
|
|
|
def rxflags (flagstr):
|
|
"""
|
|
Convert flags string into a Python flags argument.
|
|
"""
|
|
flags = 0
|
|
cases = {
|
|
"i": re.I
|
|
}
|
|
for flag in flagstr:
|
|
flags |= cases.get(flag, 0)
|
|
return flags
|
|
|
|
def translate_path (file):
|
|
"""
|
|
Translate a path from a Dougal import directory to an actual
|
|
physical path on disk.
|
|
|
|
Any user files accessible by Dougal must be under a path prefixed
|
|
by `(config.yaml).imports.paths`. The value of `imports.paths` may
|
|
be either a string, in which case this represents the prefix under
|
|
which all Dougal data resides, or a dictionary where the keys are
|
|
logical paths and their values the corresponding physical path.
|
|
"""
|
|
cfg = read()
|
|
root = pathlib.Path(DOUGAL_ROOT)
|
|
filepath = pathlib.Path(file).resolve()
|
|
import_paths = cfg["imports"]["paths"]
|
|
|
|
if filepath.is_absolute():
|
|
if type(import_paths) == str:
|
|
# Substitute the root for the real physical path
|
|
# NOTE: `root` deals with import_paths not being absolute
|
|
prefix = root.joinpath(pathlib.Path(import_paths)).resolve()
|
|
return str(pathlib.Path(prefix).joinpath(*filepath.parts[2:]))
|
|
else:
|
|
# Look for a match on the second path element
|
|
if filepath.parts[1] in import_paths:
|
|
# NOTE: `root` deals with import_paths[…] not being absolute
|
|
prefix = root.joinpath(import_paths[filepath.parts[1]])
|
|
return str(pathlib.Path(prefix).joinpath(*filepath.parts[2:]))
|
|
else:
|
|
# This path is invalid
|
|
raise TypeError("invalid path or file: {0!r}".format(filepath))
|
|
else:
|
|
# A relative filepath is always resolved relative to the logical root
|
|
root = pathlib.Path("/")
|
|
return translate_path(root.joinpath(filepath))
|
|
|
|
def untranslate_path (file):
|
|
"""
|
|
Attempt to convert a physical path into a logical one.
|
|
See `translate_path()` above for details.
|
|
"""
|
|
cfg = read()
|
|
dougal_root = pathlib.Path(DOUGAL_ROOT)
|
|
filepath = pathlib.Path(file).resolve()
|
|
import_paths = cfg["imports"]["paths"]
|
|
physical_root = pathlib.Path("/")
|
|
|
|
if filepath.is_absolute():
|
|
if type(import_paths) == str:
|
|
if filepath.is_relative_to(import_paths):
|
|
physical_root = pathlib.Path("/")
|
|
physical_prefix = pathlib.Path(import_paths)
|
|
return str(root.joinpath(filepath.relative_to(physical_prefix)))
|
|
else:
|
|
raise TypeError("invalid path or file: {0!r}".format(filepath))
|
|
else:
|
|
for key, value in import_paths.items():
|
|
value = dougal_root.joinpath(value)
|
|
physical_prefix = pathlib.Path(value)
|
|
if filepath.is_relative_to(physical_prefix):
|
|
logical_prefix = physical_root.joinpath(pathlib.Path(key)).resolve()
|
|
return str(logical_prefix.joinpath(filepath.relative_to(physical_prefix)))
|
|
|
|
# If we got here with no matches, this is not a valid
|
|
# Dougal data path
|
|
raise TypeError("invalid path or file: {0!r}".format(filepath))
|
|
else:
|
|
# A relative filepath is always resolved relative to DOUGAL_ROOT
|
|
return untranslate_path(root.joinpath(filepath))
|