Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2443. Reimplement grades_controller #97

Open
wants to merge 42 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c9058e7
Adding CreateTeams table
HasiniChenchala2 Apr 21, 2024
8ad681c
Adding Assignment model
HasiniChenchala2 Apr 21, 2024
d79daaa
Adding AssignmentAnalytic model file
HasiniChenchala2 Apr 21, 2024
8e207ab
Adding AssignmentDueDate model file
HasiniChenchala2 Apr 21, 2024
e32515e
Adding Assignment Helper file
HasiniChenchala2 Apr 21, 2024
c2c6c6f
Adding Assignment Participant model file
HasiniChenchala2 Apr 21, 2024
717f46b
Adding AssignmentTeam model file
HasiniChenchala2 Apr 21, 2024
d47326d
Merge branch 'main' of https://github.com/Shravsssss/reimplementation…
HasiniChenchala2 Apr 21, 2024
6538a72
Adding AssignmentTeamAnalytic model file
HasiniChenchala2 Apr 21, 2024
416c9bb
Adding AuthorizationHelper file
HasiniChenchala2 Apr 21, 2024
b1eb501
Adding DueDate Model file
HasiniChenchala2 Apr 21, 2024
d841e0c
Reimplementation of GradesController to perform CRUD operations
HasiniChenchala2 Apr 21, 2024
d133955
Adding GradesControllerSpec File to test CRUD operations
HasiniChenchala2 Apr 21, 2024
6de890b
Adding GradesHelper File
HasiniChenchala2 Apr 21, 2024
12bcc3d
Adding ReviewResponseMap Model File
HasiniChenchala2 Apr 21, 2024
cb689dd
Adding Routes File to Create routes file for API routing
HasiniChenchala2 Apr 21, 2024
06a20dd
Adding SignedUpTeam Model File
HasiniChenchala2 Apr 21, 2024
47eb6ee
Adding Scoring File
HasiniChenchala2 Apr 21, 2024
385acff
Adding TeamsUser Model File
HasiniChenchala2 Apr 21, 2024
a03916a
Adding StudentTaskHelper File
HasiniChenchala2 Apr 21, 2024
59b7673
Adding CreateDeadlineTypes Migration File
HasiniChenchala2 Apr 21, 2024
f76a60b
Adding CreateLatePolicies Migration File
HasiniChenchala2 Apr 21, 2024
f620703
Adding CreateDueDates Migration File
HasiniChenchala2 Apr 21, 2024
9b96ca7
ChangingAssignmentIDInDueDatesToParentID Migration File
HasiniChenchala2 Apr 21, 2024
55d167b
Adding CreateTeams Migration File
HasiniChenchala2 Apr 21, 2024
f08370d
ChangingAssignmentIdInParticipantsToParentId Migration File
HasiniChenchala2 Apr 21, 2024
3688a13
AddingUsedInRoundToAQ Migration File
HasiniChenchala2 Apr 21, 2024
83509bf
ChangingAssignmentIdInParticipantsToParentID Migration File
HasiniChenchala2 Apr 21, 2024
c088cbb
UpdatingParticipantsTable Migration File
HasiniChenchala2 Apr 21, 2024
9f27667
CreatingTeamsUsers Migration File
HasiniChenchala2 Apr 21, 2024
e25f2f2
Adding PenaltyHelper file
HasiniChenchala2 Apr 21, 2024
67bbb51
Adding DataBase Schema
HasiniChenchala2 Apr 21, 2024
a408451
Adding Auth helper
HasiniChenchala2 Apr 23, 2024
35c734e
Adding APIs
HasiniChenchala2 Apr 23, 2024
c3345f1
Adding APIspecTests
HasiniChenchala2 Apr 23, 2024
bc807fd
Adding SwaggerContentForGradesAPIs
HasiniChenchala2 Apr 23, 2024
edc4bc8
Adding APIRoutes
HasiniChenchala2 Apr 23, 2024
7d1dbb3
Adding DeadlineType
HasiniChenchala2 Apr 23, 2024
2ad7897
Resolved problem with Swagger UI caused due to default localhost
HasiniChenchala2 Apr 30, 2024
ad19d74
Added comments to grades_controller
HasiniChenchala2 Apr 30, 2024
6c29ecf
Added requestBody to put and post requests
HasiniChenchala2 Apr 30, 2024
74e5627
Removed commented code
HasiniChenchala2 Apr 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
264 changes: 264 additions & 0 deletions app/controllers/api/v1/grades_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
require_dependency 'scoring'
require_dependency 'grades_helper'

