Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
9b0ab9f
Added:
TaylorBrown96 Nov 19, 2025
d40fc01
Merge pull request #9 from cammyj1122/setupStructure
ananyarrao330 Nov 24, 2025
2c841b9
added field_mappings class
Nov 24, 2025
d851de8
Added importexport helper and import methods.
cammyj1122 Nov 26, 2025
e337816
Added capability to import users with external classes for lookup. Mo…
cammyj1122 Nov 29, 2025
dd8b30f
Finished adding the ability to have duplicate headers with comments r…
cammyj1122 Nov 29, 2025
72e49e6
Changed a small comment and moved a todo.
cammyj1122 Nov 29, 2025
85de9a4
Added the duplicate actions helper and added a place for the array of…
cammyj1122 Nov 29, 2025
3876a11
Started adding the unit tests for the helper file.
cammyj1122 Nov 30, 2025
371ca19
Added tests and fixtures for importable_exportable_helper.rb.
cammyj1122 Nov 30, 2025
0dc32fd
Updated the import controller to return the expected values.
cammyj1122 Nov 30, 2025
2643b2c
A while back the Question table was changed to the Item table. Howeve…
cammyj1122 Nov 30, 2025
afaf5cf
Added a csv file for invalid email for users.
cammyj1122 Nov 30, 2025
3d57637
Merge pull request #10 from cammyj1122/update_question_id_to_item_id
ananyarrao330 Dec 1, 2025
65ecaec
Added import export helper to the Team class
cammyj1122 Dec 1, 2025
3c63a99
Updated external classes to list their fields with the class name app…
cammyj1122 Dec 1, 2025
d13e69d
Implement assignment-specific team export functionality
TaylorBrown96 Dec 2, 2025
3430dc1
added comments that give explicitly verbose descriptions
TaylorBrown96 Dec 2, 2025
5c5dd87
Merged changes i had made to importable_exportable_helper.rb. Updated…
cammyj1122 Dec 2, 2025
58c46d0
Made small fix for user table to populate
cammyj1122 Dec 3, 2025
77e40a5
Fixed var in the backend
cammyj1122 Dec 3, 2025
c47323c
Fixed export in the backend.
cammyj1122 Dec 3, 2025
a4c8de0
Got unique actions working for imports
cammyj1122 Dec 3, 2025
2ff1fe0
Merge branch 'dev' into export
cammyj1122 Dec 3, 2025
298297f
Merge pull request #11 from cammyj1122/export
cammyj1122 Dec 3, 2025
8a46c16
Merge pull request #12 from cammyj1122/dev
cammyj1122 Dec 3, 2025
7f88a13
Added test suite
TaylorBrown96 Dec 3, 2025
e3e6898
Merge remote-tracking branch 'origin/export' into export
TaylorBrown96 Dec 3, 2025
eaa6d11
Fixed helper tests, removed commented out code.
cammyj1122 Dec 5, 2025
e4920bf
Added initial rswag tests for controller
cammyj1122 Dec 5, 2025
09551a1
Merge pull request #13 from cammyj1122/export
cammyj1122 Dec 5, 2025
b04b81d
Merge pull request #14 from cammyj1122/dev
cammyj1122 Dec 5, 2025
6117c20
Merge branch 'main' into main
vihar2712 Dec 16, 2025
cf24ad7
Changing migration to conditionally rename columns. Pushed new schema…
nadkarnik Mar 13, 2026
9a1cee4
I guess not all migrations were run so updating schema.rb to reflect …
nadkarnik Mar 15, 2026
b847f8e
reverted setup.sh location within dockerfile because I wasn't startin…
nadkarnik Mar 15, 2026
6168155
updating mandatory fields and available actions for Team and SignUpTo…
nadkarnik Mar 19, 2026
0a06bda
Make questionnaire importable-exportable (tested export)
kamatmihir2002 Mar 20, 2026
ce62daa
Add recursive export (export_helper.rb) for has_many and belongs_to m…
kamatmihir2002 Mar 20, 2026
d787275
Add route alias for project_topics in sign_up_topics controller becau…
nadkarnik Mar 21, 2026
ba5f298
modifying controller to accept both sign_up_topics and project_topics…
nadkarnik Mar 21, 2026
7e1e407
fixing implementation of the sign_up_topics and project_topics compat…
nadkarnik Mar 21, 2026
dbc6d21
Updating user_serializer with more attributes and fields to return. T…
nadkarnik Mar 22, 2026
9c86eda
Added support for default values for parent and institution IDs. Upd…
nadkarnik Mar 22, 2026
37b002b
renamed request spec for import and export of users/teams/topics to b…
nadkarnik Mar 22, 2026
875fc04
Simplify graph export
kamatmihir2002 Mar 26, 2026
cc02b8f
Remove unrelated schema and Dockerfile diffs
nadkarnik Mar 28, 2026
32e4c32
Remove unrelated migration and schema diffs
nadkarnik Mar 28, 2026
bdacc5b
Merge pull request #21 from nadkarnik/nadkarnik/fixing-E2560-main-mig…
kamatmihir2002 Mar 28, 2026
91e42d5
Fix import duplicate action fallback and topic update assignment lookup
nadkarnik Mar 28, 2026
1d20ad6
Add extra layer of abstraction when exporting
kamatmihir2002 Mar 28, 2026
5349c55
Add spoofed grades class for exporting.
kamatmihir2002 Mar 28, 2026
b873400
Update importable exportable helper to include filter methods
kamatmihir2002 Mar 28, 2026
ce98359
Merge pull request #18 from nadkarnik/nadkarnik/adding-import-export-…
kamatmihir2002 Mar 29, 2026
50ec75a
Merge branch 'E2560-main' into mskamat/export_grades_and_questionnaires
kamatmihir2002 Mar 29, 2026
ab2155f
Merge pull request #22 from nadkarnik/mskamat/export_grades_and_quest…
kamatmihir2002 Mar 29, 2026
e9d0eca
Fix merge conflicts in routes + renames
kamatmihir2002 Mar 29, 2026
f26517d
fixing migrations
nadkarnik Mar 29, 2026
6098b2d
added conditionals on newest migration
nadkarnik Mar 29, 2026
732ee95
Merge pull request #23 from nadkarnik/nadkarnik/fixing-migrations
nadkarnik Mar 29, 2026
a3d069c
ported over importable/exportable mixin into project_topic and added …
nadkarnik Mar 29, 2026
cc6375c
fixing leftover vscode diff printed into schema
nadkarnik Mar 29, 2026
d623e97
Merge pull request #24 from nadkarnik/nadkarnik/updating-schema
nadkarnik Mar 29, 2026
c6c30fd
Added seeds and additional fields within topics table to be importabl…
nadkarnik Mar 29, 2026
7ae0d92
backend changes to help link assignment teams to frontend
nadkarnik Mar 29, 2026
829ecee
customized import/export for teams to use a unique name/participant s…
nadkarnik Mar 29, 2026
7af8cb3
Merge pull request #25 from nadkarnik/nadkarnik/fixing-topics-not-sho…
nadkarnik Mar 30, 2026
39e1379
Merge branch 'main' into kdnadkar/fixing-teams-not-populating-correctly
kamatmihir2002 Mar 30, 2026
e70b584
Merge pull request #26 from nadkarnik/kdnadkar/fixing-teams-not-popul…
kamatmihir2002 Mar 30, 2026
1c4b6be
Integrate graph export into export with switch
kamatmihir2002 Mar 30, 2026
f397e9a
Add sample questionnaire with items
kamatmihir2002 Mar 30, 2026
7066256
Add hidden fields option
kamatmihir2002 Mar 30, 2026
de1aaa5
Refine grades export + Add tests for grades export
kamatmihir2002 Mar 30, 2026
0d9e750
grade export changes
kamatmihir2002 Mar 30, 2026
f498be0
Filter nonexistent headers from child models when graph exporting
kamatmihir2002 Mar 31, 2026
87fefc2
Merge pull request #27 from nadkarnik/mskamat/graph-export-integration
kamatmihir2002 Mar 31, 2026
6616b66
reset database yml username passwd
kamatmihir2002 Mar 31, 2026
7de3289
Merge pull request #28 from nadkarnik/minor-fix
kamatmihir2002 Mar 31, 2026
4d45dc6
updating schema to newest per migrations
nadkarnik Mar 31, 2026
22188bd
Merge pull request #29 from nadkarnik/kdnadkar/latest-schema
nadkarnik Mar 31, 2026
50f8eba
Add mandatory fields of external class to import-export mandatory fields
kamatmihir2002 Apr 3, 2026
1ff3e32
Merge pull request #30 from nadkarnik/mskamat/fix_import_issue
kamatmihir2002 Apr 3, 2026
a8f3b83
Branch start for incorporating final changes
kamatmihir2002 Apr 3, 2026
0037058
Add brief descriptions for method names in export helper
kamatmihir2002 Apr 3, 2026
344bd84
Make graph export model-side variable
kamatmihir2002 Apr 3, 2026
750c4fd
Change grades model path destination
kamatmihir2002 Apr 3, 2026
916ba4f
Merge pull request #31 from nadkarnik/mskamat/final_changes
kamatmihir2002 Apr 4, 2026
d682d0b
added backend implementation for course import/export
nadkarnik Apr 19, 2026
a996dde
added comments to teams and topics files to newly added methods for a…
nadkarnik Apr 19, 2026
4e79f2b
changed import and export controllers to explicitly list out the supp…
nadkarnik Apr 20, 2026
80f28ce
reworked grade export to live in the controller with minimal fields. …
nadkarnik Apr 24, 2026
96ca219
committing initial draft of changes.
nadkarnik Apr 25, 2026
4c6a4c0
split up CSV questionnaire, item, advices imports. Added additional t…
nadkarnik Apr 25, 2026
2d0d45a
Implemented template download for CSV and ZIP formats in Questionnair…
nadkarnik Apr 26, 2026
21596c4
updated ap import/export functionality with context handling and redu…
nadkarnik Apr 26, 2026
9498cd0
Merge pull request #2 from isaacmartin7777-maker/nadkarnik/adding-imp…
nadkarnik Apr 26, 2026
cb40fbf
removed graph based export code traces as alternative system will be …
nadkarnik Apr 27, 2026
0b84668
Merge pull request #7 from isaacmartin7777-maker/nadkarnik/updating-q…
nadkarnik Apr 28, 2026
8b48da3
Merge branch 'main' into nadkarnik/reworking-grades-export
nadkarnik Apr 28, 2026
c418263
deleted grades model
nadkarnik Apr 28, 2026
df12709
Merge pull request #6 from isaacmartin7777-maker/nadkarnik/reworking-…
nadkarnik Apr 28, 2026
efccbdf
Merge pull request #5 from isaacmartin7777-maker/nadkarnik/assignment…
nadkarnik Apr 28, 2026
0fde405
Merge pull request #4 from isaacmartin7777-maker/nadkarnik/commenting…
nadkarnik Apr 28, 2026
b453c28
Merge branch 'main' into nadkarnik/clarifying-importable-exportable-c…
nadkarnik Apr 28, 2026
9da1239
Merge pull request #3 from isaacmartin7777-maker/nadkarnik/clarifying…
nadkarnik Apr 28, 2026
543c8d2
updating import/export functionality to support ProjectTopic and Assi…
nadkarnik Apr 28, 2026
a040e24
Merge pull request #8 from isaacmartin7777-maker/nadkarnik/fixing-mer…
nadkarnik Apr 28, 2026
5921a2d
backend changes to switch import and export from course to course par…
nadkarnik Apr 28, 2026
1c448bf
added seeds for course participants and solidified round trip behavio…
nadkarnik Apr 28, 2026
512199e
added backend seed data for grades
nadkarnik Apr 28, 2026
c05523a
added and modified comments to methods I touched
nadkarnik Apr 28, 2026
a2329ed
using participants as mandatory fields for teams and removing assignm…
nadkarnik Apr 30, 2026
dfa0a88
modified questionnaire export service to conditionally include questi…
nadkarnik Apr 30, 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
151 changes: 151 additions & 0 deletions app/controllers/export_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# This controller handles exporting data from the application to various formats.
class ExportController < ApplicationController
SUPPORTED_EXPORT_CLASSES = {
"User" => User,
"Team" => Team,
"CourseParticipant" => CourseParticipant,
"AssignmentParticipant" => AssignmentParticipant,
"ProjectTopic" => ProjectTopic,
"Questionnaire" => Questionnaire,
"Item" => Item,
"QuestionAdvice" => QuestionAdvice
}.freeze

