├── .github ├── .gitignore └── workflows │ ├── R-CMD-check.yaml │ ├── test-coverage.yaml │ └── pr-commands.yaml ├── .gitignore ├── LICENSE ├── data ├── repos.rda ├── cran_dl_data.rda └── gh_stars_data.rda ├── tests ├── testthat.R ├── spelling.R └── testthat │ ├── test-make_table.R │ ├── test-get_data.R │ ├── test-golem-recommended.R │ ├── test-make_plots.R │ └── test-mod_plots.R ├── inst ├── app │ └── www │ │ ├── hex.png │ │ ├── favicon.ico │ │ └── styles.css ├── golem-config.yml ├── WORDLIST └── CODE_OF_CONDUCT.md ├── .Rbuildignore ├── R ├── repos.R ├── gh_stars_data.R ├── cran_dl_data.R ├── run_app.R ├── app_server.R ├── app_config.R ├── fct_make_table.R ├── mod_plots.R ├── mod_table.R ├── app_ui.R ├── fct_get_data.R ├── utils.R ├── fct_make_plots.R └── mod_sidebar.R ├── app.R ├── man ├── run_app.Rd ├── gh_plot.Rd ├── cran_plot.Rd ├── repos.Rd ├── theme_calendar.Rd ├── get_cran_name.Rd ├── get_gh_stars.Rd ├── gh_stars_data.Rd ├── cran_dl_data.Rd └── get_cran_data.Rd ├── dev ├── run_dev.R ├── 03_deploy.R ├── 01_start.R └── 02_dev.R ├── cranstars.Rproj ├── DESCRIPTION ├── LICENSE.md ├── NAMESPACE ├── data-raw └── DATASET.R └── README.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rhistory 2 | .RData 3 | .Rproj.user 4 | .Rproj -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2020 2 | COPYRIGHT HOLDER: Zauad Shahreer Abeer 3 | -------------------------------------------------------------------------------- /data/repos.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahreyar-abeer/cranstars/HEAD/data/repos.rda -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(cranstars) 3 | 4 | test_check("cranstars") 5 | -------------------------------------------------------------------------------- /data/cran_dl_data.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahreyar-abeer/cranstars/HEAD/data/cran_dl_data.rda -------------------------------------------------------------------------------- /inst/app/www/hex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahreyar-abeer/cranstars/HEAD/inst/app/www/hex.png -------------------------------------------------------------------------------- /data/gh_stars_data.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahreyar-abeer/cranstars/HEAD/data/gh_stars_data.rda -------------------------------------------------------------------------------- /inst/app/www/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shahreyar-abeer/cranstars/HEAD/inst/app/www/favicon.ico -------------------------------------------------------------------------------- /inst/golem-config.yml: -------------------------------------------------------------------------------- 1 | default: 2 | golem_name: cranstars 3 | golem_version: 0.0.0.9000 4 | app_prod: no 5 | production: 6 | app_prod: yes 7 | dev: 8 | golem_wd: !expr here::here() 9 | -------------------------------------------------------------------------------- /tests/spelling.R: -------------------------------------------------------------------------------- 1 | if(requireNamespace('spelling', quietly = TRUE)) 2 | spelling::spell_check_test(vignettes = TRUE, error = FALSE, 3 | skip_on_cran = TRUE) 4 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^data-raw$ 4 | dev_history.R 5 | ^dev$ 6 | $run_dev.* 7 | ^codecov\.yml$ 8 | ^app\.R$ 9 | ^rsconnect$ 10 | ^\.github$ 11 | ^LICENSE\.md$ 12 | -------------------------------------------------------------------------------- /inst/WORDLIST: -------------------------------------------------------------------------------- 1 | abeer 2 | codecov 3 | cran 4 | cranstars’ 5 | dl 6 | downlaods 7 | eg 8 | gh 9 | github 10 | Github 11 | golem 12 | https 13 | io 14 | Lifecycle 15 | repo 16 | Repo 17 | repos 18 | rsconnect 19 | rstudio 20 | shahreyar 21 | shinyapps 22 | tidyverse 23 | -------------------------------------------------------------------------------- /R/repos.R: -------------------------------------------------------------------------------- 1 | 2 | #' 20 tidyverse packages 3 | #' 4 | #' A dataset containing 20 tidyverse packages in the form of github repos. 5 | #' 6 | #' @format A data frame with 20 rows and 1 variable: 7 | #' \describe{ 8 | #' \item{tidypacks}{The name of the github repo} 9 | #' } 10 | "repos" -------------------------------------------------------------------------------- /tests/testthat/test-make_table.R: -------------------------------------------------------------------------------- 1 | test_that("make_table() works", { 2 | r = list( 3 | repo = "hadley/dplyr", 4 | date = c((Sys.Date() - 50), (Sys.Date() - 10)), 5 | cran_dl = cran_dl_data, 6 | gh_stars = gh_stars_data 7 | ) 8 | expect_is(make_table(r), "data.frame") 9 | }) -------------------------------------------------------------------------------- /app.R: -------------------------------------------------------------------------------- 1 | # Launch the ShinyApp (Do not remove this comment) 2 | # To deploy, run: rsconnect::deployApp() 3 | # Or use the blue button on top of this file 4 | 5 | pkgload::load_all(export_all = FALSE,helpers = FALSE,attach_testthat = FALSE) 6 | options( "golem.app.prod" = TRUE) 7 | cranstars::run_app() # add parameters here (if any) 8 | -------------------------------------------------------------------------------- /tests/testthat/test-get_data.R: -------------------------------------------------------------------------------- 1 | 2 | test_that("get_gh_data() works", { 3 | repo <- "rstudio/rsconnect" 4 | expect_is(get_gh_stars(repo), "data.frame") 5 | }) 6 | 7 | test_that("get_cran_data() works", { 8 | package_name <- "rsconnect" 9 | expect_is(get_cran_data(package_name, from = (Sys.Date() - 10)), "data.frame") 10 | }) 11 | -------------------------------------------------------------------------------- /man/run_app.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/run_app.R 3 | \name{run_app} 4 | \alias{run_app} 5 | \title{Run the Shiny Application} 6 | \usage{ 7 | run_app(...) 8 | } 9 | \arguments{ 10 | \item{...}{A series of options to be used inside the app.} 11 | } 12 | \description{ 13 | Run the Shiny Application 14 | } 15 | -------------------------------------------------------------------------------- /dev/run_dev.R: -------------------------------------------------------------------------------- 1 | # Set options here 2 | options(golem.app.prod = FALSE) # TRUE = production mode, FALSE = development mode 3 | 4 | # Detach all loaded packages and clean your environment 5 | golem::detach_all_attached() 6 | # rm(list=ls(all.names = TRUE)) 7 | 8 | # Document and reload your package 9 | golem::document_and_reload() 10 | 11 | # Run the application 12 | run_app() 13 | -------------------------------------------------------------------------------- /R/gh_stars_data.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' Github Stars for 20 tidyverse packages 4 | #' 5 | #' A dataset containing the daily number of stars the packages got 6 | #' 7 | #' @format A data frame with 12748 rows and 3 variables: 8 | #' \describe{ 9 | #' \item{date}{The date} 10 | #' \item{n_stars}{Number of stars in the date} 11 | #' \item{package}{Repo of package} 12 | #' } 13 | "gh_stars_data" -------------------------------------------------------------------------------- /R/cran_dl_data.R: -------------------------------------------------------------------------------- 1 | 2 | #' CRAN downloads for 20 tidyverse packages 3 | #' 4 | #' A dataset containing the daily number of downloads for the supplied packages 5 | #' 6 | #' @format A data frame with 77940 rows and 3 variables: 7 | #' \describe{ 8 | #' \item{date}{The date} 9 | #' \item{count}{Number of downlaods in the date} 10 | #' \item{package}{Name of package} 11 | #' } 12 | "cran_dl_data" -------------------------------------------------------------------------------- /cranstars.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 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /man/gh_plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_make_plots.R 3 | \name{gh_plot} 4 | \alias{gh_plot} 5 | \title{Title} 6 | \usage{ 7 | gh_plot(r) 8 | } 9 | \arguments{ 10 | \item{r}{\code{reactiveValues} object with 4 slots, repo, date, cran_dl & gh_stars} 11 | } 12 | \value{ 13 | \code{ggplot} 14 | } 15 | \description{ 16 | Title 17 | } 18 | -------------------------------------------------------------------------------- /man/cran_plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_make_plots.R 3 | \name{cran_plot} 4 | \alias{cran_plot} 5 | \title{Title} 6 | \usage{ 7 | cran_plot(r) 8 | } 9 | \arguments{ 10 | \item{r}{\code{reactiveValues} object with 4 slots, repo, date, cran_dl & gh_stars} 11 | } 12 | \value{ 13 | \code{ggplot} 14 | } 15 | \description{ 16 | Title 17 | } 18 | -------------------------------------------------------------------------------- /tests/testthat/test-golem-recommended.R: -------------------------------------------------------------------------------- 1 | test_that("app ui", { 2 | ui <- app_ui() 3 | golem::expect_shinytaglist(ui) 4 | }) 5 | 6 | test_that("app server", { 7 | server <- app_server 8 | expect_is(server, "function") 9 | }) 10 | 11 | # Configure this test to fit your need 12 | test_that( 13 | "app launches",{ 14 | skip_if_not(interactive()) 15 | golem::expect_running(sleep = 5) 16 | } 17 | ) -------------------------------------------------------------------------------- /R/run_app.R: -------------------------------------------------------------------------------- 1 | #' Run the Shiny Application 2 | #' 3 | #' @param ... A series of options to be used inside the app. 4 | #' 5 | #' @export 6 | #' @importFrom shiny shinyApp 7 | #' @importFrom golem with_golem_options 8 | run_app <- function( 9 | ... 10 | ) { 11 | with_golem_options( 12 | app = shinyApp( 13 | ui = app_ui, 14 | server = app_server 15 | ), 16 | golem_opts = list(...) 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /man/repos.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/repos.R 3 | \docType{data} 4 | \name{repos} 5 | \alias{repos} 6 | \title{20 tidyverse packages} 7 | \format{ 8 | A data frame with 20 rows and 1 variable: 9 | \describe{ 10 | \item{tidypacks}{The name of the github repo} 11 | } 12 | } 13 | \usage{ 14 | repos 15 | } 16 | \description{ 17 | A dataset containing 20 tidyverse packages in the form of github repos. 18 | } 19 | \keyword{datasets} 20 | -------------------------------------------------------------------------------- /man/theme_calendar.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{theme_calendar} 4 | \alias{theme_calendar} 5 | \title{Theme for the github calendar plot} 6 | \usage{ 7 | theme_calendar(base_size = 15, base_family = "sans") 8 | } 9 | \arguments{ 10 | \item{base_size}{\code{numeric} Base font size} 11 | 12 | \item{base_family}{\code{string} Base font family} 13 | } 14 | \description{ 15 | Theme for the github calendar plot 16 | } 17 | -------------------------------------------------------------------------------- /tests/testthat/test-make_plots.R: -------------------------------------------------------------------------------- 1 | test_that("cran_plot() works", { 2 | r = list( 3 | repo = "hadley/dplyr", 4 | date = c((Sys.Date() - 10), (Sys.Date() - 5)), 5 | cran_dl = cran_dl_data, 6 | gh_stars = gh_stars_data 7 | ) 8 | expect_is(cran_plot(r), "highchart") 9 | }) 10 | 11 | test_that("gh_plot() works", { 12 | r = list( 13 | repo = "hadley/dplyr", 14 | date = c((Sys.Date() - 10), (Sys.Date() - 5)), 15 | cran_dl = cran_dl_data, 16 | gh_stars = gh_stars_data 17 | ) 18 | expect_is(gh_plot(r), "ggplot") 19 | }) 20 | -------------------------------------------------------------------------------- /man/get_cran_name.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{get_cran_name} 4 | \alias{get_cran_name} 5 | \title{Get only package name from repo name} 6 | \usage{ 7 | get_cran_name(repo) 8 | } 9 | \arguments{ 10 | \item{repo}{\code{string} The repo name, e.g., \bold{"rstudio/rsconnect"}} 11 | } 12 | \value{ 13 | \code{string} The package name, e.g., \bold{"rsconnect"} 14 | } 15 | \description{ 16 | Get only package name from repo name 17 | } 18 | \examples{ 19 | get_cran_name("rsconnect/rstudio") 20 | } 21 | -------------------------------------------------------------------------------- /man/get_gh_stars.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_get_data.R 3 | \name{get_gh_stars} 4 | \alias{get_gh_stars} 5 | \title{Get the github stars for the supplied repo on a daily basis} 6 | \usage{ 7 | get_gh_stars(repo) 8 | } 9 | \arguments{ 10 | \item{repo}{A \code{character} vector containing the repo name, eg. "rstudio/rsconnect"} 11 | } 12 | \value{ 13 | A \code{data.frame} 14 | } 15 | \description{ 16 | Get the github stars for the supplied repo on a daily basis 17 | } 18 | \examples{ 19 | get_gh_stars("rstudio/rsconnect") 20 | 21 | } 22 | -------------------------------------------------------------------------------- /man/gh_stars_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gh_stars_data.R 3 | \docType{data} 4 | \name{gh_stars_data} 5 | \alias{gh_stars_data} 6 | \title{Github Stars for 20 tidyverse packages} 7 | \format{ 8 | A data frame with 12748 rows and 3 variables: 9 | \describe{ 10 | \item{date}{The date} 11 | \item{n_stars}{Number of stars in the date} 12 | \item{package}{Repo of package} 13 | } 14 | } 15 | \usage{ 16 | gh_stars_data 17 | } 18 | \description{ 19 | A dataset containing the daily number of stars the packages got 20 | } 21 | \keyword{datasets} 22 | -------------------------------------------------------------------------------- /man/cran_dl_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cran_dl_data.R 3 | \docType{data} 4 | \name{cran_dl_data} 5 | \alias{cran_dl_data} 6 | \title{CRAN downloads for 20 tidyverse packages} 7 | \format{ 8 | A data frame with 77940 rows and 3 variables: 9 | \describe{ 10 | \item{date}{The date} 11 | \item{count}{Number of downlaods in the date} 12 | \item{package}{Name of package} 13 | } 14 | } 15 | \usage{ 16 | cran_dl_data 17 | } 18 | \description{ 19 | A dataset containing the daily number of downloads for the supplied packages 20 | } 21 | \keyword{datasets} 22 | -------------------------------------------------------------------------------- /man/get_cran_data.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fct_get_data.R 3 | \name{get_cran_data} 4 | \alias{get_cran_data} 5 | \title{Title} 6 | \usage{ 7 | get_cran_data(package_names, from_date, to_date = Sys.Date()) 8 | } 9 | \arguments{ 10 | \item{package_names}{A character vector, names of packages to download data for} 11 | 12 | \item{from_date}{A string} 13 | 14 | \item{to_date}{A string, default is set to today's date} 15 | } 16 | \value{ 17 | A \code{data.frame} 18 | } 19 | \description{ 20 | Title 21 | } 22 | \examples{ 23 | get_cran_data("rsconnect", from_date = (Sys.Date() - 10)) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /R/app_server.R: -------------------------------------------------------------------------------- 1 | #' cranStars server-side 2 | #' 3 | #' @param input,output,session Internal parameters for {shiny}. 4 | #' 5 | #' @importFrom shiny reactiveValues renderUI invalidateLater tags 6 | #' @noRd 7 | app_server <- function( input, output, session ) { 8 | # List the first level callModules here 9 | r = reactiveValues( 10 | repo = NULL, 11 | date = NULL, 12 | cran_dl = NULL, 13 | gh_stars = NULL 14 | ) 15 | mod_sidebar_server("sidebar", r) 16 | mod_plot_server("cran", r, "cran") 17 | mod_plot_server("gh", r, "gh") 18 | mod_table_server("gt", r) 19 | 20 | output$quote <- renderUI({ 21 | invalidateLater(1000) 22 | tags$blockquote(icon("clock"), format(Sys.time(), "%a, %b %d, %Y | %X"), style = "color: #8a8a8a") 23 | }) 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.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 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | name: R-CMD-check 12 | 13 | jobs: 14 | R-CMD-check: 15 | runs-on: macOS-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - uses: r-lib/actions/setup-r@master 19 | - name: Install dependencies 20 | run: | 21 | install.packages(c("remotes", "rcmdcheck")) 22 | remotes::install_deps(dependencies = TRUE) 23 | shell: Rscript {0} 24 | - name: Check 25 | run: rcmdcheck::rcmdcheck(args = "--no-manual", error_on = "error") 26 | shell: Rscript {0} 27 | -------------------------------------------------------------------------------- /R/app_config.R: -------------------------------------------------------------------------------- 1 | #' Access files in the current app 2 | #' 3 | #' @param ... Character vector specifying directory and or file to 4 | #' point to inside the current package. 5 | #' 6 | #' @noRd 7 | app_sys <- function(...){ 8 | system.file(..., package = "cranstars") 9 | } 10 | 11 | 12 | #' Read App Config 13 | #' 14 | #' @param value Value to retrieve from the config file. 15 | #' @param config R_CONFIG_ACTIVE value. 16 | #' @param use_parent Logical, scan the parent directory for config file. 17 | #' 18 | #' @noRd 19 | get_golem_config <- function( 20 | value, 21 | config = Sys.getenv("R_CONFIG_ACTIVE", "default"), 22 | use_parent = TRUE 23 | ){ 24 | config::get( 25 | value = value, 26 | config = config, 27 | # Modify this if your config file is somewhere else: 28 | file = app_sys("golem-config.yml"), 29 | use_parent = use_parent 30 | ) 31 | } 32 | 33 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: cranstars 2 | Title: A Shiny app that shows CRAN downloads & Github stars statistics 3 | Version: 0.1.1 4 | Authors@R: 5 | person(given = "Zauad Shahreer", 6 | family = "Abeer", 7 | role = c("aut", "cre"), 8 | email = "shahreyar.abeer@gmail.com") 9 | Description: A shiny app that given a package name, shows cran downloads & github stars stats, made with the {golem} framework. 10 | License: MIT + file LICENSE 11 | Depends: R (>= 2.10) 12 | Imports: 13 | config, 14 | golem, 15 | shiny, 16 | dplyr, 17 | gh, 18 | lubridate, 19 | rlang, 20 | cranlogs, 21 | ggplot2, 22 | shinyjs, 23 | waiter, 24 | gt, 25 | pkgload, 26 | highcharter, 27 | attempt, 28 | DT, 29 | glue, 30 | htmltools, 31 | processx 32 | Encoding: UTF-8 33 | LazyData: true 34 | RoxygenNote: 7.1.1 35 | Suggests: 36 | testthat, 37 | covr, 38 | spelling 39 | Language: en-US 40 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2020 Zauad Shahreer Abeer 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 | -------------------------------------------------------------------------------- /dev/03_deploy.R: -------------------------------------------------------------------------------- 1 | # Building a Prod-Ready, Robust Shiny Application. 2 | # 3 | # README: each step of the dev files is optional, and you don't have to 4 | # fill every dev scripts before getting started. 5 | # 01_start.R should be filled at start. 6 | # 02_dev.R should be used to keep track of your development during the project. 7 | # 03_deploy.R should be used once you need to deploy your app. 8 | # 9 | # 10 | ###################################### 11 | #### CURRENT FILE: DEPLOY SCRIPT ##### 12 | ###################################### 13 | 14 | # Test your app 15 | 16 | ## Run checks ---- 17 | ## Check the package before sending to prod 18 | devtools::check() 19 | rhub::check_for_cran() 20 | 21 | # Deploy 22 | 23 | ## RStudio ---- 24 | ## If you want to deploy on RStudio related platforms 25 | golem::add_rstudioconnect_file() 26 | golem::add_shinyappsio_file() 27 | golem::add_shinyserver_file() 28 | 29 | ## Docker ---- 30 | ## If you want to deploy via a generic Dockerfile 31 | golem::add_dockerfile() 32 | 33 | ## If you want to deploy to ShinyProxy 34 | golem::add_dockerfile_shinyproxy() 35 | 36 | ## If you want to deploy to Heroku 37 | golem::add_dockerfile_heroku() 38 | -------------------------------------------------------------------------------- /tests/testthat/test-mod_plots.R: -------------------------------------------------------------------------------- 1 | test_that("mod_plot_ui works", { 2 | expect_is(mod_plot_ui("ID", 450, "cran"), "shiny.tag.list") 3 | }) 4 | 5 | test_that("mod_plot_server works for ggplot", { 6 | shiny::testServer( 7 | mod_plot_server, 8 | args = list( 9 | r = reactiveValues( 10 | repo = "hadley/dplyr", 11 | date = c((Sys.Date() - 10), (Sys.Date() - 5)), 12 | cran_dl = cran_dl_data, 13 | gh_stars = gh_stars_data 14 | ), 15 | type = "gh" 16 | ), { 17 | expect_type(session$output$plot_gh$src, "character") 18 | expect_equal(session$output$plot_gh$class, "shiny-scalable") 19 | }) 20 | }) 21 | 22 | 23 | test_that("mod_plot_server works for highchart", { 24 | shiny::testServer( 25 | mod_plot_server, 26 | args = list( 27 | r = reactiveValues( 28 | repo = "hadley/dplyr", 29 | date = c((Sys.Date() - 10), (Sys.Date() - 5)), 30 | cran_dl = cran_dl_data, 31 | gh_stars = gh_stars_data 32 | ), 33 | type = "cran" 34 | ), { 35 | expect_type(session$output$plot_cran, "character") 36 | expect_s3_class(session$output$plot_cran, "json") 37 | }) 38 | }) -------------------------------------------------------------------------------- /R/fct_make_table.R: -------------------------------------------------------------------------------- 1 | 2 | make_table <- function(r) { 3 | 4 | ## for R-CMD-check 5 | n_stars <- NULL 6 | 7 | repo <- get_cran_name(r$repo) 8 | date_range = r$date 9 | 10 | cran <- r$cran_dl %>% 11 | filter( 12 | .data$package == repo, 13 | date >= date_range[1] & date <= date_range[2] 14 | ) %>% 15 | mutate( 16 | yrmon = make_yrmon(date) 17 | ) 18 | 19 | gh <- r$gh_stars %>% 20 | filter( 21 | .data$package == r$repo, 22 | date >= date_range[1] & date <= date_range[2] 23 | ) %>% 24 | mutate( 25 | yrmon = make_yrmon(date) 26 | ) %>% 27 | rename(count = n_stars) 28 | 29 | col_names <- c( 30 | "Most in a day", 31 | "Best month", 32 | "Average/month" 33 | ) 34 | 35 | cran_summary <- c( 36 | get_most_count(cran), 37 | get_most_month(cran), 38 | get_avg(cran) 39 | ) 40 | 41 | gh_summary = c( 42 | get_most_count(gh), 43 | get_most_month(gh), 44 | get_avg(gh) 45 | ) 46 | 47 | cran_summary_data <- data.frame(col_names, cran_summary) 48 | gh_summary_data <- data.frame(col_names, gh_summary) 49 | 50 | cran_summary_data %>% 51 | left_join(gh_summary_data, by = "col_names") 52 | 53 | } -------------------------------------------------------------------------------- /inst/app/www/styles.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @import url(//fonts.googleapis.com/css?family=IBM+Plex+Sans); 4 | 5 | :root { 6 | --pine-tree: #292e1eff; 7 | --orange: #DA7C30; 8 | --dark-orchid: #9649cbff; 9 | --rstudio-blue: #4F84B6; 10 | --background: white; 11 | } 12 | 13 | body{ 14 | background-color: var(--rstudio-blue); 15 | margin:2em; 16 | font-family: IBM Plex Sans; 17 | } 18 | 19 | .h-title { 20 | margin-top:0px !important; 21 | } 22 | 23 | .col-sm-3, .col-sm-9 { 24 | margin-top: 20px; 25 | } 26 | 27 | .col-sm-9> .row{ 28 | background: var(--background); 29 | border:solid 1px; 30 | border-radius:25px; 31 | border-color: var(--orange); 32 | padding:2em; 33 | margin-bottom:1em 34 | } 35 | .well{ 36 | background-color: var(--background); 37 | border-radius:25px; 38 | padding:2em; 39 | border-color: var(--orange) 40 | } 41 | 42 | h2, h3{ 43 | color: var(--pine-tree); 44 | } 45 | 46 | img{ 47 | padding-top:0em; 48 | } 49 | 50 | .waiter-overlay-content { 51 | margin-top: -50px !important; 52 | } 53 | 54 | .gt_table { 55 | margin-top: 10px; 56 | } 57 | 58 | #quote { 59 | margin-left: 120px; 60 | margin-top: 120px; 61 | } 62 | 63 | .highcharts-root { 64 | font-family: IBM Plex Sans !important; 65 | font-size: 15px; 66 | } -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(cran_plot) 4 | export(get_cran_data) 5 | export(get_cran_name) 6 | export(get_gh_stars) 7 | export(gh_plot) 8 | export(run_app) 9 | import(dplyr) 10 | import(ggplot2) 11 | import(gt) 12 | import(shiny) 13 | importFrom(cranlogs,cran_downloads) 14 | importFrom(dplyr,filter) 15 | importFrom(ggplot2,theme_bw) 16 | importFrom(gh,gh) 17 | importFrom(golem,activate_js) 18 | importFrom(golem,add_resource_path) 19 | importFrom(golem,bundle_resources) 20 | importFrom(golem,favicon) 21 | importFrom(golem,with_golem_options) 22 | importFrom(highcharter,hc_caption) 23 | importFrom(highcharter,hc_subtitle) 24 | importFrom(highcharter,hc_title) 25 | importFrom(highcharter,hcaes) 26 | importFrom(highcharter,hchart) 27 | importFrom(highcharter,highchartOutput) 28 | importFrom(highcharter,renderHighchart) 29 | importFrom(lubridate,day) 30 | importFrom(lubridate,week) 31 | importFrom(lubridate,year) 32 | importFrom(lubridate,ymd_hms) 33 | importFrom(rlang,.data) 34 | importFrom(shiny,NS) 35 | importFrom(shiny,invalidateLater) 36 | importFrom(shiny,reactiveValues) 37 | importFrom(shiny,renderUI) 38 | importFrom(shiny,shinyApp) 39 | importFrom(shiny,tagList) 40 | importFrom(shiny,tags) 41 | importFrom(shinyjs,hidden) 42 | importFrom(shinyjs,hide) 43 | importFrom(shinyjs,show) 44 | importFrom(shinyjs,useShinyjs) 45 | importFrom(waiter,Waiter) 46 | importFrom(waiter,spin_loaders) 47 | importFrom(waiter,use_waiter) 48 | -------------------------------------------------------------------------------- /R/mod_plots.R: -------------------------------------------------------------------------------- 1 | #' main UI Function 2 | #' 3 | #' @description A shiny Module. 4 | #' 5 | #' @param id,input,output,session Internal parameters for {shiny}. 6 | #' 7 | #' @noRd 8 | #' 9 | #' @importFrom shiny NS tagList 10 | #' @importFrom highcharter highchartOutput 11 | mod_plot_ui <- function(id, height, type){ 12 | ns <- NS(id) 13 | tagList( 14 | fluidRow( 15 | if ( type == "cran" ) highchartOutput(ns("plot_cran"), height = height) 16 | else plotOutput(ns("plot_gh"), height = height) 17 | ) 18 | ) 19 | } 20 | 21 | #' main Server Function 22 | #' @importFrom highcharter renderHighchart 23 | #' @noRd 24 | mod_plot_server <- function(id, r, type = c("cran", "gh")){ 25 | 26 | type <- match.arg(type) 27 | 28 | moduleServer( 29 | id = id, 30 | function(input, output, session) { 31 | ns <- session$ns 32 | 33 | if ( type == "cran" ) { 34 | output$plot_cran <- renderHighchart({ 35 | cran_plot(r) 36 | }) 37 | } else { 38 | output$plot_gh <- renderCachedPlot({ 39 | 40 | req(r$cran_dl) 41 | req(r$gh_stars) 42 | # if (type == "cran") { 43 | # gg <- cran_plot(r) 44 | # } else { 45 | # gg <- gh_plot(r) 46 | # } 47 | 48 | gh_plot(r) 49 | 50 | }, cacheKeyExpr = {list(r$repo, r$date, r$gh_stars, r$cran_dl)} ) 51 | } 52 | 53 | 54 | } 55 | ) 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /inst/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who 4 | contribute through reporting issues, posting feature requests, updating documentation, 5 | submitting pull requests or patches, and other activities. 6 | 7 | We are committed to making participation in this project a harassment-free experience for 8 | everyone, regardless of level of experience, gender, gender identity and expression, 9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 10 | 11 | Examples of unacceptable behavior by participants include the use of sexual language or 12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment, 13 | insults, or other unprofessional conduct. 14 | 15 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed 18 | from the project team. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 21 | opening an issue or contacting one or more of the project maintainers. 22 | 23 | This Code of Conduct is adapted from the Contributor Covenant 24 | (https://www.contributor-covenant.org), version 1.0.0, available at 25 | https://contributor-covenant.org/version/1/0/0/. -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | pull_request: 6 | branches: 7 | - master 8 | 9 | name: test-coverage 10 | 11 | jobs: 12 | test-coverage: 13 | runs-on: macOS-latest 14 | env: 15 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - uses: r-lib/actions/setup-r@master 20 | 21 | - uses: r-lib/actions/setup-pandoc@master 22 | 23 | - name: Query dependencies 24 | run: | 25 | install.packages('remotes') 26 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) 27 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") 28 | shell: Rscript {0} 29 | 30 | - name: Cache R packages 31 | uses: actions/cache@v2 32 | with: 33 | path: ${{ env.R_LIBS_USER }} 34 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} 35 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- 36 | 37 | - name: Install dependencies 38 | run: | 39 | install.packages(c("remotes")) 40 | remotes::install_deps(dependencies = TRUE) 41 | remotes::install_cran("covr") 42 | shell: Rscript {0} 43 | 44 | - name: Test coverage 45 | run: covr::codecov() 46 | shell: Rscript {0} 47 | -------------------------------------------------------------------------------- /data-raw/DATASET.R: -------------------------------------------------------------------------------- 1 | 2 | # github stars data ------------------------------------------------------- 3 | 4 | ## list tidyverse packages 5 | tidypacks <- 6 | c("tidyverse/broom", 7 | "hadley/dplyr", 8 | "wesm/feather", 9 | "tidyverse/forcats", 10 | "hadley/ggplot2", 11 | "tidyverse/haven", 12 | "hadley/httr", 13 | "rstats-db/hms", 14 | "jeroenooms/jsonlite", 15 | "hadley/lubridate", 16 | "tidyverse/magrittr", 17 | "hadley/modelr", 18 | "hadley/purrr", 19 | "tidyverse/readr", 20 | "hadley/readxl", 21 | "tidyverse/stringr", 22 | "tidyverse/tibble", 23 | "hadley/rvest", 24 | "tidyverse/tidyr", 25 | "hadley/xml2") 26 | 27 | repos = data.frame(tidypacks) 28 | ## making the list of repos available as a data set 29 | usethis::use_data(repos, overwrite = TRUE) 30 | 31 | ## getting the github stars using the get_gh_stars() defined in this package 32 | gh_stars_data <- purrr::map(tidypacks, get_gh_stars) 33 | gh_stars_data <- dplyr::bind_rows(gh_stars_data) 34 | 35 | usethis::use_data(gh_stars_data, overwrite = TRUE) 36 | 37 | 38 | # cran downloads data ----------------------------------------------------- 39 | 40 | ## getting cran names, function get_cran_name() defined in the package 41 | tidypacks_cran <- get_cran_name(tidypacks) 42 | 43 | ## getting cran data for 10+ years 44 | cran_dl_data <- get_cran_data(tidypacks_cran, 45 | from_date = "2010-01-01", 46 | to_date = "2020-09-01") 47 | 48 | usethis::use_data(cran_dl_data, overwrite = TRUE) 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://www.tidyverse.org/lifecycle/#experimental) 5 | [![R build status](https://github.com/shahreyar-abeer/cranstars/workflows/R-CMD-check/badge.svg)](https://github.com/shahreyar-abeer/cranstars/actions) 6 | [![codecov](https://codecov.io/gh/shahreyar-abeer/cranstars/branch/master/graphs/badge.svg)](https://codecov.io/gh/shahreyar-abeer/cranstars) 7 | 8 | 9 | 10 | # cranstars 11 | 12 | `{cranstars}` is simple shiny app that given a package name, shows CRAN downloads & github star statistics. 13 | This is developed with the {golem} framework. 14 | 15 | App available at 16 | 17 | 18 | The following is a [feedback](https://twitter.com/_ColinFay/status/1319188197759459328) given by [Colin Fay](https://twitter.com/_ColinFay) from [Thinkr](https://thinkr.fr/) 19 | 20 | 21 | ![image](https://user-images.githubusercontent.com/20732893/123015643-a5a82400-d3ea-11eb-8d07-ffbb9ae6e633.png) 22 | 23 | 24 | ## Installing & Running 25 | 26 | Install `{cranstars}` on your machine with: 27 | 28 | ``` r 29 | # Install 30 | devtools::install_github("shahreyar-abeer/cranstars") 31 | 32 | # Run 33 | cranstars::run_app() 34 | ``` 35 | 36 | 37 | Please note that the ‘cranstars’ project is released with a [Contributor 38 | Code of Conduct](inst/CODE_OF_CONDUCT.md). By contributing to this project, 39 | you agree to abide by its terms. 40 | 41 | -------------------------------------------------------------------------------- /R/mod_table.R: -------------------------------------------------------------------------------- 1 | #' table UI Function 2 | #' 3 | #' @description A shiny Module. 4 | #' 5 | #' @param id,input,output,session Internal parameters for {shiny}. 6 | #' 7 | #' @noRd 8 | #' 9 | #' @importFrom shiny NS tagList 10 | #' @import gt 11 | mod_table_ui <- function(id){ 12 | ns <- NS(id) 13 | tagList( 14 | fluidRow( 15 | gt_output(ns("gt")) 16 | ) 17 | ) 18 | } 19 | 20 | #' table Server Function 21 | #' 22 | #' @noRd 23 | mod_table_server <- function(id, r){ 24 | 25 | moduleServer( 26 | id = id, 27 | function(input, output, session) { 28 | 29 | ns <- session$ns 30 | 31 | output$gt <- render_gt({ 32 | 33 | req(r$cran_dl) 34 | req(r$gh_stars) 35 | 36 | make_table(r) %>% 37 | gt(rowname_col = "col_names") %>% 38 | cols_label( 39 | cran_summary = md(paste(web_image("https://cran.r-project.org/Rlogo.svg", 40 | height = "18px; padding-top:0px !important"), 41 | "CRAN Downloads")), 42 | gh_summary = md(paste(web_image("https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png", 43 | height = "25px; padding-top:0px !important"), 44 | "Github Stars")) 45 | ) %>% 46 | opt_table_lines() %>% 47 | tab_header( 48 | title = "Summary Statistics", 49 | subtitle = paste0("{", r$repo, "}") 50 | ) %>% 51 | tab_footnote("In case of ties, only the first value is shown", locations = cells_stub(rows = c("Most in a day"))) 52 | }) 53 | } 54 | ) 55 | 56 | } 57 | 58 | 59 | -------------------------------------------------------------------------------- /R/app_ui.R: -------------------------------------------------------------------------------- 1 | #' cranStars User-Interface 2 | #' 3 | #' @param request Internal parameter for `{shiny}`. 4 | #' DO NOT REMOVE. 5 | #' @import shiny 6 | #' @noRd 7 | app_ui <- function(request) { 8 | tagList( 9 | golem_add_external_resources(), 10 | fluidPage( 11 | sidebarLayout( 12 | sidebarPanel( 13 | width = 3, 14 | fluidRow( 15 | h1("cranStars", class = "h-title"), 16 | mod_sidebar_ui("sidebar") 17 | ) 18 | ), 19 | 20 | mainPanel( 21 | width = 9, 22 | fluidRow( 23 | column( 24 | width = 6, 25 | mod_plot_ui("gh", 450, "gh") 26 | ), 27 | column( 28 | width = 6, 29 | mod_plot_ui("cran", 450, "cran") 30 | ), 31 | column( 32 | width = 6, 33 | mod_table_ui("gt") 34 | ), 35 | column( 36 | width = 6, 37 | uiOutput("quote") 38 | ) 39 | ) 40 | ) 41 | ) 42 | ) 43 | ) 44 | } 45 | 46 | #' Add external Resources to the Application 47 | #' 48 | #' This function is internally used to add external 49 | #' resources inside the Shiny application. 50 | #' 51 | #' @importFrom shiny tags 52 | #' @importFrom golem add_resource_path activate_js favicon bundle_resources 53 | #' @importFrom waiter use_waiter 54 | #' @importFrom shinyjs useShinyjs 55 | #' 56 | #' @noRd 57 | golem_add_external_resources <- function(){ 58 | 59 | add_resource_path( 60 | 'www', app_sys('app/www') 61 | ) 62 | 63 | tags$head( 64 | favicon(), 65 | bundle_resources( 66 | path = app_sys('app/www'), 67 | app_title = 'cranstars' 68 | ), 69 | use_waiter(), 70 | useShinyjs() 71 | ) 72 | } 73 | 74 | -------------------------------------------------------------------------------- /.github/workflows/pr-commands.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | issue_comment: 3 | types: [created] 4 | name: Commands 5 | jobs: 6 | document: 7 | if: startsWith(github.event.comment.body, '/document') 8 | name: document 9 | runs-on: macOS-latest 10 | env: 11 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: r-lib/actions/pr-fetch@master 15 | with: 16 | repo-token: ${{ secrets.GITHUB_TOKEN }} 17 | - uses: r-lib/actions/setup-r@master 18 | - name: Install dependencies 19 | run: Rscript -e 'install.packages(c("remotes", "roxygen2"))' -e 'remotes::install_deps(dependencies = TRUE)' 20 | - name: Document 21 | run: Rscript -e 'roxygen2::roxygenise()' 22 | - name: commit 23 | run: | 24 | git config --local user.email "actions@github.com" 25 | git config --local user.name "GitHub Actions" 26 | git add man/\* NAMESPACE 27 | git commit -m 'Document' 28 | - uses: r-lib/actions/pr-push@master 29 | with: 30 | repo-token: ${{ secrets.GITHUB_TOKEN }} 31 | style: 32 | if: startsWith(github.event.comment.body, '/style') 33 | name: style 34 | runs-on: macOS-latest 35 | env: 36 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 37 | steps: 38 | - uses: actions/checkout@v2 39 | - uses: r-lib/actions/pr-fetch@master 40 | with: 41 | repo-token: ${{ secrets.GITHUB_TOKEN }} 42 | - uses: r-lib/actions/setup-r@master 43 | - name: Install dependencies 44 | run: Rscript -e 'install.packages("styler")' 45 | - name: Style 46 | run: Rscript -e 'styler::style_pkg()' 47 | - name: commit 48 | run: | 49 | git config --local user.email "actions@github.com" 50 | git config --local user.name "GitHub Actions" 51 | git add \*.R 52 | git commit -m 'Style' 53 | - uses: r-lib/actions/pr-push@master 54 | with: 55 | repo-token: ${{ secrets.GITHUB_TOKEN }} 56 | -------------------------------------------------------------------------------- /R/fct_get_data.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' Get the github stars for the supplied repo on a daily basis 4 | #' 5 | #' @param repo A \code{character} vector containing the repo name, eg. "rstudio/rsconnect" 6 | #' 7 | #' @return A \code{data.frame} 8 | #' 9 | #' 10 | #' @export 11 | #' 12 | #' @importFrom lubridate ymd_hms 13 | #' @importFrom gh gh 14 | #' @importFrom rlang .data 15 | #' @import dplyr 16 | #' 17 | #' 18 | #' @examples 19 | #' get_gh_stars("rstudio/rsconnect") 20 | #' 21 | get_gh_stars <- function(repo) { 22 | # get data, loop because pagination 23 | stardates <- NULL 24 | 25 | geht <- TRUE 26 | page <- 1 27 | while(geht){ 28 | stars <- try(gh::gh(paste0("/repos/", repo, "/stargazers"), 29 | .send_headers = c("Accept" = 'application/vnd.github.v3.star+json'), 30 | .token = Sys.getenv("GITHUB_PAT"), 31 | page = page), 32 | silent = TRUE) 33 | 34 | if(inherits(stars, "try-error")) return(stars) 35 | 36 | geht <- length(stars) != 0 37 | if(geht){ 38 | stardates <- c(stardates, vapply(stars, "[[", "", "starred_at")) 39 | page <- page + 1 40 | } 41 | } 42 | 43 | star_table <- data.frame(time = ymd_hms(stardates)) %>% 44 | mutate(date = as.Date(.data$time)) %>% 45 | group_by(date) %>% 46 | summarise(n_stars = n()) %>% 47 | mutate(package = repo) 48 | 49 | star_table 50 | } 51 | 52 | 53 | #' Title 54 | #' 55 | #' @param package_names A character vector, names of packages to download data for 56 | #' @param from_date A string 57 | #' @param to_date A string, default is set to today's date 58 | #' 59 | #' @return A \code{data.frame} 60 | #' 61 | #' @importFrom cranlogs cran_downloads 62 | #' 63 | #' @export 64 | #' 65 | #' @examples 66 | #' get_cran_data("rsconnect", from_date = (Sys.Date() - 10)) 67 | #' 68 | get_cran_data <- function(package_names, from_date, to_date = Sys.Date()) { 69 | cran_downloads( 70 | package_names, 71 | from = from_date, 72 | to = as.character(to_date) 73 | ) 74 | } 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /dev/01_start.R: -------------------------------------------------------------------------------- 1 | # Building a Prod-Ready, Robust Shiny Application. 2 | # 3 | # README: each step of the dev files is optional, and you don't have to 4 | # fill every dev scripts before getting started. 5 | # 01_start.R should be filled at start. 6 | # 02_dev.R should be used to keep track of your development during the project. 7 | # 03_deploy.R should be used once you need to deploy your app. 8 | # 9 | # 10 | ######################################## 11 | #### CURRENT FILE: ON START SCRIPT ##### 12 | ######################################## 13 | 14 | ## Fill the DESCRIPTION ---- 15 | ## Add meta data about your application 16 | golem::fill_desc( 17 | pkg_name = "cranstars", # The Name of the package containing the App 18 | pkg_title = "PKG_TITLE", # The Title of the package containing the App 19 | pkg_description = "PKG_DESC.", # The Description of the package containing the App 20 | author_first_name = "AUTHOR_FIRST", # Your First Name 21 | author_last_name = "AUTHOR_LAST", # Your Last Name 22 | author_email = "AUTHOR@MAIL.COM", # Your Email 23 | repo_url = NULL # The URL of the GitHub Repo (optional) 24 | ) 25 | 26 | ## Set {golem} options ---- 27 | golem::set_golem_options() 28 | 29 | ## Create Common Files ---- 30 | ## See ?usethis for more information 31 | usethis::use_mit_license( name = "Golem User" ) # You can set another license here 32 | usethis::use_readme_rmd( open = FALSE ) 33 | usethis::use_code_of_conduct() 34 | usethis::use_lifecycle_badge( "Experimental" ) 35 | usethis::use_news_md( open = FALSE ) 36 | 37 | ## Use git ---- 38 | usethis::use_git() 39 | 40 | ## Init Testing Infrastructure ---- 41 | ## Create a template for tests 42 | golem::use_recommended_tests() 43 | 44 | ## Use Recommended Packages ---- 45 | golem::use_recommended_deps() 46 | 47 | ## Favicon ---- 48 | # If you want to change the favicon (default is golem's one) 49 | golem::remove_favicon() 50 | golem::use_favicon() # path = "path/to/ico". Can be an online file. 51 | 52 | ## Add helper functions ---- 53 | golem::use_utils_ui() 54 | golem::use_utils_server() 55 | 56 | # You're now set! ---- 57 | 58 | # go to dev/02_dev.R 59 | rstudioapi::navigateToFile( "dev/02_dev.R" ) 60 | 61 | -------------------------------------------------------------------------------- /dev/02_dev.R: -------------------------------------------------------------------------------- 1 | # Building a Prod-Ready, Robust Shiny Application. 2 | # 3 | # README: each step of the dev files is optional, and you don't have to 4 | # fill every dev scripts before getting started. 5 | # 01_start.R should be filled at start. 6 | # 02_dev.R should be used to keep track of your development during the project. 7 | # 03_deploy.R should be used once you need to deploy your app. 8 | # 9 | # 10 | ################################### 11 | #### CURRENT FILE: DEV SCRIPT ##### 12 | ################################### 13 | 14 | # Engineering 15 | 16 | ## Dependencies ---- 17 | ## Add one line by package you want to add as dependency 18 | usethis::use_package( "thinkr" ) 19 | 20 | ## Add modules ---- 21 | ## Create a module infrastructure in R/ 22 | golem::add_module( name = "name_of_module1" ) # Name of the module 23 | golem::add_module( name = "name_of_module2" ) # Name of the module 24 | 25 | ## Add helper functions ---- 26 | ## Creates ftc_* and utils_* 27 | golem::add_fct( "helpers" ) 28 | golem::add_utils( "helpers" ) 29 | 30 | ## External resources 31 | ## Creates .js and .css files at inst/app/www 32 | golem::add_js_file( "script" ) 33 | golem::add_js_handler( "handlers" ) 34 | golem::add_css_file( "custom" ) 35 | 36 | ## Add internal datasets ---- 37 | ## If you have data in your package 38 | usethis::use_data_raw( name = "my_dataset", open = FALSE ) 39 | 40 | ## Tests ---- 41 | ## Add one line by test you want to create 42 | usethis::use_test( "app" ) 43 | 44 | # Documentation 45 | 46 | ## Vignette ---- 47 | usethis::use_vignette("cranstars") 48 | devtools::build_vignettes() 49 | 50 | ## Code Coverage ---- 51 | ## Set the code coverage service ("codecov" or "coveralls") 52 | usethis::use_coverage() 53 | 54 | ## CI ---- 55 | ## Use this part of the script if you need to set up a CI 56 | ## service for your application 57 | ## 58 | ## (You'll need GitHub there) 59 | usethis::use_github() 60 | 61 | # GitHub Actions 62 | usethis::use_github_action() 63 | # Chose one of the three 64 | # See https://usethis.r-lib.org/reference/use_github_action.html 65 | usethis::use_github_action_check_release() 66 | usethis::use_github_action_check_standard() 67 | usethis::use_github_action_check_full() 68 | # Add action for PR 69 | usethis::use_github_action_pr_commands() 70 | 71 | # Travis CI 72 | usethis::use_travis() 73 | usethis::use_travis_badge() 74 | 75 | # AppVeyor 76 | usethis::use_appveyor() 77 | usethis::use_appveyor_badge() 78 | 79 | # Circle CI 80 | usethis::use_circleci() 81 | usethis::use_circleci_badge() 82 | 83 | # Jenkins 84 | usethis::use_jenkins() 85 | 86 | # GitLab CI 87 | usethis::use_gitlab_ci() 88 | 89 | # You're now set! ---- 90 | # go to dev/03_deploy.R 91 | rstudioapi::navigateToFile("dev/03_deploy.R") 92 | 93 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | 2 | #' Get only package name from repo name 3 | #' 4 | #' @param repo \code{string} The repo name, e.g., \bold{"rstudio/rsconnect"} 5 | #' 6 | #' @return \code{string} The package name, e.g., \bold{"rsconnect"} 7 | #' @export 8 | #' 9 | #' @examples 10 | #' get_cran_name("rsconnect/rstudio") 11 | get_cran_name <- function(repo) { 12 | broken <- strsplit(repo, "/") 13 | unlist(lapply(broken, "[[", 2)) 14 | } 15 | 16 | 17 | orange <- "#DA7C30" 18 | 19 | feedback <- function(r) { 20 | repo = get_cran_name(r$repo) 21 | adjectives <- c("outstanding", "great", "first-class", "awesome", "luminous", 22 | "extraordinary", "polished", "stylish", "magnificent", "stunning", 23 | "superb", "praiseworthy", "marvelous", "sensational", "remarkable") 24 | ds <- r$cran_dl %>% 25 | filter(.data$package == repo) %>% 26 | select(count) %>% 27 | pull() %>% 28 | sum() 29 | if ( ds > 10000 ) paste0("{", repo, "} ", " is ", sample(adjectives, 1), "!") 30 | else "Is the clock ticking?" 31 | } 32 | 33 | 34 | make_yrmon <- function(date) { 35 | paste0(format(date, "%b"), ", ", format(date, "%Y")) 36 | } 37 | 38 | 39 | get_most_count <- function(data) { 40 | d <- data %>% 41 | filter(count == max(count, na.rm = TRUE)) %>% 42 | slice(1) %>% 43 | select(date, count) %>% 44 | mutate( 45 | date = format(date, "%d %b, %Y"), 46 | count = ifelse(count > 1000, paste0(round(count/1000, 1), "k"), count) 47 | ) 48 | paste(d$date, " (", d$count, ")") 49 | } 50 | 51 | get_most_month <- function(data) { 52 | d <- data %>% 53 | count(.data$yrmon, wt = count) %>% 54 | filter(n == max(n, na.rm = TRUE)) %>% 55 | slice(1) %>% 56 | mutate(n = ifelse(n > 1000, paste0(round(n/1000, 1), "k"), n)) 57 | paste(d$yrmon, "(", d$n, ")") 58 | } 59 | 60 | get_avg <- function(data) { 61 | avg <- NULL 62 | avg2 <- NULL 63 | data %>% 64 | group_by(.data$yrmon) %>% 65 | summarise(avg = mean(count, na.rm = TRUE)) %>% 66 | #glimpse() %>% 67 | summarise(avg2 = mean(avg)) %>% 68 | mutate(avg2 = ifelse(avg2 > 1000, paste0(round(avg2/1000, 1), "k"), round(avg2, 1))) %>% 69 | pull() 70 | } 71 | 72 | 73 | 74 | #' Theme for the github calendar plot 75 | #' 76 | #' @param base_size \code{numeric} Base font size 77 | #' @param base_family \code{string} Base font family 78 | #' 79 | #' @importFrom ggplot2 theme_bw 80 | theme_calendar <- function(base_size = 15, base_family = "sans") { 81 | ret <- theme_bw(base_family = base_family, base_size = base_size) + 82 | theme(legend.background = element_blank(), 83 | legend.key = element_blank(), 84 | panel.background = element_blank(), 85 | panel.border = element_blank(), 86 | strip.background = element_blank(), 87 | plot.background = element_blank(), 88 | axis.line = element_blank(), 89 | panel.grid = element_blank()) 90 | ret 91 | } -------------------------------------------------------------------------------- /R/fct_make_plots.R: -------------------------------------------------------------------------------- 1 | 2 | 3 | #' Title 4 | #' 5 | #' @param r \code{reactiveValues} object with 4 slots, repo, date, cran_dl & gh_stars 6 | #' 7 | #' @importFrom dplyr filter 8 | #' @importFrom highcharter hchart hcaes hc_title hc_subtitle hc_caption 9 | #' 10 | #' @return \code{ggplot} 11 | #' @export 12 | #' 13 | 14 | cran_plot <- function(r) { 15 | repo <- get_cran_name(r$repo) 16 | date_range = r$date 17 | 18 | 19 | r$cran_dl %>% 20 | filter( 21 | .data$package == repo, 22 | date >= date_range[1] & date <= date_range[2] 23 | ) %>% 24 | hchart(type = "line", 25 | hcaes(x = date, y = count, group = .data$package), 26 | color = orange) %>% 27 | hc_title(text = "CRAN Downloads", style = list(fontFamily = "IBM Plex Sans", 28 | fontSize = "18px", fontWeight = "bold", 29 | color = "#292e1eff"), 30 | align = "left", x = 30) %>% 31 | hc_subtitle(text = paste0("CRAN downloads data for {", repo, "}"), 32 | style = list(fontFamily = "IBM Plex Sans", fontSize = "15px"), 33 | align = "left", x = 30) %>% 34 | hc_caption(text = feedback(r), style = list(fontFamily = "IBM Plex Sans", fontSize = "13px"), 35 | align = "right") 36 | 37 | # title = "CRAN Downloads", 38 | # subtitle = paste0("CRAN downloads for {", repo, "}") 39 | # ) + 40 | # theme_minimal(base_size = 18, base_family = "sans") 41 | } 42 | 43 | 44 | 45 | #' Title 46 | #' 47 | #' @param r \code{reactiveValues} object with 4 slots, repo, date, cran_dl & gh_stars 48 | #' 49 | #' @return \code{ggplot} 50 | #' 51 | #' @importFrom lubridate year week day 52 | #' @import ggplot2 53 | #' 54 | #' @export 55 | #' 56 | gh_plot <- function(r) { 57 | #praise = paste0("{", repo, "} ", " is ", sample(adjectives, 1), "!") 58 | 59 | ## for R-CMD-check 60 | n_stars <- NULL 61 | 62 | repo = r$repo 63 | date_range = r$date 64 | 65 | start = date_range[1] 66 | end = date_range[2] 67 | all_dates <- data.frame(date = seq(start, end, by = "day")) 68 | 69 | stars <- r$gh_stars %>% 70 | filter(.data$package == repo) 71 | 72 | joined <- all_dates %>% 73 | left_join(stars) %>% 74 | mutate(year = year(date), week = week(date), day = weekdays(date, TRUE)) %>% 75 | filter(date >= date_range[1] & date <= date_range[2]) 76 | 77 | 78 | joined %>% 79 | ggplot(aes(x = week, y = day, fill = n_stars)) + 80 | 81 | scale_fill_gradient( 82 | low = "#c1f5ca", 83 | high = "#366E39", 84 | na.value = "grey90", 85 | limits = c(0, max(joined$n_stars) 86 | )) + 87 | 88 | geom_tile(color = "white", size = 0.4) + 89 | facet_wrap("year", ncol = 1) + 90 | scale_x_continuous( 91 | expand = c(0, 0), 92 | breaks = seq(1, 52, length = 12), 93 | labels = c("Jan", "Feb", "Mar", "Apr", "May", "Jun", 94 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")) + 95 | scale_y_discrete( 96 | expand = c(0, 0), 97 | #breaks = seq(1, 7), 98 | labels = c("Thu", "Wed", "Tue", "Mon", "Sun", "Sat", "Fri"), 99 | ) + 100 | labs( 101 | title = "Github Stars (in a github calendar plot)", 102 | subtitle = paste0("Stars for {", repo, "}") 103 | ) + 104 | theme_calendar() + 105 | theme(axis.title = element_blank(), 106 | axis.ticks = element_blank(), 107 | legend.position = "bottom", 108 | legend.key.width = unit(1, "cm"), 109 | strip.text = element_text(hjust = 0.01, face = "bold", size = 15), 110 | title = element_text(size = 18), 111 | plot.subtitle = element_text(color = "#666666"), 112 | legend.title = element_blank()) 113 | } 114 | -------------------------------------------------------------------------------- /R/mod_sidebar.R: -------------------------------------------------------------------------------- 1 | #' sidebar UI Function 2 | #' 3 | #' @description Module for the sidebar. The main data processing is handled by it. 4 | #' 5 | #' @param id Module's ID 6 | #' 7 | #' @import shiny 8 | #' @importFrom shinyjs hidden 9 | 10 | #' 11 | #' @noRd 12 | mod_sidebar_ui <- function(id){ 13 | ns <- NS(id) 14 | tagList( 15 | tags$h3("Overview"), 16 | tags$p("This app shows CRAN downloads & Github Stars statistics for a given package (repo)."), 17 | tags$p("You can check out some tidyverse packages that comes with the app.", 18 | "And if you have a package on CRAN, see how its doing."), 19 | selectizeInput( 20 | inputId = ns("repo"), 21 | label = "Repo", 22 | choices = cranstars::repos$tidypacks 23 | ), 24 | dateRangeInput( 25 | inputId = ns("date"), 26 | label = "Date Range", 27 | min = "2010-01-01", 28 | max = Sys.Date() - 3, 29 | start = "2018-01-01", 30 | end = Sys.Date() - 3, 31 | startview = "year" 32 | ), 33 | hidden(textInput( 34 | inputId = ns("my_repo"), 35 | label = "Your Repo", 36 | placeholder = "e.g., rstudio/rsconnect", 37 | value = "dreamRs/esquisse" 38 | )), 39 | hidden(actionButton( 40 | inputId = ns("go"), 41 | label = "Go" 42 | )), 43 | checkboxInput( 44 | inputId = ns("check_my_repo"), 45 | label = "Let's see some other packages, possibly mine!" 46 | ), 47 | uiOutput(ns("downloading"), style="height:100px;"), 48 | tags$p("Because the data from github is downloaded page by page, 49 | the more stars you have, the longer you'll have to wait."), 50 | tags$blockquote("Popularity is a curse sometimes", 51 | style = "color: #8a8a8a; font-size:14px;"), 52 | tags$p("I'll leave it to you to ponder on that."), 53 | tags$hr(), 54 | tags$footer( 55 | tags$img(src = "https://raw.githubusercontent.com/rstudio/hex-stickers/master/thumbs/shiny.png", 56 | height = "60px", width = "50px", style = "padding-top:0 !important; float:right"), 57 | tags$img(src = "https://raw.githubusercontent.com/ThinkR-open/golem/master/inst/rstudio/templates/project/golem.png", 58 | height = "60px", width = "50px", style = "padding-top:0 !important; float:right"), 59 | tags$a(href = "https://github.com/shahreyar-abeer/cranstars", target = "_blank", 60 | icon("link"), "Github"), 61 | HTML(" "), 62 | tags$a(href = "https://thewaywer.rbind.io/projects/cranstars-a-first-golem-app-possibly-cran/", 63 | target = "_blank", icon("link"), "Blog Post"), 64 | br(), 65 | br(), 66 | "by", 67 | tags$a(href = "https://thewaywer.rbind.io/about/", "Zauad Shahreer Abeer"), 68 | style = "text-align: center;" 69 | ) 70 | ) 71 | } 72 | 73 | #' sidebar Server Function 74 | #' 75 | #' @param id Module's ID 76 | #' @param input,output,session Internal parameters for {shiny}. 77 | #' 78 | #' @importFrom shinyjs show hide 79 | #' @importFrom waiter Waiter spin_loaders 80 | #' @noRd 81 | mod_sidebar_server <- function(id, r){ 82 | moduleServer( 83 | id = id, 84 | function(input, output, session) { 85 | ns <- session$ns 86 | 87 | 88 | w = Waiter$new( 89 | id = ns("downloading"), 90 | color = "transparent", 91 | html = tagList( 92 | spin_loaders(id = 38, color = "#292e1eff"), 93 | tags$p("connecting & downloading", style = "color:#292e1eff") 94 | ) 95 | ) 96 | 97 | observeEvent(input$repo, { 98 | 99 | if( isFALSE(input$check_my_repo) ) { 100 | r$repo = input$repo 101 | r$cran_dl = cranstars::cran_dl_data 102 | r$gh_stars = cranstars::gh_stars_data 103 | } 104 | 105 | }) 106 | 107 | observeEvent(input$date, { 108 | r$date = input$date 109 | }) 110 | 111 | observeEvent(input$check_my_repo, { 112 | if ( isTRUE(input$check_my_repo) ) { 113 | hide("repo", anim = TRUE, animType = "fade", time = .2) 114 | show("my_repo", anim = TRUE, animType = "slide", time = 1.2) 115 | show("go", anim = TRUE, animType = "slide", time = 1.2) 116 | } else { 117 | hide("my_repo", anim = TRUE, animType = "fade", time = .2) 118 | hide("go", anim = TRUE, animType = "fade", time = .2) 119 | show("repo", anim = TRUE, animType = "slide", time = 1.2) 120 | } 121 | }) 122 | 123 | observeEvent(input$go, { 124 | 125 | r$cran_dl <- NULL 126 | r$gh_stars <- NULL 127 | 128 | if ( isTRUE(input$check_my_repo) ) { 129 | 130 | package = input$my_repo 131 | date = input$date 132 | 133 | if ( isTRUE(grepl("/", package)) ) { 134 | 135 | gh_stars <- reactive({ 136 | w$show() 137 | g <- get_gh_stars(package) 138 | cran_name <- get_cran_name(package) 139 | r$cran_dl <- get_cran_data( 140 | package_names = cran_name, 141 | from_date = date[1], 142 | to_date = date[2] 143 | ) 144 | g 145 | }) 146 | 147 | output$downloading <- renderUI({ 148 | # ... 149 | }) 150 | 151 | if( inherits(gh_stars(), "try-error") ) { 152 | showModal(modalDialog( 153 | title = "Error! Cannot Proceed", 154 | icon("warning"), 155 | "Please make sure the github repo exists.", 156 | style="background: #ff9966" 157 | )) 158 | } else { 159 | 160 | if( sum(r$cran_dl$count) == 0 ) { 161 | showModal(modalDialog( 162 | title = "Is it on CRAN yet?", 163 | icon("warning"), 164 | "0 downloads from CRAN. Maybe its not on CRAN yet, is it?", 165 | style = "background: #ffcc00" 166 | )) 167 | } else { 168 | showModal(modalDialog( 169 | title = "Success!", 170 | icon("check"), 171 | " Data successfully downloaded from CRAN & Github.", 172 | style = "background: #99cc33;" 173 | )) 174 | } 175 | r$repo <- package 176 | r$gh_stars <- gh_stars() 177 | } 178 | } else { 179 | showModal(modalDialog( 180 | title = "Error! Cannot Proceed.", 181 | icon("warning"), 182 | "Please provide your repo name in the form", tags$em('rstudio/rsconnect'), 183 | style="background: #ff9966" 184 | )) 185 | } 186 | } 187 | }) 188 | } 189 | ) 190 | } 191 | 192 | 193 | --------------------------------------------------------------------------------