Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/deploy-quiz.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: Deploy Quiz to shinyapps.io

on:
push:
branches: [main, dev]
workflow_dispatch: # Allow manual deployment
pull_request:
types: [opened, synchronize, ready_for_review]
workflow_dispatch:

jobs:
deploy:
Expand Down
3 changes: 2 additions & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ Imports:
rmarkdown,
shiny,
bslib,
rsconnect
rsconnect,
yaml
Suggests:
pkgdown
Remotes:
Expand Down
158 changes: 44 additions & 114 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,25 @@ This directory contains interactive learnr quizzes for the openwashdata course a

## Structure

- `app.R` - Quiz landing page that links to all deployed quizzes
- `app.R` - Quiz landing page that automatically displays all configured quizzes
- `build.R` - Deployment script with helper functions for automated deployment
- `config.R` - Shared configuration file defining all available quizzes
- `modules/` - Directory containing all quiz files
- `md-01-quiz.Rmd` - Module 1 quiz on Quarto basics (learnr tutorial)
- `_github_username.Rmd` - Reusable component for GitHub username input
- `_github_username.Rmd` - Reusable component for GitHub username input with CSV data
- `_submission.Rmd` - Reusable component for quiz submission
- `github_usernames.csv` - Student GitHub username database
- Additional quiz files can be added as `md-XX-quiz.Rmd`

## Required Packages

Make sure these packages are installed:

All required packages are defined in `DESCRIPTION` and automatically installed during deployment. For local development:

```r
install.packages(c("learnr", "tidyverse", "gapminder", "knitr", "rsconnect", "httr", "digest"))

# Install packages from GitHub
if (!requireNamespace("remotes", quietly = TRUE)) {
install.packages("remotes")
}
remotes::install_github("rstudio/gradethis")
remotes::install_github("rundel/learnrhash")
# Install from DESCRIPTION file
devtools::install_github("ds4owd-dev/quiz")
```

## ShinyApps.io Authentication
Expand Down Expand Up @@ -53,7 +52,6 @@ The quizzes now include automatic submission to Google Forms for tracking studen
- **GitHub username collection**: Students enter their GitHub username at the start
- **Learnrhash generation**: Quiz responses are encoded using learnrhash
- **Automatic submission**: Results are submitted to Google Form via POST request
- **Error handling**: Provides fallback hash if submission fails
- **Modular components**: Reusable username and submission components

### Google Form Setup
Expand All @@ -66,15 +64,6 @@ The quizzes now include automatic submission to Google Forms for tracking studen
- Hash column: Complete learnrhash for decoding
- Username column: GitHub username for filtering
- Module column: Module identifier (fetched from tutorial ID metadata)
- **Authentication**: None required - form accepts responses from anyone with the link

### Advantages of Google Forms
- **No authentication needed**: Forms can accept anonymous submissions
- **Tidy data format**: Each field in its own column for easy analysis
- **Reliable**: Google handles all the backend infrastructure
- **Easy to view**: Responses automatically appear in Google Sheets
- **Error-resistant**: Works even with network issues
- **Easy filtering**: Separate columns for username and module

## Deployment Process

Expand All @@ -97,13 +86,11 @@ Quizzes are automatically deployed to shinyapps.io via GitHub Actions when chang
- `SHINYAPPS_SECRET`: The secret from shinyapps.io

3. **Add new quizzes to automated deployment**:
- Edit `build.R` and add your new quiz:
- Edit `config.R` and add your new quiz:
```r
# Deploy new quiz module
rsconnect::deployDoc(
doc = "modules/md-02-quiz.Rmd",
appName = "openwashdata-module2-quiz",
forceUpdate = TRUE
quiz_names <- c(
"md-01-quiz",
"md-02-quiz" # Add new quiz here
)
```

Expand All @@ -118,49 +105,26 @@ The GitHub Action (`.github/workflows/deploy-quiz.yml`):

