├── .Rbuildignore ├── .github ├── .gitignore └── workflows │ ├── check-standard.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── get_checksum.R ├── is_number_valid.R └── is_valid.R ├── README.md ├── cran-comments.md ├── docs ├── 404.html ├── CNAME ├── LICENSE-text.html ├── LICENSE.html ├── authors.html ├── bootstrap-toc.css ├── bootstrap-toc.js ├── docsearch.css ├── docsearch.js ├── index.html ├── link.svg ├── news │ └── index.html ├── pkgdown.css ├── pkgdown.js ├── pkgdown.yml ├── reference │ ├── Rplot001.png │ ├── get_checksum.html │ ├── index.html │ ├── is_number_valid.html │ └── is_valid.html └── sitemap.xml ├── man ├── get_checksum.Rd ├── is_number_valid.Rd └── is_valid.Rd ├── nhsnumber.Rproj └── tests ├── testthat.R └── testthat ├── test-get_checksum.R ├── test-is_number_valid.R └── test-is_valid.R /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^CRAN-RELEASE$ 5 | ^cran-comments\.md$ 6 | ^\.github$ 7 | docs 8 | -------------------------------------------------------------------------------- /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.github/workflows/check-standard.yaml: -------------------------------------------------------------------------------- 1 | # For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. 2 | # https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: 9 | branches: 10 | - main 11 | - master 12 | 13 | name: R-CMD-check 14 | 15 | jobs: 16 | R-CMD-check: 17 | runs-on: ${{ matrix.config.os }} 18 | 19 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | config: 25 | # - {os: windows-latest, r: 'release'} 26 | # - {os: macOS-latest, r: 'release'} 27 | - {os: ubuntu-20.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} 28 | # - {os: ubuntu-20.04, r: 'devel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/focal/latest"} 29 | 30 | env: 31 | R_REMOTES_NO_ERRORS_FROM_WARNINGS: true 32 | RSPM: ${{ matrix.config.rspm }} 33 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | steps: 36 | - uses: actions/checkout@v2 37 | 38 | - uses: r-lib/actions/setup-r@v1 39 | with: 40 | r-version: ${{ matrix.config.r }} 41 | 42 | - uses: r-lib/actions/setup-pandoc@v1 43 | 44 | - name: Query dependencies 45 | run: | 46 | install.packages('remotes') 47 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) 48 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") 49 | shell: Rscript {0} 50 | 51 | - name: Cache R packages 52 | if: runner.os != 'Windows' 53 | uses: actions/cache@v2 54 | with: 55 | path: ${{ env.R_LIBS_USER }} 56 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} 57 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- 58 | 59 | - name: Install system dependencies 60 | if: runner.os == 'Linux' 61 | run: | 62 | while read -r cmd 63 | do 64 | eval sudo $cmd 65 | done < <(Rscript -e 'writeLines(remotes::system_requirements("ubuntu", "20.04"))') 66 | 67 | - name: Install dependencies 68 | run: | 69 | remotes::install_deps(dependencies = TRUE) 70 | remotes::install_cran("rcmdcheck") 71 | shell: Rscript {0} 72 | 73 | - name: Check 74 | env: 75 | _R_CHECK_CRAN_INCOMING_REMOTE_: false 76 | run: rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") 77 | shell: Rscript {0} 78 | 79 | - name: Upload check results 80 | if: failure() 81 | uses: actions/upload-artifact@main 82 | with: 83 | name: ${{ runner.os }}-r${{ matrix.config.r }}-results 84 | path: check 85 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | - master 6 | pull_request: 7 | branches: 8 | - main 9 | - master 10 | 11 | name: test-coverage 12 | 13 | jobs: 14 | test-coverage: 15 | runs-on: ubuntu-20.04 16 | env: 17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - uses: r-lib/actions/setup-r@v1 22 | 23 | # - uses: r-lib/actions/setup-pandoc@v1 24 | 25 | - name: Query dependencies 26 | run: | 27 | cran_mirror <- "https://packagemanager.rstudio.com/cran/__linux__/focal/latest" 28 | install.packages('remotes', repos = cran_mirror) 29 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) 30 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") 31 | shell: Rscript {0} 32 | 33 | - name: Cache R packages 34 | uses: actions/cache@v2 35 | with: 36 | path: ${{ env.R_LIBS_USER }} 37 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} 38 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- 39 | 40 | - name: Install dependencies 41 | run: | 42 | cran_mirror <- "https://packagemanager.rstudio.com/cran/__linux__/focal/latest" 43 | install.packages(c("remotes"), repos = cran_mirror) 44 | remotes::install_deps(dependencies = TRUE, repos = cran_mirror) 45 | remotes::install_cran("covr", repos = cran_mirror) 46 | shell: Rscript {0} 47 | 48 | - name: Test coverage 49 | run: covr::codecov() 50 | shell: Rscript {0} 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: nhsnumber 2 | Type: Package 3 | Title: Tools for Working with NHS Number Checksums 4 | Version: 0.1.2 5 | Authors@R: c( 6 | person("Mark", "Sellors", , "rstats@5vcc.com", c("aut", "cre", "cph")) 7 | ) 8 | Description: Provides functions for working with NHS number checksums. 9 | The UK's National Health Service issues NHS numbers to all users of its 10 | services and this package implements functions for verifying that the 11 | numbers are valid according to the checksum scheme the NHS use. 12 | Numbers can be validated and checksums created. 13 | URL: https://github.com/sellorm/nhsnumber 14 | BugReports: https://github.com/sellorm/nhsnumber/issues 15 | License: MIT + file LICENSE 16 | Encoding: UTF-8 17 | LazyData: true 18 | Roxygen: list(markdown = TRUE) 19 | RoxygenNote: 7.1.2 20 | Suggests: 21 | testthat (>= 2.1.0) 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2019 2 | COPYRIGHT HOLDER: Mark Sellors 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 Mark Sellors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(get_checksum) 4 | export(is_valid) 5 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # nhsnumber 0.1.2 2 | 3 | * Properly handles `NA` in input data (#3) 4 | * Consolidated email addresses used on CRAN - now matches my other package, rlog 5 | 6 | # nhsnumber 0.1.1 7 | 8 | * Added a `NEWS.md` file to track changes to the package. 9 | * Preparing for first CRAN submission 10 | -------------------------------------------------------------------------------- /R/get_checksum.R: -------------------------------------------------------------------------------- 1 | #' Calculates NHS number checksums 2 | #' 3 | #' NHS numbers are 10 digit numbers where the 10th digit is a checksum. 4 | #' This function takes the 9 core digits of the NHS number and calculates the 5 | #' checksums. 6 | #' 7 | #' This function has been created using information from: 8 | #' https://en.wikipedia.org/wiki/NHS_number 9 | #' and various NHS sources 10 | #' 11 | #' @param nhs_number A 9 digit NHS numbers to validate 12 | #' @param full_output Boolean that controls display of warning messages 13 | #' @return Checksum or the complete NHS number including the checksum. 14 | #' @examples 15 | #' get_checksum(123456788) 16 | #' get_checksum(123456788, full_output = TRUE) 17 | #' @export 18 | 19 | get_checksum <- function(nhs_number, full_output = FALSE) { 20 | if (is.na( 21 | suppressWarnings( 22 | as.numeric(nhs_number) 23 | ) 24 | ) 25 | ) { 26 | stop("nhs_number is invalid - 27 | should be a 9 digit number, not text") 28 | } 29 | if (nchar(nhs_number) != 9) { 30 | stop("nhs_number is incorrect length - 31 | should be 9 digits as checksum is not required") 32 | } 33 | split_number <- as.numeric(strsplit(as.character(nhs_number), "")[[1]]) 34 | multipliers <- c(10, 9, 8, 7, 6, 5, 4, 3, 2) 35 | multiplied <- split_number * multipliers 36 | remainder <- sum(multiplied) %% 11 37 | check_digit <- 11 - remainder 38 | 39 | # A check digit of 10 is not permitted 40 | # an example number with a checksum of 10 is 123456789 41 | if (check_digit == 10) { 42 | stop("Input sequence is invalid 43 | Checksum was 10 which is not permissable") 44 | } 45 | 46 | # A check digit of 11 is re-written as a 0 47 | if (check_digit == 11) { 48 | check_digit <- 0 49 | } 50 | if (full_output) { 51 | as.numeric(paste0(nhs_number, check_digit)) 52 | } else { 53 | check_digit 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /R/is_number_valid.R: -------------------------------------------------------------------------------- 1 | #' Checks validity of an NHS number 2 | #' 3 | #' NHS numbers are 10 digit numbers where the 10th digit is a checksum. 4 | #' This function validates the checksums of the supplied NHS numbers. 5 | #' 6 | #' @param nhs_number A 10 digit NHS number to validate 7 | #' @param warn Boolean that controls display of warning messages 8 | #' @examples 9 | #' is_valid(1234567881) 10 | #' is_valid(1234567890, warn = FALSE) 11 | is_number_valid <- function(nhs_number, warn = TRUE) { 12 | if (is.na(nhs_number)) { 13 | if (warn == TRUE) { 14 | warning("nhs_number contains 'NA', corresponding result will be FALSE") 15 | } 16 | return(FALSE) 17 | } 18 | 19 | if (nchar(nhs_number) != 10) { 20 | if (warn == TRUE) { 21 | warning("nhs_number is incorrect length - should be 10 digits") 22 | } 23 | } 24 | 25 | no_checksum <- substr(nhs_number, 1, 9) 26 | # returns FALSE if get_checksum throws an error 27 | checksum <- tryCatch( 28 | { 29 | nhsnumber::get_checksum(no_checksum) 30 | }, 31 | error = function(e) { 32 | FALSE 33 | } 34 | ) 35 | 36 | if (checksum == substr(nhs_number, 10, 10)) { 37 | TRUE 38 | } else { 39 | FALSE 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /R/is_valid.R: -------------------------------------------------------------------------------- 1 | #' Checks validity of NHS numbers 2 | #' 3 | #' NHS numbers are 10 digit numbers where the 10th digit is a checksum. 4 | #' This function validates the checksums of the supplied NHS numbers. 5 | #' 6 | #' @param nhs_number A vector of 10 digit NHS numbers to validate 7 | #' @param warn Boolean that controls display of warning messages 8 | #' @return A logical vector indicating the validity of each input value 9 | #' @examples 10 | #' is_valid(1234567881) 11 | #' is_valid(c(1234567881, 1234512345, 123456789)) 12 | #' is_valid(1234567890, warn = FALSE) 13 | #' @export 14 | 15 | is_valid <- function(nhs_number, warn = TRUE) { 16 | if (!is.vector(nhs_number)) { 17 | stop("nhs_number must be a vector") 18 | } 19 | unlist(lapply(nhs_number, function(x) { 20 | is_number_valid(x, warn = warn) 21 | })) 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # nhsnumber 3 | 4 | 5 | [![CRAN status](https://www.r-pkg.org/badges/version/nhsnumber)](https://CRAN.R-project.org/package=nhsnumber) 6 | [![R-CMD-check](https://github.com/sellorm/nhsnumber/workflows/R-CMD-check/badge.svg)](https://github.com/sellorm/nhsnumber/actions?query=workflow%3AR-CMD-check) 7 | [![codecov](https://codecov.io/gh/sellorm/nhsnumber/branch/master/graph/badge.svg?token=UUKS3SYQ0B)](https://app.codecov.io/gh/sellorm/nhsnumber) 8 | [![Lifecycle: stable](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://lifecycle.r-lib.org/articles/stages.html#stable) 9 | 10 | 11 | The goal of nhsnumber is to provide some simple functions for working with NHS numbers in R. 12 | 13 | ## NHS Number Overview 14 | 15 | NHS numbers are issued to patients of the NHS in the UK. 16 | 17 | The number consists of 9 digits and a single digit checksum. 18 | 19 | For more information, please see the [NHS number Wikipedia article](https://en.wikipedia.org/wiki/NHS_number) on the subject. 20 | 21 | ## Installation 22 | 23 | You can install the released version of nhsnumber from [CRAN](https://cran.r-project.org/package=nhsnumber) with: 24 | 25 | ``` r 26 | install.packages("nhsnumber") 27 | ``` 28 | 29 | You can install the development version of nhsnumber from [GitHub](https://github.com/sellorm/nhsnumber) with: 30 | 31 | ``` r 32 | devtools::install_github("sellorm/nhsnumber") 33 | ``` 34 | 35 | ## Example 36 | 37 | The `is_valid` function takes a vector of NHS numbers and returns TRUE or FALSE depending on whether the checksum is successfully validated. 38 | 39 | ``` r 40 | x <- c(9876543210, 1234567890, 1234567881) 41 | nhsnumber::is_valid(x) 42 | ``` 43 | 44 | Which returns: 45 | 46 | ``` 47 | TRUE FALSE TRUE 48 | ``` 49 | 50 | It's also possible to generate the checksums using the `get_checksum` function. This function uses the 9 core digits and returns either the checksum on its own, or the full 10 digit number. 51 | 52 | ``` r 53 | nhsnumber::get_checksum(123456788) 54 | ``` 55 | 56 | which returns: 57 | 58 | ``` 59 | 1 60 | ``` 61 | 62 | or with the full output: 63 | 64 | ``` r 65 | nhsnumber::get_checksum(123456788, full_output = TRUE) 66 | ``` 67 | 68 | Which returns: 69 | 70 | ``` 71 | 1234567881 72 | ``` 73 | 74 | Some number combinations are invalid and these will throw an error, for example: 75 | 76 | ``` r 77 | nhsnumber::get_checksum(123456789) 78 | ``` 79 | 80 | Which results in this: 81 | 82 | ``` 83 | Error in nhsnumber::get_checksum(123456789) : Input sequence is invalid 84 | Checksum was 10 which is not permissable 85 | ``` 86 | 87 | 88 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | ## Resubmission 2 | 3 | * Fixes URL redirects as requested. 4 | 5 | ## Test environment 6 | 7 | * MacOS 12.0.1 (x86) with R 4.1.2 8 | * Windows Server 2008 R2 SP1, R-devel, 32/64 bit 9 | * Ubuntu Linux 20.04.1 LTS, R-release, GCC 10 | * Fedora Linux, R-devel, clang, gfortran 11 | 12 | ## R CMD check results 13 | 14 | 0 errors | 0 warnings | 1 notes 15 | 16 | * This is an update to an existing release. 17 | 18 | The note relates to a change in the maintainer email address. I have two 19 | packages on CRAN, this one and `rlog`, both using a different valid email 20 | address. This change consolidates my CRAN presence into a single email address. 21 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Page not found (404) • nhsnumber 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 |
24 |
61 | 62 | 63 | 64 | 65 |
66 |
67 | 70 | 71 | Content not found. Please use links in the navbar. 72 | 73 |
74 | 75 | 79 | 80 |
81 | 82 | 83 | 84 | 95 |
96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | nhsnumber.sellorm.com -------------------------------------------------------------------------------- /docs/LICENSE-text.html: -------------------------------------------------------------------------------- 1 | 2 | License • nhsnumber 6 | 7 | 8 |
9 |
40 | 41 | 42 | 43 |
44 |
45 | 48 | 49 |
YEAR: 2019
50 | COPYRIGHT HOLDER: Mark Sellors
51 | 
52 | 53 |
54 | 55 | 58 | 59 |
60 | 61 | 62 | 63 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /docs/LICENSE.html: -------------------------------------------------------------------------------- 1 | 2 | MIT License • nhsnumber 6 | 7 | 8 |
9 |
40 | 41 | 42 | 43 |
44 |
45 | 48 | 49 |
50 | 51 |

