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
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: learnrTrackR
Title: Track Simulated learnr Tutorial Attempts
Version: 0.1.1
Version: 0.1.1.9000
Authors@R:
person("Aurélien", "Nicosia", , "116816597+AurelienNicosiaULaval@users.noreply.github.com",
role = c("aut", "cre"))
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# learnrTrackR 0.1.1.9000

- Added a course pilot example showing a realistic tracked `learnr` tutorial,
CSV configuration, simulated learner results, Moodle export, and teacher
report generation.

# learnrTrackR 0.1.1

- Added `setup_learnr_tracking()` and `open_learnr_tracking_db()` to simplify
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ store simulated attempts and export results for inspection.
- Provide a minimal example and unit tests.
- Provide a minimal `learnr` and `gradethis` prototype for tracked
questions and code exercises.
- Provide a course pilot example with CSV configuration, simulated learner
results, Moodle export, and teacher report generation.

## What the MVP does not do yet

Expand Down Expand Up @@ -224,6 +226,36 @@ load_tracking_config(con, "tracking.yml")
The minimal `learnr` example includes both a YAML configuration file and a CSV
configuration directory.

## Course pilot example

A course-like pilot is available in `inst/examples/course-pilot/`. It contains
a simulated descriptive statistics tutorial with tracked `learnr` questions,
tracked `gradethis` code exercises, CSV configuration files, a cohort
simulation script, Moodle export, and teacher report generation.

To create simulated pilot results:

```r
source("inst/examples/course-pilot/simulate-results.R")
source("inst/examples/course-pilot/inspect-results.R")
```

The tutorial can also be launched manually:

```r
Sys.setenv(
LEARNRTRACKR_DB = file.path(tempdir(), "learnrtrackr-course-pilot.sqlite"),
LEARNRTRACKR_STUDENT_ID = "student_demo",
LEARNRTRACKR_GROUP_ID = "A"
)

learnr::run_tutorial(
"inst/examples/course-pilot/tutorial.Rmd",
clean = TRUE,
as_rstudio_job = FALSE
)
```

## PostgreSQL prototype

SQLite remains the default backend for local prototypes. For a server-backed
Expand Down
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ articles:
contents:
- getting-started
- learnr-context
- course-pilot
- teacher-workflow
- moodle-export
- deployment-postgresql
Expand Down
106 changes: 106 additions & 0 deletions inst/examples/course-pilot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Course pilot example

This example is a course-like pilot for `learnrTrackR`. It uses a simulated
descriptive statistics tutorial and shows the full teacher workflow:

- launch a tracked `learnr` tutorial;
- load course, tutorial, student, and question metadata from CSV files;
- record tracked `learnr` questions and `gradethis` code checks;
- simulate a small cohort without manual browser interaction;
- inspect attempts, scores, gradebook rows, Moodle-ready grades, export
bundles, and a teacher report.

The data are simulated and do not represent real students.

## Files

```text
course-pilot/
tutorial.Rmd
pilot-data.R
simulate-results.R
inspect-results.R
tracking-strategy.md
config-csv/
courses.csv
tutorials.csv
students.csv
questions.csv
```

## Run the tutorial

From the package source directory, install the package locally first:

```r
devtools::install(dependencies = FALSE)
```

Then launch the tutorial:

```r
Sys.setenv(
LEARNRTRACKR_DB = file.path(tempdir(), "learnrtrackr-course-pilot.sqlite"),
LEARNRTRACKR_STUDENT_ID = "student_demo",
LEARNRTRACKR_GROUP_ID = "A"
)

learnr::run_tutorial(
"inst/examples/course-pilot/tutorial.Rmd",
clean = TRUE,
as_rstudio_job = FALSE
)
```

For the intended pilot workflow, use one of the identifiers listed in
`config-csv/students.csv` so the group filters and teacher exports match the
configured cohort.

## Simulate a cohort

To create a reproducible database without manually using the browser:

