智能组卷

dev_jupyter
cxt 5 years ago
parent 6fcfac707d
commit d206e1c37e

@ -1,14 +1,89 @@
class ExaminationIntelligentSettingsController < ApplicationController
before_action :require_login
before_action :find_exam, only: [:exchange_one_item, :exchange_items]
def optinal_items
items = OptionalItemQuery.call(params)
@single_question_count = items[:single_question_count]
@multiple_question_count = items[:multiple_question_count]
@judgement_question_count = items[:judgement_question_count]
@program_question_count = items[:program_question_count]
sub_discipline_id = params[:sub_discipline_id]
tag_discipline_id = params[:tag_discipline_id]
difficulty = params[:difficulty]
source = params[:source]
items = OptionalItemQuery.call(sub_discipline_id, tag_discipline_id, difficulty, source)
@single_question_count = items.select{ |item| item.item_type == "SINGLE" }.size
@multiple_question_count = items.select{ |item| item.item_type == "MULTIPLE" }.size
@judgement_question_count = items.select{ |item| item.item_type == "JUDGMENT" }.size
@program_question_count = items.select{ |item| item.item_type == "PROGRAM" }.size
end
def create
ActiveRecord::Base.transaction do
exam = ExaminationIntelligentSetting.new(user: current_user)
# 保存试卷基础信息
exam = ExaminationIntelligentSettings::SaveSettingService.call(exam, form_params)
render_ok({exam_setting_id: exam.id})
end
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def exchange_one_item
item = @exam.item_baskets.find_by!(id: params[:item_id])
exam_type_setting = @exam.examination_type_settings.find_by!(item_type: item.item_type)
# 获取可选的题
items = OptionalItemQuery.call(@exam.sub_discipline_id, @exam.tag_discipline_containers.pluck(:tag_discipline_id), @exam.difficulty, @exam.public)
type_items = items.select{ |t_item| t_item.item_type == item.item_type }
# 如果可选的题数小于等于设置的题数则提示无可换的题
tip_exception("无可换的题") if type_items.size <= exam_type_setting.count
# 可选题中去掉已组卷的同题型试题
optional_item_ids = type_items.pluck(:id) - @exam.item_baskets.where(item_type: item.item_type).pluck(:item_bank_id)
new_item = ItemBank.find optional_item_ids.sample(1).first
ActiveRecord::Base.transaction do
@exam.item_baskets << ItemBasket.new(item_bank_id: new_item.id, position: item.position, score: item.score, item_type: new_item.item_type)
item.destroy!
end
render_ok
end
def exchange_items
exam_type_setting = @exam.examination_type_settings.find_by!(item_type: params[:item_type])
choosed_items = @exam.item_baskets.where(item_type: params[:item_type])
# 获取可选的题
items = OptionalItemQuery.call(@exam.sub_discipline_id, @exam.tag_discipline_containers.pluck(:tag_discipline_id), @exam.difficulty, @exam.public)
type_items = items.select{ |t_item| t_item.item_type == params[:item_type] }
# 如果可选的题数小于等于设置的题数则提示无可换的题
tip_exception("无可换的题") if type_items.size <= exam_type_setting.count
# 可选题中去掉已组卷的同题型试题
choosed_item_ids = choosed_items.pluck(:item_bank_id)
optional_item_ids = type_items.pluck(:id) - choosed_item_ids
# 如果可选题数小于设置的题数n则在原来的选题中随机选n个确保换题时能选到新的题
if optional_item_ids.size < exam_type_setting.count
absence_count = exam_type_setting.count - optional_item_ids.size
optional_item_ids = optional_item_ids + choosed_item_ids.sample(absence_count)
end
ActiveRecord::Base.transaction do
# 取试题分数
score = choosed_items.first&.score || (params[:item_type] == "PROGRAM" ? 10 : 5)
choosed_items.destroy_all
optional_item_ids.sample(exam_type_setting.count).each_with_index do |item_id, index|
new_item = ItemBank.find item_id
@exam.item_baskets << ItemBasket.new(item_bank_id: new_item.id, position: index+1, score: score, item_type: new_item.item_type)
end
end
render_ok
end
private
def find_exam
@exam = ExaminationIntelligentSetting.find_by!(id: params[:id])
tip_exception(403,"无权限编辑") unless current_user.admin_or_business? || @exam.user_id == current_user.id
end
def form_params
params.permit(:discipline_id, :sub_discipline_id, :difficulty, :source, tag_discipline_id: [], question_settings: %i[item_type count])
end
end

