diff --git a/.travis.yml b/.travis.yml
index 3a255982b..5abbdc828 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,8 +24,11 @@ env:
notifications:
email:
recipients:
- - todd.sedano@sv.cmu.edu
- - rofaida.abdelaal@sv.cmu.edu
+ - anubhav.aeron@sv.cmu.edu
+ - bo.liu@sv.cmu.edu
+ - ching.lun.lin@sv.cmu.edu
+ - surya.kiran@sv.cmu.edu
+ - tushar.dadlani@sv.cmu.edu
# on_success: [always|never|change] # default: change
# on_failure: [always|never|change] # default: always
on_success: change
diff --git a/Gemfile b/Gemfile
index 3ed1d5117..4a5d3c4cb 100644
--- a/Gemfile
+++ b/Gemfile
@@ -68,6 +68,7 @@ end
group :development, :test do
gem 'launchy'
gem 'taps'
+
# gem 'rake'
# see this link for details on which gem to install for debugger
@@ -93,6 +94,8 @@ group :development, :test do
# gem 'autotest-growl' if RUBY_PLATFORM =~ /darwin/
# gem 'test-unit' #, '1.2.3' #Downgrading so that autotest, rspec will work
+ # gem for strong parameters
+ gem 'strong_parameters', '0.2.1'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 345f150f3..e425694d5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -242,6 +242,10 @@ GEM
rack (>= 1.0)
spreadsheet (0.8.3)
ruby-ole (>= 1.0)
+ strong_parameters (0.2.1)
+ actionpack (~> 3.0)
+ activemodel (~> 3.0)
+ railties (~> 3.0)
taps (0.3.24)
rack (>= 1.0.1)
rest-client (>= 1.4.0, < 1.7.0)
@@ -309,6 +313,7 @@ DEPENDENCIES
seedbank
shoulda
spreadsheet
+ strong_parameters (= 0.2.1)
taps
thin
vestal_versions!
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 86d573d5d..850e74d28 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -49,11 +49,10 @@ def current_person
end
end
-## In development, if you want to pretend to be a different user, you can set it easily here
-# def current_user
-# User.find_by_id 725 #Cecile
-## User.last
-# end
+ ## In development, if you want to pretend to be a different user, you can set it easily here
+ #def current_user
+ # User.find_by_id 1
+ #end
def authenticate_user!
if !current_user
diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb
index 6aaa1175f..0d649ff80 100644
--- a/app/controllers/courses_controller.rb
+++ b/app/controllers/courses_controller.rb
@@ -9,7 +9,6 @@ def index
@all_courses = true
@courses = Course.order("year DESC, semester DESC, number ASC").all
@courses = @courses.sort_by { |c| -c.sortable_value } # note the '-' is for desc sorting
-
@registered_for_these_courses_during_current_semester = current_person.registered_for_these_courses_during_current_semester
@teaching_these_courses_during_current_semester = current_person.teaching_these_courses_during_current_semester
end
@@ -129,7 +128,7 @@ def create
authorize! :create, Course
@last_offering = Course.last_offering(params[:course][:number])
if @last_offering.nil?
- @course = Course.new(:name => "New Course", :mini => "Both", :number => params[:course][:number])
+ @course = Course.new(course_params)
else
@course = @last_offering.copy_as_new_course
end
@@ -140,7 +139,6 @@ def create
respond_to do |format|
@course.updated_by_user_id = current_user.id if current_user
if @course.save
-
flash[:notice] = 'Course was successfully created.'
format.html { redirect_to edit_course_path(@course) }
format.xml { render :xml => @course, :status => :created, :location => @course }
@@ -164,10 +162,11 @@ def update
@course.configured_by_user_id = current_user.id
end
+ params.permit(:teachers => [])
params[:course][:faculty_assignments_override] = params[:teachers]
respond_to do |format|
@course.updated_by_user_id = current_user.id if current_user
- @course.attributes = params[:course]
+ @course.attributes = course_params
if @course.save
flash[:notice] = 'Course was successfully updated.'
format.html { redirect_back_or_default(course_path(@course)) }
@@ -250,4 +249,9 @@ def index_core
format.xml { render :xml => @courses }
end
end
+
+ def course_params
+ params.require(:course).permit(:number,:name,:short_name,:semester,:mini, :year)
+ end
+
end
diff --git a/app/controllers/deliverables_controller.rb b/app/controllers/deliverables_controller.rb
index 7a02eafd9..1729d566d 100644
--- a/app/controllers/deliverables_controller.rb
+++ b/app/controllers/deliverables_controller.rb
@@ -1,5 +1,4 @@
class DeliverablesController < ApplicationController
-
layout 'cmu_sv'
before_filter :authenticate_user!
@@ -26,8 +25,36 @@ def grading_queue_for_course
flash.now[:error] = I18n.t(:default_grading_rule_for_course)
end
+ # This will be used for the 'Assignments including' select one box
+ @assignments = Assignment.fetch_submittable_assignments_by_course_id @course.id
+
if (current_user.is_admin? || @course.faculty.include?(current_user))
- @deliverables = Deliverable.where(:course_id => @course.id).all
+ # By Default fetch data for my teams
+ team_selection = 1 # MY_TEAMS
+
+ # Check what the session value is ::
+ if( session[:team_selection] != nil )
+ team_selection = session[:team_selection]
+ #puts ">>> Team selection used from session preference: #{team_selection}"
+ end
+
+ # Remember that user selection overrides the session preference
+ if params[:teams] == "all_teams"
+ team_selection = 2 # ALL_TEAMS
+ end
+ if params[:teams] == 'my_teams'
+ team_selection = 1 # MY_TEAMS
+ end
+
+ # For future requests save this preference back in the session variable
+ session[:team_selection] = team_selection
+ #puts "<<< Team selection: #{team_selection} set to session as preference"
+
+ @team_deliverables = Deliverable.team_deliverables_for_grading_queue(@course, current_user, team_selection)
+ @individual_deliverables = Deliverable.individual_deliverables_for_grading_queue(@course, current_user, team_selection)
+
+ # Return all team deliverables and Individual deliverables for the current course in 1 object
+ @deliverables = [@team_deliverables.to_a, @individual_deliverables.to_a].flatten
else
has_permissions_or_redirect(:admin, root_path)
end
@@ -108,6 +135,8 @@ def new
@deliverable = Deliverable.new(:creator => current_user)
@courses = current_user.registered_for_these_courses_during_current_semester
+ # permitting parameters to protect against mass assignment
+ params.permit(:course_id)
if params[:course_id]
@deliverable.course_id = params[:course_id]
@assignments = Course.find(params[:course_id]).assignments.where(:is_submittable => true)
@@ -142,8 +171,9 @@ def edit
# POST /deliverables
# POST /deliverables.xml
def create
+
# Make sure that a file was specified
- @deliverable = Deliverable.new(params[:deliverable])
+ @deliverable = Deliverable.new(deliverable_params)
@deliverable.creator = current_user
@deliverable.is_team_deliverable ? @deliverable.update_team : @deliverable.team = nil
@@ -164,7 +194,7 @@ def create
end
return
end
- @attachment = DeliverableAttachment.new(params[:deliverable_attachment])
+ @attachment = DeliverableAttachment.new(deliverable_attachment_params)
@attachment.submitter = @deliverable.creator
@deliverable.attachment_versions << @attachment
@attachment.deliverable = @deliverable
@@ -205,12 +235,12 @@ def update
there_is_an_attachment = params[:deliverable_attachment][:attachment]
if there_is_an_attachment
- @attachment = DeliverableAttachment.new(params[:deliverable_attachment])
+ @attachment = DeliverableAttachment.new(deliverable_attachment_params)
@attachment.submitter = current_user
@deliverable.attachment_versions << @attachment
@attachment.deliverable = @deliverable
- if @attachment.valid? and @deliverable.valid? and @deliverable.update_attributes(params[:deliverable])
+ if @attachment.valid? and @deliverable.valid? and @deliverable.update_attributes(deliverable_params)
@deliverable.send_deliverable_upload_email(url_for(@deliverable))
flash[:notice] = 'Deliverable was successfully updated.'
redirect_to(@deliverable)
@@ -218,7 +248,7 @@ def update
render :action => "edit"
end
else
- if @deliverable.valid? and @deliverable.update_attributes(params[:deliverable])
+ if @deliverable.valid? and @deliverable.update_attributes(deliverable_params)
flash[:notice] = 'Deliverable was successfully updated.'
redirect_to(@deliverable)
else
@@ -289,14 +319,14 @@ def update_feedback
end
respond_to do |format|
- if flash[:error].blank?
- flash[:error] = nil
- flash[:notice] = 'Feedback successfully saved.'
- format.html {redirect_to(course_deliverables_path(@deliverable.course))}
- else
- flash[:error] = flash[:error].join("
")
- format.html { redirect_to(@deliverable) }
- end
+ if flash[:error].blank?
+ flash[:error] = nil
+ flash[:notice] = 'Feedback successfully saved.'
+ format.html {redirect_to(course_deliverables_path(@deliverable.course))}
+ else
+ flash[:error] = flash[:error].join("
")
+ format.html { redirect_to(@deliverable) }
+ end
end
end
@@ -313,4 +343,14 @@ def get_assignments_for_student
end
end
+ private
+ # Permitted params
+ def deliverable_params
+ params.require(:deliverable).permit(:assignment_id,:course_id)
+ end
+
+ def deliverable_attachment_params
+ params.require(:deliverable_attachment).permit(:comment,:attachment)
+ end
+
end
diff --git a/app/controllers/teams_controller.rb b/app/controllers/teams_controller.rb
index 2ca027cc8..f90fda697 100644
--- a/app/controllers/teams_controller.rb
+++ b/app/controllers/teams_controller.rb
@@ -181,8 +181,9 @@ def edit
# POST /courses/1/teams.xml
def create
if has_permissions_or_redirect(:staff, root_path)
+ params.permit(:persons => [])
params[:team][:members_override] = params[:persons]
- @team = Team.new(params[:team])
+ @team = Team.new(team_params)
@team.course_id = params[:course_id]
@course = Course.find(params[:course_id])
@@ -214,7 +215,7 @@ def update
update_course_faculty_label
respond_to do |format|
- @team.attributes = params[:team]
+ @team.attributes = team_params
if @team.save(params[:team])
flash[:notice] = 'Team was successfully updated.'
format.html { redirect_to(course_teams_path(@team.course)) }
@@ -291,6 +292,7 @@ def peer_evaluation_update
def update_course_faculty_label
+ params.permit(:primary_faculty_lable,:secondary_faculty_label)
@course = Course.find(params[:course_id])
if @course.primary_faculty_label != params[:primary_faculty_label] || @course.secondary_faculty_label != params[:seconday_faculty_label] then
@course.primary_faculty_label = params[:primary_faculty_label]
@@ -298,4 +300,10 @@ def update_course_faculty_label
@course.save
end
end
+
+ private
+
+ def team_params
+ params.require(:team).permit(:name,:section,:primary_faculty_id,:secondary_faculty_id,:email)
+ end
end
diff --git a/app/models/assignment.rb b/app/models/assignment.rb
index e78232435..6b7c72c88 100644
--- a/app/models/assignment.rb
+++ b/app/models/assignment.rb
@@ -127,4 +127,8 @@ def set_due_date date, hour, minute
self.due_date = "#{self.date} #{self.hour}:#{self.minute}"
end
+ def self.fetch_submittable_assignments_by_course_id course_id
+ Assignment.where(:course_id => course_id, :is_submittable => 't').order('id ASC')
+ end
+
end
diff --git a/app/models/course.rb b/app/models/course.rb
index f28c043d3..8ad6d2ea7 100644
--- a/app/models/course.rb
+++ b/app/models/course.rb
@@ -30,6 +30,7 @@
# Course has grading rules. These include grading cut_offs for grade's like A,A-,B+ etc.
class Course < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
has_many :teams
belongs_to :course_number
has_many :pages, :order => "position"
diff --git a/app/models/deliverable.rb b/app/models/deliverable.rb
index d209a7b1b..e5c457057 100644
--- a/app/models/deliverable.rb
+++ b/app/models/deliverable.rb
@@ -34,6 +34,7 @@
class Deliverable < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
belongs_to :team
belongs_to :course
@@ -334,4 +335,23 @@ def inaccurate_course_and_assignment_check
end
end
end
+
+ # This method fetches team deliverables for the specified course and filter selections
+ def self.team_deliverables_for_grading_queue(course, current_user, team_selection)
+ sql_query = DeliverableQueryHelper.generate_query_for_team_deliverables(course, current_user, team_selection)
+ return execute_custom_sql(sql_query)
+ end
+
+ # This method fetches individual deliverables for the specified course and filter selections
+ def self.individual_deliverables_for_grading_queue(course, current_user, team_selection)
+ sql_query = DeliverableQueryHelper.generate_query_for_individual_deliverables(course, current_user, team_selection)
+ return execute_custom_sql(sql_query)
+ end
+
+ # This is a utility method to execute a fully formed custom SQL query
+ def self.execute_custom_sql(sql_query_str)
+ sql = self.sanitize_sql([sql_query_str])
+
+ return self.connection.execute(sql)
+ end
end
diff --git a/app/models/deliverable_attachment.rb b/app/models/deliverable_attachment.rb
index 72d76f90b..14f787941 100644
--- a/app/models/deliverable_attachment.rb
+++ b/app/models/deliverable_attachment.rb
@@ -1,4 +1,5 @@
class DeliverableAttachment < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
set_table_name "deliverable_attachment_versions"
belongs_to :submitter, :class_name => "User", :foreign_key => "submitter_id"
diff --git a/app/models/deliverable_query_helper.rb b/app/models/deliverable_query_helper.rb
new file mode 100644
index 000000000..552b3d870
--- /dev/null
+++ b/app/models/deliverable_query_helper.rb
@@ -0,0 +1,121 @@
+# This class is used to generate the Query that helps to fetch the result set
+# for Grading Queue page for a professor or TA.
+#
+# Author - Surya Kiran
+# Change log:
+# 10/27 - Initial Add
+# 11/06 - Improving query performance for team and individual deliverables
+# 11/06 - Adding code comments
+#
+
+class DeliverableQueryHelper
+ # This method will be called from the Model Class to generate the team deliverables query.
+ # Depending on what options were selected for fetching the results, it calls the
+ # get_team_deliverables_query method to prepare the actual custom SQL query.
+ def self.generate_query_for_team_deliverables(course, current_user, team_selection)
+ if team_selection == TeamSelection::MY_TEAMS
+ faculty_id = current_user.id
+
+ where_clause = " and (teams.primary_faculty_id = #{faculty_id} or teams.secondary_faculty_id = #{faculty_id}) "
+
+ get_team_deliverables_query(course.id, where_clause)
+ elsif team_selection == TeamSelection::ALL_TEAMS
+ get_team_deliverables_query(course.id)
+ end
+ end
+
+ # This method will be called from the Model Class to generate the individual deliverables query.
+ # Depending on what options were selected for fetching the results, it calls the
+ # get_individual_deliverables_query method to prepare the actual custom SQL query.
+ def self.generate_query_for_individual_deliverables(course, current_user, team_selection)
+ if team_selection == TeamSelection::MY_TEAMS
+ where_clause = " where advisor_name = '#{current_user.human_name}' "
+
+ get_individual_deliverables_query(course.id, where_clause)
+ elsif team_selection == TeamSelection::ALL_TEAMS
+ get_individual_deliverables_query(course.id)
+ end
+ end
+
+ # This method populates the query based on the course and any custom where clause provided
+ def self.get_team_deliverables_query(course_id, append_where_clause = '')
+ sql_query = ' select distinct courses.name as course_name , assignments.task_number , ' +
+ ' assignments.name as deliverable_name , teams.name as owner_name , ' +
+ ' users.human_name as advisor_name , ' +
+ ' case when grades.is_student_visible = \'t\' then \'graded\' ' +
+ ' when grades.is_student_visible = \'f\' then \'drafted\' ' +
+ ' when grades.is_student_visible is null then \'ungraded\' end as grading_status , ' +
+ ' assignments.is_team_deliverable , deliverables.id as deliverable_id , ' +
+ ' deliverables.team_id , deliverables.course_id , deliverables.assignment_id , ' +
+ ' assignments.assignment_order ' +
+ ' from teams join deliverables on deliverables.team_id = teams.id ' +
+ ' and teams.course_id = deliverables.course_id ' +
+ ' join team_assignments on teams.id = team_assignments.team_id ' +
+ ' join assignments on deliverables.assignment_id = assignments.id ' +
+ ' and assignments.course_id = deliverables.course_id ' +
+ ' and assignments.course_id = teams.course_id' +
+ ' join courses on courses.id = deliverables.course_id ' +
+ ' and courses.id = assignments.course_id and courses.id = teams.course_id' +
+ ' left outer join users on users.id = coalesce(teams.primary_faculty_id, teams.secondary_faculty_id) ' +
+ ' left outer join grades on grades.course_id = deliverables.course_id ' +
+ ' and grades.assignment_id = deliverables.assignment_id ' +
+ ' and team_assignments.user_id = grades.student_id ' +
+ ' where courses.id = ' + course_id.to_s + ' and assignments.is_submittable = \'t\' ' +
+ ' and assignments.is_team_deliverable = \'t\' ' + append_where_clause +
+ ' order by task_number, assignments.assignment_order, advisor_name, owner_name'
+
+ return sql_query
+ end
+
+ # This method populates the query based on the course and any custom where clause provided
+ def self.get_individual_deliverables_query(course_id, append_where_clause = '')
+ sql_query = ' select * from ' +
+ ' (select student_deliverables.course_name , student_deliverables.task_number , ' +
+ ' student_deliverables.deliverable_name , student_deliverables.owner_name , ' +
+ ' student_coach_map.advisor_name , student_deliverables.grading_status , ' +
+ ' student_deliverables.is_team_deliverable , student_deliverables.deliverable_id , ' +
+ ' student_deliverables.team_id , student_deliverables.course_id , ' +
+ ' student_deliverables.assignment_id , student_deliverables.assignment_order ' +
+ ' from (select courses.name as course_name , assignments.task_number , ' +
+ ' assignments.name as deliverable_name , stud.id as student_id , ' +
+ ' stud.human_name as owner_name , ' +
+ ' case when grades.is_student_visible = \'t\' then \'graded\' ' +
+ ' when grades.is_student_visible = \'f\' then \'drafted\' ' +
+ ' when grades.is_student_visible is null then \'ungraded\' end as grading_status , ' +
+ ' assignments.is_team_deliverable , deliverables.id as deliverable_id , ' +
+ ' deliverables.team_id , deliverables.course_id , deliverables.assignment_id , ' +
+ ' assignments.assignment_order ' +
+ ' from users stud join deliverables on deliverables.creator_id = stud.id ' +
+ ' left outer join courses on courses.id = deliverables.course_id ' +
+ #' left outer join registrations on stud.id = registrations.user_id ' +
+ #' and registrations.course_id = courses.id ' +
+ ' left outer join assignments on assignments.course_id = courses.id ' +
+ ' and assignments.id = deliverables.assignment_id ' +
+ ' left outer join grades on grades.course_id = courses.id ' +
+ ' and grades.assignment_id = deliverables.assignment_id ' +
+ ' and grades.student_id = stud.id ' +
+ ' where courses.id = ' + course_id.to_s + ' and stud.is_student = \'t\' ' +
+ ' and assignments.is_submittable = \'t\' ' +
+ ' and assignments.is_team_deliverable = \'f\' ) student_deliverables ' +
+ ' left join (select courses.id as course_id, courses.name as course_name, ' +
+ ' prof.id as prof_id, prof.human_name as advisor_name, teams.id as team_id, ' +
+ ' teams.name as team_name, stud.id as stud_id, stud.human_name as stud_name ' +
+ ' from courses join teams on courses.id = teams.course_id ' +
+ ' join team_assignments on teams.id = team_assignments.team_id ' +
+ ' join users stud on stud.id = team_assignments.user_id ' +
+ ' join users prof on prof.id = coalesce(teams.primary_faculty_id, teams.secondary_faculty_id) ' +
+ ' where stud.is_student = \'t\' and courses.id = ' + course_id.to_s + ') student_coach_map ' +
+ ' on student_deliverables.student_id = student_coach_map.stud_id ' +
+ ' and student_deliverables.course_id = student_coach_map.course_id ' +
+ ' order by task_number, assignment_order, advisor_name, owner_name) indi_deliv ' + append_where_clause
+
+ return sql_query
+ end
+end
+
+# ENum for Team Selection
+class TeamSelection
+ MY_TEAMS = 1
+ ALL_TEAMS = 2
+end
+
diff --git a/app/models/faculty_assignment.rb b/app/models/faculty_assignment.rb
index 6676f3de2..21e7f5635 100644
--- a/app/models/faculty_assignment.rb
+++ b/app/models/faculty_assignment.rb
@@ -1,4 +1,5 @@
class FacultyAssignment < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
belongs_to :user
belongs_to :course
diff --git a/app/models/registered_course.rb b/app/models/registered_course.rb
index eb8cd3c3b..586fef6a4 100644
--- a/app/models/registered_course.rb
+++ b/app/models/registered_course.rb
@@ -1,4 +1,5 @@
class RegisteredCourse < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
belongs_to :person
belongs_to :course
end
diff --git a/app/models/registration.rb b/app/models/registration.rb
index c521f50b1..c72d13066 100644
--- a/app/models/registration.rb
+++ b/app/models/registration.rb
@@ -1,4 +1,5 @@
class Registration < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
belongs_to :user
belongs_to :course
end
diff --git a/app/models/team.rb b/app/models/team.rb
index 1eee150fb..d65788698 100644
--- a/app/models/team.rb
+++ b/app/models/team.rb
@@ -1,4 +1,5 @@
class Team < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
belongs_to :course
belongs_to :primary_faculty, :class_name => 'User', :foreign_key => "primary_faculty_id"
belongs_to :secondary_faculty, :class_name => 'User', :foreign_key => "secondary_faculty_id"
diff --git a/app/models/team_assignment.rb b/app/models/team_assignment.rb
index 6808b7573..9050e6a6c 100644
--- a/app/models/team_assignment.rb
+++ b/app/models/team_assignment.rb
@@ -1,4 +1,6 @@
class TeamAssignment < ActiveRecord::Base
+ include ActiveModel::ForbiddenAttributesProtection
+
belongs_to :user
belongs_to :team
diff --git a/app/views/courses/show.html.erb b/app/views/courses/show.html.erb
index 88ae7e970..a5f2a5061 100644
--- a/app/views/courses/show.html.erb
+++ b/app/views/courses/show.html.erb
@@ -76,8 +76,8 @@