Copyright (c) 2019 Mark Sellors

52 |

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

53 |

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

54 |

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

55 |
56 | 57 |
58 | 59 | 62 | 63 |
64 | 65 | 66 | 67 |
76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /docs/authors.html: -------------------------------------------------------------------------------- 1 | 2 | Authors and Citation • nhsnumber 6 | 7 | 8 |
9 |
40 | 41 | 42 | 43 |
44 |
45 |
46 | 49 | 50 | 51 |
  • 52 |

    Mark Sellors. Author, maintainer, copyright holder. 53 |

    54 |
  • 55 |
56 |
57 |
58 |

Citation

59 | Source: DESCRIPTION 60 |
61 |
62 | 63 | 64 |

Sellors M (2021). 65 | nhsnumber: Tools for Working with NHS Number Checksums. 66 | R package version 0.1.2, https://github.com/sellorm/nhsnumber. 67 |

68 |
@Manual{,
69 |   title = {nhsnumber: Tools for Working with NHS Number Checksums},
70 |   author = {Mark Sellors},
71 |   year = {2021},
72 |   note = {R package version 0.1.2},
73 |   url = {https://github.com/sellorm/nhsnumber},
74 | }
75 | 76 |
77 | 78 |
79 | 80 | 81 | 82 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | 6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */ 7 | 8 | /* All levels of nav */ 9 | nav[data-toggle='toc'] .nav > li > a { 10 | display: block; 11 | padding: 4px 20px; 12 | font-size: 13px; 13 | font-weight: 500; 14 | color: #767676; 15 | } 16 | nav[data-toggle='toc'] .nav > li > a:hover, 17 | nav[data-toggle='toc'] .nav > li > a:focus { 18 | padding-left: 19px; 19 | color: #563d7c; 20 | text-decoration: none; 21 | background-color: transparent; 22 | border-left: 1px solid #563d7c; 23 | } 24 | nav[data-toggle='toc'] .nav > .active > a, 25 | nav[data-toggle='toc'] .nav > .active:hover > a, 26 | nav[data-toggle='toc'] .nav > .active:focus > a { 27 | padding-left: 18px; 28 | font-weight: bold; 29 | color: #563d7c; 30 | background-color: transparent; 31 | border-left: 2px solid #563d7c; 32 | } 33 | 34 | /* Nav: second level (shown on .active) */ 35 | nav[data-toggle='toc'] .nav .nav { 36 | display: none; /* Hide by default, but at >768px, show it */ 37 | padding-bottom: 10px; 38 | } 39 | nav[data-toggle='toc'] .nav .nav > li > a { 40 | padding-top: 1px; 41 | padding-bottom: 1px; 42 | padding-left: 30px; 43 | font-size: 12px; 44 | font-weight: normal; 45 | } 46 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 47 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 48 | padding-left: 29px; 49 | } 50 | nav[data-toggle='toc'] .nav .nav > .active > a, 51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 53 | padding-left: 28px; 54 | font-weight: 500; 55 | } 56 | 57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */ 58 | nav[data-toggle='toc'] .nav > .active > ul { 59 | display: block; 60 | } 61 | -------------------------------------------------------------------------------- /docs/bootstrap-toc.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/) 3 | * Copyright 2015 Aidan Feldman 4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */ 5 | (function() { 6 | 'use strict'; 7 | 8 | window.Toc = { 9 | helpers: { 10 | // return all matching elements in the set, or their descendants 11 | findOrFilter: function($el, selector) { 12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/ 13 | // http://stackoverflow.com/a/12731439/358804 14 | var $descendants = $el.find(selector); 15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])'); 16 | }, 17 | 18 | generateUniqueIdBase: function(el) { 19 | var text = $(el).text(); 20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-'); 21 | return anchor || el.tagName.toLowerCase(); 22 | }, 23 | 24 | generateUniqueId: function(el) { 25 | var anchorBase = this.generateUniqueIdBase(el); 26 | for (var i = 0; ; i++) { 27 | var anchor = anchorBase; 28 | if (i > 0) { 29 | // add suffix 30 | anchor += '-' + i; 31 | } 32 | // check if ID already exists 33 | if (!document.getElementById(anchor)) { 34 | return anchor; 35 | } 36 | } 37 | }, 38 | 39 | generateAnchor: function(el) { 40 | if (el.id) { 41 | return el.id; 42 | } else { 43 | var anchor = this.generateUniqueId(el); 44 | el.id = anchor; 45 | return anchor; 46 | } 47 | }, 48 | 49 | createNavList: function() { 50 | return $(''); 51 | }, 52 | 53 | createChildNavList: function($parent) { 54 | var $childList = this.createNavList(); 55 | $parent.append($childList); 56 | return $childList; 57 | }, 58 | 59 | generateNavEl: function(anchor, text) { 60 | var $a = $(''); 61 | $a.attr('href', '#' + anchor); 62 | $a.text(text); 63 | var $li = $('
  • '); 64 | $li.append($a); 65 | return $li; 66 | }, 67 | 68 | generateNavItem: function(headingEl) { 69 | var anchor = this.generateAnchor(headingEl); 70 | var $heading = $(headingEl); 71 | var text = $heading.data('toc-text') || $heading.text(); 72 | return this.generateNavEl(anchor, text); 73 | }, 74 | 75 | // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). 76 | getTopLevel: function($scope) { 77 | for (var i = 1; i <= 6; i++) { 78 | var $headings = this.findOrFilter($scope, 'h' + i); 79 | if ($headings.length > 1) { 80 | return i; 81 | } 82 | } 83 | 84 | return 1; 85 | }, 86 | 87 | // returns the elements for the top level, and the next below it 88 | getHeadings: function($scope, topLevel) { 89 | var topSelector = 'h' + topLevel; 90 | 91 | var secondaryLevel = topLevel + 1; 92 | var secondarySelector = 'h' + secondaryLevel; 93 | 94 | return this.findOrFilter($scope, topSelector + ',' + secondarySelector); 95 | }, 96 | 97 | getNavLevel: function(el) { 98 | return parseInt(el.tagName.charAt(1), 10); 99 | }, 100 | 101 | populateNav: function($topContext, topLevel, $headings) { 102 | var $context = $topContext; 103 | var $prevNav; 104 | 105 | var helpers = this; 106 | $headings.each(function(i, el) { 107 | var $newNav = helpers.generateNavItem(el); 108 | var navLevel = helpers.getNavLevel(el); 109 | 110 | // determine the proper $context 111 | if (navLevel === topLevel) { 112 | // use top level 113 | $context = $topContext; 114 | } else if ($prevNav && $context === $topContext) { 115 | // create a new level of the tree and switch to it 116 | $context = helpers.createChildNavList($prevNav); 117 | } // else use the current $context 118 | 119 | $context.append($newNav); 120 | 121 | $prevNav = $newNav; 122 | }); 123 | }, 124 | 125 | parseOps: function(arg) { 126 | var opts; 127 | if (arg.jquery) { 128 | opts = { 129 | $nav: arg 130 | }; 131 | } else { 132 | opts = arg; 133 | } 134 | opts.$scope = opts.$scope || $(document.body); 135 | return opts; 136 | } 137 | }, 138 | 139 | // accepts a jQuery object, or an options object 140 | init: function(opts) { 141 | opts = this.helpers.parseOps(opts); 142 | 143 | // ensure that the data attribute is in place for styling 144 | opts.$nav.attr('data-toggle', 'toc'); 145 | 146 | var $topContext = this.helpers.createChildNavList(opts.$nav); 147 | var topLevel = this.helpers.getTopLevel(opts.$scope); 148 | var $headings = this.helpers.getHeadings(opts.$scope, topLevel); 149 | this.helpers.populateNav($topContext, topLevel, $headings); 150 | } 151 | }; 152 | 153 | $(function() { 154 | $('nav[data-toggle="toc"]').each(function(i, el) { 155 | var $nav = $(el); 156 | Toc.init($nav); 157 | }); 158 | }); 159 | })(); 160 | -------------------------------------------------------------------------------- /docs/docsearch.css: -------------------------------------------------------------------------------- 1 | /* Docsearch -------------------------------------------------------------- */ 2 | /* 3 | Source: https://github.com/algolia/docsearch/ 4 | License: MIT 5 | */ 6 | 7 | .algolia-autocomplete { 8 | display: block; 9 | -webkit-box-flex: 1; 10 | -ms-flex: 1; 11 | flex: 1 12 | } 13 | 14 | .algolia-autocomplete .ds-dropdown-menu { 15 | width: 100%; 16 | min-width: none; 17 | max-width: none; 18 | padding: .75rem 0; 19 | background-color: #fff; 20 | background-clip: padding-box; 21 | border: 1px solid rgba(0, 0, 0, .1); 22 | box-shadow: 0 .5rem 1rem rgba(0, 0, 0, .175); 23 | } 24 | 25 | @media (min-width:768px) { 26 | .algolia-autocomplete .ds-dropdown-menu { 27 | width: 175% 28 | } 29 | } 30 | 31 | .algolia-autocomplete .ds-dropdown-menu::before { 32 | display: none 33 | } 34 | 35 | .algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-] { 36 | padding: 0; 37 | background-color: rgb(255,255,255); 38 | border: 0; 39 | max-height: 80vh; 40 | } 41 | 42 | .algolia-autocomplete .ds-dropdown-menu .ds-suggestions { 43 | margin-top: 0 44 | } 45 | 46 | .algolia-autocomplete .algolia-docsearch-suggestion { 47 | padding: 0; 48 | overflow: visible 49 | } 50 | 51 | .algolia-autocomplete .algolia-docsearch-suggestion--category-header { 52 | padding: .125rem 1rem; 53 | margin-top: 0; 54 | font-size: 1.3em; 55 | font-weight: 500; 56 | color: #00008B; 57 | border-bottom: 0 58 | } 59 | 60 | .algolia-autocomplete .algolia-docsearch-suggestion--wrapper { 61 | float: none; 62 | padding-top: 0 63 | } 64 | 65 | .algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column { 66 | float: none; 67 | width: auto; 68 | padding: 0; 69 | text-align: left 70 | } 71 | 72 | .algolia-autocomplete .algolia-docsearch-suggestion--content { 73 | float: none; 74 | width: auto; 75 | padding: 0 76 | } 77 | 78 | .algolia-autocomplete .algolia-docsearch-suggestion--content::before { 79 | display: none 80 | } 81 | 82 | .algolia-autocomplete .ds-suggestion:not(:first-child) .algolia-docsearch-suggestion--category-header { 83 | padding-top: .75rem; 84 | margin-top: .75rem; 85 | border-top: 1px solid rgba(0, 0, 0, .1) 86 | } 87 | 88 | .algolia-autocomplete .ds-suggestion .algolia-docsearch-suggestion--subcategory-column { 89 | display: block; 90 | padding: .1rem 1rem; 91 | margin-bottom: 0.1; 92 | font-size: 1.0em; 93 | font-weight: 400 94 | /* display: none */ 95 | } 96 | 97 | .algolia-autocomplete .algolia-docsearch-suggestion--title { 98 | display: block; 99 | padding: .25rem 1rem; 100 | margin-bottom: 0; 101 | font-size: 0.9em; 102 | font-weight: 400 103 | } 104 | 105 | .algolia-autocomplete .algolia-docsearch-suggestion--text { 106 | padding: 0 1rem .5rem; 107 | margin-top: -.25rem; 108 | font-size: 0.8em; 109 | font-weight: 400; 110 | line-height: 1.25 111 | } 112 | 113 | .algolia-autocomplete .algolia-docsearch-footer { 114 | width: 110px; 115 | height: 20px; 116 | z-index: 3; 117 | margin-top: 10.66667px; 118 | float: right; 119 | font-size: 0; 120 | line-height: 0; 121 | } 122 | 123 | .algolia-autocomplete .algolia-docsearch-footer--logo { 124 | background-image: url("data:image/svg+xml;utf8,"); 125 | background-repeat: no-repeat; 126 | background-position: 50%; 127 | background-size: 100%; 128 | overflow: hidden; 129 | text-indent: -9000px; 130 | width: 100%; 131 | height: 100%; 132 | display: block; 133 | transform: translate(-8px); 134 | } 135 | 136 | .algolia-autocomplete .algolia-docsearch-suggestion--highlight { 137 | color: #FF8C00; 138 | background: rgba(232, 189, 54, 0.1) 139 | } 140 | 141 | 142 | .algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight { 143 | box-shadow: inset 0 -2px 0 0 rgba(105, 105, 105, .5) 144 | } 145 | 146 | .algolia-autocomplete .ds-suggestion.ds-cursor .algolia-docsearch-suggestion--content { 147 | background-color: rgba(192, 192, 192, .15) 148 | } 149 | -------------------------------------------------------------------------------- /docs/docsearch.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // register a handler to move the focus to the search bar 4 | // upon pressing shift + "/" (i.e. "?") 5 | $(document).on('keydown', function(e) { 6 | if (e.shiftKey && e.keyCode == 191) { 7 | e.preventDefault(); 8 | $("#search-input").focus(); 9 | } 10 | }); 11 | 12 | $(document).ready(function() { 13 | // do keyword highlighting 14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */ 15 | var mark = function() { 16 | 17 | var referrer = document.URL ; 18 | var paramKey = "q" ; 19 | 20 | if (referrer.indexOf("?") !== -1) { 21 | var qs = referrer.substr(referrer.indexOf('?') + 1); 22 | var qs_noanchor = qs.split('#')[0]; 23 | var qsa = qs_noanchor.split('&'); 24 | var keyword = ""; 25 | 26 | for (var i = 0; i < qsa.length; i++) { 27 | var currentParam = qsa[i].split('='); 28 | 29 | if (currentParam.length !== 2) { 30 | continue; 31 | } 32 | 33 | if (currentParam[0] == paramKey) { 34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20")); 35 | } 36 | } 37 | 38 | if (keyword !== "") { 39 | $(".contents").unmark({ 40 | done: function() { 41 | $(".contents").mark(keyword); 42 | } 43 | }); 44 | } 45 | } 46 | }; 47 | 48 | mark(); 49 | }); 50 | }); 51 | 52 | /* Search term highlighting ------------------------------*/ 53 | 54 | function matchedWords(hit) { 55 | var words = []; 56 | 57 | var hierarchy = hit._highlightResult.hierarchy; 58 | // loop to fetch from lvl0, lvl1, etc. 59 | for (var idx in hierarchy) { 60 | words = words.concat(hierarchy[idx].matchedWords); 61 | } 62 | 63 | var content = hit._highlightResult.content; 64 | if (content) { 65 | words = words.concat(content.matchedWords); 66 | } 67 | 68 | // return unique words 69 | var words_uniq = [...new Set(words)]; 70 | return words_uniq; 71 | } 72 | 73 | function updateHitURL(hit) { 74 | 75 | var words = matchedWords(hit); 76 | var url = ""; 77 | 78 | if (hit.anchor) { 79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor; 80 | } else { 81 | url = hit.url + '?q=' + escape(words.join(" ")); 82 | } 83 | 84 | return url; 85 | } 86 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Tools for Working with NHS Number Checksums • nhsnumber 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 24 | 25 | 26 | 27 | 28 |
    29 |
    66 | 67 | 68 | 69 | 70 |
    71 |
    72 |
    73 | 75 | 76 | 77 |

    The goal of nhsnumber is to provide some simple functions for working with NHS numbers in R.

    78 |
    79 |

    NHS Number Overview 80 |

    81 |

    NHS numbers are issued to patients of the NHS in the UK.

    82 |

    The number consists of 9 digits and a single digit checksum.

    83 |

    For more information, please see the NHS number Wikipedia article on the subject.

    84 |
    85 |
    86 |

    Installation 87 |

    88 |

    You can install the released version of nhsnumber from CRAN with:

    89 |
     90 | install.packages("nhsnumber")
    91 |

    You can install the development version of nhsnumber from GitHub with:

    92 |
     93 | devtools::install_github("sellorm/nhsnumber")
    94 |
    95 |
    96 |

    Example 97 |

    98 |

    The is_valid function takes a vector of NHS numbers and returns TRUE or FALSE depending on whether the checksum is successfully validated.

    99 |
    100 | x <- c(9876543210, 1234567890, 1234567881)
    101 | nhsnumber::is_valid(x)
    102 |

    Which returns:

    103 |
    TRUE FALSE  TRUE
    104 |

    It’s also possible to generate the checksums using the get_checksum function. This function uses the 9 core digits and returns either the checksum on its own, or the full 10 digit number.

    105 |
    106 | nhsnumber::get_checksum(123456788)
    107 |

    which returns:

    108 |
    1
    109 |

    or with the full output:

    110 |
    111 | nhsnumber::get_checksum(123456788, full_output = TRUE)
    112 |

    Which returns:

    113 |
    1234567881
    114 |

    Some number combinations are invalid and these will throw an error, for example:

    115 |
    116 | nhsnumber::get_checksum(123456789)
    117 |

    Which results in this:

    118 |
    Error in nhsnumber::get_checksum(123456789) : Input sequence is invalid
    119 |            Checksum was 10 which is not permissable
    120 |
    121 |
    122 |
    123 | 124 | 168 |
    169 | 170 | 171 |
    175 | 176 |
    177 |

    178 |

    Site built with pkgdown 2.0.1.

    179 |
    180 | 181 |
    182 |
    183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /docs/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /docs/news/index.html: -------------------------------------------------------------------------------- 1 | 2 | Changelog • nhsnumber 6 | 7 | 8 |
    9 |
    40 | 41 | 42 | 43 |
    44 |
    45 | 49 | 50 |
    51 | 52 |
    • Properly handles NA in input data (#3)
    • 53 |
    • Consolidated email addresses used on CRAN - now matches my other package, rlog
    • 54 |
    55 |
    56 | 57 |
    • Added a NEWS.md file to track changes to the package.
    • 58 |
    • Preparing for first CRAN submission
    • 59 |
    60 |
    61 | 62 | 65 | 66 |
    67 | 68 | 69 |
    72 | 73 |
    74 |

    Site built with pkgdown 2.0.1.

    75 |
    76 | 77 |
    78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /docs/pkgdown.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer */ 2 | 3 | /** 4 | * Basic idea: https://philipwalton.github.io/solved-by-flexbox/demos/sticky-footer/ 5 | * Details: https://github.com/philipwalton/solved-by-flexbox/blob/master/assets/css/components/site.css 6 | * 7 | * .Site -> body > .container 8 | * .Site-content -> body > .container .row 9 | * .footer -> footer 10 | * 11 | * Key idea seems to be to ensure that .container and __all its parents__ 12 | * have height set to 100% 13 | * 14 | */ 15 | 16 | html, body { 17 | height: 100%; 18 | } 19 | 20 | body { 21 | position: relative; 22 | } 23 | 24 | body > .container { 25 | display: flex; 26 | height: 100%; 27 | flex-direction: column; 28 | } 29 | 30 | body > .container .row { 31 | flex: 1 0 auto; 32 | } 33 | 34 | footer { 35 | margin-top: 45px; 36 | padding: 35px 0 36px; 37 | border-top: 1px solid #e5e5e5; 38 | color: #666; 39 | display: flex; 40 | flex-shrink: 0; 41 | } 42 | footer p { 43 | margin-bottom: 0; 44 | } 45 | footer div { 46 | flex: 1; 47 | } 48 | footer .pkgdown { 49 | text-align: right; 50 | } 51 | footer p { 52 | margin-bottom: 0; 53 | } 54 | 55 | img.icon { 56 | float: right; 57 | } 58 | 59 | /* Ensure in-page images don't run outside their container */ 60 | .contents img { 61 | max-width: 100%; 62 | height: auto; 63 | } 64 | 65 | /* Fix bug in bootstrap (only seen in firefox) */ 66 | summary { 67 | display: list-item; 68 | } 69 | 70 | /* Typographic tweaking ---------------------------------*/ 71 | 72 | .contents .page-header { 73 | margin-top: calc(-60px + 1em); 74 | } 75 | 76 | dd { 77 | margin-left: 3em; 78 | } 79 | 80 | /* Section anchors ---------------------------------*/ 81 | 82 | a.anchor { 83 | display: none; 84 | margin-left: 5px; 85 | width: 20px; 86 | height: 20px; 87 | 88 | background-image: url(./link.svg); 89 | background-repeat: no-repeat; 90 | background-size: 20px 20px; 91 | background-position: center center; 92 | } 93 | 94 | h1:hover .anchor, 95 | h2:hover .anchor, 96 | h3:hover .anchor, 97 | h4:hover .anchor, 98 | h5:hover .anchor, 99 | h6:hover .anchor { 100 | display: inline-block; 101 | } 102 | 103 | /* Fixes for fixed navbar --------------------------*/ 104 | 105 | .contents h1, .contents h2, .contents h3, .contents h4 { 106 | padding-top: 60px; 107 | margin-top: -40px; 108 | } 109 | 110 | /* Navbar submenu --------------------------*/ 111 | 112 | .dropdown-submenu { 113 | position: relative; 114 | } 115 | 116 | .dropdown-submenu>.dropdown-menu { 117 | top: 0; 118 | left: 100%; 119 | margin-top: -6px; 120 | margin-left: -1px; 121 | border-radius: 0 6px 6px 6px; 122 | } 123 | 124 | .dropdown-submenu:hover>.dropdown-menu { 125 | display: block; 126 | } 127 | 128 | .dropdown-submenu>a:after { 129 | display: block; 130 | content: " "; 131 | float: right; 132 | width: 0; 133 | height: 0; 134 | border-color: transparent; 135 | border-style: solid; 136 | border-width: 5px 0 5px 5px; 137 | border-left-color: #cccccc; 138 | margin-top: 5px; 139 | margin-right: -10px; 140 | } 141 | 142 | .dropdown-submenu:hover>a:after { 143 | border-left-color: #ffffff; 144 | } 145 | 146 | .dropdown-submenu.pull-left { 147 | float: none; 148 | } 149 | 150 | .dropdown-submenu.pull-left>.dropdown-menu { 151 | left: -100%; 152 | margin-left: 10px; 153 | border-radius: 6px 0 6px 6px; 154 | } 155 | 156 | /* Sidebar --------------------------*/ 157 | 158 | #pkgdown-sidebar { 159 | margin-top: 30px; 160 | position: -webkit-sticky; 161 | position: sticky; 162 | top: 70px; 163 | } 164 | 165 | #pkgdown-sidebar h2 { 166 | font-size: 1.5em; 167 | margin-top: 1em; 168 | } 169 | 170 | #pkgdown-sidebar h2:first-child { 171 | margin-top: 0; 172 | } 173 | 174 | #pkgdown-sidebar .list-unstyled li { 175 | margin-bottom: 0.5em; 176 | } 177 | 178 | /* bootstrap-toc tweaks ------------------------------------------------------*/ 179 | 180 | /* All levels of nav */ 181 | 182 | nav[data-toggle='toc'] .nav > li > a { 183 | padding: 4px 20px 4px 6px; 184 | font-size: 1.5rem; 185 | font-weight: 400; 186 | color: inherit; 187 | } 188 | 189 | nav[data-toggle='toc'] .nav > li > a:hover, 190 | nav[data-toggle='toc'] .nav > li > a:focus { 191 | padding-left: 5px; 192 | color: inherit; 193 | border-left: 1px solid #878787; 194 | } 195 | 196 | nav[data-toggle='toc'] .nav > .active > a, 197 | nav[data-toggle='toc'] .nav > .active:hover > a, 198 | nav[data-toggle='toc'] .nav > .active:focus > a { 199 | padding-left: 5px; 200 | font-size: 1.5rem; 201 | font-weight: 400; 202 | color: inherit; 203 | border-left: 2px solid #878787; 204 | } 205 | 206 | /* Nav: second level (shown on .active) */ 207 | 208 | nav[data-toggle='toc'] .nav .nav { 209 | display: none; /* Hide by default, but at >768px, show it */ 210 | padding-bottom: 10px; 211 | } 212 | 213 | nav[data-toggle='toc'] .nav .nav > li > a { 214 | padding-left: 16px; 215 | font-size: 1.35rem; 216 | } 217 | 218 | nav[data-toggle='toc'] .nav .nav > li > a:hover, 219 | nav[data-toggle='toc'] .nav .nav > li > a:focus { 220 | padding-left: 15px; 221 | } 222 | 223 | nav[data-toggle='toc'] .nav .nav > .active > a, 224 | nav[data-toggle='toc'] .nav .nav > .active:hover > a, 225 | nav[data-toggle='toc'] .nav .nav > .active:focus > a { 226 | padding-left: 15px; 227 | font-weight: 500; 228 | font-size: 1.35rem; 229 | } 230 | 231 | /* orcid ------------------------------------------------------------------- */ 232 | 233 | .orcid { 234 | font-size: 16px; 235 | color: #A6CE39; 236 | /* margins are required by official ORCID trademark and display guidelines */ 237 | margin-left:4px; 238 | margin-right:4px; 239 | vertical-align: middle; 240 | } 241 | 242 | /* Reference index & topics ----------------------------------------------- */ 243 | 244 | .ref-index th {font-weight: normal;} 245 | 246 | .ref-index td {vertical-align: top; min-width: 100px} 247 | .ref-index .icon {width: 40px;} 248 | .ref-index .alias {width: 40%;} 249 | .ref-index-icons .alias {width: calc(40% - 40px);} 250 | .ref-index .title {width: 60%;} 251 | 252 | .ref-arguments th {text-align: right; padding-right: 10px;} 253 | .ref-arguments th, .ref-arguments td {vertical-align: top; min-width: 100px} 254 | .ref-arguments .name {width: 20%;} 255 | .ref-arguments .desc {width: 80%;} 256 | 257 | /* Nice scrolling for wide elements --------------------------------------- */ 258 | 259 | table { 260 | display: block; 261 | overflow: auto; 262 | } 263 | 264 | /* Syntax highlighting ---------------------------------------------------- */ 265 | 266 | pre, code, pre code { 267 | background-color: #f8f8f8; 268 | color: #333; 269 | } 270 | pre, pre code { 271 | white-space: pre-wrap; 272 | word-break: break-all; 273 | overflow-wrap: break-word; 274 | } 275 | 276 | pre { 277 | border: 1px solid #eee; 278 | } 279 | 280 | pre .img, pre .r-plt { 281 | margin: 5px 0; 282 | } 283 | 284 | pre .img img, pre .r-plt img { 285 | background-color: #fff; 286 | } 287 | 288 | code a, pre a { 289 | color: #375f84; 290 | } 291 | 292 | a.sourceLine:hover { 293 | text-decoration: none; 294 | } 295 | 296 | .fl {color: #1514b5;} 297 | .fu {color: #000000;} /* function */ 298 | .ch,.st {color: #036a07;} /* string */ 299 | .kw {color: #264D66;} /* keyword */ 300 | .co {color: #888888;} /* comment */ 301 | 302 | .error {font-weight: bolder;} 303 | .warning {font-weight: bolder;} 304 | 305 | /* Clipboard --------------------------*/ 306 | 307 | .hasCopyButton { 308 | position: relative; 309 | } 310 | 311 | .btn-copy-ex { 312 | position: absolute; 313 | right: 0; 314 | top: 0; 315 | visibility: hidden; 316 | } 317 | 318 | .hasCopyButton:hover button.btn-copy-ex { 319 | visibility: visible; 320 | } 321 | 322 | /* headroom.js ------------------------ */ 323 | 324 | .headroom { 325 | will-change: transform; 326 | transition: transform 200ms linear; 327 | } 328 | .headroom--pinned { 329 | transform: translateY(0%); 330 | } 331 | .headroom--unpinned { 332 | transform: translateY(-100%); 333 | } 334 | 335 | /* mark.js ----------------------------*/ 336 | 337 | mark { 338 | background-color: rgba(255, 255, 51, 0.5); 339 | border-bottom: 2px solid rgba(255, 153, 51, 0.3); 340 | padding: 1px; 341 | } 342 | 343 | /* vertical spacing after htmlwidgets */ 344 | .html-widget { 345 | margin-bottom: 10px; 346 | } 347 | 348 | /* fontawesome ------------------------ */ 349 | 350 | .fab { 351 | font-family: "Font Awesome 5 Brands" !important; 352 | } 353 | 354 | /* don't display links in code chunks when printing */ 355 | /* source: https://stackoverflow.com/a/10781533 */ 356 | @media print { 357 | code a:link:after, code a:visited:after { 358 | content: ""; 359 | } 360 | } 361 | 362 | /* Section anchors --------------------------------- 363 | Added in pandoc 2.11: https://github.com/jgm/pandoc-templates/commit/9904bf71 364 | */ 365 | 366 | div.csl-bib-body { } 367 | div.csl-entry { 368 | clear: both; 369 | } 370 | .hanging-indent div.csl-entry { 371 | margin-left:2em; 372 | text-indent:-2em; 373 | } 374 | div.csl-left-margin { 375 | min-width:2em; 376 | float:left; 377 | } 378 | div.csl-right-inline { 379 | margin-left:2em; 380 | padding-left:1em; 381 | } 382 | div.csl-indent { 383 | margin-left: 2em; 384 | } 385 | -------------------------------------------------------------------------------- /docs/pkgdown.js: -------------------------------------------------------------------------------- 1 | /* http://gregfranko.com/blog/jquery-best-practices/ */ 2 | (function($) { 3 | $(function() { 4 | 5 | $('.navbar-fixed-top').headroom(); 6 | 7 | $('body').css('padding-top', $('.navbar').height() + 10); 8 | $(window).resize(function(){ 9 | $('body').css('padding-top', $('.navbar').height() + 10); 10 | }); 11 | 12 | $('[data-toggle="tooltip"]').tooltip(); 13 | 14 | var cur_path = paths(location.pathname); 15 | var links = $("#navbar ul li a"); 16 | var max_length = -1; 17 | var pos = -1; 18 | for (var i = 0; i < links.length; i++) { 19 | if (links[i].getAttribute("href") === "#") 20 | continue; 21 | // Ignore external links 22 | if (links[i].host !== location.host) 23 | continue; 24 | 25 | var nav_path = paths(links[i].pathname); 26 | 27 | var length = prefix_length(nav_path, cur_path); 28 | if (length > max_length) { 29 | max_length = length; 30 | pos = i; 31 | } 32 | } 33 | 34 | // Add class to parent
  • , and enclosing
  • if in dropdown 35 | if (pos >= 0) { 36 | var menu_anchor = $(links[pos]); 37 | menu_anchor.parent().addClass("active"); 38 | menu_anchor.closest("li.dropdown").addClass("active"); 39 | } 40 | }); 41 | 42 | function paths(pathname) { 43 | var pieces = pathname.split("/"); 44 | pieces.shift(); // always starts with / 45 | 46 | var end = pieces[pieces.length - 1]; 47 | if (end === "index.html" || end === "") 48 | pieces.pop(); 49 | return(pieces); 50 | } 51 | 52 | // Returns -1 if not found 53 | function prefix_length(needle, haystack) { 54 | if (needle.length > haystack.length) 55 | return(-1); 56 | 57 | // Special case for length-0 haystack, since for loop won't run 58 | if (haystack.length === 0) { 59 | return(needle.length === 0 ? 0 : -1); 60 | } 61 | 62 | for (var i = 0; i < haystack.length; i++) { 63 | if (needle[i] != haystack[i]) 64 | return(i); 65 | } 66 | 67 | return(haystack.length); 68 | } 69 | 70 | /* Clipboard --------------------------*/ 71 | 72 | function changeTooltipMessage(element, msg) { 73 | var tooltipOriginalTitle=element.getAttribute('data-original-title'); 74 | element.setAttribute('data-original-title', msg); 75 | $(element).tooltip('show'); 76 | element.setAttribute('data-original-title', tooltipOriginalTitle); 77 | } 78 | 79 | if(ClipboardJS.isSupported()) { 80 | $(document).ready(function() { 81 | var copyButton = ""; 82 | 83 | $("div.sourceCode").addClass("hasCopyButton"); 84 | 85 | // Insert copy buttons: 86 | $(copyButton).prependTo(".hasCopyButton"); 87 | 88 | // Initialize tooltips: 89 | $('.btn-copy-ex').tooltip({container: 'body'}); 90 | 91 | // Initialize clipboard: 92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', { 93 | text: function(trigger) { 94 | return trigger.parentNode.textContent.replace(/\n#>[^\n]*/g, ""); 95 | } 96 | }); 97 | 98 | clipboardBtnCopies.on('success', function(e) { 99 | changeTooltipMessage(e.trigger, 'Copied!'); 100 | e.clearSelection(); 101 | }); 102 | 103 | clipboardBtnCopies.on('error', function() { 104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy'); 105 | }); 106 | }); 107 | } 108 | })(window.jQuery || window.$) 109 | -------------------------------------------------------------------------------- /docs/pkgdown.yml: -------------------------------------------------------------------------------- 1 | pandoc: 2.16.2 2 | pkgdown: 2.0.1 3 | pkgdown_sha: ~ 4 | articles: {} 5 | last_built: 2021-12-06T15:42Z 6 | 7 | -------------------------------------------------------------------------------- /docs/reference/Rplot001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sellorm/nhsnumber/3cca298c95e1efd101b39915024123e29d1213c3/docs/reference/Rplot001.png -------------------------------------------------------------------------------- /docs/reference/get_checksum.html: -------------------------------------------------------------------------------- 1 | 2 | Calculates NHS number checksums — get_checksum • nhsnumber 8 | 9 | 10 |
    11 |
    42 | 43 | 44 | 45 |
    46 |
    47 | 52 | 53 |
    54 |

    NHS numbers are 10 digit numbers where the 10th digit is a checksum. 55 | This function takes the 9 core digits of the NHS number and calculates the 56 | checksums.

    57 |
    58 | 59 |
    60 |
    get_checksum(nhs_number, full_output = FALSE)
    61 |
    62 | 63 |
    64 |

    Arguments

    65 |
    nhs_number
    66 |

    A 9 digit NHS numbers to validate

    67 |
    full_output
    68 |

    Boolean that controls display of warning messages

    69 |
    70 |
    71 |

    Value

    72 |

    Checksum or the complete NHS number including the checksum.

    73 |
    74 |
    75 |

    Details

    76 |

    This function has been created using information from: 77 | https://en.wikipedia.org/wiki/NHS_number 78 | and various NHS sources

    79 |
    80 | 81 |
    82 |

    Examples

    83 |
    get_checksum(123456788)
     84 | #> [1] 1
     85 | get_checksum(123456788, full_output = TRUE)
     86 | #> [1] 1234567881
     87 | 
    88 |
    89 |
    90 | 93 |
    94 | 95 | 96 |
    99 | 100 |
    101 |

    Site built with pkgdown 2.0.1.

    102 |
    103 | 104 |
    105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /docs/reference/index.html: -------------------------------------------------------------------------------- 1 | 2 | Function reference • nhsnumber 6 | 7 | 8 |
    9 |
    40 | 41 | 42 | 43 |
    44 |
    45 | 48 | 49 | 53 | 56 | 57 | 60 | 61 | 64 | 65 |
    50 |

    All functions

    51 |

    52 |
    54 |

    get_checksum()

    55 |

    Calculates NHS number checksums

    58 |

    is_number_valid()

    59 |

    Checks validity of an NHS number

    62 |

    is_valid()

    63 |

    Checks validity of NHS numbers

    66 | 67 | 70 |
    71 | 72 | 73 |
    76 | 77 |
    78 |

    Site built with pkgdown 2.0.1.

    79 |
    80 | 81 |
    82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /docs/reference/is_number_valid.html: -------------------------------------------------------------------------------- 1 | 2 | Checks validity of an NHS number — is_number_valid • nhsnumber 7 | 8 | 9 |
    10 |
    41 | 42 | 43 | 44 |
    45 |
    46 | 51 | 52 |
    53 |

    NHS numbers are 10 digit numbers where the 10th digit is a checksum. 54 | This function validates the checksums of the supplied NHS numbers.

    55 |
    56 | 57 |
    58 |
    is_number_valid(nhs_number, warn = TRUE)
    59 |
    60 | 61 |
    62 |

    Arguments

    63 |
    nhs_number
    64 |

    A 10 digit NHS number to validate

    65 |
    warn
    66 |

    Boolean that controls display of warning messages

    67 |
    68 | 69 |
    70 |

    Examples

    71 |
    is_valid(1234567881)
     72 | #> [1] TRUE
     73 | is_valid(1234567890, warn = FALSE)
     74 | #> [1] FALSE
     75 | 
    76 |
    77 |
    78 | 81 |
    82 | 83 | 84 |
    87 | 88 |
    89 |

    Site built with pkgdown 2.0.1.

    90 |
    91 | 92 |
    93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /docs/reference/is_valid.html: -------------------------------------------------------------------------------- 1 | 2 | Checks validity of NHS numbers — is_valid • nhsnumber 7 | 8 | 9 |
    10 |
    41 | 42 | 43 | 44 |
    45 |
    46 | 51 | 52 |
    53 |

    NHS numbers are 10 digit numbers where the 10th digit is a checksum. 54 | This function validates the checksums of the supplied NHS numbers.

    55 |
    56 | 57 |
    58 |
    is_valid(nhs_number, warn = TRUE)
    59 |
    60 | 61 |
    62 |

    Arguments

    63 |
    nhs_number
    64 |

    A vector of 10 digit NHS numbers to validate

    65 |
    warn
    66 |

    Boolean that controls display of warning messages

    67 |
    68 |
    69 |

    Value

    70 |

    A logical vector indicating the validity of each input value

    71 |
    72 | 73 |
    74 |

    Examples

    75 |
    is_valid(1234567881)
     76 | #> [1] TRUE
     77 | is_valid(c(1234567881, 1234512345, 123456789))
     78 | #> Warning: nhs_number is incorrect length - should be 10 digits
     79 | #> [1]  TRUE FALSE FALSE
     80 | is_valid(1234567890, warn = FALSE)
     81 | #> [1] FALSE
     82 | 
    83 |
    84 |
    85 | 88 |
    89 | 90 | 91 |
    94 | 95 |
    96 |

    Site built with pkgdown 2.0.1.

    97 |
    98 | 99 |
    100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /404.html 5 | 6 | 7 | /LICENSE-text.html 8 | 9 | 10 | /LICENSE.html 11 | 12 | 13 | /authors.html 14 | 15 | 16 | /index.html 17 | 18 | 19 | /news/index.html 20 | 21 | 22 | /reference/get_checksum.html 23 | 24 | 25 | /reference/index.html 26 | 27 | 28 | /reference/is_number_valid.html 29 | 30 | 31 | /reference/is_valid.html 32 | 33 | 34 | -------------------------------------------------------------------------------- /man/get_checksum.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/get_checksum.R 3 | \name{get_checksum} 4 | \alias{get_checksum} 5 | \title{Calculates NHS number checksums} 6 | \usage{ 7 | get_checksum(nhs_number, full_output = FALSE) 8 | } 9 | \arguments{ 10 | \item{nhs_number}{A 9 digit NHS numbers to validate} 11 | 12 | \item{full_output}{Boolean that controls display of warning messages} 13 | } 14 | \value{ 15 | Checksum or the complete NHS number including the checksum. 16 | } 17 | \description{ 18 | NHS numbers are 10 digit numbers where the 10th digit is a checksum. 19 | This function takes the 9 core digits of the NHS number and calculates the 20 | checksums. 21 | } 22 | \details{ 23 | This function has been created using information from: 24 | https://en.wikipedia.org/wiki/NHS_number 25 | and various NHS sources 26 | } 27 | \examples{ 28 | get_checksum(123456788) 29 | get_checksum(123456788, full_output = TRUE) 30 | } 31 | -------------------------------------------------------------------------------- /man/is_number_valid.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/is_number_valid.R 3 | \name{is_number_valid} 4 | \alias{is_number_valid} 5 | \title{Checks validity of an NHS number} 6 | \usage{ 7 | is_number_valid(nhs_number, warn = TRUE) 8 | } 9 | \arguments{ 10 | \item{nhs_number}{A 10 digit NHS number to validate} 11 | 12 | \item{warn}{Boolean that controls display of warning messages} 13 | } 14 | \description{ 15 | NHS numbers are 10 digit numbers where the 10th digit is a checksum. 16 | This function validates the checksums of the supplied NHS numbers. 17 | } 18 | \examples{ 19 | is_valid(1234567881) 20 | is_valid(1234567890, warn = FALSE) 21 | } 22 | -------------------------------------------------------------------------------- /man/is_valid.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/is_valid.R 3 | \name{is_valid} 4 | \alias{is_valid} 5 | \title{Checks validity of NHS numbers} 6 | \usage{ 7 | is_valid(nhs_number, warn = TRUE) 8 | } 9 | \arguments{ 10 | \item{nhs_number}{A vector of 10 digit NHS numbers to validate} 11 | 12 | \item{warn}{Boolean that controls display of warning messages} 13 | } 14 | \value{ 15 | A logical vector indicating the validity of each input value 16 | } 17 | \description{ 18 | NHS numbers are 10 digit numbers where the 10th digit is a checksum. 19 | This function validates the checksums of the supplied NHS numbers. 20 | } 21 | \examples{ 22 | is_valid(1234567881) 23 | is_valid(c(1234567881, 1234512345, 123456789)) 24 | is_valid(1234567890, warn = FALSE) 25 | } 26 | -------------------------------------------------------------------------------- /nhsnumber.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(nhsnumber) 3 | 4 | test_check("nhsnumber") 5 | -------------------------------------------------------------------------------- /tests/testthat/test-get_checksum.R: -------------------------------------------------------------------------------- 1 | context("Checks correct output") 2 | test_that("Test for correct checksums", { 3 | expect_equal(get_checksum(123456788), 1) 4 | expect_equal(get_checksum(987654321), 0) 5 | expect_equal(get_checksum(234567890), 9) 6 | expect_equal(get_checksum(345678901), 7) 7 | expect_equal(get_checksum(456789012), 4) 8 | expect_equal(get_checksum(567890123), 0) 9 | expect_equal(get_checksum(678901234), 6) 10 | expect_equal(get_checksum(789012345), 0) 11 | expect_equal(get_checksum(890123456), 4) 12 | expect_equal(get_checksum(901234567), 7) 13 | expect_equal(get_checksum("012345678"), 9) 14 | expect_equal(get_checksum(123456788, full_output = TRUE), 1234567881) 15 | }) 16 | 17 | context("Checks for errors") 18 | test_that("Test for errors in correct places", { 19 | expect_error(get_checksum(12345)) 20 | expect_error(get_checksum(123456789)) 21 | expect_error(get_checksum("hatstand")) 22 | expect_error(get_checksum("hatstands")) 23 | }) 24 | -------------------------------------------------------------------------------- /tests/testthat/test-is_number_valid.R: -------------------------------------------------------------------------------- 1 | context("Is that NHS Number valid?") 2 | test_that("10 digit numbers return correct responses", { 3 | expect_equal(is_number_valid(1234567881), TRUE) 4 | expect_equal(is_number_valid(1234567890), FALSE) 5 | expect_equal(is_number_valid(2345678909), TRUE) 6 | expect_equal(is_number_valid(2345678901), FALSE) 7 | expect_equal(is_number_valid(NA, warn = FALSE), FALSE) 8 | }) 9 | 10 | context("Garbage input") 11 | test_that("Incorrect input still provides the expected output", { 12 | expect_equal(is_number_valid(12345, warn = FALSE), FALSE) 13 | expect_equal(is_number_valid("hat", warn = FALSE), FALSE) 14 | expect_equal(is_number_valid(12345678901, warn = FALSE), FALSE) 15 | expect_warning(is_number_valid(1)) 16 | expect_warning(is_number_valid(NA)) 17 | }) 18 | -------------------------------------------------------------------------------- /tests/testthat/test-is_valid.R: -------------------------------------------------------------------------------- 1 | context("is_valid and the vectors") 2 | test_that("we can accept vectors", { 3 | expect_equal( 4 | is_valid(c(1234567881, 1234567890, 9876543210, 2345678901)), 5 | c(TRUE, FALSE, TRUE, FALSE) 6 | ) 7 | }) 8 | 9 | context("is_valid and dodgy inputs") 10 | test_that("We don't accept these incorrect inputs", { 11 | expect_error(is_valid(as.data.frame("hat"))) 12 | }) 13 | --------------------------------------------------------------------------------