before_action :export_params

def resolve_export_class(name)
SUPPORTED_EXPORT_CLASSES[name.to_s]
end

def index
klass = resolve_export_class(params[:class])
raise ArgumentError, "Unsupported export class: #{params[:class]}" if klass.nil?

render json: export_metadata_for(klass), status: :ok
Comment on lines +20 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing required scope IDs can lead to unintended export scope.

For Team, AssignmentParticipant, and ProjectTopic, assignment_id should be required; for CourseParticipant, course_id should be required. Right now these branches execute even when IDs are blank.

Suggested fix
   def index
     klass = resolve_export_class(params[:class])
     raise ArgumentError, "Unsupported export class: #{params[:class]}" if klass.nil?
+    validate_scope_params!(klass)

     render json: export_metadata_for(klass), status: :ok
   rescue StandardError => e
     render json: { error: e.message }, status: :unprocessable_entity
   end
@@
   def export
@@
     klass = resolve_export_class(params[:class])
     raise ArgumentError, "Unsupported export class: #{params[:class]}" if klass.nil?
+    validate_scope_params!(klass)
@@
   private
+
+  def validate_scope_params!(klass)
+    if [Team, AssignmentParticipant, ProjectTopic].include?(klass) && params[:assignment_id].blank?
+      raise ArgumentError, "assignment_id is required for #{klass.name}"
+    end
+    if klass == CourseParticipant && params[:course_id].blank?
+      raise ArgumentError, "course_id is required for CourseParticipant"
+    end
+  end