class Api::V1::GradesController < ApplicationController
# before_action :set_assignment, only: [:view, :view_my_scores, :view_team]
# rescue_from ActiveRecord::RecordNotFound, with: :assignment_not_found

# Include necessary modules
include Scoring
include PenaltyHelper
include StudentTaskHelper
include AssignmentHelper
include GradesHelper
include AuthorizationHelper

# Handles actions allowed based on user privileges and context
# Example API call: GET 'api/v1/grades/action_allowed?action=view_my_scores&id=1'
# Example URL: http://localhost:3002/api/v1/grades/1/action_allowed?action=view_scores&id=1
def action_allowed
case params[:action]
when 'view_scores'
if current_user_has_student_privileges? &&
are_needed_authorizations_present?(params[:id], 'reader', 'reviewer') &&
self_review_finished?
render json: { allowed: true }
else
render json: { allowed: false, error: 'Unauthorized' }, status: :forbidden
end
when 'view_team'
if current_user_is_a? 'Student'
participant = AssignmentParticipant.find_by(id: params[:id])
if participant && current_user_is_assignment_participant?(participant.assignment.id)
render json: { allowed: true }
else
render json: { allowed: false, error: 'Unauthorized' }, status: :forbidden
end
else
render json: { allowed: true }
end
else
if current_user_has_ta_privileges?
render json: { allowed: true }
else
render json: { allowed: false, error: 'Unauthorized' }, status: :forbidden
end
end
end

# Retrieves view data for an assignment
# Example API call: GET /api/v1/grades/:id/view
# Example URL: http://localhost:3002/api/v1/grades/4/view
def view
assignment = Assignment.find(params[:id])
questionnaires = assignment.questionnaires
if assignment.num_review_rounds > 1
questions = retrieve_questions questionnaires, assignment.id
else
questions = {}
questionnaires.each do |questionnaire|
questions[questionnaire.symbol] = questionnaire.questions
end
end
scores = review_grades(assignment, questions)
num_reviewers_assigned_scores = scores[:teams].length
averages = vector(scores)
avg_of_avg = mean(averages)
render json: { scores: scores, averages: averages, avg_of_avg: avg_of_avg, num_reviewers_assigned_scores: num_reviewers_assigned_scores }
end

# Retrieves scores view for a participant
# Example API call: GET /api/v1/grades/:id/view_scores
# Example URL: http://localhost:3002/api/v1/grades/1/view_scores
def view_scores
participant = AssignmentParticipant.find(params[:id])
assignment = participant.assignment
topic_id = SignedUpTeam.topic_id(participant.assignment.id, participant.user_id)
stage = assignment.current_stage(topic_id)

render json: { participant: participant }
end

# Retrieves team view for a participant
# Example API call: GET /api/v1/grades/:id/view_team
# Example URL: http://localhost:3002/api/v1/grades/1/view_team
def view_team
participant = AssignmentParticipant.find(params[:id])
assignment = participant.assignment
team = participant.team
questionnaires = assignment.questionnaires
questions = retrieve_questions(questionnaires, assignment.id)
pscore = participant_scores(participant, questions)

render json: { participant: participant, assignment: assignment, team: team, questions: questions, pscore: pscore }
end

# Edits data for a participant
# Example API call: GET /api/v1/grades/:id/edit
def edit
assignment = AssignmentParticipant.find(params[:id])
questions = list_questions(assignment)
scores = participant_scores(participant, questions)

render json: {
participant: participant,
assignment: assignment,
questions: questions,
scores: scores
}, status: :ok
end

# Retrieves data for a participant
# Example API call: GET /api/v1/grades/:id
def show
participant = AssignmentParticipant.find(params[:id])
assignment = participant.assignment
questions = list_questions(assignment)
scores = participant_scores(participant, questions)

render json: { participant: participant, assignment: assignment, questions: questions, scores: scores }
end

# Updates data for a participant
# Example API call: PUT /api/v1/grades/:id/update
def update
participant = AssignmentParticipant.find(params[:id])
total_score = params[:total_score]
unless format('%.2f', total_score) == params[:participant][:grade]
participant.update_attribute(:grade, params[:participant][:grade])
message = if participant.grade.nil?
"The computed score will be used for #{participant.user.name}."
else
"A score of #{params[:participant][:grade]}% has been saved for #{participant.user.name}."
end
end
render json: { message: message }
end

