From 5c0bc7c9962c5e7cd4e06c653971371da7fd9de1 Mon Sep 17 00:00:00 2001 From: "Roel M. Hogervorst" Date: Mon, 27 Nov 2023 21:30:50 +0100 Subject: [PATCH] Add ISBN provider --- R/isbn-provider.R | 91 ++++++++++++++++++++++++++++++++++++++ R/sequence-provider.R | 1 - R/ssn-provider-all.R | 12 +---- R/zzz.R | 10 +++++ tests/testthat/test-isbn.R | 32 ++++++++++++++ tests/testthat/test-zzz.R | 11 +++++ 6 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 R/isbn-provider.R create mode 100644 tests/testthat/test-isbn.R diff --git a/R/isbn-provider.R b/R/isbn-provider.R new file mode 100644 index 0000000..4f1fc24 --- /dev/null +++ b/R/isbn-provider.R @@ -0,0 +1,91 @@ +#' @title ISBNProvider +#' @description International Standard Book Number - Provider. +#' ISBN starts with group code, all English language ISBN-10 codes +#' start with a 0 or 1, and all German language books start with a 3. +#' see . +#' +#' Charlatan does not provide further helpers for you, but you can supply the +#' prefix yourself, if for instance you want to create Mexican ISBNs you can +#' by supplying the ISBN10 prefix 970, or for Andorra supply the ISBN 13 prefix +#' 97899920 (that is 978 for ISBN13, and 99920 for Andorra). +#' +#' @param n (integer) number of ISBN10s to make, default=1 +#' @param prefix (integer/character) prefix for ISBN +#' @export +#' @examples +#' z <- ISBNProvider$new() +#' z$isbn10() +#' z$isbn13() +#' z$isbn10(10) +#' z$isbn13(100) +#' # or even z$isbn10(500) +ISBNProvider <- R6::R6Class( + "ISBNProvider", + inherit = BareProvider, + public = list( + #' @description Make a ISBN10 + #' This is a completely random (apart from the prefix), but valid ISBN10 number. + isbn10 = function(n = 1, prefix = NULL) { + replicate(n, private$generate_isbn10(prefix = prefix)) + }, + #' @description Make a ISBN13. + #' This is a completely random (apart from the prefix), but valid ISBN13 number. + isbn13 = function(n = 1, prefix = NULL) { + replicate(n, private$generate_isbn13(prefix = prefix)) + } + ), + private = list( + provider_ = "ISBNProvider", + generate_isbn10 = function(prefix = NULL) { + first9isbn <- sample(0:9, size = 9, replace = TRUE) + if (!is.null(prefix)) { + first9isbn <- private$subst_vector(prefix, first9isbn) + } + final_number <- private$generate_isbn10_checkdigit(first9isbn) + isbn <- paste0(c(first9isbn, final_number), collapse = "") + isbn + }, + generate_isbn10_checkdigit = function(first9isbn) { + first_9 <- as.integer(first9isbn) + if (length(first_9) != 9) { + stop("needs exactly 9 tokens") + } + final_number <- 11 - (checksum_util(first_9, 10:2) %% 11) + if (final_number == 10) { + final_number <- "X" + } + final_number + }, + generate_isbn13 = function(prefix = NULL) { + first12isbn <- sample(0:9, 12, replace = TRUE) + if (!is.null(prefix)) { + first12isbn <- private$subst_vector(prefix, first12isbn) + } + final_number <- private$generate_isbn13_checkdigit(first12isbn) + isbn <- paste0(c(first12isbn, final_number), collapse = "") + isbn + }, + generate_isbn13_checkdigit = function(first12isbn) { + # first12isbn should be a vector of 12 long. + first_12 <- as.integer(first12isbn) + if (length(first_12) != 12) { + stop("needs exactly 12 tokens") + } + final_number <- 10 - (checksum_util(first_12, rep(c(1, 3), 6)) %% 10) + if (final_number == 10) { + final_number <- 0 + } + final_number + }, + subst_vector = function(prefix, vector) { + step <- nchar(prefix) + max_n <- length(vector) + if (step > max_n) { + step <- max_n + } + values <- strsplit(as.character(prefix), "")[[1]] + vector[1:step] <- values[1:step] + as.integer(vector) + } + ) +) diff --git a/R/sequence-provider.R b/R/sequence-provider.R index 4e17795..1ec8583 100644 --- a/R/sequence-provider.R +++ b/R/sequence-provider.R @@ -1,7 +1,6 @@ #' @title SequenceProvider #' @description genetic sequence generator #' @export -#' @keywords internal #' @examples #' z <- SequenceProvider$new() #' z$render() diff --git a/R/ssn-provider-all.R b/R/ssn-provider-all.R index a5ef24c..65ae347 100644 --- a/R/ssn-provider-all.R +++ b/R/ssn-provider-all.R @@ -52,17 +52,7 @@ SSNProvider_nl_NL <- R6Class( if (length(first_8) == 7) { first_8[8] <- 1 } - final_number <- ( - 9 * first_8[1] + - +8 * first_8[2] + - +7 * first_8[3] + - +6 * first_8[4] + - +5 * first_8[5] + - +4 * first_8[6] + - +3 * first_8[7] + - +2 * first_8[8] - ) %% 11 - + final_number <- checksum_util(first_8,9:2) %% 11 bsn <- paste0(c(first_8, final_number), collapse = "") bsn } diff --git a/R/zzz.R b/R/zzz.R index a9a1952..22ade1a 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -118,3 +118,13 @@ locale_mismatch <- function(parent_provider, child_provider) { child <- cr_loc_spec_provider(child_provider, "en_US")$allowed_locales() parent[!parent %in% child] } + +#' generalized util function for sequence multiplication +#' +#' Util function for ISBN, ean, SSN providers. +#' for cases such as sum(x[4]*5 + x[3]*4 + x[2]*3 + x[1] *2) +#' @keywords internal +checksum_util = function(vector,multiplicationvector){ + stopifnot(length(vector)==length(multiplicationvector)) + sum(vector * multiplicationvector) +} diff --git a/tests/testthat/test-isbn.R b/tests/testthat/test-isbn.R new file mode 100644 index 0000000..5c0f986 --- /dev/null +++ b/tests/testthat/test-isbn.R @@ -0,0 +1,32 @@ +test_that("ISBN 13 creates valid ISBN", { + ISBNP <- ISBNProvider$new() + # ISBN version 13 + # 978-0-306-40615-? 7 + # LOW LEVEL CHECKDIGIT + expect_equal(ISBNP$.__enclos_env__$private$generate_isbn13_checkdigit(c(9,7,8,0,3,0,6,4,0,6,1,5)), 7) + # UNDERLYING GENERATOR + expect_equal(nchar(ISBNP$.__enclos_env__$private$generate_isbn13()), 13) + # USER FACING FUNCTION + expect_equal(nchar(ISBNP$isbn13()), 13) +}) +test_that("ISBN 10 creates valid ISBN", { + ISBNP <- ISBNProvider$new() + # isbn version 10 + # 0-306-40615-? 2 + # LOW LEVEL CHECKDIGIT + expect_equal(ISBNP$.__enclos_env__$private$generate_isbn10_checkdigit(c(0,3,0,6,4,0,6,1,5)), 2) + # UNDERLYING GENERATOR + expect_equal(nchar(ISBNP$.__enclos_env__$private$generate_isbn10()), 10) + # USER FACING FUNCTION + expect_equal(nchar(ISBNP$isbn10()), 10) +}) + +test_that("prefix logic works", { + ISBNP <- ISBNProvider$new() + + expect_equal(ISBNP$.__enclos_env__$private$subst_vector("978", c(1, 1, 1, 1, 1)), c(9, 7, 8, 1, 1)) + # 978 or 979 + expect_equal(substr(ISBNP$isbn13(n = 1, prefix = 978), 1, 3), "978") + # 1 or 0 for isbn10 + expect_equal(substr(ISBNP$isbn10(n = 1, prefix = 1), 1, 1), "1") +}) diff --git a/tests/testthat/test-zzz.R b/tests/testthat/test-zzz.R index a325832..7e13969 100644 --- a/tests/testthat/test-zzz.R +++ b/tests/testthat/test-zzz.R @@ -72,3 +72,14 @@ test_that("All non-localized providers inherit from BareProvider", { } } }) + + +test_that("checksum_util works for our usecases",{ + # ISBN 10 + expect_equal(checksum_util(c(0,3,0,6,4,0,6,1,5),10:2), 130) + # ISBN 13 + expect_equal(checksum_util(c(9,7,8,0,3,0,6,4,0,6,1,5),rep(c(1,3),6)), 93) + #SSN dutch + #111222333 en 123456782 + expect_equal(checksum_util(c(1,1,1,2,2,2,3,3),9:2), 69) +})