Also applies to: 39-64, 84-122

🧰 Tools
🪛 RuboCop (1.86.1)

[convention] 23-23: Trailing whitespace detected.

(Layout/TrailingWhitespace)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/controllers/export_controller.rb` around lines 20 - 24, The export
endpoint currently allows branches for scoped classes to run with blank IDs;
update the index flow to validate required scope params before calling
export_metadata_for: after resolving klass via
resolve_export_class(params[:class]) add explicit presence checks that raise
ArgumentError when missing — require params[:assignment_id] for klasses Team,
AssignmentParticipant, and ProjectTopic, and require params[:course_id] for
klass CourseParticipant; ensure similar validations are added to the other
export entry points/branches mentioned (lines covering the other ranges) so the
same classes cannot proceed without their required IDs.

rescue StandardError => e
render json: { error: e.message }, status: :unprocessable_entity
end
Comment on lines +25 to +27

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Raw exception messages are returned to clients.

These rescues expose internal error details via e.message. Return a generic message and log the exception server-side.

Also applies to: 71-73

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/controllers/export_controller.rb` around lines 25 - 27, The rescue blocks
that currently do `rescue StandardError => e` and `render json: { error:
e.message }, status: :unprocessable_entity` should not return raw exception
messages to clients; update these rescue handlers (both occurrences of `rescue
StandardError => e` in ExportController) to render a generic error JSON (e.g.,
`{ error: "An error occurred" }`) with the same status, and instead log the full
exception and backtrace server-side using Rails.logger.error or logger.error
(include e.class, e.message, and e.backtrace.join("\n")). Ensure both the rescue
at the top (lines shown around the first diff) and the second rescue (around
lines 71-73) are changed consistently.


