Skip to content

Commit 4c8fe35

Browse files
authored
Merge pull request doubtfire-lms#473 from b0ink/fix/fetch-my-students-only-inbox
refactor: optional param to fetch tutor's student's tasks only
2 parents 44c6cd2 + a1b1360 commit 4c8fe35

File tree

3 files changed

+151
-54
lines changed

3 files changed

+151
-54
lines changed

app/api/units_api.rb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,19 @@ class UnitsApi < Grape::API
281281
end
282282

283283
desc 'Download the tasks that should be listed under the task inbox'
284+
params do
285+
optional :my_students_only, type: Boolean, desc: 'Show tasks from all tutorials or just the ones you teach'
286+
end
284287
get '/units/:id/tasks/inbox' do
285288
unit = Unit.find(params[:id])
286289

287290
unless authorise? current_user, unit, :get_students
288291
error!({ error: 'Not authorised to provide feedback for this unit' }, 403)
289292
end
290293

291-
tasks = unit.tasks_for_task_inbox(current_user)
294+
my_students_only = params[:my_students_only] || false
295+
296+
tasks = unit.tasks_for_task_inbox(current_user, my_students_only)
292297
present unit.tasks_as_hash(tasks), with: Grape::Presenters::Presenter
293298
end
294299

app/models/unit.rb

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,10 @@ def employ_staff(user, role)
566566
end
567567
end
568568

569+
def unit_role_for(user)
570+
unit_roles.find_by(user: user)
571+
end
572+
569573
# Adds a user to this project.
570574
def enrol_student(user, campus)
571575
# Validates that a student is not already assigned to the unit
@@ -1727,54 +1731,59 @@ def tasks_as_hash(data)
17271731
def tutorial_enrolment_subquery
17281732
tutorial_enrolments
17291733
.joins(:tutorial)
1730-
.select('tutorials.tutorial_stream_id as tutorial_stream_id', 'tutorials.id as tutorial_id', 'project_id').to_sql
1734+
.select('tutorials.tutorial_stream_id as tutorial_stream_id', 'tutorials.id as tutorial_id', 'project_id', 'tutorials.unit_role_id as unit_role_id').to_sql
17311735
end
17321736

