--- title: "Batch execution with zot" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Batch execution with zot} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` `zot` provides a preview, approval-record, execution, ledger, and verification sequence for batch plans. This vignette runs the sequence against an in-memory request performer. It makes no network requests and changes no Zotero library. ## Create a plan A plan is a `data.table` with one action per row. Supported actions are `"patch"`, `"create"`, and `"trash"`. Patch and Trash rows identify an existing item; create rows contain a new-item payload. ```{r} library(data.table) library(zot) Plan <- data.table( action = "patch", library = "user", itemKey = "ABCD1234", payload = list(list(title = "A reviewed title")) ) Preview <- zotPreview(Plan, describe = "Correct one reviewed title") Preview ``` `zotPreview()` validates the required columns and action values, copies the rows, and records a SHA-256 digest. It does not make an API request. ## Create an approval record `zotApprove()` stores the preview digest, optional notes, and a timestamp. ```{r} Approval <- zotApprove(Preview, notes = "Offline documentation example") identical(Approval$digest, Preview$digest) ``` The function records application metadata; it does not authenticate a person or determine whether execution is authorized. The executor requires the approval and preview digests to match. Callers should not modify `preview$plan` after creating the approval because the current executor does not recalculate its digest. ## Execute with a ledger The performer below models a versioned Zotero item. It supports the GET and PATCH calls used by the example. ```{r} Store <- new.env(parent = emptyenv()) Store$version <- 1L Store$title <- "Original title" Performer <- function(config, method, path, query, body, version) { if (method == "GET") { return(list( status = 200L, headers = list(), body = list( version = Store$version, data = list(title = Store$title) ) )) } if (method == "PATCH" && identical(as.integer(version), Store$version)) { Store$title <- body$title Store$version <- Store$version + 1L return(list(status = 204L, headers = list(), body = NULL)) } list(status = 412L, headers = list(), body = NULL) } Config <- zotConfig( userID = "42", key = "example-key", performer = Performer ) LedgerPath <- tempfile(fileext = ".json") Ledger <- zotExecute( Config, preview = Preview, approval = Approval, ledgerPath = LedgerPath ) Ledger$items$ABCD1234$status Store$title ``` For a patch row, `zotExecute()` reads the current item version, sends a conditional patch, reads the item again, and compares the requested fields. A matching result is recorded as `"done"`. Create rows are recorded as done after the new item can be read; Trash rows use the status returned by `zotTrash()`. Reusing the ledger path for the same digest skips entries already marked done: ```{r} LedgerAgain <- zotExecute( Config, preview = Preview, approval = Approval, ledgerPath = LedgerPath ) LedgerAgain$items$ABCD1234$status ``` ## Sample completed patches `zotVerify()` summarizes ledger statuses and re-reads at most ten completed patch rows. It does not sample create or Trash rows. ```{r} Verification <- zotVerify(Config, preview = Preview, ledger = LedgerAgain) Verification unlink(LedgerPath) ``` Applications should inspect failed or missing ledger entries separately. Local SQLite and semantic-index results are useful for discovery but do not replace an API read of remote state.