@ -9,7 +9,14 @@ class ItemBanksController < ApplicationController
@items_count = items.size
@items = paginate items.includes(:item_analysis, :user, :container)
exam = ExaminationBank.find_by(id: params[:exam_id]) if params[:exam_id].present?
@item_basket_ids = exam ? exam.examination_items.pluck(:item_bank_id) : current_user.item_baskets.pluck(:item_bank_id)
exam_setting = ExaminationIntelligentSetting.find_by(id: params[:exam_setting_id]) if params[:exam_setting_id].present?
@item_basket_ids = if exam
exam.examination_items.pluck(:item_bank_id)
elsif exam_setting
exam_setting.item_baskets.pluck(:item_bank_id)
else
current_user.item_baskets.pluck(:item_bank_id)
end
end
def create

@ -4,7 +4,7 @@ class ItemBasketsController < ApplicationController
helper_method :current_basket
def index
@item_baskets = current_user.item_baskets
@item_baskets = basket_items
@single_questions = @item_baskets.where(item_type: "SINGLE")
@multiple_questions = @item_baskets.where(item_type: "MULTIPLE")
@judgement_questions = @item_baskets.where(item_type: "JUDGMENT")
@ -22,41 +22,41 @@ class ItemBasketsController < ApplicationController
end
def create
ItemBaskets::SaveItemBasketService.call(current_user, create_params)
ItemBaskets::SaveItemBasketService.call(current_user, create_params, exam_setting)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def destroy
item = current_user.item_baskets.find_by!(item_bank_id: params[:id])
item = basket_items.find_by!(item_bank_id: params[:id])
ActiveRecord::Base.transaction do
current_user.item_baskets.where(item_type: item.item_type).where("position > #{item.position}").update_all("position = position -1")
basket_items.where(item_type: item.item_type).where("position > #{item.position}").update_all("position = position -1")
item.destroy!
end
render_ok
end
def delete_item_type
baskets = ItemBasket.where(item_type: params[:item_type])
baskets = basket_items.where(item_type: params[:item_type])
baskets.destroy_all
render_ok
end
def set_score
current_basket.update_attributes!(score: params[:score])
@questions_score = current_user.item_baskets.where(item_type: current_basket.item_type).pluck(:score).sum
@all_score = current_user.item_baskets.pluck(:score).sum
@questions_score = basket_items.where(item_type: current_basket.item_type).pluck(:score).sum
@all_score = basket_items.pluck(:score).sum
end
def batch_set_score
current_user.item_baskets.where(item_type: params[:item_type]).update_all(score: params[:score])
@questions_score = current_user.item_baskets.where(item_type: params[:item_type]).pluck(:score).sum
@all_score = current_user.item_baskets.pluck(:score).sum
basket_items.where(item_type: params[:item_type]).update_all(score: params[:score])
@questions_score = basket_items.where(item_type: params[:item_type]).pluck(:score).sum
@all_score = basket_items.pluck(:score).sum
end
def adjust_position
same_items = current_user.item_baskets.where(item_type: current_basket.item_type)
same_items = basket_items.where(item_type: current_basket.item_type)
max_position = same_items.size
tip_exception("position超出范围") unless params[:position].present? && params[:position].to_i <= max_position && params[:position].to_i >= 1
ActiveRecord::Base.transaction do
@ -79,8 +79,19 @@ class ItemBasketsController < ApplicationController
params.permit(item_ids: [])
end
def exam_setting
@_exam_setting = ExaminationIntelligentSetting.find_by(id: params[:exam_setting_id])
end
def basket_items
@_items = params[:exam_setting_id] ? exam_setting.item_baskets : current_user.item_baskets
end
def current_basket
@_current_basket = current_user.item_baskets.find_by!(id: params[:id])
@_current_basket = ItemBasket.find_by!(id: params[:id])
tip_exception(403, "无权限编辑") unless current_user.admin_or_business? || @_current_basket.user_id.to_i == current_user.id ||
@_current_basket.examination_intelligent_setting&.user_id.to_i == current_user.id
@_current_basket
end
def validate_score

@ -0,0 +1,11 @@
class ExaminationIntelligentSettings::SaveExamSettingForm
include ActiveModel::Model
attr_accessor :discipline_id, :sub_discipline_id, :source, :difficulty, :tag_discipline_id, :question_settings
validates :discipline_id, presence: true
validates :sub_discipline_id, presence: true
validates :source, presence: true
validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true }
validates :question_settings, presence: true
end

