Skip to content

Commit 76537f0

Browse files
Add scan profiles/presets to kalka (#1861)
Save and load entire scan configurations (active tab, app settings, tool settings) as named profiles stored in {config_dir}/profiles/. - Add save_profile(), load_profile(), list_profiles(), delete_profile() methods to AppState - Profiles are JSON files that capture all settings including enum values - Enum values are serialized as strings and restored on load - Backward-compatible: new settings missing from old profiles use defaults Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 67f3d05 commit 76537f0

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

kalka/app/state.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,86 @@ def load_settings(self):
122122
s.czkawka_cli_path = data.get("czkawka_cli_path", s.czkawka_cli_path)
123123
except (json.JSONDecodeError, OSError):
124124
pass
125+
126+
# ── Scan Profiles ──────────────────────────────────────
127+
128+
def _profiles_dir(self) -> Path:
129+
d = self._config_path / "profiles"
130+
d.mkdir(parents=True, exist_ok=True)
131+
return d
132+
133+
def list_profiles(self) -> list[str]:
134+
"""Return sorted list of saved profile names."""
135+
return sorted(
136+
p.stem for p in self._profiles_dir().glob("*.json")
137+
)
138+
139+
def save_profile(self, name: str):
140+
"""Save current settings + tool_settings + active_tab as a named profile."""
141+
import dataclasses
142+
profile = {
143+
"active_tab": self.active_tab.name,
144+
"settings": {k: v for k, v in dataclasses.asdict(self.settings).items()},
145+
"tool_settings": {},
146+
}
147+
# Serialize tool_settings, converting enums to their .value
148+
for k, v in dataclasses.asdict(self.tool_settings).items():
149+
if hasattr(v, "value"):
150+
profile["tool_settings"][k] = v.value
151+
else:
152+
profile["tool_settings"][k] = v
153+
path = self._profiles_dir() / f"{name}.json"
154+
try:
155+
path.write_text(json.dumps(profile, indent=2))
156+
except OSError:
157+
pass
158+
159+
def load_profile(self, name: str) -> bool:
160+
"""Load a named profile, returning True on success."""
161+
path = self._profiles_dir() / f"{name}.json"
162+
if not path.exists():
163+
return False
164+
try:
165+
data = json.loads(path.read_text())
166+
except (json.JSONDecodeError, OSError):
167+
return False
168+
169+
# Restore active tab
170+
tab_name = data.get("active_tab", "")
171+
for tab in ActiveTab:
172+
if tab.name == tab_name:
173+
self.set_active_tab(tab)
174+
break
175+
176+
# Restore app settings
177+
if "settings" in data:
178+
s = self.settings
179+
for k, v in data["settings"].items():
180+
if hasattr(s, k):
181+
setattr(s, k, v)
182+
183+
# Restore tool settings
184+
if "tool_settings" in data:
185+
ts = self.tool_settings
186+
for k, v in data["tool_settings"].items():
187+
if hasattr(ts, k):
188+
# Try to convert string values back to enum types
189+
current = getattr(ts, k)
190+
if hasattr(current, "__class__") and hasattr(current.__class__, "__members__"):
191+
try:
192+
setattr(ts, k, current.__class__(v))
193+
except (ValueError, KeyError):
194+
pass
195+
else:
196+
setattr(ts, k, v)
197+
198+
self.settings_changed.emit()
199+
return True
200+
201+
def delete_profile(self, name: str):
202+
"""Delete a saved profile."""
203+
path = self._profiles_dir() / f"{name}.json"
204+
try:
205+
path.unlink(missing_ok=True)
206+
except OSError:
207+
pass

0 commit comments

Comments
 (0)