The following vignette will demonstrate the basic functionality
(i.e., awesome might) of c2z
.
Collections
Your computer desktop reveals your D&D alignment. Similarly, the way you structure your Zotero library through collections reveals your personality. (Chaotic neutral is not using collections, obviously.)
c2z
lets you create nested collections on a
whim, as seen below.
Creating collections
In this example we define a vector of nine elements based on “the
quick brown fox jumps over the lazy dog”. We use the Zotero
function, a wrapper that connects with the Zotero API and combines all
major functions in c2z
, to create nine nested
collections based on the string (i.e., “dog” nested in “lazy” nested in
“the” et cetera). We set library = TRUE
, thereby querying
the Zotero library, and create = TRUE
to create any
collections that does not exist (i.e., all nine collections).
This is a recursive action, so let’s keep the noise at a minimum and
set silent = TRUE
.
Using collections
Now that we have created some collections, we can access the collections in different ways. By default, the function will find the last element in the vector (i.e., “dog”).
# Dog
create.collections$collections |>
dplyr::select(c(key, name, parentCollection))
#> # A tibble: 1 × 3
#> key name parentCollection
#> <chr> <chr> <chr>
#> 1 SEED6D85 dog UMIYBYXN
However, we often want access all, or some, nested collections. We
can achieve this using two different approaches. 1) using
recursive = TRUE
to access the given element and then
recursively find the nested collections, or 2) using
ancestor = TRUE
and trace the lineage of the specified
collection.
You may use collection.name
(you don’t have several
top-level collections with the same name, do you? If so, the function
will select the newest collection with the specified name), or
collection.key
if you keep track of such things.
c2z
will by default look for collections names
regardless of letter case, and if this is important set
case.insensitive = FALSE
.
All collections
# Find collections
collections <- Zotero(
collection.names = "The",
recursive = TRUE,
library = TRUE,
silent = TRUE
)
# print collections
collections$collections |>
dplyr::select(c(key, name, parentCollection))
#> # A tibble: 9 × 3
#> key name parentCollection
#> <chr> <chr> <chr>
#> 1 SEED6D85 dog UMIYBYXN
#> 2 NL98ESX9 the 27JU2CSX
#> 3 ICCY66AD fox FQN6MLQ7
#> 4 FQN6MLQ7 brown KYDQHK2C
#> 5 KYDQHK2C quick 6XNYYPVC
#> 6 6XNYYPVC The FALSE
#> 7 UMIYBYXN lazy NL98ESX9
#> 8 27JU2CSX over CTFK7TPJ
#> 9 CTFK7TPJ jumps ICCY66AD
Recursive path
# Find collections
recursive <- Zotero(
collection.names = c("The", "quick", "brown"),
recursive = TRUE,
library = TRUE,
silent = TRUE
)
# print collections
recursive$collections |>
dplyr::select(name)
#> # A tibble: 7 × 1
#> name
#> <chr>
#> 1 dog
#> 2 the
#> 3 fox
#> 4 brown
#> 5 lazy
#> 6 over
#> 7 jumps
Items
Okay, so we’ve created a bunch of collections. Kinda cool, I guess? But collections are somewhat pointless without items. Sooo, lets add some items!
Adding items from Cristin
One motivation to develop c2z
is an attempt at
making registering data at Cristin feel useful. The following example of
the Cristin
function will gather all publications
containing the word “cheese”, published since 2020 (it was a good year
for cheese, probably).
The zotero.import
is set to TRUE
,
indicating that the function will use CristinWrangler
to
convert Cristin metadata into an acceptable format for Zotero.
use.identifiers = TRUE
and the function will therefore use
any identifiers (i.e., ISBN or DOI) to augment the metadata.
Furthermore, crossref.search = TRUE
, meaning that the
function will search CrossRef for metadata if identifiers are missing.
Also, autosearch = TRUE
and the process is consequently
conducted automatically. The items are posted to the Zotero library
using Zotero
, where items are uploaded using the
items
argument. The index = TRUE
, creating an
index of the items.
Please note that crossref.search
should normally not be
used (see the README).
Rather than finicking over h-indexes and impact factors, Norwegians have a weird obsession with NVI (Norwegian Science Index), which is a two-level index where level one publications are ordinary and level two are great (it probably makes sense). However, Cristin has no simple method of filtering out publications that qualify for the two levels.
The specified filter
arguments try to filter out
categories that usually contain publications that are worthy of the NVI
(see all the Cristin categories).
Let’s post the results to the collection “fox”, while using the
argument get.items = FALSE
as not to add any items in the
collection to the Zotero list.
# Filter items
cristin.filter <- c(
"ACADEMICREVIEW",
"ARTICLEJOURNAL",
"ARTICLE",
"ANTHOLOGYACA",
"CHAPTER",
"CHAPTERACADEMIC",
"CHAPTERARTICLE",
"COMMENTARYACA",
"MONOGRAPHACA"
)
# Query Cristin
cristin <- Cristin(
title = "cheese",
published_since = 2020,
published_before = 2021,
filter = cristin.filter,
zotero.import = TRUE,
use.identifiers = TRUE,
crossref.search = TRUE,
autosearch = TRUE,
silent = FALSE
)
#> Found 15 results
#> Checking whether references are supported. See `CristinSupported()`
#> Looking for missing data
#> Filtered out 9 results
#> Converting 6 references from Cristin to Zotero format
#> ——————Next ID: 1912219. Progress: 16.67% (1/6). ETA: 06.04.2024 - 06:09:05————————————Next ID: 1919520. Progress: 33.33% (2/6). ETA: 06.04.2024 - 06:09:04————————————Next ID: 1920677. Progress: 50.00% (3/6). ETA: 06.04.2024 - 06:09:07————————————Next ID: 1961743. Progress: 66.67% (4/6). ETA: 06.04.2024 - 06:09:08————————————Next ID: 1979685. Progress: 83.33% (5/6). ETA: 06.04.2024 - 06:09:07———————————————————————Process: 100.00% (6/6). Elapsed time: 00:00:19—————————————————
# Get the fox key
fox <- collections$collections |>
dplyr::filter(name == "fox") |>
dplyr::pull("key")
# Post the items to the collection called fox
post.cristin <- Zotero(
collection.key = fox,
metadata = cristin$results,
library = TRUE,
index = TRUE,
post = TRUE,
post.collections = FALSE,
post.items = TRUE,
silent = TRUE
)
# Select only names in index and print
post.cristin$index |>
dplyr::select(name) |>
print(width = 80)
#> # A tibble: 6 × 1
#> name
#> <chr>
#> 1 Lundberg et al. (2021) Determination of maintenance Jarlsberg® cheese dose to…
#> 2 Olsen et al. (2021) Feeding concentrates with different protein sources to hi…
#> 3 Lundberg et al. (2020) Increased serum osteocalcin levels and vitamin K statu…
#> 4 Henriquez Parodi et al. (2021) Kavli Selling Cheese in a Tube to the World
#> 5 Mohamed et al. (2021) Manufacture and characterization of acid-coagulated fre…
#> 6 Wolka et al. (2021) Soil organic carbon and associated soil properties in Ens…
In this second example we have decided that 2019 was an even better
year for cheese and change the published_since
argument to
2019. The reason why we are doing this is to demonstrate the duplicate
function in Cristin
, which will identify any Cristin
references imported into Zotero if zotero.check = TRUE
. The
function will try to use the zotero
argument to search the
specified collection(s).
# Query Cristin (again)
cristin2 <- Cristin(
title = "cheese",
published_since = 2019,
published_before = 2021,
filter = cristin.filter,
zotero.import = TRUE,
zotero.check = TRUE,
use.identifiers = FALSE,
zotero = post.cristin
)
#> Found 21 results
#> Checking whether references exist in library
#> Removed 6 duplicates
#> Checking whether references are supported. See `CristinSupported()`
#> Looking for missing data
#> Filtered out 9 results
#> Converting 6 references from Cristin to Zotero format
#> ——————Next ID: 1678430. Progress: 16.67% (1/6). ETA: 06.04.2024 - 06:09:14————————————Next ID: 1699701. Progress: 33.33% (2/6). ETA: 06.04.2024 - 06:09:14————————————Next ID: 1709922. Progress: 50.00% (3/6). ETA: 06.04.2024 - 06:09:14————————————Next ID: 1715026. Progress: 66.67% (4/6). ETA: 06.04.2024 - 06:09:14————————————Next ID: 1721342. Progress: 83.33% (5/6). ETA: 06.04.2024 - 06:09:14———————————————————————Process: 100.00% (6/6). Elapsed time: 00:00:01—————————————————
Adding items from identifiers
Sometimes, it happens to everybody, we’re having a bunch of ISBNs and
DOIs lying around. Luckily, we can easily add them to Zotero using the
Zotero
wrapper. In this case we are adding them to the
collection “quick”.
# ISBN
isbn.items <- c("9780761973836", "9788215048451")
# DOI
doi.items <- c("https://doi.org/10.31234/osf.io/venu6",
"10.1177/1098214010376532")
# Post the items to the collections called quick
identifiers <- Zotero(
collection.names = c("The", "quick"),
isbn = isbn.items,
doi = doi.items,
library = TRUE,
index = TRUE,
post = TRUE,
post.collections = FALSE,
post.items = TRUE,
silent = TRUE,
get.items = FALSE
)
# Select only names in index and print
identifiers$index |>
dplyr::select(name) |>
print(width = 80)
#> # A tibble: 4 × 1
#> name
#> <chr>
#> 1 Ong-Dean et al. (2011) Challenges and Dilemmas in Implementing Random Assignm…
#> 2 Wijeakumar et al. (2020) Home assessment of visual working memory in pre-scho…
#> 3 Field & Hole (n.d.) How to design and report experiments
#> 4 Grøholt et al. (2022) Lærebok i barne- og ungdomspsykiatri
Adding items from the Man
When working in academia within a Norwegian context, you will
frequently need to examine the musings of politicians, or their selected
group of researchers. So, let’s add some random white papers and
official reports using ZoteroGov
.
# Combine to a single tibble using dplyr
gov.items <- dplyr::bind_rows(
# Find some random white papers
ZoteroGov(c("26 (2001-2002)", "31 (2014-2015)"), type = "meldst")$data,
# Finds some random official Norwegian reports.
ZoteroGov(c("2014: 4", "2018: 2"), type = "nou")$data
)
# Post the items to the collection called brown
## Fitting given the source
the.man <- Zotero(
collection.names = c("The", "quick", "brown"),
metadata = gov.items,
library = TRUE,
index = TRUE,
post = TRUE,
post.collections = FALSE,
post.items = TRUE,
silent = TRUE,
get.items = FALSE
)
# Select only names in index and print
the.man$index |>
dplyr::select(name) |>
print(width = 80)
#> # A tibble: 4 × 1
#> name
#> <chr>
#> 1 St.meld. nr. 26 (2001-2002) Bedre kollektivtransport
#> 2 NOU 2018: 2 (2018) Fremtidige kompetansebehov I — Kunnskapsgrunnlaget
#> 3 Meld. St. 31 (2014–2015) Garden som ressurs – marknaden som mål — Vekst og gr…
#> 4 NOU 2013: 4 (2013) Kulturutredningen 2014
Adding items from CRAN
Show some love (citations are love, yes?) for the authors that create the packages that you use! (We see you, Hadley Wickham).
Here, we’re using the ZoteroCran
function to collect
metadata from The Comprehensive R Archive Network (CRAN). The
collection.key
is defined as:
The key is “UMIYBYXN” and the argument recursive = TRUE
,
thus the function is looking for the specified collection (i.e., “lazy”)
and all its children (i.e., “dog”).
It is often useful to link items to several (nested) collections. For instance, you can create the nested collections “project -> CRAN-packages” and link the items to both collections. You can then choose to access all items under “project”, including those in “CRAN-packages”, or just access those filed under the latter.
# Find selected packages
packages <- c(
"dplyr",
"httr",
"jsonlite",
"purrr",
"rvest",
"rlang",
"tibble",
"tidyr",
"tidyselect"
)
# Post the items to the collections called lazy and dog
cran <- Zotero(
collection.key = lazy,
metadata = ZoteroCran(packages)$data,
library = TRUE,
index = TRUE,
recursive = TRUE,
post = TRUE,
post.collections = FALSE,
post.items = TRUE,
silent = FALSE,
get.items = FALSE
)
#> Searching for collections
#> Found 9 collections
#> The Zotero list contains: 2 collections, 0 items, and 0 attachments
#> Adding 9 items to library using 1 POST request
#> —————————————————Process: 100.00% (1/1). Elapsed time: 00:00:00—————————————————
#> $post.status.items
#> # A tibble: 9 × 2
#> status key
#> <fct> <chr>
#> 1 success 9PBVG7Q5
#> 2 success 4L2R5R4Y
#> 3 success 6FXDYWPN
#> 4 success 6HZ453YC
#> 5 success 6SBT4JK7
#> 6 success NSUR22DQ
#> 7 success ATBQTCL2
#> 8 success HFN85TC3
#> 9 success HPPH8T9F
#>
#> $post.summary.items
#> # A tibble: 1 × 2
#> status summary
#> <fct> <int>
#> 1 success 9
#>
#>
#> Creating index for items
# What do we got?
# Select only names in index and print
index <- cran$index |>
dplyr::select(name) |>
print(width = 80)
#> # A tibble: 9 × 1
#> name
#> <chr>
#> 1 Wickham (2023a) dplyr: A Grammar of Data Manipulation
#> 2 Wickham (2023b) httr: Tools for Working with URLs and HTTP
#> 3 Ooms (2023) jsonlite: A Simple and Robust JSON Parser and Generator for R
#> 4 Wickham (2023c) purrr: Functional Programming Tools
#> 5 Henry (2024a) rlang: Functions for Base Types and Core R and 'Tidyverse' Feat…
#> 6 Wickham (2024a) rvest: Easily Harvest (Scrape) Web Pages
#> 7 Müller (2023) tibble: Simple Data Frames
#> 8 Wickham (2024b) tidyr: Tidy Messy Data
#> 9 Henry (2024b) tidyselect: Select from a Set of Strings
Copying
There are many situations where we want to copy collections and items (and attachments) between libraries. You may want to copy from a public Zotero library to your own or share your collections with a research group.
The Zotero
function contains a (somewhat convoluted)
method of copying collections and items from a group library to a user
library (and vice versa). However, in this example we’re appending the
group collections to our long line of nested collections, making the
process somewhat messy. Therefore, we split up the process using the
ZoteroCopy
and ZoteroPost
functions. The
change.library
argument will query Zotero
and
alter the location according to specified coordinates (the default
location is the user library as defined in .Renviron
).
Please see the tutorial on how to set up user/group id and API keys.
# Access the group library (defined in .Renviron)
group.library <- Zotero(
user = FALSE,
library = TRUE,
silent = FALSE
)
#> Searching for collections
#> Found 5 collections
#> The Zotero list contains: 5 collections, 0 items, and 0 attachments
#> Searching for all items in library
#> Found 3 items
#> The Zotero list contains: 5 collections, 3 items, and 0 attachments
# Copy the library creating new keys
copy.library <- ZoteroCopy(
zotero = group.library,
change.library = TRUE,
silent = FALSE
)
#> Copying collections
#> Copying items
# Find key for dog
dog <- collections$collections |>
dplyr::filter(name == "dog") |>
dplyr::pull("key")
# Change parent collection of top-level collection to dog
copy.library$collections <- copy.library$collections |>
dplyr::mutate(
parentCollection = dplyr::case_when(
parentCollection == "FALSE" ~ dog,
TRUE ~ parentCollection
)
)
# Copy the collection and items to the user library
post.copy <- ZoteroPost(
zotero = copy.library,
silent = TRUE
)
# Select only names in index and print
ZoteroIndex(post.copy$items) |>
dplyr::select(name) |>
print(width = 80)
#> # A tibble: 3 × 1
#> name
#> <chr>
#> 1 Mishra et al. (2023) Exploring Active and Critical Engagement in Human-Robot …
#> 2 Somby & Stalheim (2023) Vygotsky med VR-briller
#> 3 Somby & Vik (2023) Vygotskys defektologi - et perspektiv på inkluderende oppl…
Bibliography
It’s a wrap!
Now it’s time to harvest the bounty that we have created during the steps above. We could create separate bibliographies, but for the sake of simplicity we are mashing it all together. That’s friendship!
The arguments used in Zotero
are somewhat
self-explanatory, however, we are also csl.type
to access a
style
repository in order to create a Citation Style Language (CSL) file
according to APA7.
# Create references.bib in biblatex format with style.csl according to APA7
bibliography <- Zotero(
collection.names = "The",
recursive = TRUE,
library = TRUE,
export = TRUE,
format = "biblatex",
save.data = TRUE,
save.path = tempdir(),
bib.name = "references",
csl.type = "apa-single-spaced",
csl.name = "style",
silent = TRUE
)
# What do we got?
sprintf(
"We now have %s collections, %s references, and %s attachments",
bibliography$n.collections,
bibliography$n.items,
bibliography$n.attachments
)
#> [1] "We now have 9 collections, 23 references, and 0 attachments"
Deleting
Housecleaning! We should clean up the mess in your library. You could
set ragnarok
and force = TRUE
, if you want to delete everything in your
library.
# Delete all collections and items belonging to initial key
delete <- ZoteroDelete(
bibliography,
delete.collections = TRUE,
delete.items = TRUE
)
#> Deleting 9 collections using 1 DELETE request
#> —————————————————Process: 100.00% (1/1). Elapsed time: 00:00:00—————————————————
#> Deleting 23 items using 1 DELETE request
#> —————————————————Process: 100.00% (1/1). Elapsed time: 00:00:00—————————————————
References
We can now display the references that we have collected and created
using c2z
.