class Shixun < ApplicationRecord # TODO: ES feature # include Searchable::Shixun # status: 0:编辑 1:申请发布 2:正式发布 3:关闭 -1:软删除 # hide_code: 隐藏代码窗口 # code_hidden: 隐藏代码目录 has_many :challenges, dependent: :destroy has_many :challenge_tags, through: :challenges has_many :myshixuns, :dependent => :destroy has_many :shixun_members, dependent: :destroy has_many :users, through: :shixun_members has_many :discusses, as: :dis, dependent: :destroy has_many :evaluate_records, dependent: :destroy has_many :shixun_mirror_repositories has_many :mirror_repositories, through: :shixun_mirror_repositories has_many :shixun_schools, :dependent => :destroy has_many :schools, :through => :shixun_schools has_many :shixun_tag_repertoires, dependent: :destroy has_many :tag_repertoires, through: :shixun_tag_repertoires has_one :first_shixun_tag_repertoire, class_name: 'ShixunTagRepertoire' has_one :first_tag_repertoire, through: :first_shixun_tag_repertoire, source: :tag_repertoire #实训的关卡 has_many :exercise_shixun_challenges, :dependent => :destroy has_many :exercise_bank_shixun_challenges, :dependent => :destroy # 注意:这个地方是一张关联表,关联三张表(多对多关系) has_many :stage_shixuns, dependent: :destroy has_many :stages, through: :stage_shixuns has_many :subjects, through: :stage_shixuns has_one :shixun_info, dependent: :destroy belongs_to :user # 实训服务配置 has_many :shixun_service_configs, :dependent => :destroy scope :search_by_name, ->(keyword) { where("name like ? or description like ? ", "%#{keyword}%", "%#{keyword}%") } #scope :include_user, ->(user_id) { where(id: Myshixun.where(user_id: user_id).pluck(:shixun_id))} scope :filter_tag, ->(tag_level, tag_id) { case tag_level when 1 #大类 where(id: Repertoire.find(tag_id).tag_repertoires.joins(:shixun_tag_repertoires).pluck("shixun_tag_repertoires.shixun_id")) when 2 #子类 where(id: SubRepertoire.find(tag_id).tag_repertoires.joins(:shixun_tag_repertoires).pluck("shixun_tag_repertoires.shixun_id")) when 3 #tag where(id: TagRepertoire.find(tag_id).shixun_tag_repertoires.pluck(:shixun_id)) end } scope :visible, -> { where.not(status: -1) } scope :published, lambda{ where(status: 2) } scope :unhidden, lambda{ where(hidden: 0, status: 2) } scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) } scope :find_by_ids,lambda{|k| where(id:k)} # REDO:  def propaedeutics shixun_info.try(:propaedeutics) end def description shixun_info.try(:description) end def evaluate_script shixun_info.try(:evaluate_script) end # 实训用户tag def user_tags_name(user = User.current) Shixun.joins(challenges: [:challenge_tags, :games]).where(games: {status: 2, user_id: user.id}, shixuns: {id:id}) .pluck("challenge_tags.name").uniq end # 实训关卡tag def challenge_tags_name ChallengeTag.where(challenge_id: challenges.pluck(:id)).pluck(:name).uniq end def repo_path "#{repo_name}.git" end # 实训对应的镜像主类别名(已选) def main_mirror_name mirror_repositories.published_main_mirror.first.try(:type_name) || "" end def main_mirror_id mirror_repositories.published_main_mirror.first.try(:id) || -1 end # 实训对应的镜像小类别名(已选) def small_mirror_name mirror_repositories.published_small_mirror.map(&:type_name) end def small_mirror_id mirror_repositories.published_small_mirror.map(&:id) end # 实训镜像名 def mirror_name names = mirror_repositories.map(&:type_name) names.blank? ? '' : names end def script_tag return unless mirror_script_id MirrorScript.find_by_id(mirror_script_id) end def standrad_script mirrors_id = mirror_repositories.map(&:id) mirror_scripts = MirrorScript.where(mirror_repository_id: mirrors_id) mirror_scripts.map { |ms| { scptname: ms.script_type, id: ms.id } } end def owner User.find(self.user_id) end def is_published? status > 1 end # 当前用户开启的实训 def current_myshixun(user_id) myshixuns.find_by(user_id: user_id) end # 实训技术平台 def show_shixun_mirror mirror_repositories.map(&:type_name).join(';') end # 实训评分 cnt-评分次数 sum-总评分 def shixun_preference game_star_info = Game.find_by_sql("select g.star from (games g left join (myshixuns m join shixuns s on s.id = m.shixun_id) on m.id = g.myshixun_id) where g.star != 0 and s.id = #{self.id}") if game_star_info.present? cnt = game_star_info.count sum = game_star_info.sum(&:star).to_f (sum / cnt.to_f).round(1) else 5.0 end end # 实训评分信息 # return [实训评分, 5星评分比例, 4星评分比例, 3星评分比例, 2星评分比例, 1星评分比例] def shixun_preference_info game_star_info = Game.joins(challenge: :shixun).where(shixuns: {id: id}).where.not(games: {star: 0}).pluck(:star) star_info = [] if game_star_info.present? 5.downto(1) do |i| star_info << ((game_star_info.select{|s| s == i}.count / game_star_info.count.to_f) * 100).round end sum = star_info.sum max = star_info.max # 四舍五入引起5星比例超过100%, 将最大的比例的评分做出调整 if sum > 100 star_info = star_info.map{|s| s == max ? s - 1 : s} elsif sum < 100 star_info = star_info.map{|s| s == max ? s + 1 : s} end cnt = game_star_info.count sum = game_star_info.sum star_info.unshift((sum / cnt.to_f).round(1)) else star_info = [5.0, 100, 0, 0, 0, 0] end star_info end # 实训关卡的总分(由于大部分是实践题,因此没关联查choose表) # 提前加载问题:由于选择题比较少,所以几乎不会触发选择题的查询,所以没必要提前载入choose_score def all_score sum = 0 challenges.each do |challenge| sum += challenge.st == 0 ? challenge.score : challenge.choose_score end sum end ### fork 数量 def fork_count self.class.where(fork_from: id).count end # 学员等级 def shixun_trainee case trainee when 1 then '初级学员' when 2 then '中级学员' when 3 then '高级学员' when 4 then '顶级学员' else '' end end def shixun_level case trainee when 1 then '初级' when 2 then '中级' when 3 then '高级' when 4 then '顶级' else '' end end # 纯选择提类型 def is_choice_type? challenges_count == challenges.where(st: 1).count end # 实训受用学校个数 def school_count UserExtension.find_by_sql("select count(distinct ue.school_id) school_count from user_extensions ue right join myshixuns on ue.user_id = myshixuns.user_id where myshixuns.shixun_id = #{self.id}").first.try(:school_count).to_i end def has_manager?(user) return true if user.admin? shixun_members.where(role: [1, 2]).exists?(user_id: user.id) end def finished_challenges_count(user) Game.joins(:myshixun).where(user_id: user.id, status: 2, myshixuns: { shixun_id: id }).count end end