Skip to contents

learnrTrackR provides a small SQLite layer for tutorial attempt tracking, registered students, registered questions, gradebook exports, and minimal learnr integration helpers.

Create a database

library(learnrTrackR)

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

Register expected questions

Registered students provide a local roster for filtering and optional student-id checks. Registered questions define the denominator used by gradebook(). In this example, q3 is expected but not answered.

register_students(
  con,
  data.frame(
    student_id = c("student_001", "student_002"),
    student_label = c("Student 1", "Student 2"),
    group_id = c("A", "A")
  )
)
#> # A tibble: 2 × 5
#>   student_id  student_label email group_id created_at          
#>   <chr>       <chr>         <chr> <chr>    <chr>               
#> 1 student_001 Student 1     NA    A        2026-06-24T12:25:30Z
#> 2 student_002 Student 2     NA    A        2026-06-24T12:25:30Z
register_questions(
  con,
  tutorial_id = "module_01",
  questions = data.frame(
    question_id = c("q1", "q2", "q3"),
    max_score = c(1, 1, 1)
  )
)
#> # A tibble: 3 × 6
#>   question_id tutorial_id question_label question_type max_score created_at     
#>   <chr>       <chr>       <chr>          <chr>             <dbl> <chr>          
#> 1 q1          module_01   q1             NA                    1 2026-06-24T12:…
#> 2 q2          module_01   q2             NA                    1 2026-06-24T12:…
#> 3 q3          module_01   q3             NA                    1 2026-06-24T12:…

Add simulated attempts

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."
)

Read attempts

get_attempts(con)
#> # A tibble: 2 × 12
#>   attempt_id session_id        student_id tutorial_id question_id attempt_number
#>        <int> <chr>             <chr>      <chr>       <chr>                <int>
#> 1          1 session_student_… student_0… module_01   q1                       1
#> 2          2 session_student_… student_0… module_01   q2                       1
#> # ℹ 6 more variables: submitted_answer <chr>, grade_status <chr>, score <dbl>,
#> #   max_score <dbl>, feedback <chr>, timestamp <chr>

Compute scores

compute_scores() summarizes only observed attempts.

compute_scores(con, tutorial_id = "module_01", rule = "last")
#>    student_id tutorial_id score max_score percent n_questions n_answered
#> 1 student_001   module_01   1.5         2      75           2          2

gradebook() uses registered questions, so unanswered questions are included.

gradebook(con, tutorial_id = "module_01", rule = "last")
#> # A tibble: 1 × 9
#>   student_id  tutorial_id score max_score percent n_questions n_answered
#>   <chr>       <chr>       <dbl>     <dbl>   <dbl>       <int>      <int>
#> 1 student_001 module_01     1.5         3      50           3          2
#> # ℹ 2 more variables: n_unanswered <int>, completed <lgl>

Export results

attempts_path <- tempfile(fileext = ".csv")
scores_path <- tempfile(fileext = ".csv")
gradebook_path <- tempfile(fileext = ".csv")
moodle_path <- tempfile(fileext = ".csv")

export_results(con, attempts_path, type = "attempts")
export_results(con, scores_path, type = "scores")
export_results(con, gradebook_path, type = "gradebook", tutorial_id = "module_01")
export_moodle_grades(
  con,
  moodle_path,
  tutorial_id = "module_01",
  grade_item = "Module 01 quiz"
)
export_canvas_grades(
  con,
  tempfile(fileext = ".csv"),
  tutorial_id = "module_01",
  assignment = "Module 01 quiz"
)

file.exists(c(attempts_path, scores_path, gradebook_path, moodle_path))
#> [1] TRUE TRUE TRUE TRUE

Export a richer teaching bundle

bundle_dir <- tempfile()
bundle_paths <- export_tracking_bundle(
  con,
  bundle_dir,
  tutorial_id = "module_01",
  group_id = "A"
)

bundle_paths
#> # A tibble: 8 × 2
#>   table         path                                                            
#>   <chr>         <chr>                                                           
#> 1 summary       /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-summary.csv  
#> 2 students      /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-students.csv 
#> 3 attempts      /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-attempts.csv 
#> 4 scores        /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-scores.csv   
#> 5 gradebook     /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-gradebook.csv
#> 6 questions     /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-questions.csv
#> 7 moodle_grades /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-moodle_grade…
#> 8 canvas_grades /tmp/RtmpCDX4x7/file1e9220850936/module_01-group-A-canvas_grade…