--- title: "Curation plans" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Curation plans} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") ``` The curation helpers in `zot` convert candidate records into plan rows or Zotero payloads. The returned objects can be inspected before any API write. ```{r} library(data.table) library(zot) ``` ## Normalize incoming metadata `ztCleanName()` preserves a filename extension while removing recognized source labels and copy suffixes. `ztCleanTitle()` also removes a trailing file extension. `ztCleanPayload()` applies the corresponding rules to text fields, creator names, and tags. ```{r} ztCleanName("Dynamics (1).pdf") ztCleanTitle("Dynamics (1).pdf") Payload <- list( itemType = "book", title = "Structural Analysis (1).pdf", creators = list(list( creatorType = "author", firstName = "A.", lastName = "Author" )), filename = "Structural Analysis (1).pdf" ) ztCleanPayload(Payload) ``` Other parenthetical text is retained. `zotPreview()` rejects a payload that still contains one of the source labels recognized by the package. ## Retitle changed records `ztRetitle()` applies a transformation and returns a patch row only when the result differs from the original title. ```{r} Items <- data.table( itemKey = c("A", "B"), library = "user", title = c("Reviewed book (1).pdf", "Existing title") ) RetitlePlan <- ztRetitle(Items, transform = ztCleanTitle) zotPreview(RetitlePlan, describe = "Normalize reviewed titles") ``` `ztRefile()` builds collection-membership patches. It reads each item's current memberships and includes their union with the requested collection, because the Zotero API replaces the complete membership list. ## Compare duplicate candidates `zotSameItem()` compares exact DOI, exact attachment MD5, or normalized title with year and first-author family name. A title alone does not produce a positive match. A record with a file is not considered equivalent to a candidate without that file. ```{r} zotSameItem( list(doi = "10.1000/Example", hasFile = TRUE), list(doi = "10.1000/example", hasFile = TRUE) ) zotSameItem( list(title = "Introduction"), list(title = "Introduction") ) ``` `ztTwins()` evaluates pairs within normalized-title groups and returns the matching verdict and its evidence class. ```{r} Records <- data.table( itemKey = c("A", "B", "C"), title = c("Paper", "Paper", "Paper"), year = c(2026L, 2026L, NA_integer_), author = c("Lovelace", "Lovelace", NA_character_), doi = NA_character_, md5 = NA_character_, hasFile = FALSE ) ztTwins(Records) ``` ## Map Crossref metadata `ztCrossref()` normally requests a DOI record from Crossref. Its injectable fetcher also permits offline and reproducible mapping. ```{r} Message <- function(doi) list( title = list("A reproducible record"), author = list(list(given = "Ada", family = "Lovelace")), `container-title` = list("Example Journal"), issued = list(`date-parts` = list(list(2026L))) ) ztCrossref("10.1000/example", getJson = Message) ``` The result is a payload; the function does not create a Zotero item. ## Build a cross-library move Zotero item keys belong to one library. `ztBlockMoveCreate()` therefore builds create rows for the destination. It rejects records with imported files, because copying their metadata alone would omit attachments. ```{r} MoveRecords <- data.table( itemKey = "OLD1", payload = list(list(itemType = "book", title = "Reviewed book")), hasFile = FALSE ) CreatePlan <- ztBlockMoveCreate( MoveRecords, toLibrary = "5107760", collectionKey = "DEST" ) CreatePlan ``` `ztBlockMoveTrash()` adds an original item to a Trash plan only when its corresponding create-ledger entry is marked `"done"`. ```{r} CreateLedger <- list(items = list( `create-1` = list(status = "done", new = "NEW1") )) TrashPlan <- ztBlockMoveTrash( MoveRecords, createLedger = CreateLedger, fromLibrary = "user" ) TrashPlan ``` The second plan is independent of the first execution. Trash is recoverable; the package does not empty Zotero's Trash.