Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 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
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
77 changes: 77 additions & 0 deletions app/controllers/export_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# This controller handles exporting data from the application to various formats.
class ExportController < ApplicationController
before_action :export_params

def resolve_export_class(name)
# Try top-level first
return name.constantize
rescue NameError
# Try Pseudo namespace
begin
"Pseudo::#{name}".constantize
rescue NameError
nil
end
end

def index
klass = resolve_export_class(params[:class])

render json: export_metadata_for(klass), status: :ok
rescue StandardError => e
render json: { error: e.message }, status: :unprocessable_entity
end

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

klass = resolve_export_class(params[:class])

csv_file = if klass == Team
Team.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)
end

def export_metadata_for(klass)
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

{
mandatory_fields: klass.mandatory_fields,
optional_fields: klass.optional_fields,
external_fields: klass.external_fields
}
end
end
116 changes: 116 additions & 0 deletions app/controllers/import_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# This controller handles importing CSV data into any supported model.
# It exposes two endpoints:
# • GET /import -> returns field requirements for the selected class
# • POST /import -> processes the uploaded CSV file
#
# The controller delegates actual import logic to:
# klass.try_import_records(...)
#
# Each model that supports importing must implement:
# mandatory_fields
# optional_fields
# external_fields
# try_import_records(file, ordered_fields, use_header:)
#

class ImportController < ApplicationController
# Ensure strong parameters are processed before each action
before_action :import_params

##
# GET /import
#
# Returns metadata about which fields a given class requires or accepts.
# The frontend uses this to build the mapping UI (drag/drop field matching).
#
def index
imported_class = params[:class].constantize

render json: import_metadata_for(imported_class), status: :ok
end

##
# POST /import
#
# This action performs the actual import process. It:
# 1. Reads the uploaded CSV file
# 2. Determines whether the CSV includes headers
# 3. Applies user-chosen field ordering (if provided)
# 4. Hands off import logic to the model via `try_import_records`
#
def import
uploaded_file = params[:csv_file]

# Convert use_headers ("true"/"false") into actual boolean
use_headers = ActiveRecord::Type::Boolean.new.deserialize(params[:use_headers])

# If the user provided a custom field ordering, load it from JSON
ordered_fields = JSON.parse(params[:ordered_fields]) if params[:ordered_fields]

# Dynamically load the model class (e.g., "User", "Team", etc.)
klass = params[:class].constantize
defaults = import_defaults_for(klass)

# Load the chosen duplicate action (Skip, Update, Change)
dup_action = params[:dup_action]&.constantize

pp dup_action

importService = Import.new(klass: klass, file: uploaded_file, headers: ordered_fields, dup_action: dup_action&.new, defaults: defaults)
result = importService.perform(use_headers)

# If no exceptions occur, return success
render json: { message: "#{klass.name} has been imported!", **result }, status: :created

rescue StandardError => e
# Catch any unexpected runtime errors
puts "An unexpected error occurred during import: #{e.message}"

render json: { error: e.message }, status: :unprocessable_entity
end

private

##
# Strong parameters for import operations
#
def import_params
params.permit(:csv_file, :use_headers, :class, :ordered_fields, :dup_action, :assignment_id)
end

def import_defaults_for(klass)
return team_import_defaults if klass == Team
return {} unless klass == User && current_user.present?

{
parent_id: current_user.id,
institution_id: current_user.institution_id
}
end

def import_metadata_for(imported_class)
if imported_class == Team
Team.with_assignment_context(params[:assignment_id]) do
return {
mandatory_fields: imported_class.mandatory_fields,
optional_fields: imported_class.optional_fields,
external_fields: imported_class.external_fields,
available_actions_on_dup: imported_class.available_actions_on_duplicate.map { |klass| klass.class.name }
}
end
end

{
mandatory_fields: imported_class.mandatory_fields,
optional_fields: imported_class.optional_fields,
external_fields: imported_class.external_fields,
available_actions_on_dup: imported_class.available_actions_on_duplicate.map { |klass| klass.class.name }
}
end

def team_import_defaults
return {} if params[:assignment_id].blank?

{ assignment_id: params[:assignment_id].to_i }
end
end
6 changes: 4 additions & 2 deletions app/controllers/project_topics_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ def index
render json: { message: 'Assignment ID is required!' }, status: :unprocessable_entity
elsif params[:topic_ids].nil?
@project_topics = ProjectTopic.where(assignment_id: params[:assignment_id])
.includes(signed_up_teams: { team: :users })
render json: @project_topics, status: :ok
else
@project_topics = ProjectTopic.where(assignment_id: params[:assignment_id], topic_identifier: params[:topic_ids])
.includes(signed_up_teams: { team: :users })
render json: @project_topics, status: :ok
end
# render json: {message: 'All selected topics have been loaded successfully.', project_topics: @stopics}, status: 200
Expand Down Expand Up @@ -85,11 +87,11 @@ def destroy

# Use callbacks to share common setup or constraints between actions.
def set_project_topic
@project_topic = ProjectTopic.find(params[:id])
@project_topic = ProjectTopic.includes(signed_up_teams: { team: :users }).find(params[:id])
end

# Only allow a list of trusted parameters through.
def project_topic_params
params.require(:project_topic).permit(:topic_identifier, :category, :topic_name, :max_choosers, :assignment_id)
params.require(:project_topic).permit(:topic_identifier, :category, :topic_name, :max_choosers, :assignment_id, :description, :link)
end
end
30 changes: 28 additions & 2 deletions app/controllers/teams_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,34 @@ def show
# POST /teams
# Creates a new team associated with the current user
def create
@team = Team.new(team_params)
@team = Team.new(normalized_team_params)
if @team.save
render json: @team, serializer: TeamSerializer, status: :created
else
render json: { errors: @team.errors.full_messages }, status: :unprocessable_entity
end
end

# PATCH/PUT /teams/:id
# Updates a specific team based on ID
def update
if @team.update(normalized_team_params)
render json: @team, serializer: TeamSerializer, status: :ok
else
render json: { errors: @team.errors.full_messages }, status: :unprocessable_entity
end
end

# DELETE /teams/:id
# Removes a specific team based on ID
def destroy
if @team.destroy
head :no_content
else
render json: { errors: @team.errors.full_messages }, status: :unprocessable_entity
end
end

# GET /teams/:id/members
# Lists all members of a specific team
def members
Expand Down Expand Up @@ -94,7 +114,13 @@ def set_team

# Whitelists the parameters allowed for team creation/updation
def team_params
params.require(:team).permit(:name, :type, :assignment_id)
params.require(:team).permit(:name, :type, :assignment_id, :parent_id)
end

def normalized_team_params
permitted = team_params.to_h
permitted[:parent_id] ||= permitted.delete('assignment_id') || permitted.delete(:assignment_id)
permitted
end

# Whitelists parameters required to add a team member
Expand Down
Loading
Loading