vegantech/sims

View on GitHub
app/models/user.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# == Schema Information
# Schema version: 20101101011500
#
# Table name: users
#
#  id               :integer(4)      not null, primary key
#  username         :string(255)
#  passwordhash     :binary
#  first_name       :string(255)
#  last_name        :string(255)
#  district_id      :integer(4)
#  created_at       :datetime
#  updated_at       :datetime
#  email            :string(255)
#  middle_name      :string(255)
#  suffix           :string(255)
#  salt             :string(255)     default("")
#  district_user_id :string(255)
#  token            :string(255)
#  roles_mask       :integer(4)      default(0)
#

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :omniauthable, :recoverable#, :registerable
#        :recoverable, :rememberable, :trackable, :validatable

  # Setup accessible (or protected) attributes for your model
  attr_accessible :email, :password, :password_confirmation, :remember_me, :district_id_for_login
  attr_accessor :district_id_for_login
  include FullName, Devise::LegacyPassword, Pageable, StatsInUse, Stats::User

  belongs_to :district
  has_many :user_school_assignments, :dependent => :destroy
  has_many :special_user_groups, :dependent => :destroy
  has_many :special_schools, :through => :special_user_groups, :source=>:school
  has_many :user_group_assignments, :dependent => :destroy, :inverse_of => :user
  has_many :groups, :through => :user_group_assignments, :order => :title
  has_many :principal_override_requests, :class_name => "PrincipalOverride", :foreign_key => :teacher_id
  has_many :principal_override_responses, :class_name => "PrincipalOverride", :foreign_key => :principal_id
  has_many :student_comments
  has_many :intervention_participants, :dependent => :delete_all
  has_many :interventions_as_participant, :through => :intervention_participants, :class_name => 'Intervention', :source => :intervention
  has_many :school_team_memberships
  has_many :school_teams, :through => :school_team_memberships
  has_many :team_consultations,:foreign_key => :requestor_id
  has_many :personal_groups, :dependent => :destroy
  has_many :staff_assignments
  has_many :checklists
  has_many :consultation_forms
  has_many :consultation_form_requests, :foreign_key =>:requestor_id
  has_many :custom_flags
  has_many :ignore_flags
  has_many :intervention_comments
  has_many :intervention_definitions
  has_many :interventions
  has_many :probe_definitions
  has_many :recommendations
  has_many :logs, :class_name => 'DistrictLog'


  attr_protected :district_id

  accepts_nested_attributes_for :staff_assignments, :allow_destroy => true, :reject_if => :duplicate_staff_assignment?
  accepts_nested_attributes_for :user_school_assignments, :allow_destroy => true

#  define_statistic :users_with_content
#  define_statistic :districts_with_users_with_content


  validates_presence_of :username
  validates_presence_of :password, :on => :create, :unless => :blank_password_ok?
