expertiza/expertiza

View on GitHub
app/models/assignment_team.rb

Summary

Maintainability
B
6 hrs
Test Coverage
A
90%
class AssignmentTeam < Team
  require File.dirname(__FILE__) + '/analytic/assignment_team_analytic'
  include AssignmentTeamAnalytic
  include Scoring

  belongs_to :assignment, class_name: 'Assignment', foreign_key: 'parent_id'
  has_many :review_mappings, class_name: 'ReviewResponseMap', foreign_key: 'reviewee_id'
  has_many :review_response_maps, foreign_key: 'reviewee_id'
  has_many :responses, through: :review_response_maps, foreign_key: 'map_id'
  # START of contributor methods, shared with AssignmentParticipant

  # Added for E1973, Team reviews.
  # Some methods prompt a reviewer for a user id. This method just returns the user id of the first user in the team
  # This is a very hacky way to deal with very complex functionality but the reasoning is this:
  # The reason this is being added is to give ReviewAssignment#reject_own_submission a way to reject the submission
  # Of the reviewer. If there are team reviews, there must be team submissions, so any team member's user id will do.
  # Hopefully, this logic applies if there are other situations where reviewer.user_id was called
  # EDIT: A situation was found which differs slightly. If the current user is on the team, we want to
  # return that instead for instances where the code uses the current user.
  def user_id
    @current_user.id if !@current_user.nil? && users.include?(@current_user)
    users.first.id
  end

  # E1973
  # stores the current user so that we can check them when returning the user_id
  def set_current_user(current_user)
    @current_user = current_user
  end

  # Whether this team includes a given participant or not
  def includes?(participant)
    participants.include?(participant)
  end

  # Get the parent of this class=>Assignment
  def parent_model
    'Assignment'
  end

  def self.parent_model(id)
    Assignment.find(id)
  end

  # Get the name of the class
  def fullname
    name
  end

  # Get the review response map
  def review_map_type
    'ReviewResponseMap'
  end

  # Prototype method to implement prototype pattern
  def self.prototype
    AssignmentTeam.new
  end

  # Use current object (AssignmentTeam) as reviewee and create the ReviewResponseMap record
  def assign_reviewer(reviewer)
    assignment = Assignment.find(parent_id)
    raise 'The assignment cannot be found.' if assignment.nil?

    ReviewResponseMap.create(reviewee_id: id, reviewer_id: reviewer.get_reviewer.id, reviewed_object_id: assignment.id, team_reviewing_enabled: assignment.team_reviewing_enabled)
  end

  # E-1973 If a team is being treated as a reviewer of an assignment, then they are the reviewer
  def get_reviewer
    self
  end

  # Evaluates whether any contribution by this team was reviewed by reviewer
  # @param[in] reviewer AssignmentParticipant object
  def reviewed_by?(reviewer)
    ReviewResponseMap.where('reviewee_id = ? && reviewer_id = ? && reviewed_object_id = ?', id, reviewer.get_reviewer.id, assignment.id).count > 0
  end

  # Topic picked by the team for the assignment
  # This method needs refactoring: it sounds like it returns a topic object but in fact it returns an id
  def topic
    SignedUpTeam.find_by(team_id: id, is_waitlisted: 0).try(:topic_id)
  end

  # Whether the team has submitted work or not
  def has_submissions?
    submitted_files.any? || submitted_hyperlinks.present?
  end

  # Get Participants of the team
  def participants
    users = self.users
    participants = []
    users.each do |user|
      participant = AssignmentParticipant.find_by(user_id: user.id, parent_id: parent_id)
      participants << participant unless participant.nil?
    end
    participants
  end
  alias get_participants participants

  # Delete the team
  def delete
    if self[:type] == 'AssignmentTeam'
      sign_up = SignedUpTeam.find_team_participants(parent_id.to_s).select { |p| p.team_id == id }
      sign_up.each(&:destroy)
    end
    super
  end

  # Delete Review response map
  def destroy
    review_response_maps.each(&:destroy)
    super
  end

  # Get the first member of the team
  def self.first_member(team_id)
    find_by(id: team_id).try(:participants).try(:first)
  end

  # Return the files residing in the directory of team submissions
  # Main calling method to return the files residing in the directory of team submissions
  def submitted_files(path = self.path)
    files = []
    files = files(path) if directory_num
    files
  end

  # REFACTOR BEGIN:: functionality of import,export, handle_duplicate shifted to team.rb
  # Import csv file to form teams directly
  def self.import(row, assignment_id, options)
    unless Assignment.find_by(id: assignment_id)
      raise ImportError, 'The assignment with the id "' + assignment_id.to_s + "\" was not found. <a href='/assignment/new'>Create</a> this assignment?"
    end

    @assignment_team = prototype
    Team.import(row, assignment_id, options, @assignment_team)
  end

  # Export the existing teams in a csv file
  def self.export(csv, parent_id, options)
    @assignment_team = prototype
    Team.export(csv, parent_id, options, @assignment_team)
  end

  # REFACTOR END:: functionality of import, export handle_duplicate shifted to team.rb

  # Copy the current Assignment team to the CourseTeam
  def copy(course_id)
    new_team = CourseTeam.create_team_and_node(course_id)
    new_team.name = name
    new_team.save
    copy_members(new_team)
  end

  # Add Participants to the current Assignment Team
  def add_participant(assignment_id, user)
    return if AssignmentParticipant.find_by(parent_id: assignment_id, user_id: user.id)

    AssignmentParticipant.create(parent_id: assignment_id, user_id: user.id, permission_granted: user.master_permission_granted)
  end

  def hyperlinks
    submitted_hyperlinks.blank? ? [] : YAML.safe_load(submitted_hyperlinks)
  end

  # Appends the hyperlink to a list that is stored in YAML format in the DB
  # @exception  If is hyperlink was already there
  #             If it is an invalid URL

  def files(directory)
    files_list = Dir[directory + '/*']
    files = []

    files_list.each do |file|
      if File.directory?(file)
        dir_files = files(file)
        dir_files.each { |f| files << f }
      end
      files << file
    end
    files
  end

  def submit_hyperlink(hyperlink)
    hyperlink.strip!
    raise 'The hyperlink cannot be empty!' if hyperlink.empty?

    hyperlink = 'http://' + hyperlink unless hyperlink.start_with?('http://', 'https://')
    # If not a valid URL, it will throw an exception
    response_code = Net::HTTP.get_response(URI(hyperlink))
    raise "HTTP status code: #{response_code}" if response_code =~ /[45][0-9]{2}/

    hyperlinks = self.hyperlinks
    hyperlinks << hyperlink
    self.submitted_hyperlinks = YAML.dump(hyperlinks)
    save
  end

  # Note: This method is not used yet. It is here in the case it will be needed.
  # @exception  If the index does not exist in the array

  def remove_hyperlink(hyperlink_to_delete)
    hyperlinks = self.hyperlinks
    hyperlinks.delete(hyperlink_to_delete)
    self.submitted_hyperlinks = YAML.dump(hyperlinks)
    save
  end

  # return the team given the participant
  def self.team(participant)
    return nil if participant.nil?

    team = nil
    teams_users = TeamsUser.where(user_id: participant.user_id)
    return nil unless teams_users

    teams_users.each do |teams_user|
      if teams_user.team_id == nil
        next
      end
      team = Team.find(teams_user.team_id)
      return team if team.parent_id == participant.parent_id
    end
    nil
  end

  # Export the fields
  def self.export_fields(options)
    fields = []
    fields.push('Team Name')
    fields.push('Team members') if options[:team_name] == 'false'
    fields.push('Assignment Name')
  end

  # Remove a team given the team id
  def self.remove_team_by_id(id)
    old_team = AssignmentTeam.find(id)
    old_team.destroy unless old_team.nil?
  end

  # Get the path of the team directory
  def path
    assignment.path + '/' + directory_num.to_s
  end

  # Set the directory num for this team
  def set_student_directory_num
    return if directory_num && (directory_num >= 0)

    max_num = AssignmentTeam.where(parent_id: parent_id).order('directory_num desc').first.directory_num
    dir_num = max_num ? max_num + 1 : 0
    update_attributes(directory_num: dir_num)
  end

  def received_any_peer_review?
    ResponseMap.where(reviewee_id: id, reviewed_object_id: parent_id).any?
  end

  # Returns the most recent submission of the team
  def most_recent_submission
    assignment = Assignment.find(parent_id)
    SubmissionRecord.where(team_id: id, assignment_id: assignment.id).order(updated_at: :desc).first
  end

  # E-1973 gets the participant id of the currently logged in user, given their user id
  # this method assumes that the team is the reviewer since it would be called on
  # AssignmentParticipant otherwise
  def get_logged_in_reviewer_id(current_user_id)
    participants.each do |participant|
      return participant.id if participant.user.id == current_user_id
    end
    nil
  end

  # determines if the team contains a participant who is currently logged in
  def current_user_is_reviewer?(current_user_id)
    get_logged_in_reviewer_id(current_user_id) != nil
  end

  # E2121 Refractor create_new_team
  def create_new_team(user_id, signuptopic)
    t_user = TeamsUser.create(team_id: id, user_id: user_id)
    SignedUpTeam.create(topic_id: signuptopic.id, team_id: id, is_waitlisted: 0)
    parent = TeamNode.create(parent_id: signuptopic.assignment_id, node_object_id: id)
    TeamUserNode.create(parent_id: parent.id, node_object_id: t_user.id)
  end
end