aboutsummaryrefslogtreecommitdiff
path: root/src/hydrilla/proxy
diff options
context:
space:
mode:
authorWojtek Kosior <koszko@koszko.org>2022-09-27 13:42:55 +0200
committerWojtek Kosior <koszko@koszko.org>2022-09-29 10:47:17 +0200
commit00487547c4aff6bf0c94438768191960a3369365 (patch)
treeb8a27aa19258a27101bc5634e87fa1f9509457f2 /src/hydrilla/proxy
parentfae35b53a4f63f60f0da96e75a0c2fc310217578 (diff)
downloadhaketilo-hydrilla-00487547c4aff6bf0c94438768191960a3369365.tar.gz
haketilo-hydrilla-00487547c4aff6bf0c94438768191960a3369365.zip
[proxy] facilitate manually pruning orphaned packages (including installed ones)
Diffstat (limited to 'src/hydrilla/proxy')
-rw-r--r--src/hydrilla/proxy/state.py15
-rw-r--r--src/hydrilla/proxy/state_impl/_operations/prune_orphans.py82
-rw-r--r--src/hydrilla/proxy/state_impl/base.py2
-rw-r--r--src/hydrilla/proxy/state_impl/concrete_state.py28
-rw-r--r--src/hydrilla/proxy/state_impl/items.py2
-rw-r--r--src/hydrilla/proxy/state_impl/repos.py2
-rw-r--r--src/hydrilla/proxy/web_ui/root.py10
-rw-r--r--src/hydrilla/proxy/web_ui/templates/index.html.jinja33
8 files changed, 135 insertions, 39 deletions
diff --git a/src/hydrilla/proxy/state.py b/src/hydrilla/proxy/state.py
index 7613d54..559a546 100644
--- a/src/hydrilla/proxy/state.py
+++ b/src/hydrilla/proxy/state.py
@@ -467,6 +467,7 @@ class MappingUseMode(Enum):
WHEN_ENABLED = 'W'
QUESTION = 'Q'
+
@dc.dataclass(frozen=True)
class HaketiloGlobalSettings:
"""...."""
@@ -481,6 +482,12 @@ class MissingItemError(ValueError):
pass
+@dc.dataclass(frozen=True)
+class OrphanItemsStats:
+ mappings: int
+ resources: int
+
+
class HaketiloState(ABC):
"""...."""
@abstractmethod
@@ -488,6 +495,14 @@ class HaketiloState(ABC):
...
@abstractmethod
+ def count_orphan_items(self) -> OrphanItemsStats:
+ ...
+
+ @abstractmethod
+ def prune_orphan_items(self) -> None:
+ ...
+
+ @abstractmethod
def rule_store(self) -> RuleStore:
...
diff --git a/src/hydrilla/proxy/state_impl/_operations/prune_orphans.py b/src/hydrilla/proxy/state_impl/_operations/prune_orphans.py
index 7123047..5eb8cf7 100644
--- a/src/hydrilla/proxy/state_impl/_operations/prune_orphans.py
+++ b/src/hydrilla/proxy/state_impl/_operations/prune_orphans.py
@@ -37,37 +37,52 @@ import sqlite3
from pathlib import Path
-_remove_item_versions_sqls = [
- '''
- CREATE TEMPORARY TABLE __removed_versions(
- item_version_id INTEGER PRIMARY KEY
- );
- ''', '''
- INSERT INTO
- __removed_versions
- SELECT
- iv.item_version_id
- FROM
- item_versions AS iv
- JOIN orphan_iterations AS oi USING (repo_iteration_id)
- WHERE
- iv.installed != 'I';
- ''', '''
- UPDATE
- mapping_statuses
- SET
- active_version_id = NULL
- WHERE
- active_version_id IN __removed_versions;
- ''', '''
- DELETE FROM
- item_versions
- WHERE
- item_version_id IN __removed_versions;
- ''', '''
- DROP TABLE __removed_versions;
- '''
-]
+def _remove_item_versions(cursor: sqlite3.Cursor, with_installed: bool) -> None:
+ cursor.execute(
+ '''
+ CREATE TEMPORARY TABLE __removed_versions(
+ item_version_id INTEGER PRIMARY KEY
+ );
+ '''
+ )
+
+ condition = "iv.active != 'R'" if with_installed else "iv.installed != 'I'"
+
+ cursor.execute(
+ f'''
+ INSERT INTO
+ __removed_versions
+ SELECT
+ iv.item_version_id
+ FROM
+ item_versions AS iv
+ JOIN orphan_iterations AS oi USING (repo_iteration_id)
+ WHERE
+ {condition};
+ '''
+ )
+
+ cursor.execute(
+ '''
+ UPDATE
+ mapping_statuses
+ SET
+ active_version_id = NULL
+ WHERE
+ active_version_id IN __removed_versions;
+ '''
+ )
+
+ cursor.execute(
+ '''
+ DELETE FROM
+ item_versions
+ WHERE
+ item_version_id IN __removed_versions;
+ '''
+ )
+
+ cursor.execute('DROP TABLE __removed_versions;')
_remove_items_sql = '''
WITH removed_items AS (
@@ -159,11 +174,10 @@ WHERE
repo_id IN removed_repos;
'''
-def prune_orphans(cursor: sqlite3.Cursor) -> None:
+def prune_orphans(cursor: sqlite3.Cursor, aggressive: bool = False) -> None:
assert cursor.connection.in_transaction
- for sql in _remove_item_versions_sqls:
- cursor.execute(sql)
+ _remove_item_versions(cursor, with_installed=aggressive)
cursor.execute(_remove_items_sql)
cursor.execute(_remove_files_sql)
cursor.execute(_forget_files_data_sql)
diff --git a/src/hydrilla/proxy/state_impl/base.py b/src/hydrilla/proxy/state_impl/base.py
index d603504..d99feab 100644
--- a/src/hydrilla/proxy/state_impl/base.py
+++ b/src/hydrilla/proxy/state_impl/base.py
@@ -227,7 +227,7 @@ class HaketiloStateWithFields(st.HaketiloState):
...
@abstractmethod
- def prune_orphans(self) -> None:
+ def soft_prune_orphan_items(self) -> None:
...
@abstractmethod
diff --git a/src/hydrilla/proxy/state_impl/concrete_state.py b/src/hydrilla/proxy/state_impl/concrete_state.py
index 04bb67f..d8706e8 100644
--- a/src/hydrilla/proxy/state_impl/concrete_state.py
+++ b/src/hydrilla/proxy/state_impl/concrete_state.py
@@ -146,7 +146,33 @@ class ConcreteHaketiloState(base.HaketiloStateWithFields):
self.rebuild_structures(rules=False)
- def prune_orphans(self) -> None:
+ def count_orphan_items(self) -> st.OrphanItemsStats:
+ with self.cursor() as cursor:
+ cursor.execute(
+ '''
+ SELECT
+ COALESCE(SUM(i.type = 'M'), 0),
+ COALESCE(SUM(i.type = 'R'), 0)
+ FROM
+ item_versions AS iv
+ JOIN items AS i USING (item_id)
+ JOIN orphan_iterations AS oi USING (repo_iteration_id)
+ WHERE
+ iv.active != 'R';
+ '''
+ )
+
+ (orphan_mappings, orphan_resources), = cursor.fetchall()
+
+ return st.OrphanItemsStats(orphan_mappings, orphan_resources)
+
+ def prune_orphan_items(self) -> None:
+ with self.cursor(transaction=True) as cursor:
+ _operations.prune_orphans(cursor, aggressive=True)
+
+ self.recompute_dependencies()
+
+ def soft_prune_orphan_items(self) -> None:
with self.cursor() as cursor:
assert self.connection.in_transaction
diff --git a/src/hydrilla/proxy/state_impl/items.py b/src/hydrilla/proxy/state_impl/items.py
index e9cabb5..d312db9 100644
--- a/src/hydrilla/proxy/state_impl/items.py
+++ b/src/hydrilla/proxy/state_impl/items.py
@@ -124,7 +124,7 @@ def _uninstall_version(ref: VersionRefVar) -> t.Optional[VersionRefVar]:
_set_installed_status(cursor, ref.id, 'N')
- ref.state.prune_orphans()
+ ref.state.soft_prune_orphan_items()
if active_status != 'N':
ref.state.recompute_dependencies()
diff --git a/src/hydrilla/proxy/state_impl/repos.py b/src/hydrilla/proxy/state_impl/repos.py
index 998548e..85117c8 100644
--- a/src/hydrilla/proxy/state_impl/repos.py
+++ b/src/hydrilla/proxy/state_impl/repos.py
@@ -194,7 +194,7 @@ class ConcreteRepoRef(st.RepoRef):
(self.id,)
)
- self.state.prune_orphans()
+ self.state.soft_prune_orphan_items()
self.state.recompute_dependencies()
def update(
diff --git a/src/hydrilla/proxy/web_ui/root.py b/src/hydrilla/proxy/web_ui/root.py
index a28fde8..24ff73f 100644
--- a/src/hydrilla/proxy/web_ui/root.py
+++ b/src/hydrilla/proxy/web_ui/root.py
@@ -111,7 +111,13 @@ app_lock = Lock()
@app.route('/', methods=['GET'])
def home(errors: t.Mapping[str, bool] = {}) -> werkzeug.Response:
- html = flask.render_template('index.html.jinja', **errors)
+ state = _app.get_haketilo_state()
+
+ html = flask.render_template(
+ 'index.html.jinja',
+ orphan_item_stats = state.count_orphan_items(),
+ **errors
+ )
return flask.make_response(html, 200)
@app.route('/', methods=['POST'])
@@ -134,6 +140,8 @@ def home_post() -> werkzeug.Response:
state.update_settings(advanced_user=True)
elif action == 'user_make_simple':
state.update_settings(advanced_user=False)
+ elif action == 'prune_orphans':
+ state.prune_orphan_items()
else:
raise ValueError()
diff --git a/src/hydrilla/proxy/web_ui/templates/index.html.jinja b/src/hydrilla/proxy/web_ui/templates/index.html.jinja
index d95937a..ce77b24 100644
--- a/src/hydrilla/proxy/web_ui/templates/index.html.jinja
+++ b/src/hydrilla/proxy/web_ui/templates/index.html.jinja
@@ -129,4 +129,37 @@ code in a proprietary work, I am not going to enforce this in court.
{'action': 'user_make_simple'})
])
}}
+
+ {% if orphan_item_stats.mappings > 0 or orphan_item_stats.resources > 0 %}
+ <div class="horizontal-separator"></div>
+
+ <p>
+ {% if settings.advanced_user %}
+ {% if orphan_item_stats.mappings > 0 %}
+ {{
+ _('web_ui.home.orphans_to_delete_{mappings}')
+ .format(mappings = orphan_item_stats.mappings)
+ }}
+ {% else %}
+ {{ _('web_ui.home.orphans_to_delete_exist') }}
+ {% endif %}
+ {% else %}
+ {{
+ _('web_ui.home.orphans_to_delete_{mappings}_{resources}')
+ .format(
+ mappings = orphan_item_stats.mappings,
+ resources = orphan_item_stats.resources
+ )
+ }}
+ {% endif %}
+ </p>
+
+ {% set prune_but_text = _('web_ui.home.prune_orphans_button') %}
+
+ {{
+ button_row([
+ (['green-button'], prune_but_text, {'action': 'prune_orphans'})
+ ])
+ }}
+ {% endif %}
{% endblock %}