Rate-limit backup_if_protected to one folder per day via content hash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Safi
2026-05-25 09:14:20 +01:00
co-authored by Claude Sonnet 4.6
parent 98100f3b35
commit 3efae3827f
2 changed files with 28 additions and 8 deletions
+11 -4
View File
@@ -1,5 +1,6 @@
# write graph to HTML, JSON, SVG, GraphML, Obsidian vault, and Neo4j Cypher
from __future__ import annotations
import hashlib
import html as _html
import json
import math
@@ -62,10 +63,16 @@ def backup_if_protected(out_dir: Path) -> "Path | None":
reason = "+".join(filter(None, ["semantic" if is_semantic else "", "curated" if is_curated else ""]))
today = date.today().isoformat()
backup_dir = out / today
suffix = 2
while backup_dir.exists():
backup_dir = out / f"{today}_{suffix}"
suffix += 1
graph_src = out / "graph.json"
# Skip re-copying if today's backup already has identical graph.json content.
# If content differs (graph changed since the last backup today), overwrite
# the backup in place — one folder per day, always the latest pre-overwrite state.
if backup_dir.exists() and (backup_dir / "graph.json").exists():
src_hash = hashlib.sha256(graph_src.read_bytes()).hexdigest()
bak_hash = hashlib.sha256((backup_dir / "graph.json").read_bytes()).hexdigest()
if src_hash == bak_hash:
return backup_dir # identical content, nothing to do
try:
backup_dir.mkdir(parents=True, exist_ok=True)
+17 -4
View File
@@ -228,8 +228,8 @@ def test_backup_default_labels_only(tmp_path):
assert backup_if_protected(tmp_path) is None
def test_backup_same_day_collision(tmp_path):
"""Second backup on same day gets _2 suffix."""
def test_backup_same_day_no_accumulation(tmp_path):
"""Same content on same day returns existing backup dir without re-copying."""
from graphify.export import backup_if_protected
from datetime import date
(tmp_path / "graph.json").write_text('{"nodes":[],"links":[]}')
@@ -237,8 +237,21 @@ def test_backup_same_day_collision(tmp_path):
b1 = backup_if_protected(tmp_path)
b2 = backup_if_protected(tmp_path)
assert b1 is not None and b2 is not None
assert b1 != b2
assert b2.name == f"{date.today().isoformat()}_2"
assert b1 == b2 # same dir, no _2 accumulation
assert b1.name == date.today().isoformat()
def test_backup_same_day_changed_content(tmp_path):
"""Changed graph.json on same day overwrites the existing backup in place."""
from graphify.export import backup_if_protected
from datetime import date
(tmp_path / "graph.json").write_text('{"nodes":[],"links":[]}')
(tmp_path / ".graphify_semantic_marker").write_text("{}")
b1 = backup_if_protected(tmp_path)
(tmp_path / "graph.json").write_text('{"nodes":[{"id":"x"}],"links":[]}')
b2 = backup_if_protected(tmp_path)
assert b1 == b2 # still one folder per day
assert (b2 / "graph.json").read_text() == '{"nodes":[{"id":"x"}],"links":[]}'
def test_backup_env_disable(tmp_path, monkeypatch):