diff options
author | Wojtek Kosior <koszko@koszko.org> | 2022-08-29 13:05:35 +0200 |
---|---|---|
committer | Wojtek Kosior <koszko@koszko.org> | 2022-09-28 12:54:54 +0200 |
commit | 7fc6312d6df526b8eb49288aecf88d04668e7c45 (patch) | |
tree | bc9bda05270991892383839379c101515a440576 /src/hydrilla/proxy/state_impl | |
parent | 367ea85057368047a50ae98a3510e0113eadd744 (diff) | |
download | haketilo-hydrilla-7fc6312d6df526b8eb49288aecf88d04668e7c45.tar.gz haketilo-hydrilla-7fc6312d6df526b8eb49288aecf88d04668e7c45.zip |
[proxy] make it possible to also view and install/uninstall libraries (resources) through the web UI
Diffstat (limited to 'src/hydrilla/proxy/state_impl')
-rw-r--r-- | src/hydrilla/proxy/state_impl/concrete_state.py | 24 | ||||
-rw-r--r-- | src/hydrilla/proxy/state_impl/items.py (renamed from src/hydrilla/proxy/state_impl/mappings.py) | 265 |
2 files changed, 208 insertions, 81 deletions
diff --git a/src/hydrilla/proxy/state_impl/concrete_state.py b/src/hydrilla/proxy/state_impl/concrete_state.py index f180ec6..6bc6404 100644 --- a/src/hydrilla/proxy/state_impl/concrete_state.py +++ b/src/hydrilla/proxy/state_impl/concrete_state.py @@ -47,7 +47,7 @@ from .. import state as st from .. import policies from .. import simple_dependency_satisfying as sds from . import base -from . import mappings +from . import items from . import repos from . import payloads from . import _operations @@ -61,15 +61,6 @@ class ConcreteRepoIterationRef(st.RepoIterationRef): pass -@dc.dataclass(frozen=True, unsafe_hash=True) -class ConcreteResourceRef(st.ResourceRef): - pass - - -@dc.dataclass(frozen=True, unsafe_hash=True) -class ConcreteResourceVersionRef(st.ResourceVersionRef): - pass - @dc.dataclass class ConcreteHaketiloState(base.HaketiloStateWithFields): def __post_init__(self) -> None: @@ -243,17 +234,16 @@ class ConcreteHaketiloState(base.HaketiloStateWithFields): return ConcreteRepoIterationRef(repo_iteration_id) def mapping_store(self) -> st.MappingStore: - return mappings.ConcreteMappingStore(self) + return items.ConcreteMappingStore(self) def mapping_version_store(self) -> st.MappingVersionStore: - return mappings.ConcreteMappingVersionStore(self) + return items.ConcreteMappingVersionStore(self) - def get_resource(self, resource_id: str) -> st.ResourceRef: - return ConcreteResourceRef(resource_id) + def resource_store(self) -> st.ResourceStore: + return items.ConcreteResourceStore(self) - def get_resource_version(self, resource_version_id: str) \ - -> st.ResourceVersionRef: - return ConcreteResourceVersionRef(resource_version_id) + def resource_version_store(self) -> st.ResourceVersionStore: + return items.ConcreteResourceVersionStore(self) def get_payload(self, payload_id: str) -> st.PayloadRef: raise NotImplementedError() diff --git a/src/hydrilla/proxy/state_impl/mappings.py b/src/hydrilla/proxy/state_impl/items.py index eb8b4d2..b538dc5 100644 --- a/src/hydrilla/proxy/state_impl/mappings.py +++ b/src/hydrilla/proxy/state_impl/items.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later -# Haketilo proxy data and configuration (MappingRef and MappingStore subtypes). +# Haketilo proxy data and configuration (ResourceStore and MappingStore +# implementations). # # This file is part of Hydrilla&Haketilo. # @@ -25,7 +26,8 @@ # in a proprietary program, I am not going to enforce this in court. """ -This module provides an interface to interact with mappings inside Haketilo. +This module provides an interface to interact with mappings, and resources +inside Haketilo. """ # Enable using with Python 3.7. @@ -39,6 +41,74 @@ from ... import item_infos from .. import state as st from . import base +def _set_installed_status(cursor: sqlite3.Cursor, id: str, new_status: str) \ + -> None: + cursor.execute( + 'UPDATE item_versions SET installed = ? WHERE item_version_id = ?;', + (new_status, id) + ) + +def _get_statuses(cursor: sqlite3.Cursor, id: str) -> t.Tuple[str, str]: + cursor.execute( + ''' + SELECT + installed, active + FROM + item_versions + WHERE + item_version_id = ?; + ''', + (id,) + ) + + rows = cursor.fetchall() + + if rows == []: + raise st.MissingItemError() + + (installed_status, active_status), = rows + + return installed_status, active_status + +VersionRefVar = t.TypeVar( + 'VersionRefVar', + 'ConcreteResourceVersionRef', + 'ConcreteMappingVersionRef' +) + +def _install_version(ref: VersionRefVar) -> None: + with ref.state.cursor(transaction=True) as cursor: + installed_status, _ = _get_statuses(cursor, ref.id) + + if installed_status == 'I': + return + + _set_installed_status(cursor, ref.id, 'I') + + ref.state.pull_missing_files() + +def _uninstall_version(ref: VersionRefVar) -> t.Optional[VersionRefVar]: + with ref.state.cursor(transaction=True) as cursor: + installed_status, active_status = _get_statuses(cursor, ref.id) + + if installed_status == 'N': + return ref + + _set_installed_status(cursor, ref.id, 'N') + + ref.state.prune_orphans() + + if active_status == 'R': + ref.state.recompute_dependencies() + + cursor.execute( + 'SELECT COUNT(*) FROM item_versions WHERE item_version_id = ?;', + (ref.id,) + ) + + (version_still_present,), = cursor.fetchall() + return ref if version_still_present else None + @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteMappingRef(st.MappingRef): @@ -60,8 +130,8 @@ class ConcreteMappingRef(st.MappingRef): ive.is_local, ms.enabled FROM - item_versions_extra AS ive - JOIN mapping_statuses AS ms USING (item_id) + item_versions_extra AS ive + JOIN mapping_statuses AS ms USING (item_id) WHERE ive.item_id = ?; ''', @@ -111,6 +181,9 @@ class ConcreteMappingStore(st.MappingStore): with self.state.cursor() as cursor: cursor.execute( ''' + WITH available_item_ids AS ( + SELECT DISTINCT item_id FROM item_versions + ) SELECT i.item_id, i.identifier, @@ -128,8 +201,9 @@ class ConcreteMappingStore(st.MappingStore): JOIN mapping_statuses AS ms USING (item_id) LEFT JOIN item_versions_extra AS ive - ON ms.active_version_id = ive.item_version_id AND - ive.active IN ('R', 'A'); + ON ms.active_version_id = ive.item_version_id + WHERE + i.item_id IN available_item_ids; ''' ) @@ -181,79 +255,142 @@ class ConcreteMappingStore(st.MappingStore): @dc.dataclass(frozen=True, unsafe_hash=True) class ConcreteMappingVersionRef(st.MappingVersionRef): - state: base.HaketiloStateWithFields = dc.field(hash=False, compare=False) + state: base.HaketiloStateWithFields - def _set_installed_status(self, cursor: sqlite3.Cursor, new_status: str) \ - -> None: - cursor.execute( - ''' - UPDATE - item_versions - SET - installed = ? - WHERE - item_version_id = ?; - ''', - (new_status, self.id,) - ) + def install(self) -> None: + return _install_version(self) - def _get_statuses(self, cursor: sqlite3.Cursor) -> t.Tuple[str, str]: - cursor.execute( - ''' - SELECT - installed, active - FROM - item_versions - WHERE - item_version_id = ?; - ''', - (self.id,) - ) + def uninstall(self) -> t.Optional['ConcreteMappingVersionRef']: + return _uninstall_version(self) - rows = cursor.fetchall() + def get_all_version_display_infos(self) \ + -> t.Sequence[st.MappingVersionDisplayInfo]: + with self.state.cursor() as cursor: + cursor.execute( + ''' + SELECT + item_id + FROM + item_versions + WHERE + item_version_id = ?; + ''', + (self.id,) + ) - if rows == []: - raise st.MissingItemError() + rows = cursor.fetchall() + if rows == []: + raise st.MissingItemError() - (installed_status, active_status), = rows + (mapping_id,), = rows - return installed_status, active_status + mapping_ref = ConcreteMappingRef(str(mapping_id), self.state) - def install(self) -> None: - with self.state.cursor(transaction=True) as cursor: - installed_status, _ = self._get_statuses(cursor) + return mapping_ref.get_version_display_infos() + + +@dc.dataclass(frozen=True) +class ConcreteMappingVersionStore(st.MappingVersionStore): + state: base.HaketiloStateWithFields + + def get(self, id: str) -> st.MappingVersionRef: + return ConcreteMappingVersionRef(str(int(id)), self.state) + + +@dc.dataclass(frozen=True, unsafe_hash=True) +class ConcreteResourceRef(st.ResourceRef): + state: base.HaketiloStateWithFields = dc.field(hash=False, compare=False) - if installed_status == 'I': - return + def get_version_display_infos(self) \ + -> t.Sequence[st.ResourceVersionDisplayInfo]: + with self.state.cursor() as cursor: + cursor.execute( + ''' + SELECT + ive.item_version_id, + ive.definition, + ive.repo, + ive.repo_iteration, + ive.installed, + ive.active, + ive.is_orphan, + ive.is_local + FROM + item_versions_extra AS ive + JOIN items AS i USING (item_id) + WHERE + i.type = 'R' AND ive.item_id = ?; + ''', + (self.id,) + ) - self._set_installed_status(cursor, 'I') + rows = cursor.fetchall() - self.state.pull_missing_files() + if rows == []: + raise st.MissingItemError() - def uninstall(self) -> None: - with self.state.cursor(transaction=True) as cursor: - installed_status, active_status = self._get_statuses(cursor) + result = [] - if installed_status == 'N': - return + for (item_version_id, definition, repo, repo_iteration, + installed_status, active_status, is_orphan, is_local) in rows: + ref = ConcreteResourceVersionRef(str(item_version_id), self.state) - self._set_installed_status(cursor, 'N') + item_info = item_infos.ResourceInfo.load( + definition, + repo, + repo_iteration + ) - self.state.prune_orphans() + display_info = st.ResourceVersionDisplayInfo( + ref = ref, + info = item_info, + installed = st.InstalledStatus(installed_status), + active = st.ActiveStatus(active_status), + is_orphan = is_orphan, + is_local = is_local + ) + result.append(display_info) + + return sorted(result, key=(lambda di: di.info)) + + +@dc.dataclass(frozen=True) +class ConcreteResourceStore(st.ResourceStore): + state: base.HaketiloStateWithFields - if active_status == 'R': - self.state.recompute_dependencies() + def get(self, id: str) -> st.ResourceRef: + return ConcreteResourceRef(str(int(id)), self.state) + def get_display_infos(self) -> t.Sequence[st.ResourceDisplayInfo]: + with self.state.cursor() as cursor: cursor.execute( - 'SELECT COUNT(*) FROM item_versions WHERE item_version_id = ?;', - (self.id,) + "SELECT item_id, identifier FROM items WHERE type = 'R';" ) - (version_still_present,), = cursor.fetchall() - return self if version_still_present else None + rows = cursor.fetchall() + + result = [] + + for item_id, identifier in rows: + ref = ConcreteResourceRef(str(item_id), self.state) + + result.append(st.ResourceDisplayInfo(ref, identifier)) + + return sorted(result, key=(lambda di: di.identifier)) + + +@dc.dataclass(frozen=True, unsafe_hash=True) +class ConcreteResourceVersionRef(st.ResourceVersionRef): + state: base.HaketiloStateWithFields + + def install(self) -> None: + return _install_version(self) + + def uninstall(self) -> t.Optional['ConcreteResourceVersionRef']: + return _uninstall_version(self) def get_all_version_display_infos(self) \ - -> t.Sequence[st.MappingVersionDisplayInfo]: + -> t.Sequence[st.ResourceVersionDisplayInfo]: with self.state.cursor() as cursor: cursor.execute( ''' @@ -271,16 +408,16 @@ class ConcreteMappingVersionRef(st.MappingVersionRef): if rows == []: raise st.MissingItemError() - (mapping_id,), = rows + (resource_id,), = rows - mapping_ref = ConcreteMappingRef(str(mapping_id), self.state) + resource_ref = ConcreteResourceRef(str(resource_id), self.state) - return mapping_ref.get_version_display_infos() + return resource_ref.get_version_display_infos() @dc.dataclass(frozen=True) -class ConcreteMappingVersionStore(st.MappingVersionStore): +class ConcreteResourceVersionStore(st.ResourceVersionStore): state: base.HaketiloStateWithFields - def get(self, id: str) -> st.MappingVersionRef: - return ConcreteMappingVersionRef(str(int(id)), self.state) + def get(self, id: str) -> st.ResourceVersionRef: + return ConcreteResourceVersionRef(str(int(id)), self.state) |