class Shixun < ApplicationRecord include Searchable::Shixun attr_accessor :page_no #管理员页面 实训配置更新状态时,需要接受page_no参数 # status: 0:编辑 1:申请发布 2:正式发布 3:关闭 -1:软删除 # public: 0:未公开 1:申请公开 2:公开 # hide_code: 隐藏代码窗口 # code_hidden: 隐藏代码目录 # task_pass: 跳关 # webssh 0:不开启webssh;1:开启练习模式; 2:开启评测模式 # trainee 实训的难度 # vnc: VCN实训是否用于评测 validates_presence_of :name, message: "实训名称不能为空" has_many :challenges, -> {order("challenges.position asc")}, 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 :homework_commons_shixuns, class_name: 'HomeworkCommonsShixun' has_many :homework_commons, through: :homework_commons_shixuns has_many :fork_shixuns, foreign_key: "fork_from", class_name: 'Shixun' #实训的关卡 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 # 第二版本库 has_one :shixun_secret_repository, dependent: :destroy belongs_to :user # 实训服务配置 has_many :shixun_service_configs, :dependent => :destroy has_many :tidings, as: :container, dependent: :destroy # 实训审核记录 has_many :shixun_reviews, -> {order("shixun_reviews.created_at desc")}, :dependent => :destroy has_many :laboratory_shixuns, dependent: :destroy belongs_to :laboratory, optional: true # Jupyter数据集,附件 has_many :data_sets, ->{where(attachtype: 2)}, class_name: 'Attachment', as: :container, dependent: :destroy # 试卷的实训题 has_many :exercise_questions 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 :published_closed, lambda{ where(status: [2, 3]) } scope :none_closed, lambda{ where(status: [0, 1, 2]) } scope :unhidden, lambda{ where(hidden: 0, status: 2) } scope :publiced, lambda{ where(public: 2) } scope :field_for_recommend, lambda{ select([:id, :name, :identifier, :myshixuns_count]) } scope :find_by_ids,lambda{|k| where(id:k)} scope :no_jupyter, -> { where(is_jupyter: false) } after_create :send_tiding #同步到trustie after_create do SyncTrustieJob.perform_later("practical_training_project", 1) if allow_sync_to_trustie? end # REDO:  def propaedeutics shixun_info.try(:propaedeutics) end def description shixun_info.try(:description) end def evaluate_script shixun_info.try(:evaluate_script) end def fork_reason case shixun_info.try(:fork_reason) when 'Shixun' '实训内容升级' when 'Course' '课堂教学使用' when 'Subject' '实践课程使用' else shixun_info.try(:fork_reason) end end def fork_identifier self.fork_from.nil? ? "--" : fork_shixuns.first&.identifier end def shixun_status status = "" case self.status when 0 status = "编辑中" when 1 status = "审核中" when 2 status = "已发布" when 3 status = "已关闭" end status end def is_tag_used?(id) tag_repertoires.map(&:id).include?(id) 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 names = mirror_repositories.small_mirror.map(&:type_name) names.blank? ? [] : names 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_by_id(self.user_id) end def shixun_main_name self.mirror_repositories.published_main_mirror.first.try(:type_name) 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 self.challenges.pluck(:score).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? || user.business? 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 def has_web_route? self.mirror_name.include?('JavaWeb') || self.mirror_name.include?('PHP') && self.mirror_name.include?('Mysql') || self.mirror_name.include?('Web') end # 所属实践课程 def relation_path subjects.visible.unhidden.uniq end private def send_tiding self.tidings << Tiding.new(:user_id => user_id, :trigger_user_id => 1, :belong_container_id => id, :belong_container_type =>'Shixun', :tiding_type => "System", :viewed => 0) end end