Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
148cbba
fix: ensure long comments are appended on a new page #303
b0ink Nov 13, 2025
5e3b4cc
feat: env option to skip jplag submission clusters (#551)
b0ink Nov 25, 2025
08d609c
chore: delete old marking sessions with no duration (#549)
b0ink Nov 25, 2025
bce9f44
Merge branch '10.0.x' into fix/long-comments-new-page
b0ink Nov 25, 2025
e469e0b
refactor: split comments into 2000 character chunks
b0ink Nov 25, 2025
1fa64f5
Merge pull request #548 from b0ink/fix/long-comments-new-page
b0ink Nov 25, 2025
b8954ad
chore(release): 10.0.0-57
b0ink Nov 25, 2025
d1d839b
fix: allow pdf gen and overseer job to be queued at the same time
b0ink Nov 25, 2025
c925c6f
chore(release): 10.0.0-58
b0ink Nov 25, 2025
7981de8
docs: fix typo in API root, add doc_version
SteveDala Nov 30, 2025
0e89378
Merge branch '10.0.x' of https://github.com/doubtfire-lms/doubtfire-a…
SteveDala Nov 30, 2025
ab38360
feat: discussion prompts (#546)
b0ink Dec 1, 2025
18d439c
refactor: overseer improvements (#554)
b0ink Dec 3, 2025
42389be
chore(release): 10.0.0-59
b0ink Dec 3, 2025
14b91e8
chore: remove work dir only on success
b0ink Dec 3, 2025
7810a09
chore: set script permissions in overseer container
b0ink Dec 3, 2025
69db8e9
chore: mount correct work dir
b0ink Dec 3, 2025
d3d60d6
chore(release): 10.0.0-60
b0ink Dec 3, 2025
f6b7007
Revert "chore: set script permissions in overseer container"
b0ink Dec 3, 2025
e61c669
chore(release): 10.0.0-61
b0ink Dec 3, 2025
84b08d8
fix: set correct mount
b0ink Dec 3, 2025
65c9d3f
chore(release): 10.0.0-62
b0ink Dec 3, 2025
399d9ef
docs: fix typo in API root, add doc_version (#555)
SteveDala Dec 3, 2025
e106eeb
feat: add basic health endpoint (#553)
b0ink Dec 3, 2025
ec98b04
chore(release): 10.0.0-63
b0ink Dec 3, 2025
e8a1df5
chore: silence healthcheck requests
b0ink Dec 3, 2025
0ee67c3
chore(release): 10.0.0-64
b0ink Dec 3, 2025
88e0c7a
feat: attention required task status (#557)
b0ink Dec 12, 2025
069a023
Merge branch '10.0.x' of https://github.com/SteveDala/doubtfire-api i…
SteveDala Dec 13, 2025
835bf79
Merge branch '10.0.x' of https://github.com/SteveDala/doubtfire-api i…
SteveDala Jan 4, 2026
d2e49bf
feat: overseer pipeline (#559)
b0ink Jan 5, 2026
2a50638
feat: custom project task deadlines (#550)
b0ink Jan 5, 2026
c32d462
chore(release): 10.0.0-65
b0ink Jan 5, 2026
201f8f6
chore: remove overseer work dir on completion
b0ink Jan 5, 2026
339b649
fix: check correct user permissions when toggling observer only (#560)
b0ink Jan 12, 2026
1312537
chore: update gems
SteveDala Jan 17, 2026
a90072d
feat(api): add staff grant extension endpoint
Apr 6, 2025
d27fd32
refactor(tests): replace double quotes with single quotes in non-inte…
SahiruWithanage Apr 9, 2025
fb80917
refactor(api): unify extension handling via shared service
SahiruWithanage Apr 11, 2025
ed7ae3d
feat(notifications): send extension notifications
SahiruWithanage Apr 27, 2025
c4162a1
Make a comment line change
SahiruWithanage May 11, 2025
b885a34
fix(mailer): fix email sender and add error handling
SahiruWithanage May 19, 2025
82ea701
feat: add notification table and model
samindiii May 18, 2025
6a310ba
feat: define notification api to GET and DELETE
samindiii May 18, 2025
3c99671
Create in-system notifications for students with successfull extensions
samindiii May 18, 2025
dca5e00
feat: fix email notifications for SGE feature
SahiruWithanage Sep 14, 2025
6bee233
refactor: use unit code instead of unit name in extension notifications
SahiruWithanage Sep 14, 2025
fe6cd01
Merge branch 'feature/staff-grant-extension-backend-t1-complete' of h…
SteveDala Jan 17, 2026
755f467
chore: update gems, regen lockfile, schema version
SteveDala Jan 17, 2026
1b8c724
fix: remove duplicate api endpoints
SteveDala Jan 17, 2026
4b2978b
chore: update latest from doubtfire
SteveDala Jan 17, 2026
c7f9b66
chore: address rubocop nits
SteveDala Jan 17, 2026
7a98ff2
chore: Remove shebang from Rakefile (rubocop)
SteveDala Jan 17, 2026
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
931 changes: 141 additions & 790 deletions .rubocop_todo.yml

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,61 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

## [10.0.0-65](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-64...v10.0.0-65) (2026-01-05)


### Features

* attention required task status ([#557](https://github.com/b0ink/doubtfire-deploy/issues/557)) ([88e0c7a](https://github.com/b0ink/doubtfire-deploy/commit/88e0c7a9e65a71aed0b9a8453d75af0aa52a09f4))
* custom project task deadlines ([#550](https://github.com/b0ink/doubtfire-deploy/issues/550)) ([2a50638](https://github.com/b0ink/doubtfire-deploy/commit/2a5063848d9cde0b0277f6e2463613945d720b99))
* overseer pipeline ([#559](https://github.com/b0ink/doubtfire-deploy/issues/559)) ([d2e49bf](https://github.com/b0ink/doubtfire-deploy/commit/d2e49bfd57e69281923458b8853226a85c9da390))

## [10.0.0-64](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-63...v10.0.0-64) (2025-12-03)

## [10.0.0-63](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-62...v10.0.0-63) (2025-12-03)


### Features

* add basic health endpoint ([#553](https://github.com/b0ink/doubtfire-deploy/issues/553)) ([e106eeb](https://github.com/b0ink/doubtfire-deploy/commit/e106eebf331bd7f1b12ff1a65fa590d4fba8d9e9))

## [10.0.0-62](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-61...v10.0.0-62) (2025-12-03)


### Bug Fixes

* set correct mount ([84b08d8](https://github.com/b0ink/doubtfire-deploy/commit/84b08d8c3e6a18e38585792c873fa174e9bd338a))

## [10.0.0-61](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-60...v10.0.0-61) (2025-12-03)

## [10.0.0-60](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-59...v10.0.0-60) (2025-12-03)

## [10.0.0-59](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-58...v10.0.0-59) (2025-12-03)


### Features

* discussion prompts ([#546](https://github.com/b0ink/doubtfire-deploy/issues/546)) ([ab38360](https://github.com/b0ink/doubtfire-deploy/commit/ab38360a5c847a1db0d03547fc432b000a61ebb9))

## [10.0.0-58](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-57...v10.0.0-58) (2025-11-25)


### Bug Fixes

* allow pdf gen and overseer job to be queued at the same time ([d1d839b](https://github.com/b0ink/doubtfire-deploy/commit/d1d839ba10ee369faf354d758dd1c746bea9e4a9))

## [10.0.0-57](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-56...v10.0.0-57) (2025-11-25)


### Features

* env option to skip jplag submission clusters ([#551](https://github.com/b0ink/doubtfire-deploy/issues/551)) ([5e3b4cc](https://github.com/b0ink/doubtfire-deploy/commit/5e3b4cc437fd296519e904a37e53305b579140dd))


### Bug Fixes

* ensure long comments are appended on a new page [#303](https://github.com/b0ink/doubtfire-deploy/issues/303) ([148cbba](https://github.com/b0ink/doubtfire-deploy/commit/148cbba601020eb41d975bb39f17a72378fc36ad))

## [10.0.0-56](https://github.com/b0ink/doubtfire-deploy/compare/v10.0.0-55...v10.0.0-56) (2025-11-10)


Expand Down
22 changes: 11 additions & 11 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ GEM
bunny-pub-sub (0.5.2)
bunny (~> 2.14)
byebug (12.0.0)
cgi (0.4.2)
chronic_duration (0.10.6)
numerizer (~> 0.1.1)
ci_reporter (2.1.0)
Expand Down Expand Up @@ -152,6 +153,8 @@ GEM
dry-inflector (~> 1.0)
dry-logic (~> 1.4)
zeitwerk (~> 2.6)
erb (4.0.4)
cgi (>= 0.3.3)
erubi (1.13.1)
erubis (2.7.0)
et-orbi (1.2.11)
Expand All @@ -173,7 +176,6 @@ GEM
faraday (>= 1, < 3)
faraday-net_http (3.4.0)
net-http (>= 0.5.0)
ffi (1.17.1-aarch64-linux-gnu)
ffi (1.17.1-x86_64-linux-gnu)
fugit (1.11.1)
et-orbi (~> 1, >= 1.2.11)
Expand Down Expand Up @@ -208,7 +210,7 @@ GEM
ice_cube (~> 0.16)
ostruct
ice_cube (0.17.0)
io-console (0.8.0)
io-console (0.8.1)
irb (1.15.1)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
Expand Down Expand Up @@ -279,8 +281,6 @@ GEM
net-protocol
netrc (0.11.0)
nio4r (2.7.4)
nokogiri (1.18.7-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.7-x86_64-linux-gnu)
racc (~> 1.4)
numerizer (0.1.1)
Expand Down Expand Up @@ -308,7 +308,7 @@ GEM
pp (0.6.2)
prettyprint
prettyprint (0.2.0)
prism (1.4.0)
prism (1.5.1)
psych (5.2.3)
date
stringio
Expand Down Expand Up @@ -374,7 +374,8 @@ GEM
rbs (3.9.2)
logger
rbtree (0.4.6)
rdoc (6.13.1)
rdoc (6.14.0)
erb
psych (>= 4.0.0)
redis (5.4.0)
redis-client (>= 0.22.0)
Expand Down Expand Up @@ -537,7 +538,7 @@ GEM
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uri (1.0.3)
uri (1.0.4)
useragent (0.16.11)
version_gem (1.1.6)
warden (1.2.9)
Expand All @@ -556,8 +557,7 @@ GEM
zeitwerk (2.7.2)

PLATFORMS
aarch64-linux
x86_64-linux
x86_64-linux-gnu

DEPENDENCIES
better_errors
Expand Down Expand Up @@ -623,7 +623,7 @@ DEPENDENCIES
webmock

RUBY VERSION
ruby 3.4.2p28
ruby 3.4.7p58

BUNDLED WITH
2.6.6
2.6.9
1 change: 0 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

Expand Down
13 changes: 11 additions & 2 deletions app/api/api_root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ class ApiRoot < Grape::API
mount ScormExtensionCommentsApi
mount GroupSetsApi
mount LearningOutcomesApi
# mount LearningAlignmentApi
# the mount above is available in 9.x but has not been ported to `10.0.x`
mount NotificationsApi
mount ProjectsApi
mount SettingsApi
mount StaffGrantExtensionApi
mount StudentsApi
mount Submission::PortfolioApi
mount Submission::PortfolioEvidenceApi
Expand Down Expand Up @@ -102,6 +106,8 @@ class ApiRoot < Grape::API
mount WebcalApi
mount WebcalPublicApi
mount MarkingSessionsApi
mount DiscussionPromptsApi
mount OverseerStepsApi

mount Feedback::FeedbackChipApi

Expand All @@ -118,6 +124,7 @@ class ApiRoot < Grape::API
AuthenticationHelpers.add_auth_to GroupSetsApi
AuthenticationHelpers.add_auth_to LearningOutcomesApi
AuthenticationHelpers.add_auth_to ProjectsApi
AuthenticationHelpers.add_auth_to StaffGrantExtensionApi
AuthenticationHelpers.add_auth_to StudentsApi
AuthenticationHelpers.add_auth_to Submission::PortfolioApi
AuthenticationHelpers.add_auth_to Submission::PortfolioEvidenceApi
Expand Down Expand Up @@ -150,13 +157,15 @@ class ApiRoot < Grape::API
AuthenticationHelpers.add_auth_to D2lIntegrationApi::D2lApi
AuthenticationHelpers.add_auth_to Feedback::FeedbackChipApi
AuthenticationHelpers.add_auth_to MarkingSessionsApi
AuthenticationHelpers.add_auth_to DiscussionPromptsApi
AuthenticationHelpers.add_auth_to OverseerStepsApi

add_swagger_documentation \
base_path: nil,
api_version: 'v1',
doc_version: 'v10.0.0',
hide_documentation_path: true,
info: {
title: 'Doubtfire API Documentaion',
title: 'Doubtfire API Documentation',
description: 'Doubtfire is a modern, lightweight learning management system.',
license: 'AGPL v3.0',
license_url: 'https://github.com/doubtfire-lms/doubtfire-api/blob/master/LICENSE'
Expand Down
122 changes: 122 additions & 0 deletions app/api/discussion_prompts_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
require 'grape'

class DiscussionPromptsApi < Grape::API
helpers AuthenticationHelpers
helpers AuthorisationHelpers

before do
authenticated?
end

desc "Get all the discussion prompts for a task definition"
params do
requires :task_definition_id, type: Integer, desc: 'Task definition to fetch discussion prompts for'
end
get '/task_definitions/:task_definition_id/discussion_prompts' do
task_definition = TaskDefinition.find(params[:task_definition_id])

unless authorise? current_user, task_definition, :get_discussion_prompt
error!({ error: 'You do not have permission to access this project' }, 403)
end

result = task_definition.discussion_prompts
.order(priority: :desc)

present result, with: Entities::DiscussionPromptEntity, user: current_user
end

desc "Create a new discussion prompt for a task definition"
params do
requires :task_definition_id, type: Integer, desc: 'Task definition to fetch discussion prompts for'
requires :content, type: String, desc: 'Discussion prompt content'
requires :priority, type: Integer, desc: 'The priority of the disucssion prompt'
end
post '/task_definitions/:task_definition_id/discussion_prompts' do
task_definition = TaskDefinition.find(params[:task_definition_id])

unless authorise? current_user, task_definition, :create_discussion_prompt
error!({ error: 'You do not have permission to access this project' }, 403)
end

unit = task_definition.unit

content = params[:content].to_s
priority = params[:priority].to_i

discussion_prompt = DiscussionPrompt.create!({
task_definition: task_definition,
content: content,
priority: priority
})

present discussion_prompt, with: Entities::DiscussionPromptEntity
end

desc "Update a discussion prompt for a task definition"
params do
requires :task_definition_id, type: Integer, desc: 'Task definition to fetch discussion prompts for'
requires :content, type: String, desc: 'Discussion prompt content'
requires :priority, type: Integer, desc: 'The priority of the disucssion prompt'
requires :id, type: Integer, desc: 'The ID of the discussion prompt'
end
put '/task_definitions/:task_definition_id/discussion_prompts/:id' do
task_definition = TaskDefinition.find(params[:task_definition_id])

unless authorise? current_user, task_definition, :create_discussion_prompt
error!({ error: 'You do not have permission to access this project' }, 403)
end

discussion_prompt = task_definition.discussion_prompts.find(params[:id])

content = params[:content].to_s
priority = params[:priority].to_i

discussion_prompt.update({
task_definition: task_definition,
content: content,
priority: priority
})
end

desc "Delete a discussion prompt for a task definition"
params do
requires :task_definition_id, type: Integer, desc: 'Task definition to fetch discussion prompts for'
requires :id, type: Integer, desc: 'The ID of the discussion prompt'
end
delete '/task_definitions/:task_definition_id/discussion_prompts/:id' do
task_definition = TaskDefinition.find(params[:task_definition_id])

unless authorise? current_user, task_definition, :create_discussion_prompt
error!({ error: 'You do not have permission to access this project' }, 403)
end

discussion_prompt = task_definition.discussion_prompts.find(params[:id])

discussion_prompt.destroy!
present discussion_prompt.destroyed?, with: Grape::Presenters::Presenter
end

desc "Get all the discussion prompts for a project"
params do
requires :project_id, type: Integer, desc: 'Project to fetch discussion prompts for'
end
get 'projects/:project_id/discussion_prompts' do
project = Project.find(params[:project_id])

# TODO: should convenor permissions exist on the project ?
unless authorise? current_user, project, :get_discussion_prompt
error!({ error: 'You do not have permission to access this project' }, 403)
end

tasks_to_discuss = project.tasks.where(task_status: [TaskStatus.discuss, TaskStatus.attention_required, TaskStatus.demonstrate])
task_definition_ids = tasks_to_discuss.pluck(:task_definition_id)

result = DiscussionPrompt
.joins(:task_definition)
.where(task_definition_id: task_definition_ids)
.order('task_definitions.abbreviation ASC, discussion_prompts.priority DESC')

present result, with: Entities::DiscussionPromptEntity, user: current_user
end

end
8 changes: 8 additions & 0 deletions app/api/entities/discussion_prompt_entity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Entities
class DiscussionPromptEntity < Grape::Entity
expose :id
expose :task_definition_id
expose :content
expose :priority
end
end
3 changes: 3 additions & 0 deletions app/api/entities/overseer_assessment_entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ class OverseerAssessmentEntity < Grape::Entity
expose :status
expose :created_at
expose :updated_at

expose :total_steps
expose :passed_steps
end
end
47 changes: 47 additions & 0 deletions app/api/entities/overseer_step_entity.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Entities
class OverseerStepEntity < Grape::Entity
expose :id
expose :task_definition_id

def staff?(my_role)
Role.teaching_staff_ids.include?(my_role.id) unless my_role.nil?
end

expose :name, if: ->(_unit, options) { staff?(options[:my_role]) }
expose :description, if: ->(_unit, options) { staff?(options[:my_role]) }

expose :display_name
expose :display_description

expose :run_command, if: ->(_unit, options) { staff?(options[:my_role]) }

expose :timeout, if: ->(_unit, options) { staff?(options[:my_role]) }
expose :sort_order, if: ->(_unit, options) { staff?(options[:my_role]) }

expose :step_type
expose :partial_output_diff, if: ->(_unit, options) { staff?(options[:my_role]) }

expose :stdin_input_file, if: ->(_unit, options) { staff?(options[:my_role]) }
expose :expected_output_file, if: ->(_unit, options) { staff?(options[:my_role]) }

expose :feedback_message, if: ->(_unit, options) { staff?(options[:my_role]) }

expose :status_on_success,
if: ->(_obj, options) { staff?(options[:my_role]) } do |overseer_step|
TaskStatus.find_by(id: overseer_step.status_on_success_id)&.status_key || 'no_change'
end

expose :status_on_failure,
if: ->(_obj, options) { staff?(options[:my_role]) } do |overseer_step|
TaskStatus.find_by(id: overseer_step.status_on_failure_id)&.status_key || 'no_change'
end

expose :halt_on_success, if: ->(_unit, options) { staff?(options[:my_role]) }
expose :halt_on_failure, if: ->(_unit, options) { staff?(options[:my_role]) }
expose :show_expected_output, if: ->(_unit, options) { staff?(options[:my_role]) }
expose :show_stdin, if: ->(_unit, options) { staff?(options[:my_role]) }
expose :show_stdout, if: ->(_unit, options) { staff?(options[:my_role]) }

expose :enabled
end
end
Loading