# Saves grade and comment for submission
# Example API call: POST /api/v1/grades/:id/save_grade_and_comment_for_submission
def save_grade_and_comment_for_submission
participant = AssignmentParticipant.find(params[:id])
@team = participant.team
@team.grade_for_submission = params[:grade]
@team.comment_for_submission = params[:comment]
begin
@team.save!
render json: { success: "Grade '#{params[:grade]}' and comment '#{params[:comment]}' for submission successfully saved." }
rescue StandardError => e
render json: { error: e.message }, status: :unprocessable_entity
end
end

private

# Sets assignment
def set_assignment
@assignment = Assignment.find(params[:id])
end

# Lists questions for an assignment
def list_questions(assignment)
questions = {}
questionnaires = assignment.questionnaires
questionnaires.each do |questionnaire|
questions[questionnaire.symbol] = questionnaire.questions
end
questions
end

# Checks if self review is finished
def self_review_finished?
participant = Participant.find(params[:id])
assignment = participant.try(:assignment)
self_review_enabled = assignment.try(:is_selfreview_enabled)
not_submitted = unsubmitted_self_review?(participant.try(:id))
if self_review_enabled
!not_submitted
else
true
end
end
end


# def assign_all_penalties
# participant = AssignmentParticipant.find(params[:id])
# all_penalties = {
# submission: params[:penalties][:submission],
# review: params[:penalties][:review],
# meta_review: params[:penalties][:meta_review],
# total_penalty: @total_penalty
# }
# participant.update(all_penalties: all_penalties)
# { success: true, message: 'All penalties assigned successfully' }
# rescue StandardError => e
# { error: e.message }
# end


# GET /api/v1/grades/:id/instructor_review
# def instructor_review
# participant = AssignmentParticipant.find(params[:id])
# reviewer = AssignmentParticipant.find_or_create_by(user_id: session[:user].id, parent_id: participant.assignment.id)
# reviewer.set_handle if reviewer.new_record?
# reviewee = participant.team
# revieweemapping = ReviewResponseMap.find_or_create_by(reviewee_id: reviewee.id, reviewer_id: reviewer.id, reviewed_object_id: participant.assignment.id)
#
# render json: { participant: participant, session_user: session[:user] }
# end

# def populate_view_model(questionnaire)
# vm = VmQuestionResponse.new(questionnaire, @assignment, @round)
# vmquestions = questionnaire.questions
# vm.add_questions(vmquestions)
# vm.add_team_members(@team)
# qn = AssignmentQuestionnaire.where(assignment_id: @assignment.id, used_in_round: 2).size >= 1
# vm.add_reviews(@participant, @team, @assignment.varying_rubrics_by_round?)
# vm.calculate_metrics
# vm.to_json # Convert the view model object to JSON for API response
# end

# def redirect_when_disallowed
# if @participant.assignment.max_team_size > 1
# team = @participant.team
# unless team.nil? || (team.user_id? session[:user])
# { error: 'You are not on the team that wrote this feedback' } # Return error message as JSON
# end
# else
# reviewer = AssignmentParticipant.where(user_id: session[:user].id, parent_id: @participant.assignment.id).first
# unless current_user_id?(reviewer.try(:user_id))
# { error: 'Unauthorized access' } # Return error message as JSON
# end
# end
# { success: true } # Return success message as JSON if conditions are met
# end

# def fetch_pscore_data(assignment)
#
# end
#
# def make_chart
# assignment = Assignment.find(params[:id])
# grades_bar_charts = {}
# participant_score_types = %i[metareview feedback teammate]
# pscore = fetch_pscore_data(assignment) # You need to implement a method to fetch pscore data
#
# if pscore[:review]
# scores = []
# if assignment.varying_rubrics_by_round?
# (1..assignment.rounds_of_reviews).each do |round|
# responses = pscore[:review][:assessments].select { |response| response.round == round }
# scores.concat(score_vector(responses, "review#{round}"))
# scores -= [-1.0]
# end
# grades_bar_charts[:review] = bar_chart(scores)
# else
# grades_bar_charts[:review] = charts(:review) # Assuming charts method is defined elsewhere
# end
# end
#
# participant_score_types.each { |symbol| grades_bar_charts[symbol] = charts(symbol) }
#
# grades_bar_charts.to_json
# end
11 changes: 11 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
class ApplicationController < ActionController::API
include JwtToken
def are_needed_authorizations_present?(id, *authorizations)
participant = Participant.find_by(id: id)
return false if participant.nil?