17331737
#
17341738
# Return all tasks from the database for this unit and given user
17351739
#
1736-
def get_all_tasks_for(user)
1737-
student_tasks.
1738-
joins(:task_status).
1739-
joins("LEFT OUTER JOIN (#{tutorial_enrolment_subquery}) as sq ON sq.project_id = projects.id AND (sq.tutorial_stream_id = task_definitions.tutorial_stream_id OR sq.tutorial_stream_id IS NULL)").
1740-
joins("LEFT JOIN task_comments ON task_comments.task_id = tasks.id AND (task_comments.type IS NULL OR task_comments.type <> 'TaskStatusComment')").
1741-
joins("LEFT JOIN comments_read_receipts crr ON crr.task_comment_id = task_comments.id AND crr.user_id = #{user.id}").
1742-
joins("LEFT JOIN task_pins ON task_pins.task_id = tasks.id AND task_pins.user_id = #{user.id}").
1743-
joins('LEFT OUTER JOIN task_similarities ON tasks.id = task_similarities.task_id').
1744-
select(
1745-
'sq.tutorial_id AS tutorial_id',
1746-
'sq.tutorial_stream_id AS tutorial_stream_id',
1747-
'tasks.id',
1748-
"SUM(case when crr.user_id is null AND NOT task_comments.id is null then 1 else 0 end) as number_unread",
1749-
'COUNT(distinct task_pins.task_id) != 0 as pinned',
1750-
"SUM(case when task_comments.date_extension_assessed IS NULL AND task_comments.type = 'ExtensionComment' AND NOT task_comments.id IS NULL THEN 1 ELSE 0 END) > 0 as has_extensions",
1751-
'project_id',
1752-
'tasks.id as task_id',
1753-
'task_definition_id',
1754-
'task_definitions.start_date as start_date',
1755-
'task_statuses.id as status_id',
1756-
'completion_date',
1757-
'times_assessed',
1758-
'submission_date',
1759-
'tasks.grade as grade',
1760-
'quality_pts',
1761-
'SUM(case when task_similarities.flagged then 1 else 0 end) as similar_to_count'
1762-
).
1763-
group(
1764-
'sq.tutorial_id',
1765-
'sq.tutorial_stream_id',
1766-
'task_statuses.id',
1767-
'project_id',
1768-
'tasks.id',
1769-
'task_definition_id',
1770-
'task_definitions.start_date',
1771-
'status_id',
1772-
'completion_date',
1773-
'times_assessed',
1774-
'submission_date',
1775-
'grade',
1776-
'quality_pts'
1777-
)
1740+
def get_all_tasks_for(user, my_tutorials_only = false)
1741+
result = student_tasks.
1742+
joins(:task_status).
1743+
joins("LEFT OUTER JOIN (#{tutorial_enrolment_subquery}) as sq ON sq.project_id = projects.id AND (sq.tutorial_stream_id = task_definitions.tutorial_stream_id OR sq.tutorial_stream_id IS NULL)").
1744+
joins("LEFT JOIN task_comments ON task_comments.task_id = tasks.id AND (task_comments.type IS NULL OR task_comments.type <> 'TaskStatusComment')").
1745+
joins("LEFT JOIN comments_read_receipts crr ON crr.task_comment_id = task_comments.id AND crr.user_id = #{user.id}").
1746+
joins("LEFT JOIN task_pins ON task_pins.task_id = tasks.id AND task_pins.user_id = #{user.id}").
1747+
joins('LEFT OUTER JOIN task_similarities ON tasks.id = task_similarities.task_id').
1748+
select(
1749+
'sq.tutorial_id AS tutorial_id',
1750+
'sq.tutorial_stream_id AS tutorial_stream_id',
1751+
'tasks.id',
1752+
"SUM(case when crr.user_id is null AND NOT task_comments.id is null then 1 else 0 end) as number_unread",
1753+
'COUNT(distinct task_pins.task_id) != 0 as pinned',
1754+
"SUM(case when task_comments.date_extension_assessed IS NULL AND task_comments.type = 'ExtensionComment' AND NOT task_comments.id IS NULL THEN 1 ELSE 0 END) > 0 as has_extensions",
1755+
'project_id',
1756+
'tasks.id as task_id',
1757+
'task_definition_id',
1758+
'task_definitions.start_date as start_date',
1759+
'task_statuses.id as status_id',
1760+
'completion_date',
1761+
'times_assessed',
1762+
'submission_date',
1763+
'tasks.grade as grade',
1764+
'quality_pts',
1765+
'SUM(case when task_similarities.flagged then 1 else 0 end) as similar_to_count'
1766+
).
1767+
group(
1768+
'sq.tutorial_id',
1769+
'sq.tutorial_stream_id',
1770+
'task_statuses.id',
1771+
'project_id',
1772+
'tasks.id',
1773+
'task_definition_id',
1774+
'task_definitions.start_date',
1775+
'status_id',
1776+
'completion_date',
1777+
'times_assessed',
1778+
'submission_date',
1779+
'grade',
1780+
'quality_pts'
1781+
)
1782+
if my_tutorials_only
1783+
result = result.where('sq.unit_role_id = :unit_role_id', unit_role_id: unit_role_for(user).id)
1784+
end
1785+
1786+
result
17781787
end
17791788

17801789
#
@@ -1797,8 +1806,8 @@ def tasks_awaiting_feedback(user)
17971806
# time a task has been "actioned", either the submission date or latest
17981807
# student comment -- whichever is newer.
17991808
#
1800-
def tasks_for_task_inbox(user)
1801-
get_all_tasks_for(user)
1809+
def tasks_for_task_inbox(user, my_students_only = false)
1810+
get_all_tasks_for(user, my_students_only)
18021811
.having('task_statuses.id IN (:ids) OR COUNT(task_pins.task_id) > 0 OR SUM(case when crr.user_id is null AND NOT task_comments.id is null then 1 else 0 end) > 0', ids: [TaskStatus.ready_for_feedback, TaskStatus.need_help])
18031812
.order('pinned DESC, submission_date ASC, MAX(task_comments.created_at) ASC, task_definition_id ASC')
18041813
end

test/api/units/feedback_test.rb

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,109 @@ def test_get_awaiting_feedback
3333
end
3434