@ -15,9 +15,9 @@ class ItemBanks::SaveItemForm
return unless errors.blank?
choices.each { |item| SubForm.new(item).validate! } if %W(SINGLE MULTIPLE JUDGMENT).include?(item_type)
return unless errors.blank?
if [0, 2].include?(item_type) && choices.pluck(:is_answer).select{|item| item == 1}.length > 1
if ["SINGLE", "JUDGMENT"].include?(item_type) && choices.pluck(:is_answer).select{|item| item == 1}.length > 1
raise("正确答案只能有一个")
elsif item_type == 1 && choices.pluck(:is_answer).select{|item| item == 1}.length <= 1
elsif item_type == "MULTIPLE" && choices.pluck(:is_answer).select{|item| item == 1}.length <= 1
raise("多选题至少有两个正确答案")
end
end

@ -1,4 +1,7 @@
class ExaminationIntelligentSetting < ApplicationRecord
belongs_to :sub_discipline
belongs_to :user
has_many :examination_type_settings, dependent: :destroy
has_many :tag_discipline_containers, as: :container, dependent: :destroy
has_many :item_baskets, dependent: :destroy
end

@ -1,3 +1,4 @@
class ExaminationTypeSetting < ApplicationRecord
enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 }
belongs_to :examination_intelligent_setting
end

@ -2,13 +2,6 @@ class ItemBasket < ApplicationRecord
enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 }
belongs_to :item_bank
belongs_to :user
def all_score
User.current.item_baskets.map(&:score).sum
end
def question_count
User.current.item_baskets.size
end
belongs_to :user, optional: true
belongs_to :examination_intelligent_setting, optional: true
end

@ -156,6 +156,8 @@ class User < ApplicationRecord
# 题库
has_many :item_banks, dependent: :destroy
has_many :item_baskets, -> { order("item_baskets.position ASC") }, dependent: :destroy
has_many :examination_banks, dependent: :destroy
has_many :examination_intelligent_settings, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }

@ -1,32 +1,35 @@
class OptionalItemQuery < ApplicationQuery
attr_reader :params
attr_reader :sub_discipline_id, :tag_discipline_id, :difficulty, :source
def initialize(params)
@params = params
def initialize(sub_discipline_id, tag_discipline_id, difficulty, source)
@sub_discipline_id = sub_discipline_id
@tag_discipline_id = tag_discipline_id
@difficulty = difficulty
@source = source
end
def call
items = ItemBank.all
if params[:tag_discipline_id].present? && params[:sub_discipline_id].present?
items = items.joins(:tag_discipline_containers).where(tag_discipline_containers: {tag_discipline_id: params[:tag_discipline_id]})
hacks = Hack.joins(:tag_discipline_containers).where(tag_discipline_containers: {tag_discipline_id: params[:tag_discipline_id]})
elsif params[:sub_discipline_id].present?
items = items.where(sub_discipline_id: params[:sub_discipline_id])
hacks = Hack.where(sub_discipline_id: params[:sub_discipline_id])
if tag_discipline_id.present? && sub_discipline_id.present?
items = items.joins(:tag_discipline_containers).where(tag_discipline_containers: {tag_discipline_id: tag_discipline_id})
hacks = Hack.joins(:tag_discipline_containers).where(tag_discipline_containers: {tag_discipline_id: tag_discipline_id})
elsif sub_discipline_id.present?
items = items.where(sub_discipline_id: sub_discipline_id)
hacks = Hack.where(sub_discipline_id: sub_discipline_id)
end
if hacks.present?
items = ItemBank.where(container_id: hacks.pluck(:id), container_type: "Hack").or(ItemBank.where(id: items.pluck(:id)))
end
# 来源
source = params[:source].present? ? params[:source].to_i : 1
public = source == 3 ? [0, 1] : source
public = source.present? ? source.to_i : 1
public = public == 2 ? [0, 1] : public
items = items.where(public: public)
# 难度
difficulty = params[:difficulty] ? params[:difficulty].to_i : 1
difficulty = difficulty ? difficulty.to_i : 1
items = items.where(difficulty: difficulty)
single_question_count = items.select{ |item| item.item_type == "SINGLE" }.size
multiple_question_count = items.select{ |item| item.item_type == "MULTIPLE" }.size
judgement_question_count = items.select{ |item| item.item_type == "JUDGMENT" }.size
program_question_count = hacks.present? ? hacks.pluck(:item_bank_id).reject(&:blank?).size : items.select{ |item| item.item_type == "PROGRAM" }.size
{single_question_count: single_question_count, multiple_question_count: multiple_question_count, judgement_question_count: judgement_question_count, program_question_count: program_question_count}
items
end
end

