Skip to contents

learnrTrackR is an early-stage R package for tracking learnr-style tutorial attempts in SQLite or PostgreSQL.

The long-term goal is to provide a light, open, reproducible layer for storing answers, attempts, grading results, scores, and exports from interactive teaching workflows built around learnr-style tutorials.

The current pilot-ready version validates a complete controlled teaching workflow while keeping learnr, gradethis, LMS, and dashboard integration explicit and inspectable.

Problem

Interactive tutorials are useful for teaching R and statistics, but instructors also need reliable records of what students submitted, how many times they tried, what feedback was returned, and what score should be exported.

The package now supports a controlled pilot workflow: local or server-backed tracking, explicit tutorial instrumentation, teacher inspection outputs, and LMS-oriented CSV exports.

Current pilot-ready scope

  • Create a SQLite tracking database.
  • Create the required schema.
  • Record simulated attempts.
  • Register courses and tutorials.
  • Register expected student identifiers.
  • Register expected tutorial questions.
  • Load course, tutorial, student, and question metadata from YAML or CSV configuration files.
  • Read and filter attempts.
  • Compute simple scores using first, last, or best attempt rules.
  • Build a gradebook that counts unanswered registered questions.
  • Export attempts or scores to CSV.
  • Export a simple Moodle-ready CSV grade table.
  • Export a Canvas Gradebook-style CSV grade table.
  • Document a cautious Moodle CSV import workflow.
  • Export rich CSV bundles for a tutorial, group, or student.
  • Open a minimal local Shiny teacher dashboard.
  • Filter dashboard tables and dashboard CSV exports by registered group.
  • 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.

Current limits before stable use

  • It does not automatically instrument arbitrary learnr tutorials.
  • It does not capture gradethis checks unless the tracking helper is called explicitly.
  • It does not provide institutional authentication or role-based access control for the dashboard.
  • It does not connect to Moodle by API; Moodle export is CSV-based.
  • It does not verify real student identity beyond optional local registry checks.
  • It is not designed yet for high-concurrency production use.
  • It is a controlled pilot release, not an institutional authentication, retention, or production deployment layer.

Installation during development

From the package directory:

devtools::load_all()

or:

devtools::install()

Minimal example

library(learnrTrackR)

db_path <- tempfile(fileext = ".sqlite")
con <- init_tracking_db(db_path, overwrite = TRUE)

register_students(
  con,
  data.frame(
    student_id = c("student_001", "student_002"),
    student_label = c("Student 1", "Student 2"),
    group_id = c("A", "A")
  )
)

register_courses(
  con,
  data.frame(
    course_id = "stat101",
    course_label = "Statistics 101",
    semester = "W2026"
  )
)

register_tutorials(
  con,
  data.frame(
    tutorial_id = "module_01",
    course_id = "stat101",
    tutorial_label = "Module 01"
  )
)

register_questions(
  con,
  tutorial_id = "module_01",
  questions = data.frame(
    question_id = c("q1", "q2", "q3"),
    max_score = c(1, 1, 1)
  )
)

track_attempt(
  con = con,
  student_id = "student_001",
  tutorial_id = "module_01",
  question_id = "q1",
  submitted_answer = "mean(x)",
  grade_status = "correct",
  score = 1,
  max_score = 1,
  feedback = "Correct.",
  require_registered_student = TRUE
)

track_attempt(
  con = con,
  student_id = "student_001",
  tutorial_id = "module_01",
  question_id = "q2",
  submitted_answer = "summarise(df, x = mean(x))",
  grade_status = "partial",
  score = 0.5,
  max_score = 1,
  feedback = "Good idea, but check grouping."
)

get_attempts(con)
compute_scores(con, tutorial_id = "module_01", rule = "last")
gradebook(con, tutorial_id = "module_01", rule = "last")

export_results(con, tempfile(fileext = ".csv"), type = "attempts")
export_results(con, tempfile(fileext = ".csv"), type = "scores")
export_results(
  con,
  tempfile(fileext = ".csv"),
  type = "gradebook",
  tutorial_id = "module_01"
)
export_moodle_grades(
  con,
  tempfile(fileext = ".csv"),
  tutorial_id = "module_01",
  grade_item = "Module 01 quiz"
)
export_canvas_grades(
  con,
  tempfile(fileext = ".csv"),
  tutorial_id = "module_01",
  assignment = "Module 01 quiz"
)
export_tracking_bundle(
  con,
  tempfile(),
  tutorial_id = "module_01",
  group_id = "A"
)

