spacr.qt.screens.data_manager¶
The Data Manager screen — what the project costs, and how to get it back.
Three surfaces over spacr.data_manager, in the order a user needs
them:
Usage — one row per artifact kind, measured by walking the project, with the unregistered bytes called out. The row that is always the surprise is “unregistered”: bytes spaCR did not put there and cannot account for, which is also why none of them is ever offered for deletion.
Prune — the plan, in full, before anything happens. Every candidate with its size and the module that would make it again; every kept item with the rule that kept it, because a user who expected 300 GB back and was offered 12 needs to read why rather than guess. Deleting requires the confirmation dialog, and the dialog shows the file list and the total one more time.
Archive — move the project, or part of it, somewhere else, and leave the record that says where it went.
The screen never deletes anything itself: it holds a
spacr.data_manager.PrunePlan and hands it back with its own token.
A plan that has gone stale — because a run wrote into the project while the
screen was open — is refused by the module, not by this file, which is where
that check belongs.
Registered through spacr.qt.app.register_app() and
spacr.qt.theme.register_widget_qss() rather than by editing app.py
or theme.py.
Classes¶
The last thing between a plan and an irreversible deletion. |
|
Disk usage, pruning and archiving for one project. |
Functions¶
|
Factory handed to |
|
Put the Data Manager in the app registry. Idempotent. |
Module Contents¶
- class spacr.qt.screens.data_manager.ConfirmDeleteDialog(plan: spacr.data_manager.PrunePlan, parent=None, *, threaded: bool = True)[source]¶
Bases:
PySide6.QtWidgets.QDialogThe last thing between a plan and an irreversible deletion.
It shows the total, the item list and every file that would go — the same list
spacr.data_manager.PrunePlan.file_list()returns, not a summary of it — and its accept button stays disabled until the user ticks the box that says they have read it. The box is deliberately not pre-ticked and deliberately not a plain OK: this data is somebody’s experiment and there is no undo.IT FILLS IN TWO STAGES, and the reason is the whole point of this exercise. The file list is not stored on the plan: every call to
file_listrunsos.walkover every candidate directory, because a plan for a project with millions of crops must not carry millions of strings. Doing that in__init__put a full recursive walk of the user’s project on the GUI thread, at the one moment they are waiting for a window to appear — and on a share that is asleep it is the walk’s FIRST stat that spends twenty seconds waking the mount, not the walk. So the dialog opens with the half that needs nothing from the disk (the totals, every candidate, every kept item and its reason) and the file list arrives behind it. Nothing is dropped: the user still reads every path before anything is deleted, which is why the acknowledgement is held disabled until the list is on screen — or, when the walk fails, until_on_listing_failed()has said so in the list’s place. The one thing that never happens is that box being armed over a placeholder.- Parameters:
plan – the plan to confirm.
parent – Qt parent.
threaded – enumerate the files off the GUI thread.
Falsewalks inline, so a test has the finished dialog when the constructor returns; both paths produce the same text and the same enabled states.
Ask the user to confirm a deletion, in words they must read.
- Parameters:
plan – what is about to be deleted.
parent – parent widget.
threaded – whether the deletion runs on a worker.
- closeEvent(event)[source]¶
The same teardown for the window’s own close button.
- Parameters:
event – the close event.
- describe() str[source]¶
The whole text — the plan, then every file — walked right now.
WALKS THE PROJECT, AND THE DIALOG NO LONGER CALLS IT.
spacr.data_manager.PrunePlan.file_list()runsos.walkover every candidate each time it is asked, which is the twenty seconds this exercise removed, so nothing on the GUI thread may call this. The dialog builds the same string in two pieces instead:_heading()at once, then_with_the_files()on the walk that_start_the_file_list()sent to a worker.It stays public, and blocking, because the whole text in one call is what a caller outside Qt wants — a test, or a CLI that has a thread to spare. Read
listingfor what is actually on screen.
- done(result: int) None[source]¶
Close the dialog, having first stopped the walk.
Both Delete and Cancel come through here and NEITHER sends a close event —
QDialog.donehides the widget — so this, notcloseEvent, is where a modal dialog’s teardown has to live. Qt aborts the process outright if a runningQThreadis destroyed with its owner, and the walk can still be out when the user cancels.- Parameters:
result – the dialog code to finish with.
- class spacr.qt.screens.data_manager.DataManagerScreen(parent=None, *, project: str = '', threaded: bool = True)[source]¶
Bases:
PySide6.QtWidgets.QWidgetDisk usage, pruning and archiving for one project.
- Parameters:
parent – Qt parent.
project – open straight onto this project root, skipping the folder picker.
threaded – run the scan and the plan on a worker thread. Tests pass False so a scan is finished when the call returns; both paths run the same code and emit the same signals.
Build the manager’s three tabs and its totals row.
- Parameters:
parent – parent widget.
- closeEvent(event)[source]¶
Stop background work and unlink before going away.
- Parameters:
event – the Qt close event.
- execute_prune(plan: spacr.data_manager.PrunePlan) bool[source]¶
Carry out
plan. The screen’s only destructive call.The token comes from the plan the screen is holding, so a plan that has gone stale — a run wrote into the project while this was open — is refused by
spacr.data_manager.prune(), which re-checks the tree, rather than by a check duplicated here that could answer differently.- Parameters:
plan – the prune plan to carry out; its own
tokenis passed as the confirmation tospacr.data_manager.prune().
- set_destination(path: str) None[source]¶
Set the archive destination without touching anything.
- Parameters:
path – the archive destination folder; stored as a string and shown, and any previous archive plan is discarded.
- set_project(root: str) None[source]¶
Point the screen at
rootand scan it.- Parameters:
root – the project folder to show and scan; previous scan results and plans are discarded.
- property plan: spacr.data_manager.PrunePlan | None[source]¶
The last prune plan, or None.
- property usage: spacr.data_manager.ProjectUsage | None[source]¶
The last scan, or None.
- spacr.qt.screens.data_manager.make_data_manager_screen(app_key: str | None = None) PySide6.QtWidgets.QWidget[source]¶
Factory handed to
spacr.qt.app.register_app().
- spacr.qt.screens.data_manager.register() bool[source]¶
Put the Data Manager in the app registry. Idempotent.
Called at import time, so importing this module is all it takes for the app to exist. Returns rather than raises on a duplicate key, so a re-import is a no-op instead of taking the import down.
- Returns:
True when this call is what registered it.
Nested helpers¶
- DataManagerScreen._follow_path_probes.redraw(path: str, _answer: bool) None¶
Re-run the enable pass now that this root’s state is known.
- Parameters:
path – the path whose probe just answered.
_answer – what it answered; unused, because
_update_controlsreads it back from the cache along with everything else it depends on.
spacr/qt/screens/data_manager.py:955
- DataManagerScreen._run._job(payload: Dict[str, Any]) None¶
Call the wrapped function, stashing its result in the payload.
The payload is how a value crosses back from the worker: a return would be swallowed by the runner.
spacr/qt/screens/data_manager.py:825
- DataManagerScreen.plan_prune.plan()¶
The same worker-side isdir as scan(), for the same reason.
spacr/qt/screens/data_manager.py:1058
- DataManagerScreen.scan.measure()¶
Measure
root, having first made sure it is a folder.The isdir the guard above can no longer do lives here, where the thread waiting on it is a worker and waiting is free. The sentence it raises is the one the guard has always said, because a root that is not a folder has to read the same to the user however spaCR found out.
spacr/qt/screens/data_manager.py:996