3535
def test_tasks_for_task_inbox
36-
unit = FactoryBot.create(:unit, perform_submissions: true, unenrolled_student_count: 0, part_enrolled_student_count: 0, tutorials: 2, staff_count: 2)
37-
38-
expected_count = unit.tasks.where(task_status: [TaskStatus.ready_for_feedback, TaskStatus.need_help]).count
39-
40-
unit.teaching_staff.each do |user|
41-
expected_response = unit.tasks_for_task_inbox(user)
42-
36+
unit = FactoryBot.create(:unit, perform_submissions: true, unenrolled_student_count: 0, part_enrolled_student_count: 0, tutorials: 2, staff_count: 2, student_count: 2, task_count: 0)
37+
38+
td1 = TaskDefinition.create!({
39+
unit_id: unit.id,
40+
tutorial_stream: unit.tutorial_streams.first,
41+
name: 'Code task',
42+
description: 'Code task',
43+
weighting: 4,
44+
target_grade: 0,
45+
start_date: Time.zone.now - 2.weeks,
46+
target_date: Time.zone.now + 1.week,
47+
abbreviation: 'CodeTask',
48+
restrict_status_updates: false,
49+
upload_requirements: [{ "key" => 'file0', "name" => 'Shape Class', "type" => 'code' }],
50+
plagiarism_warn_pct: 0.8,
51+
is_graded: true,
52+
max_quality_pts: 0
53+
})
54+
55+
td2 = TaskDefinition.create!({
56+
unit_id: unit.id,
57+
tutorial_stream: unit.tutorial_streams.first,
58+
name: 'Code task2',
59+
description: 'Code task2',
60+
weighting: 4,
61+
target_grade: 0,
62+
start_date: Time.zone.now - 2.weeks,
63+
target_date: Time.zone.now + 1.week,
64+
abbreviation: 'CodeTask2',
65+
restrict_status_updates: false,
66+
upload_requirements: [{ "key" => 'file0', "name" => 'Shape Class', "type" => 'code' }],
67+
plagiarism_warn_pct: 0.8,
68+
is_graded: true,
69+
max_quality_pts: 0
70+
})
71+
72+
student1 = unit.active_projects.first
73+
student2 = unit.active_projects.second
74+
tutor1 = unit.tutors.first
75+
tutor2 = unit.tutors.second
76+
77+
tutorial1 = unit.tutorials.first
78+
tutorial2 = unit.tutorials.second
79+
80+
assert_not_nil student1
81+
assert_not_nil student2
82+
assert_not_nil tutor1
83+
assert_not_nil tutor2
84+
assert_not_nil tutorial1
85+
assert_not_nil tutorial2
86+
87+
unit.employ_staff(tutor1, Role.tutor)
88+
unit.employ_staff(tutor2, Role.tutor)
89+
90+
tutorial1.assign_tutor(tutor1)
91+
tutorial2.assign_tutor(tutor2)
92+
93+
student1.enrol_in(tutorial1)
94+
student2.enrol_in(tutorial2)
95+
96+
# Submit tasks ready for feedback
97+
student1_task1 = student1.task_for_task_definition(td1)
98+
student2_task1 = student2.task_for_task_definition(td1)
99+
DatabasePopulator.assess_task(student1, student1_task1, tutor1, TaskStatus.ready_for_feedback, Time.zone.now)
100+
DatabasePopulator.assess_task(student2, student2_task1, tutor2, TaskStatus.ready_for_feedback, Time.zone.now)
101+
102+
# Add comments to unsubmitted task
103+
student1_task2 = student1.task_for_task_definition(td2)
104+
student2_task2 = student2.task_for_task_definition(td2)
105+
comment1 = student1_task2.add_text_comment(student1.user, "Test 1")
106+
comment2 = student2_task2.add_text_comment(student2.user, "Test 1")
107+
assert_not_nil comment1
108+
assert_not_nil comment2
109+
110+
[tutor1, tutor2].each do |user|
43111
# Add auth_token and username to header
44112
add_auth_header_for(user: user)
45113

114+
# Defaults inbox?my_students_only = false
46115
get "/api/units/#{unit.id}/tasks/inbox"
47116

117+
# 2 submissions ready for feedback + 2 tasks with unread comments = 4 total
48118
assert_equal 200, last_response.status, last_response_body
49-
assert_equal expected_count, last_response_body.count, last_response_body
119+
assert_equal 4, last_response_body.count, last_response_body
50120

51121
# check each is the same
122+
expected_response = unit.tasks_for_task_inbox(user)
52123
last_response_body.zip(expected_response).each do |response, expected|
53124
assert_json_matches_model expected, response, ['id']
54125
end
55126

127+
get "/api/units/#{unit.id}/tasks/inbox?my_students_only=true"
128+
129+
# 1 submission ready for feedback + 1 task with unread comments = 2 total
130+
assert_equal 200, last_response.status, last_response_body
131+
assert_equal 2, last_response_body.count, last_response_body
132+
133+
# check each is the same
134+
expected_response2 = unit.tasks_for_task_inbox(user, true)
135+
last_response_body.zip(expected_response2).each do |response, expected|
136+
assert_json_matches_model expected, response, ['id']
137+
end
138+
56139
# Test task explorer
57140
get "/api/units/#{unit.id}/task_definitions/#{unit.task_definitions.first.id}/tasks"
58141

0 commit comments

Comments
 (0)