DBI::dbDisconnect(con)

Teacher dashboard

A minimal local Shiny dashboard can inspect attempts, registered questions, gradebook rows, and export files:

run_dashboard(db_path)

For an open DBI connection, including PostgreSQL, use:

run_dashboard_connection(con, group_id = "A")

The PostgreSQL launcher can also read the Docker example environment variables:

run_dashboard_postgres(
  postgres_schema = "learnrtrackr_pilot",
  tutorial_id = "stat_descriptive_pilot",
  group_id = "A"
)

Registered student groups can be selected in the dashboard. The same filter is applied to the displayed students, attempts, gradebook rows, and downloaded CSV files.

By default, the dashboard is launched on 127.0.0.1. A simple local access token can be required by setting an environment variable:

Sys.setenv(LEARNRTRACKR_DASHBOARD_TOKEN = "replace-with-a-long-token")
run_dashboard(db_path)

Running on a non-local host without a token is refused by default. The token gate is intended for local inspection and small prototypes. It is not a secured production deployment interface and does not replace institutional authentication.

Teacher configuration

Course, tutorial, student, and question metadata can be declared in YAML or in a directory of CSV files:

create_tracking_config_template("config", format = "csv")
config/
  courses.csv
  tutorials.csv
  students.csv
  questions.csv

Then load the configuration into the tracking database:

load_tracking_config(con, "config")

For a single-file setup, create a YAML template instead:

create_tracking_config_template("tracking.yml", format = "yaml")
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, explicit student and teacher launch scripts, Moodle export, Canvas export, dashboard launch, a preflight checklist, and teacher report generation.

The pilot includes both a CSV configuration directory and a single-file YAML configuration:

inst/examples/course-pilot/config-csv/
inst/examples/course-pilot/config/tracking.yml

In these configuration files, the version field records the version of the tutorial content used for the pilot. It is independent from the package release version and should be updated only when the tutorial content changes.

To create simulated pilot results:

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

For a student-style launch, copy the example environment file and run the student launcher:

cd inst/examples/course-pilot
cp student.env.example student.env
Rscript run-student.R

The teacher output workflow can be launched separately:

cd inst/examples/course-pilot
cp teacher.env.example teacher.env
Rscript run-teacher.R

The tutorial can also be launched manually from 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
)

Before a real controlled pilot, read inst/examples/course-pilot/pilot-checklist.md and inst/examples/course-pilot/pilot-protocol.md.

For a lighter pre-course rehearsal with only fictional learners, use the installed dry-run template:

source("inst/examples/ulaval-pilot-template/run-dry-run.R")

It writes readiness checks, scoped attempts, scores, gradebook rows, Moodle-ready grades, Canvas Gradebook grades, and a rich export bundle for the fictional group A cohort.

For an adoption-oriented guide written from the perspective of an external teacher, see:

vignette("instructor-adoption", package = "learnrTrackR")

PostgreSQL prototype

SQLite remains the default backend for local prototypes. For a server-backed course setup, install RPostgres and connect to an existing PostgreSQL database:

con <- connect_postgres_tracking_db(
  dbname = "learnrtrackr",
  host = "localhost",
  user = "learnrtrackr",
  password = Sys.getenv("LEARNRTRACKR_POSTGRES_PASSWORD"),
  initialize = TRUE
)

load_tracking_config(con, "config")

The PostgreSQL path is intended for controlled deployments. Use dashboard_data(con, ...) for non-interactive inspection, run_dashboard_connection(con, ...) for an open DBI connection, or run_dashboard_postgres(...) for environment-variable based PostgreSQL launches.

For a reproducible local PostgreSQL rehearsal, see:

vignette("deployment-postgresql", package = "learnrTrackR")

The Docker Compose example is included in inst/examples/postgres-docker/.

The same Docker example also includes a controlled course pilot rehearsal:

cd inst/examples/postgres-docker
cp env.example .env
docker compose --env-file .env up -d
Rscript course-pilot-smoke-test.R
Rscript run-dashboard.R

The pilot smoke test loads the course-pilot CSV configuration, records the simulated cohort in PostgreSQL, prepares dashboard data, writes Moodle-ready and Canvas Gradebook grades filtered by group, writes an export bundle, and renders an HTML teacher report when rmarkdown is installed.

Privacy utilities