#  validates_presence_of :passwordhash, :on => :update, :unless => :blank_password_ok?
  validates_uniqueness_of :username, :scope => :district_id
  validates_confirmation_of :password
  validate :validate_unique_user_school_assignments



  def authorized_groups_for_school(school,grade=nil)
    if all_students_in_school?(school)
      if grade
        school.groups.by_grade(grade)
      else
        school.groups
      end
    else
      if grade
        groups.by_school(school).by_grade(grade)
      else
        groups.by_school(school)
      end
    end
  end

  def cached_authorized_groups_for_school(school, grade=nil)
    @cached_groups_for_school ||= Hash.new
    @cached_groups_for_school[[school.id,grade]] ||= authorized_groups_for_school(school,grade).only_title_and_id
  end

  def filtered_groups_by_school(school,opts={})
    #opts can be grade and prompt and user?
    #default prompt is "*-Filter by Group"
    #the - separates id and prompt
    opts.stringify_keys!
    grade = opts['grade']
    grade = nil if grade == "*"
    grps = cached_authorized_groups_for_school(school,grade)

    if opts["user"].present?
      u_grp_ids = connection.select_values "select group_id from user_group_assignments where user_id = #{opts['user'].to_i}"
      grps = grps.select{ |u_group| u_grp_ids.include? u_group.id}
    end
    personal_groups.by_school_and_grade(school,grade) |grps
  end

  def filtered_members_by_school(school,opts={})
  #opts can be grade, user_id
  #blank grade defaults to *
  #blank user defaults to *
    opts.stringify_keys!
    opts.reverse_merge!( "grade"=>"*")
    grade = opts['grade']
    grade = nil if grade == "*"
    g_ids = cached_authorized_groups_for_school(school,grade).collect(&:id)
    User.find(:all,:select => 'distinct users.*',:joins => :groups ,:conditions=> {:groups=>{:id=>g_ids}}, :order => 'last_name, first_name')
  end

  def authorized_for?(controller)
    !new_record? && Role.has_controller?(controller.to_s,roles)
  end

  def grouped_principal_overrides
    overrides={}
    overrides[:user_requests]=principal_override_requests
    if principal?
      overrides[:principal_responses] = principal_override_responses
      overrides[:pending_requests]=PrincipalOverride.pending_for_principal(self)
    end

    overrides
  end

  def principal?
    !!(user_group_assignments.principal.first || special_user_groups.principal.first)
  end

  def orphaned_interventions_where_principal(school)
    return [] if school.blank?
    Intervention.find_all_by_active(true,:select => "distinct interventions.*",
                                    :joins => "inner join students on interventions.student_id = students.id and students.district_id = #{district_id}
        left outer join special_user_groups on  special_user_groups.user_id = #{self.id} and is_principal=true
        left outer join enrollments on enrollments.student_id = students.id and enrollments.school_id = #{school.id}
        left outer join ( groups_students inner join user_group_assignments on groups_students.group_id = user_group_assignments.group_id
          and user_group_assignments.user_id = #{self.id} and user_group_assignments.is_principal=true
          )
         on groups_students.student_id = students.id
        left outer join (intervention_participants ip  inner join users iu  on ip.user_id = iu.id ) on
        ip.intervention_id = interventions.id

        ",
           :conditions => "(interventions.end_date < '#{Date.today}' or iu.id is null or iu.district_id != students.district_id
           or not exists (  select 2 from special_user_groups sug where sug.user_id = iu.id and  ((iu.all_students = 1 )
           or ( sug.school_id = enrollments.school_id
           and ( sug.grade is null or sug.grade = enrollments.grade ) ))
           union select 2 from groups_students gs inner join user_group_assignments uga on gs.group_id =uga.group_id where gs.student_id = students.id and uga.user_id = iu.id
           )

           ) and ((iu.all_students = 1 ) or
          (special_user_groups.school_id = enrollments.school_id
          and ( special_user_groups.grade is null or special_user_groups.grade = enrollments.grade )
          ) or user_group_assignments.id is not null)
    ")#.select(&:orphaned?)


  end

 def self.remove_from_district(user_ids = [])
    user_ids = Array(user_ids).flatten.collect(&:to_i)
    return nil if user_ids.blank?
    User.connection.update("update users set username = concat(district_id,'-',username,'-',#{Time.now.usec}), roles_mask=0, passwordhash='disabled',district_id=NULL,email=NULL,encrypted_password='' where id in (#{user_ids.join(",")})")
    UserSchoolAssignment.delete_all(["user_id in (?)",user_ids])
    SpecialUserGroup.delete_all(["user_id in (?)",user_ids])
    UserGroupAssignment.delete_all(["user_id in (?)",user_ids])
    StaffAssignment.delete_all(["user_id in (?)",user_ids])
    SchoolTeamMembership.delete_all(["user_id in (?)",user_ids])
  end
  def remove_from_district
    User.remove_from_district(self[:id])
  end

  def roles=(roles)
    @roles = nil
    self.roles_mask = Role.roles_to_mask(roles)
  end


  def roles
    @roles ||= Role.mask_to_roles(roles_mask)
  end

  def role?(role)
    roles.include?(role.to_s)
  end

  def self.find_all_by_role(role,options = {})
    with_scope :find => options do
      find(:all,:conditions => ["roles_mask & ? ",1 << Role::ROLES.index(role)]) unless Role::ROLES.index(role).nil?
    end
  end

  def last_login
    @last_login ||=logs.success.order("updated_at desc").first.try(:updated_at)
  end

  def all_students_in_school?(school)
    all_students? || special_user_groups.all_students_in_school?(school)
  end

  def admin_of_school?(school)
    user_school_assignments.admin.exists?(:school_id => school.id)
  end

   def schools
    s=School.where(:district_id => district_id).order("schools.name")
    if all_schools_in_district?
      s
    else
      s.where("schools.id in (#{user_school_assignments.school_id.to_sql})
        or
        schools.id in (#{special_user_groups.school_id.to_sql})")
    end
  end

  def all_schools_in_district?
    all_students? || all_schools?
  end

  def students_for_school(school)
    s=Student.includes(:enrollments).where(:enrollments=>{:school_id => school}, :district_id=> district_id)
    if all_students?
      s
    else
      s.where("students.id in (#{special_user_groups.for_school(school).student_id.to_sql}) or
              students.id in (#{user_group_assignments.student_id_for_school(school).to_sql})
              ")
    end
  end

  def self.find_first_by_auth_conditions(conditions)
    conditions[:district_id] = conditions.delete(:district_id_for_login) unless conditions.keys == [:reset_password_token]
    super conditions
  end

  def self.find_for_googleapps_oauth(access_token, signed_in_resource=nil)
    data = access_token['info']

    if user = User.where(:email => data['email']).first
      return user
    end
  end

  def self.new_with_session(params, session)
    super.tap do |user|
      if data = session['devise.googleapps_data'] && session['devise.googleapps_data']['user_info']
        user.email = data['email']
      end
    end
  end

  def send_reset_password_instructions(use_key = false)
    if email.blank?
      errors.add(:base, 'User does not have email assigned in SIMS.  Contact your LSA for assistance')
    elsif !use_key && !district.try(:forgot_password)
      errors.add(:base, "This district does not support password recovery.  Contact your LSA for assistance")
    else
      super()
    end
  end

  def custom_interventions_enabled?
    district.custom_interventions.blank? ||
      district.custom_interventions == "only_author" ||
      district.custom_interventions == "one_off" ||
      (district.custom_interventions == 'content_admins' && roles.include?('content_admin') )
  end

protected

  def student_ids_where_principal(school_id)
    #TODO TEST THIS
    ##User.connection.select_values(User.find(10).send( :student_ids_where_principal,School.last.id))
 Student.send(:construct_finder_sql, :select => "students.id",
                  :joins =>
"left outer join special_user_groups on  special_user_groups.user_id = #{self.id}
         left outer join enrollments on enrollments.student_id = students.id
         left outer join ( groups_students inner join user_group_assignments on groups_students.group_id = user_group_assignments.group_id
           and user_group_assignments.user_id = #{self.id})
          on groups_students.student_id = students.id",
                  :conditions => "students.district_id = #{self.district_id} and enrollments.school_id = #{school_id}")



  end

  def blank_password_ok?
    email.present? && district && district.key.present?
  end

  def validate_unique_user_school_assignments
    validate_uniqueness_of_in_memory(
      user_school_assignments, [:school_id, :admin], 'Duplicate User.')
  end

  def duplicate_staff_assignment?(attributes)
     staff_assignments.reject(&:marked_for_destruction?).collect(&:school_id).include?(attributes[:school_id])
  end

  def duplicate_user_school_assignment?(attributes)
     staff_assignments.reject(&:marked_for_destruction?).collect{|r| [r.school_id, r.admin]}.include?([attributes[:school_id], attributes[:admin]])
  end
end