### Manual Deployment

This quiz system uses a two-part deployment approach:

### 1. Deploy Individual Quizzes

Each quiz is deployed as a separate learnr tutorial using `deployDoc()`:

```r
# Deploy Module 1 quiz
rsconnect::deployDoc(
doc = "modules/md-01-quiz.Rmd",
appName = "openwashdata-module1-quiz",
forceUpdate = TRUE
)
```
For manual deployment, simply run the build script which handles everything automatically:

For additional quizzes:
```r
# Deploy Module 2 quiz (when created)
rsconnect::deployDoc(
doc = "modules/md-02-quiz.Rmd",
appName = "openwashdata-module2-quiz",
forceUpdate = TRUE
)
# Deploy all quizzes and landing page
source("build.R")
```

### 2. Deploy the Landing Page

The landing page is deployed as a regular Shiny app using `deployApp()`:

```r
rsconnect::deployApp(
appName = "openwashdata-quiz-hub",
forceUpdate = TRUE
)
```
The deployment system features:
- **Automatic file bundling**: CSV files and dependencies are automatically included
- **Streamlined process**: One script deploys everything configured in `config.R`

## Adding New Quizzes

To add a new quiz module:
The system now uses automatic configuration - adding a new quiz is simple:

1. **Create the quiz file** (e.g., `modules/md-02-quiz.Rmd`) using this template:
### 1. Create the Quiz File

```r
Create `modules/md-02-quiz.Rmd` with the standardized YAML header:

```yaml
---
title: "Module 2: Your Title"
output: learnr::tutorial
Expand All @@ -169,68 +133,34 @@ description: "Your quiz description"
tutorial:
id: "module2-your-id"
---
```

`​``{r setup, include=FALSE}
library(learnr)
library(tidyverse)
library(gradethis)
library(learnrhash)
library(httr)

tutorial_options(
exercise.eval = FALSE,
exercise.checker = gradethis::grade_learnr
)

knitr::opts_chunk$set(echo = FALSE)