Student-level records can be deleted, pseudonymised, or stripped of direct identifiers:

delete_student_data(con, "student_001")
pseudo <- pseudonymise_results(tracking_export_data(con, "module_01"))
anonymous <- anonymise_results(pseudo$data)

See privacy.md for practical data-governance notes.

Citation and archiving

The repository includes citation and archival metadata for future scholarly software publication work:

CITATION.cff
inst/CITATION
.zenodo.json
publication-plan.md

CITATION.cff provides human-readable and machine-readable citation metadata for GitHub and other tools. The R citation file in inst/CITATION supports citation("learnrTrackR") after installation. The .zenodo.json file prepares release metadata for GitHub-to-Zenodo archiving, but it does not create a DOI by itself. A DOI will require enabling Zenodo for the GitHub repository and archiving a tagged release.

The metadata follow the Citation File Format documentation and Zenodo’s GitHub metadata documentation:

Pedagogical analytics

The package can produce teacher-facing summaries for an individual tutorial:

summarise_questions(con, "module_01")
summarise_students(con, "module_01")
detect_difficult_questions(con, "module_01")
detect_stalled_students(con, "module_01")
check_pilot_readiness(con, "module_01")
generate_teacher_report(con, "teacher-report.html", "module_01")

These helpers use the same scoring rules as gradebook() and can be filtered by registered group.

Short roadmap after 0.3.0

  1. Run a small controlled pilot with the course-pilot protocol and record the result with inst/examples/course-pilot/pilot-record-template.md.
  2. Decide the institutional student identifier, authentication boundary, and data-retention period outside the package.
  3. Add stricter operational checks for duplicate learners, missing questions, and unexpected LMS export rows.
  4. Add a deployment guide for a managed Shiny or Posit Connect environment.
  5. Revise paper/paper.md with descriptive pilot evidence, while avoiding unsupported claims about learning gains.

Minimal learnr prototype

The directory inst/examples/minimal-learnr/ contains a small tutorial with tracked radio, text, and numeric questions, plus two tracked code exercises. The setup chunk creates a reusable context with setup_learnr_tracking(). The questions use the generic tracked_question() helper with that context. The code exercises use explicit calls to track_gradethis_attempt() inside gradethis::grade_this() check chunks.

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

devtools::install(dependencies = FALSE)

Then run the tutorial:

Sys.setenv(
  LEARNRTRACKR_DB = file.path(tempdir(), "learnrtrackr-minimal.sqlite"),
  LEARNRTRACKR_STUDENT_ID = "student_demo"
)

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

After submitting the code exercises:

source("inst/examples/minimal-learnr/inspect-results.R")

You can also inspect the same database with the teacher dashboard:

learnrTrackR::run_dashboard(Sys.getenv("LEARNRTRACKR_DB"))

The four built-in learnr question families can be tracked with tracked_question(), or with the explicit wrappers tracked_question_radio(), tracked_question_checkbox(), tracked_question_text(), and tracked_question_numeric(). Use get_learnr_tracking_env() to validate the launch environment, then setup_learnr_tracking() in a tutorial setup chunk to initialize the database, load configuration, register the current learner, and pass the returned context to tracked questions.

For a compact guide to this workflow, see:

vignette("learnr-context", package = "learnrTrackR")

Student identity is read with get_tracking_student_id(), which checks the LEARNRTRACKR_STUDENT_ID environment variable and throws an informative error when it is missing.

Expected student identifiers can be stored with register_students(). Attempts can then require a registered identifier by setting require_registered_student = TRUE in track_attempt().

export_tracking_bundle() writes richer teacher-facing CSV files, including students, attempts, scores, gradebook rows, question metadata, summary metrics, and Moodle-ready grades. The export can be filtered by group_id or student_id.

export_moodle_grades() writes a compact CSV with one student identifier column and one grade item column. Moodle then asks the teacher to map these columns to the appropriate user field and grade item during CSV import.

For the full Moodle CSV workflow, see:

vignette("moodle-export", package = "learnrTrackR")

export_canvas_grades() writes a Canvas Gradebook-style CSV with the standard leading columns Student, ID, SIS User ID, SIS Login ID, and Section, plus one assignment column. By default, learnrTrackR student identifiers are exported to SIS User ID, and raw gradebook scores are exported as assignment points. The identifiers must match the target Canvas course before import.

For the Canvas CSV workflow, see:

vignette("canvas-export", package = "learnrTrackR")