def export
# Parse ordered fields from JSON, if provided
ordered_fields =
begin
JSON.parse(params[:ordered_fields]) if params[:ordered_fields]
rescue JSON::ParserError
render json: { error: "Invalid JSON for ordered_fields" }, status: :unprocessable_entity
return
end
Comment on lines +31 to +37

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate ordered_fields type after JSON parse.

Line 33 accepts any JSON value. Non-array input (object/string/number) can flow into export mapping and produce invalid headers/rows or runtime errors.

Suggested fix
     ordered_fields =
       begin
         JSON.parse(params[:ordered_fields]) if params[:ordered_fields]
       rescue JSON::ParserError
         render json: { error: "Invalid JSON for ordered_fields" }, status: :unprocessable_entity
         return
       end
+
+    if ordered_fields && (!ordered_fields.is_a?(Array) || ordered_fields.any? { |f| !f.is_a?(String) })
+      render json: { error: "ordered_fields must be a JSON array of strings" }, status: :unprocessable_entity
+      return
+    end
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/controllers/export_controller.rb` around lines 31 - 37, After parsing
params[:ordered_fields] with JSON.parse into ordered_fields, ensure the result
is an Array before continuing: if ordered_fields is nil skip, but if present and
not an Array (use ordered_fields.is_a?(Array)), render json: { error:
"ordered_fields must be a JSON array" }, status: :unprocessable_entity and
return; adjust the rescue block around JSON.parse (the existing rescue
JSON::ParserError) to also validate type after parse so only a proper Array
flows into the export mapping logic.


klass = resolve_export_class(params[:class])
raise ArgumentError, "Unsupported export class: #{params[:class]}" if klass.nil?

csv_file = if klass == Team
Team.with_assignment_context(params[:assignment_id]) do
Export.perform(klass, ordered_fields)
end
elsif klass == AssignmentParticipant
# AssignmentParticipant export should include only the
# participants for the selected assignment.
AssignmentParticipant.with_assignment_context(params[:assignment_id], current_user) do
named_export_files(Export.perform(klass, ordered_fields), assignment_participant_export_name(params[:assignment_id]))
end
elsif klass == CourseParticipant
# CourseParticipant export should include only the
# participants for the selected course.
CourseParticipant.with_course_context(params[:course_id], current_user) do
named_export_files(Export.perform(klass, ordered_fields), course_participant_export_name(params[:course_id]))
end
elsif klass == ProjectTopic
ProjectTopic.with_assignment_context(params[:assignment_id]) do
Export.perform(klass, ordered_fields)
end
else
Export.perform(klass, ordered_fields)
end

render json: {
message: "#{params[:class]} has been exported!",
file: csv_file
}, status: :ok

rescue StandardError => e
render json: { error: e.message }, status: :unprocessable_entity
end

private

def export_params
params.permit(:class, :ordered_fields, :assignment_id, :course_id)
end

def export_metadata_for(klass)
# The participant CSV intentionally exposes username only. Other user
# details are previewed from the users table but not exported as input.
if klass == AssignmentParticipant
AssignmentParticipant.with_assignment_context(params[:assignment_id], current_user) do
return {
mandatory_fields: klass.mandatory_fields,
optional_fields: klass.optional_fields,
external_fields: klass.external_fields
}
end
end

if klass == CourseParticipant
CourseParticipant.with_course_context(params[:course_id], current_user) do
return {
mandatory_fields: klass.mandatory_fields,
optional_fields: klass.optional_fields,
external_fields: klass.external_fields
}
end
end

if klass == Team
Team.with_assignment_context(params[:assignment_id]) do
return {
mandatory_fields: klass.mandatory_fields,
optional_fields: klass.optional_fields,
external_fields: klass.external_fields
}
end
end

if klass == ProjectTopic
ProjectTopic.with_assignment_context(params[:assignment_id]) do
return {
mandatory_fields: klass.mandatory_fields,
optional_fields: klass.optional_fields,
external_fields: klass.external_fields
}
end
end

{
mandatory_fields: klass.mandatory_fields,
optional_fields: klass.optional_fields,
external_fields: klass.external_fields
}
end

# Scoped participant exports use readable filenames while keeping the CSV
# body limited to import-friendly fields such as username.
def named_export_files(files, name)
Array(files).map { |file| file.merge(name: name) }
end

def assignment_participant_export_name(assignment_id)
assignment = Assignment.find_by(id: assignment_id)
scoped_export_name('AssignmentParticipant', assignment&.name, assignment_id)
end

def course_participant_export_name(course_id)
course = Course.find_by(id: course_id)
scoped_export_name('CourseParticipant', course&.name, course_id)
end

def scoped_export_name(base_name, scope_name, scope_id)
parts = [base_name, scope_name.presence || 'scope', scope_id.presence].compact
parts.join('_').parameterize(separator: '_')
end
end
49 changes: 47 additions & 2 deletions app/controllers/grades_controller.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
require 'csv'

class GradesController < ApplicationController
include GradesHelper

GRADES_EXPORT_HEADERS = %w[username grade comment].freeze
GRADES_EXPORT_OPTIONAL_HEADERS = %w[email].freeze

def action_allowed?
case params[:action]
when 'view_our_scores','view_my_scores'
set_participant_and_team_via_assignment
current_user_is_assignment_participant?(params[:assignment_id])
when 'view_all_scores', 'get_review_tableau_data'
when 'view_all_scores', 'get_review_tableau_data', 'export'
current_user_teaching_staff_of_assignment?(params[:assignment_id])
when 'edit', 'assign_grade', 'instructor_review'
set_team_and_assignment_via_participant
Expand Down Expand Up @@ -37,6 +42,20 @@ def view_all_scores
}
end

# export (GET /grades/:assignment_id/export)
# Exports fixed, gradebook-friendly CSV columns for an assignment.
def export
assignment = Assignment.find(params[:assignment_id])
filename = "#{assignment.name.parameterize.presence || 'assignment'}-grades.csv"

send_data(
grades_csv_for(assignment, include_email: include_email_in_grades_export?),
type: 'text/csv; charset=utf-8',
disposition: 'attachment',
filename: filename
)
end


# view_our_scores (GET /grades/:assignment_id/view_our_scores)
# similar to view but scoped to the requesting student’s own team.
Expand Down Expand Up @@ -245,6 +264,32 @@ def set_participant_and_team_via_assignment
@assignment = @participant.assignment
end

# Export one row per assignment participant. A participant-specific grade
# overrides the team submission grade; submission comments remain team-level.
def grades_csv_for(assignment, include_email: false)
headers = GRADES_EXPORT_HEADERS + (include_email ? GRADES_EXPORT_OPTIONAL_HEADERS : [])

CSV.generate(headers: true) do |csv|
csv << headers

assignment.participants.includes(:user).find_each do |participant|
team = participant.team
row = [
participant.user_name,
participant.grade || team&.grade_for_submission,
team&.comment_for_submission
]
row << participant.user&.email if include_email

csv << row
Comment on lines +277 to +284

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Prevent CSV formula injection in export rows.

Line 278/280/282 write user-controlled text directly into CSV cells. Spreadsheet clients can evaluate cells starting with =, +, -, or @.

🔒 Proposed fix
 def grades_csv_for(assignment, include_email: false)
   headers = GRADES_EXPORT_HEADERS + (include_email ? GRADES_EXPORT_OPTIONAL_HEADERS : [])

   CSV.generate(headers: true) do |csv|
     csv << headers

     assignment.participants.includes(:user).find_each do |participant|
       team = participant.team
       row = [
-        participant.user_name,
+        sanitize_csv_cell(participant.user_name),
         participant.grade || team&.grade_for_submission,
-        team&.comment_for_submission
+        sanitize_csv_cell(team&.comment_for_submission)
       ]
-      row << participant.user&.email if include_email
+      row << sanitize_csv_cell(participant.user&.email) if include_email

       csv << row
     end
   end
 end
+
+def sanitize_csv_cell(value)
+  text = value.to_s
+  text.match?(/\A[=\-+@]/) ? "'#{text}" : text
+end
🧰 Tools
🪛 RuboCop (1.86.1)

[convention] 278-278: Use 2 spaces for indentation in an array, relative to the start of the line where the left square bracket is.

(Layout/FirstArrayElementIndentation)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/controllers/grades_controller.rb` around lines 277 - 284, The CSV export
writes user-controlled fields (participant.user_name, participant.grade or
team&.grade_for_submission, team&.comment_for_submission, and
participant.user&.email) directly into CSV rows, which risks spreadsheet formula
injection; fix by sanitizing each cell before appending to row: add a helper
(e.g., sanitize_csv_cell) used where the row is constructed to return nil for
nil values, leave non-string values untouched, and for strings that begin with
"=", "+", "-", or "@" prefix the string with a single quote (') or otherwise
escape the leading char; apply this sanitizer to participant.user_name, the
grade/comment values, and participant.user&.email before csv << row.

end
end
end

def include_email_in_grades_export?
ActiveModel::Type::Boolean.new.cast(params[:include_email])
end


# returns the heatgrid data required for a team to view their scores and average score of their work for an assignment
def get_our_scores_data(team)
Expand Down Expand Up @@ -374,4 +419,4 @@ def get_answer(score, index)
reviewee_name: reviewee_name
}
end
end
end
Loading
Loading