@ -0,0 +1,63 @@
class ExaminationIntelligentSettings::SaveSettingService < ApplicationService
attr_reader :exam, :params
def initialize(exam, params)
@exam = exam
@params = params
end
def call
ExaminationIntelligentSettings::SaveExamSettingForm.new(params).validate!
items = OptionalItemQuery.call(params[:sub_discipline_id], params[:tag_discipline_id], params[:difficulty], params[:source])
params[:question_settings].each do |setting|
raise "超出可选题数范围" if items.select{ |item| item.item_type == setting[:item_type] }.size.to_i < setting[:count].to_i
end
exam.difficulty = params[:difficulty]
exam.sub_discipline_id = params[:sub_discipline_id]
exam.public = params[:source].present? ? params[:source].to_i : 1
exam.save!
# 知识点的创建
params[:tag_discipline_id].each do |tag_id|
exam.tag_discipline_containers << TagDisciplineContainer.new(tag_discipline_id: tag_id)
end
# 智能选题的设置
params[:question_settings].each do |setting|
if setting[:count].to_i > 0
exam.examination_type_settings << ExaminationTypeSetting.new(item_type: setting[:item_type], count: setting[:count].to_i)
end
end
# 选题
choose_question items
exam
end
private
def choose_question items
exam.examination_type_settings.each do |setting|
questions = items.select{ |item| item.item_type == setting.item_type }
questions.pluck(:id).sample(setting.count).each_with_index do |item_id, index|
item = ItemBank.find item_id
exam.item_baskets << ItemBasket.new(item_bank_id: item.id, position: index+1, score: item_score(item.item_type), item_type: item.item_type)
end
end
end
def item_score item_type
score =
case item_type
when "SINGLE", "MULTIPLE", "JUDGMENT"
5
when "PROGRAM"
10
else
5
end
score
end
end

@ -1,9 +1,10 @@
class ItemBaskets::SaveItemBasketService < ApplicationService
attr_reader :user, :params
attr_reader :user, :params, :exam_setting
def initialize(user, params)
def initialize(user, params, exam_setting)
@user = user
@params = params
@exam_setting = exam_setting
end
def call
@ -13,9 +14,14 @@ class ItemBaskets::SaveItemBasketService < ApplicationService
items = ItemBank.where(public: 1).or(ItemBank.where(user_id: user.id))
# 已选到过试题篮的不重复选用
item_ids = params[:item_ids] - user.item_baskets.pluck(:item_bank_id)
item_ids = params[:item_ids] - basket_items.pluck(:item_bank_id)
items.where(id: item_ids).each do |item|
new_item = ItemBasket.new(user_id: user.id, item_bank_id: item.id, item_type: item.item_type)
new_item = ItemBasket.new(item_bank_id: item.id, item_type: item.item_type)
if exam_setting.present?
new_item.examination_intelligent_setting_id = exam_setting.id
else
new_item.user_id = user.id
end
new_item.score = item_score item.item_type
new_item.position = item_position item.item_type
new_item.save!
@ -25,8 +31,8 @@ class ItemBaskets::SaveItemBasketService < ApplicationService
private
def item_score item_type
if user.item_baskets.where(item_type: item_type).last.present?
score = user.item_baskets.where(item_type: item_type).last.score
if basket_items.where(item_type: item_type).last.present?
score = basket_items.where(item_type: item_type).last.score
else
score =
case item_type
@ -42,6 +48,10 @@ class ItemBaskets::SaveItemBasketService < ApplicationService
end
def item_position item_type
user.item_baskets.where(item_type: item_type).last&.position.to_i + 1
basket_items.where(item_type: item_type).last&.position.to_i + 1
end
def basket_items
exam_setting.present? ? exam_setting.item_baskets : user.item_baskets
end
end

@ -100,6 +100,11 @@ Rails.application.routes.draw do
collection do
get :optinal_items
end
member do
post :exchange_one_item
post :exchange_items
end
end
resources :hacks, path: :problems, param: :identifier do

@ -0,0 +1,5 @@
class AddUserIdToIntelligentSettings < ActiveRecord::Migration[5.2]
def change
add_column :examination_intelligent_settings, :user_id, :integer, index: true
end
end

@ -0,0 +1,5 @@
class AddIntelligentSettingIdToItemBaskets < ActiveRecord::Migration[5.2]
def change
add_column :item_baskets, :examination_intelligent_setting_id, :integer, index: true
end
end
Loading…
Cancel
Save