Skip to content

zettelkasten.dashboard.backend.routes.coverage

zettelkasten.dashboard.backend.routes.coverage

Source coverage + source-DOI routes.

Split out of the former flat routes.py; behaviour is unchanged.

set_graph_coverage

set_graph_coverage(name: str, req: CoverageRequest) -> dict

Set a source's coverage.{human,agent} and invalidate the papers cache.

The source to update is req.source (defaulting to the path name); name itself is validated/resolved so federated (read-only) graphs are rejected. Reuses the same write_coverage seam as the MCP set_coverage tool so the two writers cannot diverge. Invalid levels → 400; unknown source → 404.

Source code in zettelkasten/dashboard/backend/routes/coverage.py
@router.post("/graphs/{name}/coverage")
def set_graph_coverage(name: str, req: CoverageRequest) -> dict:
    """Set a source's ``coverage.{human,agent}`` and invalidate the papers cache.

    The source to update is ``req.source`` (defaulting to the path ``name``);
    ``name`` itself is validated/resolved so federated (read-only) graphs are
    rejected. Reuses the same ``write_coverage`` seam as the MCP ``set_coverage``
    tool so the two writers cannot diverge. Invalid levels → 400; unknown
    source → 404.
    """
    if _resolve_ref(name)[1] is not None:
        raise HTTPException(status_code=403, detail="Federated graphs are read-only.")
    source = req.source or name
    try:
        coverage = write_coverage(source, agent=req.agent, human=req.human)
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"Source '{source}' not found.")
    # A source can belong to many scopes (any project that includes it, plus its
    # own graph view), so drop the whole bounded papers cache rather than guess
    # which keys are affected; the coverage-state hash already prevents serving a
    # stale payload, this just stops stale entries accumulating. The Claims view
    # rides on the same coverage state (a claim's core-paper read-state derives
    # from coverage_human), so its twin cache must be dropped here too.
    _papers_cache.clear()
    _claims_cache.clear()
    return {"source": source, "coverage": coverage}

set_graph_source_doi

set_graph_source_doi(name: str, req: SourceDoiRequest) -> dict

Set a promoted source's bibliographic doi in its _meta.yaml.

Mirrors the coverage endpoint: req.source (defaulting to the path name) is the source to edit, while name is resolved so federated (read-only) graphs are rejected. Writing the DOI lets a subsequent Refresh citation metrics resolve a previously DOI-less work, so its Influence stops reading as unknown. The value is normalized (URL/doi: prefixes stripped); an empty string clears it. Unknown source → 404.

Clears the papers/claims caches: the _meta signature already keys on doi (so a stale payload can't be served), this just stops stale entries lingering — exactly as the coverage write does.

Source code in zettelkasten/dashboard/backend/routes/coverage.py
@router.post("/graphs/{name}/source-doi")
def set_graph_source_doi(name: str, req: SourceDoiRequest) -> dict:
    """Set a promoted source's bibliographic ``doi`` in its ``_meta.yaml``.

    Mirrors the coverage endpoint: ``req.source`` (defaulting to the path
    ``name``) is the source to edit, while ``name`` is resolved so federated
    (read-only) graphs are rejected. Writing the DOI lets a subsequent *Refresh
    citation metrics* resolve a previously DOI-less work, so its Influence stops
    reading as unknown. The value is normalized (URL/``doi:`` prefixes stripped);
    an empty string clears it. Unknown source → 404.

    Clears the papers/claims caches: the ``_meta`` signature already keys on
    ``doi`` (so a stale payload can't be served), this just stops stale entries
    lingering — exactly as the coverage write does.
    """
    if _resolve_ref(name)[1] is not None:
        raise HTTPException(status_code=403, detail="Federated graphs are read-only.")
    source = req.source or name
    try:
        doi = write_source_doi(source, req.doi, graphs_dir=GRAPHS_DIR)
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"Source '{source}' not found.")
    _papers_cache.clear()
    _claims_cache.clear()
    return {"source": source, "doi": doi}