fix: force re-render campaign tracking, cache dir loading, Joplin link preservation. Co-Authored-By: Fable 5
This commit is contained in:
+41
-14
@@ -107,7 +107,11 @@ class Cache:
|
||||
self._save()
|
||||
|
||||
def get_new_or_updated(
|
||||
self, provider: str, conversations: list[dict], force: bool = False
|
||||
self,
|
||||
provider: str,
|
||||
conversations: list[dict],
|
||||
force: bool = False,
|
||||
campaign_at: str | None = None,
|
||||
) -> list[dict]:
|
||||
"""Filter a conversation list to only new or updated conversations.
|
||||
|
||||
@@ -115,19 +119,23 @@ class Cache:
|
||||
provider: "chatgpt" or "claude"
|
||||
conversations: List of raw conversation dicts from the provider.
|
||||
Each must have an ``id``/``uuid`` and ``updated_at``/``update_time``.
|
||||
force: When True, return every conversation regardless of cache
|
||||
state — used by ``export --force`` to re-render everything
|
||||
without clearing the manifest (which would drop Joplin links).
|
||||
force: When True, return conversations to re-render regardless of
|
||||
cache freshness — used by ``export --force``.
|
||||
campaign_at: In force mode, exclude conversations already re-exported
|
||||
at/after this ISO timestamp (the re-render campaign start), so
|
||||
each run advances and finished providers do nothing.
|
||||
|
||||
Returns:
|
||||
Subset that needs to be exported.
|
||||
Subset that needs to be exported, ordered oldest-export-first in
|
||||
force mode.
|
||||
"""
|
||||
if force:
|
||||
# Re-export everything, but order by least-recently-exported first
|
||||
# (never-exported sorts first). With a per-run cap, the batch just
|
||||
# re-rendered gets a fresh exported_at and sinks to the back, so
|
||||
# each subsequent run advances through the archive instead of
|
||||
# repeating the same head. Conversations without an id are dropped.
|
||||
# Re-export for a re-render campaign, ordered least-recently-exported
|
||||
# first (never-exported sorts first). When ``campaign_at`` is given,
|
||||
# conversations already re-exported during this campaign
|
||||
# (exported_at >= campaign_at) are excluded — so finished providers
|
||||
# do no work and the remaining count shrinks to zero. Each capped run
|
||||
# re-renders the next-oldest batch. Conversations without an id drop.
|
||||
entries = self._data.get(provider, {})
|
||||
|
||||
def _exported_at(conv: dict) -> str:
|
||||
@@ -135,10 +143,10 @@ class Cache:
|
||||
entry = entries.get(conv_id) or {}
|
||||
return entry.get("exported_at") or ""
|
||||
|
||||
return sorted(
|
||||
(c for c in conversations if c.get("id") or c.get("uuid")),
|
||||
key=_exported_at,
|
||||
)
|
||||
candidates = [c for c in conversations if c.get("id") or c.get("uuid")]
|
||||
if campaign_at is not None:
|
||||
candidates = [c for c in candidates if _exported_at(c) < campaign_at]
|
||||
return sorted(candidates, key=_exported_at)
|
||||
|
||||
result = []
|
||||
for conv in conversations:
|
||||
@@ -283,6 +291,25 @@ class Cache:
|
||||
"""Return the ISO8601 timestamp of the last export run, or None."""
|
||||
return self._data.get("last_run")
|
||||
|
||||
def exported_at(self, provider: str, conv_id: str) -> str:
|
||||
"""Return the recorded ``exported_at`` for a conversation, or '' if absent."""
|
||||
entry = self._data.get(provider, {}).get(conv_id)
|
||||
return entry.get("exported_at", "") if isinstance(entry, dict) else ""
|
||||
|
||||
# Force re-render campaign marker (top-level scalar; skipped by stats/clear,
|
||||
# which only act on dict-valued provider keys).
|
||||
def get_force_campaign(self) -> str | None:
|
||||
"""Return the active force-re-render campaign start timestamp, or None."""
|
||||
return self._data.get("force_campaign_at")
|
||||
|
||||
def set_force_campaign(self, started_at: str) -> None:
|
||||
self._data["force_campaign_at"] = started_at
|
||||
self._save()
|
||||
|
||||
def clear_force_campaign(self) -> None:
|
||||
if self._data.pop("force_campaign_at", None) is not None:
|
||||
self._save()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Private helpers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user