mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2025-09-08 07:18:47 +00:00
Add profile stats feature and menu option
This commit is contained in:
@@ -254,6 +254,8 @@ Back in the Settings menu you can:
|
||||
* Choose `9` to set an additional backup location.
|
||||
* Select `10` to change the inactivity timeout.
|
||||
* Choose `11` to lock the vault and require re-entry of your password.
|
||||
* Select `12` to return to the main menu.
|
||||
* Choose `13` to view seed profile stats.
|
||||
|
||||
## Running Tests
|
||||
|
||||
|
17
src/main.py
17
src/main.py
@@ -222,6 +222,17 @@ def handle_display_npub(password_manager: PasswordManager):
|
||||
print(colored(f"Error: Failed to display npub: {e}", "red"))
|
||||
|
||||
|
||||
def handle_display_stats(password_manager: PasswordManager) -> None:
|
||||
"""Print seed profile statistics."""
|
||||
try:
|
||||
display_fn = getattr(password_manager, "display_stats", None)
|
||||
if callable(display_fn):
|
||||
display_fn()
|
||||
except Exception as e: # pragma: no cover - display best effort
|
||||
logging.error(f"Failed to display stats: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to display stats: {e}", "red"))
|
||||
|
||||
|
||||
def handle_post_to_nostr(
|
||||
password_manager: PasswordManager, alt_summary: str | None = None
|
||||
):
|
||||
@@ -556,6 +567,7 @@ def handle_settings(password_manager: PasswordManager) -> None:
|
||||
print("10. Set inactivity timeout")
|
||||
print("11. Lock Vault")
|
||||
print("12. Back")
|
||||
print("13. Stats")
|
||||
choice = input("Select an option: ").strip()
|
||||
if choice == "1":
|
||||
handle_profiles_menu(password_manager)
|
||||
@@ -585,6 +597,8 @@ def handle_settings(password_manager: PasswordManager) -> None:
|
||||
password_manager.unlock_vault()
|
||||
elif choice == "12":
|
||||
break
|
||||
elif choice == "13":
|
||||
handle_display_stats(password_manager)
|
||||
else:
|
||||
print(colored("Invalid choice.", "red"))
|
||||
|
||||
@@ -606,6 +620,9 @@ def display_menu(
|
||||
5. Settings
|
||||
6. Exit
|
||||
"""
|
||||
display_fn = getattr(password_manager, "display_stats", None)
|
||||
if callable(display_fn):
|
||||
display_fn()
|
||||
while True:
|
||||
if time.time() - password_manager.last_activity > inactivity_timeout:
|
||||
print(colored("Session timed out. Vault locked.", "yellow"))
|
||||
|
@@ -34,7 +34,7 @@ from utils.key_derivation import (
|
||||
derive_index_key,
|
||||
EncryptionMode,
|
||||
)
|
||||
from utils.checksum import calculate_checksum, verify_checksum
|
||||
from utils.checksum import calculate_checksum, verify_checksum, json_checksum
|
||||
from utils.password_prompt import (
|
||||
prompt_for_password,
|
||||
prompt_existing_password,
|
||||
@@ -1826,3 +1826,93 @@ class PasswordManager:
|
||||
except Exception as e:
|
||||
logging.error(f"Failed to change password: {e}", exc_info=True)
|
||||
print(colored(f"Error: Failed to change password: {e}", "red"))
|
||||
|
||||
def get_profile_stats(self) -> dict:
|
||||
"""Return various statistics about the current seed profile."""
|
||||
if not all([self.entry_manager, self.config_manager, self.backup_manager]):
|
||||
return {}
|
||||
|
||||
stats: dict[str, object] = {}
|
||||
|
||||
# Entry counts by type
|
||||
data = self.entry_manager.vault.load_index()
|
||||
entries = data.get("entries", {})
|
||||
counts: dict[str, int] = {}
|
||||
for entry in entries.values():
|
||||
etype = entry.get("type", EntryType.PASSWORD.value)
|
||||
counts[etype] = counts.get(etype, 0) + 1
|
||||
stats["entries"] = counts
|
||||
stats["total_entries"] = len(entries)
|
||||
|
||||
# Schema version and checksum status
|
||||
stats["schema_version"] = data.get("schema_version")
|
||||
current_checksum = json_checksum(data)
|
||||
chk_path = self.entry_manager.checksum_file
|
||||
if chk_path.exists():
|
||||
stored = chk_path.read_text().strip()
|
||||
stats["checksum_ok"] = stored == current_checksum
|
||||
else:
|
||||
stored = None
|
||||
stats["checksum_ok"] = False
|
||||
stats["checksum"] = stored
|
||||
|
||||
# Relay info
|
||||
cfg = self.config_manager.load_config(require_pin=False)
|
||||
relays = cfg.get("relays", [])
|
||||
stats["relays"] = relays
|
||||
stats["relay_count"] = len(relays)
|
||||
|
||||
# Backup info
|
||||
backups = list(
|
||||
self.backup_manager.backup_dir.glob("entries_db_backup_*.json.enc")
|
||||
)
|
||||
stats["backup_count"] = len(backups)
|
||||
stats["backup_dir"] = str(self.backup_manager.backup_dir)
|
||||
stats["additional_backup_path"] = (
|
||||
self.config_manager.get_additional_backup_path()
|
||||
)
|
||||
|
||||
# Nostr sync info
|
||||
manifest = getattr(self.nostr_client, "current_manifest", None)
|
||||
if manifest is not None:
|
||||
stats["chunk_count"] = len(manifest.chunks)
|
||||
stats["delta_since"] = manifest.delta_since
|
||||
else:
|
||||
stats["chunk_count"] = 0
|
||||
stats["delta_since"] = None
|
||||
stats["pending_deltas"] = len(getattr(self.nostr_client, "_delta_events", []))
|
||||
|
||||
return stats
|
||||
|
||||
def display_stats(self) -> None:
|
||||
"""Print a summary of :meth:`get_profile_stats` to the console."""
|
||||
stats = self.get_profile_stats()
|
||||
if not stats:
|
||||
print(colored("No statistics available.", "red"))
|
||||
return
|
||||
|
||||
print(colored("\n=== Seed Profile Stats ===", "yellow"))
|
||||
print(colored(f"Total entries: {stats['total_entries']}", "cyan"))
|
||||
for etype, count in stats["entries"].items():
|
||||
print(colored(f" {etype}: {count}", "cyan"))
|
||||
print(colored(f"Relays configured: {stats['relay_count']}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Backups: {stats['backup_count']} (dir: {stats['backup_dir']})", "cyan"
|
||||
)
|
||||
)
|
||||
if stats.get("additional_backup_path"):
|
||||
print(
|
||||
colored(f"Additional backup: {stats['additional_backup_path']}", "cyan")
|
||||
)
|
||||
print(colored(f"Schema version: {stats['schema_version']}", "cyan"))
|
||||
print(
|
||||
colored(
|
||||
f"Checksum ok: {'yes' if stats['checksum_ok'] else 'no'}",
|
||||
"cyan",
|
||||
)
|
||||
)
|
||||
print(colored(f"Snapshot chunks: {stats['chunk_count']}", "cyan"))
|
||||
print(colored(f"Pending deltas: {stats['pending_deltas']}", "cyan"))
|
||||
if stats.get("delta_since"):
|
||||
print(colored(f"Latest delta id: {stats['delta_since']}", "cyan"))
|
||||
|
Reference in New Issue
Block a user