# Google Form setup
form_url <- "https://docs.google.com/forms/d/e/1FAIpQLScnw9R8wMU5SfFqNVXGeEkiIygLTB_Dc6jWBmbwEeHuekBDzg/formResponse"
`​``

## Introduction

Your introduction text here.
Add your quiz content following the existing pattern, including:
- GitHub username collection: `{r github-username, child='_github_username.Rmd'}`
- Quiz submission: `{r submission-section, child='_submission.Rmd'}`

`​``{r github-username, child='_github_username.Rmd'}
`​``
### 2. Update Configuration

## Your Quiz Content
Edit `config.R` to include your new quiz:

Add your questions and exercises here...
```r
quiz_names <- c(
"md-01-quiz",
"md-02-quiz" # Add new quiz here
)
```

`​``{r submission-section, child='_submission.Rmd'}
`​``
### 3. Deploy

## Summary
Run the build script to deploy everything:

Your summary here.
```r
source("build.R")
```
2. **Deploy the quiz**:
```r
rsconnect::deployDoc(
doc = "md-02-quiz.Rmd",
appName = "openwashdata-module2-quiz",
forceUpdate = TRUE
)
```
3. **Update the landing page** by adding the new quiz to the `quizzes` list in `app.R`:
```r
list(
id = "module2",
title = "Module 2: Your Title",
description = "Quiz description",
url = "https://your-account.shinyapps.io/openwashdata-module2-quiz/",
available = TRUE
)
```
4. **Redeploy the landing page**:
```r
rsconnect::deployApp(
appName = "openwashdata-quiz-hub",
forceUpdate = TRUE
)
```

This will:
- Automatically deploy the new quiz
- Update the landing page to show the new quiz

## Local Testing

Expand Down
72 changes: 54 additions & 18 deletions app.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,61 @@

library(shiny)
library(bslib)
library(yaml)

# Define available quizzes with their deployed URLs
quizzes <- list(
list(
id = "module1",
title = "Module 1: Quarto Basics",
description = "Test your understanding of Quarto basics for openwashdata package documentation",
url = "https://hjj91u-nicolo-massari.shinyapps.io/openwashdata-module1-quiz/",
available = TRUE
)
# Add more quizzes here as you deploy them
# list(
# id = "module2",
# title = "Module 2: Data Visualization",
# description = "Learn about creating effective visualizations",
# url = "https://hjj91u-nicolo-massari.shinyapps.io/openwashdata-module2-quiz/",
# available = FALSE
# )
)
# Load configuration
source("config.R")

# Function to extract quiz metadata from Rmd files
extract_quiz_metadata <- function(quiz_name) {
rmd_path <- file.path("modules", paste0(quiz_name, ".Rmd"))

if (!file.exists(rmd_path)) {
return(NULL)
}

# Read the Rmd file and extract YAML header
rmd_content <- readLines(rmd_path)
yaml_start <- which(rmd_content == "---")[1]
yaml_end <- which(rmd_content == "---")[2]

if (is.na(yaml_start) || is.na(yaml_end) || yaml_start >= yaml_end) {
return(NULL)
}

yaml_content <- paste(rmd_content[(yaml_start + 1):(yaml_end - 1)], collapse = "\n")
yaml_data <- yaml::yaml.load(yaml_content)

# Extract title and description
title <- yaml_data$title %||% paste("Quiz:", quiz_name)
description <- yaml_data$description %||% "Interactive quiz module"

return(list(title = title, description = description))
}

# Auto-generate quiz list from quiz names
generate_quiz_list <- function(quiz_names) {
# Generate quiz list with metadata
quiz_list <- lapply(quiz_names, function(quiz_name) {
metadata <- extract_quiz_metadata(quiz_name)

# Generate URL based on quiz name
quiz_url <- paste0(base_url, quiz_name, "/")

list(
id = quiz_name,
title = metadata$title %||% paste("Quiz:", quiz_name),
description = metadata$description %||% "Interactive quiz module",
url = quiz_url,
available = TRUE
)
})

return(quiz_list)
}

# Generate quiz list automatically from config
quizzes <- generate_quiz_list(quiz_names)

# UI
ui <- page_navbar(
Expand Down
36 changes: 23 additions & 13 deletions build.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
rsconnect::deployApp(
appName = "openwashdata-quiz-hub",
forceUpdate = TRUE
)
# Load configuration
source("config.R")

# HELPER

rsconnect::deployDoc(
doc = "modules/md-01-quiz.Rmd",
appName = "openwashdata-module1-quiz",
deploy_quiz <- function(module_name) {
module_path <- paste0(file.path("modules", module_name), ".Rmd")
rsconnect::deployDoc(
doc = module_path,
appName = module_name,
forceUpdate = TRUE,
logLevel = "verbose"
)
}


# QUIZ DEPLOYMENT

rsconnect::deployApp(
appName = main_app_name,
forceUpdate = TRUE
)

# Example of what to add when creating new quiz:
for (quiz in quiz_names) {
deploy_quiz(quiz)
}

#rsconnect::deployDoc(
# doc = "modules/md-02-quiz.Rmd",
# appName = "openwashdata-module2-quiz",
# forceUpdate = TRUE
#)
# To add new quizzes, edit config.R
16 changes: 16 additions & 0 deletions config.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Quiz Configuration
# Shared configuration file for build.R and app.R

# List of quiz modules to deploy and display
quiz_names <- c(
"md-01-quiz"
# Add new quizzes here:
# "md-02-quiz",
# "md-03-quiz"
)

# Base URL for deployed quizzes
base_url <- "https://hjj91u-nicolo-massari.shinyapps.io/"

# Main app configuration
main_app_name <- "openwashdata-quiz-hub"
Loading