Files
vaiola/modelspace/ModelSpace.py

149 lines
5.5 KiB
Python

from pathlib import Path
import os
from modelspace.Essentials import SetsDict
from modelspace.ModelPackage import ModelPackage
from modelspace.ModelPackageSelector import ModelPackageSelector
from modelspace.ModelSpaceDatabase import ModelSpaceDatabase
from modelspace.Repository import Repository
from modelspace.PathMap import map
class ModelSpace:
def __init__(self, repo: Repository, path, layout):
self.layout = map.get(layout, None)
if not self.layout: raise RuntimeError(f"No such layout: {layout}")
self.repo = repo
self.path = path
self.config_dir = Path(self.path) / '.vaiola'
os.makedirs(self.config_dir, exist_ok=True)
self._copier = self._hard_link_copier
self.db = ModelSpaceDatabase(str(self.config_dir))
self.selector = ModelPackageSelector(self.repo.model_sub_repo, set(self.installed_packages))
self.temp_installed_resources = self.installed_resources
self.temp_files_pending = SetsDict()
self.temp_file_conflicts = SetsDict()
def _reset_selector(self):
self.selector = ModelPackageSelector(self.repo.model_sub_repo, set(self.installed_packages))
self.temp_installed_resources = self.installed_resources
self.temp_files_pending = SetsDict()
self.temp_file_conflicts = SetsDict()
def check_for_file_conflicts(self, pkg_id, path: str):
path = Path(path)
self.temp_files_pending.add(pkg_id, path)
if path.exists() or path in self.temp_files_pending.index:
self.temp_file_conflicts.add(pkg_id, path)
def get_dest_dir(self, package: ModelPackage, no_lineage = False):
type_subdir = self.layout.get(package.package_type, None)
if not type_subdir: raise RuntimeError(f"This type ({package.package_type}) of packages not supported by current modelspace")
lineage_subdir = package.lineage
if no_lineage: return str(self.path / type_subdir)
return str(self.path / type_subdir / lineage_subdir)
def _register_package_in_temp(self, package: ModelPackage):
for resource in package.provides: self.temp_installed_resources.add(resource, package.uuid)
def _deptree(self, resource: str, answer = None, depth = 0) -> set[ModelPackage]:
selected_packages = set()
packages = self.selector.run(resource, answer)
deps = self.repo.model_sub_repo.deps_from_pkg_list(packages)
deps = deps - set(self.temp_installed_resources.keys)
for package in packages: self._register_package_in_temp(package)
for dep in deps:
selected_packages = selected_packages | self._deptree(dep, answer, depth + 1)
return selected_packages | packages
def install(self, resources: str | list[str], depth = 0, answer = None) -> None:
if depth == 0: self._reset_selector()
if isinstance(resources, str): resources = [resources]
selected_packages = set()
for resource in resources:
selected_packages = selected_packages | self._deptree(resource, answer)
files = SetsDict()
for package in selected_packages:
for file in package.files:
files.add(package.uuid, Path(self.get_dest_dir(package, self.layout.no_lineage)) / file)
for pkg_id in files.keys:
for file in files.by_key(pkg_id):
self.check_for_file_conflicts(pkg_id, file)
if len(self.temp_file_conflicts.index) != 0: raise RuntimeError("File conflicts detected")
# TODO run copier
pass
@staticmethod
def _hard_link_copier(source_dir, dest_dir, file_paths):
"""Создает жесткие ссылки для файлов из источника в директорию назначения
Args:
source_dir (str): Путь к директории-источнику
dest_dir (str): Путь к директории-назначению
file_paths (list): Список относительных путей файлов в директории источника
"""
for file_path in file_paths:
# Формируем полный путь к исходному файлу
source_file = os.path.join(source_dir, file_path)
# Формируем полный путь к целевому файлу
dest_file = os.path.join(dest_dir, file_path)
# Создаем необходимые поддиректории в директории назначения
dest_dir_name = os.path.dirname(dest_file)
if dest_dir_name and not os.path.exists(dest_dir_name):
os.makedirs(dest_dir_name)
# Создаем жесткую ссылку
os.link(source_file, dest_file)
@property
def available_packages(self): return self.repo.model_sub_repo.packages
@property
def available_resources(self): return self.repo.model_sub_repo.resources
@property
def installed_manuals(self): return [m[0] if m and len(m) > 0 else None for m in self.db.get_all_manuals()] if self.db.get_all_manuals() else list()
@property
def installed_deps(self): return [m[0] if m and len(m) > 0 else None for m in self.db.get_all_deps()] if self.db.get_all_deps() else list()
@property
def installed_packages(self): return self.installed_manuals + self.installed_deps
@property
def installed_resources(self): return self.repo.model_sub_repo.resources_from_pkg_list(self.installed_packages)