authorization = participant.authorization
!authorizations.include?(authorization)
end

def current_user_id?(user_id)
current_user.try(:id) == user_id
end
end
102 changes: 102 additions & 0 deletions app/helpers/assignment_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
module AssignmentHelper
def course_options(instructor = nil)
courses = []
if session[:user].role.name == 'Teaching Assistant'
ta = Ta.find(session[:user].id)
ta.ta_mappings.each { |mapping| courses << Course.find(mapping.course_id) }
# If a TA created some courses before, s/he can still add new assignments to these courses.
# Only those courses should be shown in the dropdown list of courses, the assignment is part of and the instructor or TA has access to.
courses << Course.where(instructor_id: ta.id)
# Administrator and Super-Administrator can see all courses
elsif session[:user].role.name == 'Administrator' || session[:user].role.name == 'Super-Administrator'
courses << Course.all
elsif session[:user].role.name == 'Instructor'
courses << Course.where(instructor_id: session[:user].id)
# instructor can see courses his/her TAs created
ta_ids = []
instructor = Instructor.find(session[:user].id)
ta_ids << instructor.my_tas
ta_ids.flatten!
ta_ids.each do |ta_id|
ta = Ta.find(ta_id)
ta.ta_mappings.each { |mapping| courses << Course.find(mapping.course_id) }
end
end
courses.flatten!
options = []
if session[:user].role.name == 'Administrator' || session[:user].role.name == 'Super-Administrator' || session[:user].role.name == 'Instructor'
options << ['-----------', nil]
end
courses.each do |course|
options << [course.name, course.id]
end
options.uniq.sort
end

# round=0 added by E1450
def questionnaire_options(type)
questionnaires = Questionnaire.where(['private = 0 or instructor_id = ?', session[:user].id]).order('name')
options = []
questionnaires.select { |x| x.type == type }.each do |questionnaire|
options << [questionnaire.name, questionnaire.id]
end
options
end

def review_strategy_options
review_strategy_options = []
Assignment::REVIEW_STRATEGIES.each do |strategy|
review_strategy_options << [strategy.to_s, strategy.to_s]
end
review_strategy_options
end

# retrieve or create a due_date
# use in views/assignment/edit.html.erb
# Be careful it is a tricky method, for types other than "submission" and "review",
# the parameter "round" should always be 0; for "submission" and "review" if you want
# to get the due date for round n, the parameter "round" should be n-1.
def due_date(assignment, type, round = 0)
due_dates = assignment.find_due_dates(type)

due_dates.delete_if { |due_date| due_date.due_at.nil? }
due_dates.sort! { |x, y| x.due_at <=> y.due_at }

if due_dates[round].nil? || round < 0
due_date = AssignmentDueDate.new
due_date.deadline_type_id = DeadlineType.find_by(name: type).id
# creating new round
due_date.submission_allowed_id = AssignmentDueDate.default_permission(type, 'submission_allowed')
due_date.review_allowed_id = AssignmentDueDate.default_permission(type, 'can_review')
due_date.review_of_review_allowed_id = AssignmentDueDate.default_permission(type, 'review_of_review_allowed')
due_date
else
due_dates[round]
end
end

def get_data_for_list_submissions(team)
teams_users = TeamsUser.where(team_id: team.id)
topic = SignedUpTeam.where(team_id: team.id).first.try :topic
topic_identifier = topic.try :topic_identifier
topic_name = topic.try :topic_name
users_for_curr_team = []
participants = []
teams_users.each do |teams_user|
user = User.find(teams_user.user_id)
users_for_curr_team << user
participants << Participant.where(['parent_id = ? AND user_id = ?', @assignment.id, user.id]).first
end
topic_identifier ||= ''
topic_name ||= ''
[topic_identifier, topic_name, users_for_curr_team, participants]
end

def get_team_name_color_in_list_submission(team)
if team.try(:grade_for_submission) && team.try(:comment_for_submission)
'#cd6133' # brown. submission grade has been assigned.
else
'#0984e3' # submission grade is not assigned yet.
end
end
end