```r
source("inst/examples/course-pilot/simulate-results.R")
```

This creates a SQLite database at `LEARNRTRACKR_DB`, or at a temporary path when
the environment variable is not set.

## Inspect teacher outputs

After running the tutorial or the simulation:

```r
source("inst/examples/course-pilot/inspect-results.R")
```

The script writes:

- attempts CSV;
- scores CSV;
- gradebook CSV;
- Moodle-ready CSV;
- rich export bundle;
- HTML teacher report, when `rmarkdown` is installed.

The default output directory is:

```r
file.path(dirname(Sys.getenv("LEARNRTRACKR_DB")), "course-pilot-outputs")
```

You can override it with:

```r
Sys.setenv(LEARNRTRACKR_OUTPUT_DIR = "course-pilot-outputs")
```

## Open the dashboard

```r
learnrTrackR::run_dashboard(Sys.getenv("LEARNRTRACKR_DB"), group_id = "A")
```

The dashboard remains a local inspection tool. It is not an institutional
authentication layer.
2 changes: 2 additions & 0 deletions inst/examples/course-pilot/config-csv/courses.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
course_id,course_label,semester
stat_intro,Introduction to statistics with R,pilot
7 changes: 7 additions & 0 deletions inst/examples/course-pilot/config-csv/questions.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
tutorial_id,question_id,question_label,question_type,max_score
stat_descriptive_pilot,q1_variable_type,Identify a quantitative variable,radio,1
stat_descriptive_pilot,q2_robust_summaries,Choose robust summary statistics,checkbox,1
stat_descriptive_pilot,q3_n_rows,Count data rows,numeric,1
stat_descriptive_pilot,q4_highest_median,Identify the highest median region,text,1
stat_descriptive_pilot,q5_mean_study_hours,Compute mean study hours,code,1
stat_descriptive_pilot,q6_region_summary,Summarise study hours by region,code,2
5 changes: 5 additions & 0 deletions inst/examples/course-pilot/config-csv/students.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
student_id,student_label,email,group_id
student_demo,Demo learner,,A
student_a01,Student A01,,A
student_a02,Student A02,,A
student_b01,Student B01,,B
2 changes: 2 additions & 0 deletions inst/examples/course-pilot/config-csv/tutorials.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
tutorial_id,course_id,tutorial_label,version
stat_descriptive_pilot,stat_intro,Descriptive statistics pilot tutorial,0.1.1.9000
114 changes: 114 additions & 0 deletions inst/examples/course-pilot/inspect-results.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
run_course_pilot_inspection <- function() {
tutorial_id <- "stat_descriptive_pilot"
db_path <- Sys.getenv(
"LEARNRTRACKR_DB",
unset = file.path(tempdir(), "learnrtrackr-course-pilot.sqlite")
)
group_id <- Sys.getenv("LEARNRTRACKR_GROUP_ID", unset = "")
group_filter <- if (nzchar(group_id)) group_id else NULL
output_dir <- Sys.getenv(
"LEARNRTRACKR_OUTPUT_DIR",
unset = file.path(dirname(db_path), "course-pilot-outputs")
)

if (!file.exists(db_path)) {
stop(
"No tracking database was found at: ",
db_path,
"\nRun simulate-results.R or launch the tutorial before inspecting results.",
call. = FALSE
)
}

if (!dir.exists(output_dir)) {
dir.create(output_dir, recursive = TRUE)
}

con <- learnrTrackR::connect_tracking_db(db_path)
on.exit(DBI::dbDisconnect(con), add = TRUE)

attempts <- learnrTrackR::get_attempts(con, tutorial_id = tutorial_id)
scores <- learnrTrackR::compute_scores(con, tutorial_id = tutorial_id, rule = "last")
grades <- learnrTrackR::gradebook(con, tutorial_id = tutorial_id, rule = "last")
dashboard <- learnrTrackR::dashboard_data(
con,
tutorial_id = tutorial_id,
group_id = group_filter,
rule = "last"
)
report <- learnrTrackR::teacher_report_data(
con,
tutorial_id = tutorial_id,
group_id = group_filter,
rule = "last"
)

attempts_path <- file.path(output_dir, "course-pilot-attempts.csv")
scores_path <- file.path(output_dir, "course-pilot-scores.csv")
gradebook_path <- file.path(output_dir, "course-pilot-gradebook.csv")
moodle_path <- file.path(output_dir, "course-pilot-moodle.csv")
bundle_dir <- file.path(output_dir, "course-pilot-bundle")
report_path <- file.path(output_dir, "course-pilot-teacher-report.html")

learnrTrackR::export_results(con, attempts_path, type = "attempts", tutorial_id = tutorial_id)
learnrTrackR::export_results(con, scores_path, type = "scores", tutorial_id = tutorial_id)
learnrTrackR::export_results(con, gradebook_path, type = "gradebook", tutorial_id = tutorial_id)
learnrTrackR::export_moodle_grades(
con,
moodle_path,
tutorial_id = tutorial_id,
grade_item = "Descriptive statistics pilot"
Comment on lines +56 to +60

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter the standalone Moodle export by group

When LEARNRTRACKR_GROUP_ID is set (for example to "A" as shown in the example), this script filters the dashboard/report/bundle via group_filter, but the standalone Moodle CSV is still generated with export_moodle_grades() over the whole tutorial gradebook. With the included simulation that means the file contains student_b01 from group B even while the rest of the inspection output is group A, so a teacher can export grades for the wrong cohort.

Useful? React with 👍 / 👎.

)
bundle_paths <- learnrTrackR::export_tracking_bundle(
con,
bundle_dir,
tutorial_id = tutorial_id,
group_id = group_filter
)

if (requireNamespace("rmarkdown", quietly = TRUE)) {
learnrTrackR::generate_teacher_report(
con,
report_path,
tutorial_id = tutorial_id,
group_id = group_filter,
rule = "last"
)
} else {
report_path <- NA_character_
}

print(attempts)
print(scores)
print(grades)
print(dashboard$summary)
print(report$summary)

message("Wrote attempts to: ", attempts_path)
message("Wrote scores to: ", scores_path)
message("Wrote gradebook to: ", gradebook_path)
message("Wrote Moodle-ready grades to: ", moodle_path)
message("Wrote rich export bundle to: ", bundle_dir)
message("Wrote teacher report to: ", report_path)
print(bundle_paths)

invisible(
list(
attempts = attempts,
scores = scores,
gradebook = grades,
dashboard = dashboard,
report = report,
paths = list(
attempts = attempts_path,
scores = scores_path,
gradebook = gradebook_path,
moodle = moodle_path,
bundle = bundle_dir,
report = report_path
)
)
)
}

run_course_pilot_inspection()
16 changes: 16 additions & 0 deletions inst/examples/course-pilot/pilot-data.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pilot_survey <- tibble::tibble(
learner_case = paste0("case_", sprintf("%02d", 1:12)),
region = c(
"Quebec City", "Quebec City", "Quebec City", "Quebec City",
"Montreal", "Montreal", "Montreal", "Montreal",
"Sherbrooke", "Sherbrooke", "Sherbrooke", "Sherbrooke"
),
program = c(
"Statistics", "Data science", "Statistics", "Mathematics",
"Data science", "Statistics", "Mathematics", "Data science",
"Statistics", "Mathematics", "Data science", "Statistics"
),
study_hours = c(6.5, 7.0, 5.5, 8.0, 4.0, 5.0, 6.0, 5.5, 7.5, 8.5, 6.5, 7.0),
commute_minutes = c(20, 15, 25, 30, 45, 35, 50, 40, 25, 20, 30, 35),
quiz_score = c(78, 82, 74, 88, 70, 73, 76, 79, 85, 90, 81, 84)
)
Loading
Loading