dev_cxt
cxt 5 years ago
commit 63b6f91480

@ -0,0 +1,124 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-disciplines-index-page').length > 0) {
// ============== 新建 ===============
var $modal = $('.modal.admin-create-discipline-modal');
var $form = $modal.find('form.admin-create-discipline-form');
var $nameInput = $form.find('input[name="name"]');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
name: {
required: true
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function () {
$nameInput.val('');
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'json',
url: url,
data: $form.serialize(),
success: function(){
$.notify({ message: '创建成功' });
$modal.modal('hide');
setTimeout(function(){
window.location.reload();
}, 500);
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
}
});
}
});
$(".discipline-list-container").on("change", '.discipline-source-form', function () {
var s_id = $(this).attr("data-id");
var s_value = $(this).val();
var s_name = $(this).attr("name");
var json = {};
json[s_name] = s_value;
$.ajax({
url: "/admins/disciplines/" + s_id,
type: "PUT",
dataType:'script',
data: json
});
});
// 导入学生
var $importDisciplineModal = $('.modal.admin-import-discipline-modal');
var $importDisciplineForm = $importDisciplineModal.find('form.admin-import-discipline-form');
$importDisciplineModal.on('show.bs.modal', function(){
resetFileInputFunc($importDisciplineModal.find('.upload-file-input'));
$importDisciplineModal.find('.file-names').html('选择文件');
$importDisciplineModal.find('.upload-file-input').trigger('click');
});
$importDisciplineModal.on('change', '.upload-file-input', function(e){
var file = $(this)[0].files[0];
$importDisciplineModal.find('.file-names').html(file ? file.name : '请选择文件');
});
var importDisciplineFormValid = function(){
if($importDisciplineForm.find('input[name="file"]').val() == undefined || $importDisciplineForm.find('input[name="file"]').val().length == 0){
$importDisciplineForm.find('.error').html('请选择文件');
return false;
}
return true;
};
var buildResultMessage = function(data){
var messageHtml = "<div>导入结果:成功" + data.success + "条,失败"+ data.fail.length + "条</div>";
return messageHtml;
};
$importDisciplineModal.on('click', '.submit-btn', function(){
$importDisciplineForm.find('.error').html('');
if (importDisciplineFormValid()) {
$('body').mLoading({ text: '正在导入...' });
$.ajax({
method: 'POST',
dataType: 'json',
url: '/admins/import_disciplines',
data: new FormData($importDisciplineForm[0]),
processData: false,
contentType: false,
success: function(data){
$('body').mLoading('destroy');
$importDisciplineModal.modal('hide');
showMessageModal(buildResultMessage(data), function(){
window.location.reload();
});
},
error: function(res){
$('body').mLoading('destroy');
var data = res.responseJSON;
$importDisciplineForm.find('.error').html(data.message);
}
});
}
});
}
});

@ -0,0 +1,31 @@
$(document).on('turbolinks:load', function() {
$('.admin-modal-container').on('show.bs.modal', '.modal.admin-edit-discipline-modal', function(){
var $modal = $('.modal.admin-edit-discipline-modal');
var $form = $modal.find('form.admin-edit-discipline-form');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
'discipline[name]': {
required: true,
maxlength: 20
}
}
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
var url = $form.attr('action');
if ($form.valid()) {
$.ajax({
method: 'PATCH',
dataType: 'script',
url: url,
data: $form.serialize()
});
}
});
});
});

@ -0,0 +1,31 @@
$(document).on('turbolinks:load', function() {
$('.admin-modal-container').on('show.bs.modal', '.modal.admin-edit-sub-discipline-modal', function(){
var $modal = $('.modal.admin-edit-sub-discipline-modal');
var $form = $modal.find('form.admin-edit-sub-discipline-form');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
'sub_discipline[name]': {
required: true,
maxlength: 20
}
}
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
var url = $form.attr('action');
if ($form.valid()) {
$.ajax({
method: 'PATCH',
dataType: 'script',
url: url,
data: $form.serialize()
});
}
});
});
});

@ -0,0 +1,31 @@
$(document).on('turbolinks:load', function() {
$('.admin-modal-container').on('show.bs.modal', '.modal.admin-edit-tag-discipline-modal', function(){
var $modal = $('.modal.admin-edit-tag-discipline-modal');
var $form = $modal.find('form.admin-edit-tag-discipline-form');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
'tag_discipline[name]': {
required: true,
maxlength: 20
}
}
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
var url = $form.attr('action');
if ($form.valid()) {
$.ajax({
method: 'PATCH',
dataType: 'script',
url: url,
data: $form.serialize()
});
}
});
});
});

@ -0,0 +1,65 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-sub-disciplines-index-page').length > 0) {
// ============== 新建 ===============
var $modal = $('.modal.admin-create-sub-discipline-modal');
var $form = $modal.find('form.admin-create-sub-discipline-form');
var $nameInput = $form.find('input[name="name"]');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
name: {
required: true
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function () {
$nameInput.val('');
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'json',
url: url,
data: $form.serialize(),
success: function(){
$.notify({ message: '创建成功' });
$modal.modal('hide');
setTimeout(function(){
window.location.reload();
}, 500);
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
}
});
}
});
$(".sub-discipline-list-container").on("change", '.sub-discipline-source-form', function () {
var s_id = $(this).attr("data-id");
var s_value = $(this).val();
var s_name = $(this).attr("name");
var json = {};
json[s_name] = s_value;
$.ajax({
url: "/admins/sub_disciplines/" + s_id,
type: "PUT",
dataType:'script',
data: json
});
});
}
});

@ -0,0 +1,65 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-tag-disciplines-index-page').length > 0) {
// ============== 新建 ===============
var $modal = $('.modal.admin-create-tag-discipline-modal');
var $form = $modal.find('form.admin-create-tag-discipline-form');
var $nameInput = $form.find('input[name="name"]');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
name: {
required: true
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function () {
$nameInput.val('');
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'json',
url: url,
data: $form.serialize(),
success: function(){
$.notify({ message: '创建成功' });
$modal.modal('hide');
setTimeout(function(){
window.location.reload();
}, 500);
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
}
});
}
});
$(".tag-discipline-list-container").on("change", '.tag-discipline-source-form', function () {
var s_id = $(this).attr("data-id");
var s_value = $(this).val();
var s_name = $(this).attr("name");
var json = {};
json[s_name] = s_value;
$.ajax({
url: "/admins/tag_disciplines/" + s_id,
type: "PUT",
dataType:'script',
data: json
});
});
}
});

@ -0,0 +1,49 @@
class Admins::DisciplinesController < Admins::BaseController
def index
@disciplines = Discipline.all
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if Discipline.where(name: name).exists?
Discipline.create!(name: name)
render_ok
end
def edit
@discipline = current_discipline
end
def update
if params[:discipline] && params[:discipline][:name].present?
name = params[:discipline][:name].to_s.strip
current_discipline.update_attributes!(name: name)
else
ActiveRecord::Base.transaction do
current_discipline.update_attributes!(setting_params)
current_discipline.sub_disciplines.each do |sub|
sub.tag_disciplines.each do |tag|
tag.update_attributes!(setting_params)
end
sub.update_attributes!(setting_params)
end
end
end
@disciplines = Discipline.all
end
def destroy
@discipline_id = params[:id]
current_discipline.destroy!
end
private
def current_discipline
@_current_discipline = Discipline.find params[:id]
end
def setting_params
params.permit(:shixun, :subject, :question)
end
end

@ -0,0 +1,10 @@
class Admins::ImportDisciplinesController < Admins::BaseController
def create
return render_error('请上传正确的文件') if params[:file].blank? || !params[:file].is_a?(ActionDispatch::Http::UploadedFile)
result = Admins::ImportDisciplineService.call(params[:file].to_io)
render_ok(result)
rescue Admins::ImportDisciplineService::Error => ex
render_error(ex)
end
end

@ -0,0 +1,52 @@
class Admins::SubDisciplinesController < Admins::BaseController
def index
@discipline = current_discipline
@sub_disciplines = current_discipline.sub_disciplines
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if current_discipline.sub_disciplines.where(name: name).exists?
SubDiscipline.create!(name: name, discipline_id: current_discipline.id)
render_ok
end
def edit
@sub_discipline = current_sub_discipline
end
def update
if params[:sub_discipline] && params[:sub_discipline][:name].present?
name = params[:sub_discipline][:name].to_s.strip
current_sub_discipline.update_attributes!(name: name)
else
ActiveRecord::Base.transaction do
current_sub_discipline.update_attributes!(setting_params)
current_sub_discipline.tag_disciplines.each do |tag|
tag.update_attributes!(setting_params)
end
end
end
@sub_disciplines = current_sub_discipline.discipline&.sub_disciplines
end
def destroy
@sub_discipline_id = params[:id]
current_sub_discipline.destroy!
end
private
def current_sub_discipline
@_current_sub_discipline = SubDiscipline.find params[:id]
end
def current_discipline
@_current_discipline = Discipline.find params[:discipline_id]
end
def setting_params
params.permit(:shixun, :subject, :question)
end
end

@ -0,0 +1,47 @@
class Admins::TagDisciplinesController < Admins::BaseController
def index
@sub_discipline = current_sub_discipline
@tag_disciplines = current_sub_discipline.tag_disciplines
end
def create
name = params[:name].to_s.strip
return render_error('名称重复') if current_sub_discipline.tag_disciplines.where(name: name).exists?
TagDiscipline.create!(name: name, sub_discipline_id: current_sub_discipline.id, user_id: current_user.id)
render_ok
end
def edit
@tag_discipline = current_tag_discipline
end
def update
if params[:tag_discipline] && params[:tag_discipline][:name].present?
name = params[:tag_discipline][:name].to_s.strip
current_tag_discipline.update_attributes!(name: name)
else
current_tag_discipline.update_attributes!(setting_params)
end
@tag_disciplines = current_tag_discipline.sub_discipline&.tag_disciplines
end
def destroy
@tag_discipline_id = params[:id]
current_tag_discipline.destroy!
end
private
def current_sub_discipline
@_current_sub_discipline = SubDiscipline.find params[:sub_discipline_id]
end
def current_tag_discipline
@_current_tag_discipline = TagDiscipline.find params[:id]
end
def setting_params
params.permit(:shixun, :subject, :question)
end
end

@ -23,21 +23,23 @@ class ApplicationController < ActionController::Base
# 所有请求必须合法签名
def check_sign
Rails.logger.info("66666 #{params}")
suffix = request.url.split(".").last.split("?").first
suffix_arr = ["xls", "xlsx", "pdf"] # excel文件先注释
unless suffix_arr.include?(suffix)
if params[:client_key].present?
randomcode = params[:randomcode]
# tip_exception(501, "请求不合理") unless (Time.now.to_i - randomcode.to_i).between?(0,5)
sign = Digest::MD5.hexdigest("#{OPENKEY}#{randomcode}")
Rails.logger.info("2222 #{sign}")
tip_exception(501, "请求不合理") if sign != params[:client_key]
else
tip_exception(501, "请求不合理")
end
end
# unless Rails.env.development?
# Rails.logger.info("66666 #{params}")
# suffix = request.url.split(".").last.split("?").first
# suffix_arr = ["xls", "xlsx", "pdf"] # excel文件先注释
# unless suffix_arr.include?(suffix)
# if params[:client_key].present?
# randomcode = params[:randomcode]
# # tip_exception(501, "请求不合理") unless (Time.now.to_i - randomcode.to_i).between?(0,5)
#
# sign = Digest::MD5.hexdigest("#{OPENKEY}#{randomcode}")
# Rails.logger.info("2222 #{sign}")
# tip_exception(501, "请求不合理") if sign != params[:client_key]
# else
# tip_exception(501, "请求不合理")
# end
# end
# end
end
# 全局配置参数

@ -0,0 +1,6 @@
class DisciplinesController < ApplicationController
def index
end
end

@ -0,0 +1,85 @@
class ExaminationBanksController < ApplicationController
include PaginateHelper
before_action :require_login
before_action :find_exam, except: [:index, :create]
before_action :edit_auth, only: [:update, :destroy, :set_public]
def index
exams = ExaminationBankQuery.call(params)
@exams_count = exams.size
@exams = paginate exams.includes(:user, :examination_items)
end
def show
@items = @exam.examination_items
@single_questions = @items.where(item_type: "SINGLE")
@multiple_questions = @items.where(item_type: "MULTIPLE")
@judgement_questions = @items.where(item_type: "JUDGMENT")
@program_questions = @items.where(item_type: "PROGRAM")
end
def create
ActiveRecord::Base.transaction do
exam = ExaminationBank.new(user: current_user)
# 保存试卷基础信息
exam = ExaminationBanks::SaveExaminationBankService.call(exam, form_params)
# 将试题篮中的试题发送到试卷,试卷的题目与试题独立
current_user.item_baskets.includes(:item_bank).each do |basket|
item = basket.item_bank
if item.present?
new_item = ExaminationItem.new(examination_bank: exam, item_bank: item, name: item.name, item_type: item.item_type,
difficulty: item.difficulty, score: basket.score, position: basket.position)
new_item.save!
item.increment!(:quotes)
if item.item_type == "PROGRAM"
new_hack = item.container.fork
new_item.update_attributes!(container: new_hack)
else
new_item.examination_item_analysis.create!(analysis: item.analysis)
item.item_choices.each do |choice|
new_item.examination_item_choices << ExaminationItemChoice.new(choice_text: choice.choice_text, is_answer: choice.is_answer)
end
end
end
end
current_user.item_baskets.destroy_all
end
render_ok
end
def edit
end
def update
end
def destroy
@exam.destroy!
render_ok
end
def set_public
tip_exception(-1, "该试卷已公开") if @exam.public?
@exam.update_attributes!(public: 1)
render_ok
end
private
def form_params
params.permit(:discipline_id, :sub_discipline_id, :difficulty, :name, :duration, tag_discipline_id: [])
end
def find_exam
@exam = ExaminationBank.find_by!(id: params[:id])
end
def edit_auth
current_user.admin_or_business? || @exam.user == current_user
end
end

@ -30,10 +30,10 @@ class HacksController < ApplicationController
# 筛选过滤与排序
params_filter_or_order
# 我解决的编程题数
user_codes = HackUserLastestCode.joins(:hack).mine_hack(current_user).passed
user_codes = HackUserLastestCode.joins(:hack).mine_hack(current_user).passed
@simple_count = user_codes.where(hacks: {difficult: 1}).count
@medium_count = user_codes.where(hacks: {difficult: 2}).count
@diff_count = user_codes.where(hacks: {difficult: 3}).count
@diff_count = user_codes.where(hacks: {difficult: 3}).count
@pass_count = @simple_count + @medium_count + @diff_count
@hacks_count = @hacks.count("hacks.id")
@ -43,17 +43,23 @@ class HacksController < ApplicationController
def create
begin
logger.info("##########{hack_params}")
tip_exception("一次只能增加50个测试集") if hack_sets_params.size > 50
tip_exception("一次只能增加50个知识点") if params[:tags].size > 50
hack = Hack.new(hack_params)
hack.user_id = current_user.id
hack.identifier = generate_identifier Hack, 8
tag_params = params[:tags].map{|tag| {tag_discipline_id: tag}}
ActiveRecord::Base.transaction do
hack.save!
# 创建测试集与代码
hack.hack_sets.create!(hack_sets_params)
# 新建知识点
hack.tag_discipline_containers.create!(tag_params) if tag_params.present?
hack_codes = hack.hack_codes.new(hack_code_params)
hack_codes.modify_time = Time.now
hack_codes.save!
new_item_params = item_params.merge(container: hack, item_type: 'PROGRAM', difficulty: params[:hack][:difficult], user_id: current_user.id)
ItemBank.create!(new_item_params)
end
render_ok({identifier: hack.identifier})
rescue Exception => e
@ -64,6 +70,10 @@ class HacksController < ApplicationController
def update
begin
# 知识点
tag_discipline_ids = @hack.tag_discipline_containers.pluck(:tag_discipline_id)
new_tag_ids = params[:tags].to_a - tag_discipline_ids
tag_params = new_tag_ids.map{|tag| {tag_discipline_id: tag}}
ActiveRecord::Base.transaction do
@hack.update_attributes!(hack_params)
set_ids = @hack.hack_sets.pluck(:id)
@ -74,6 +84,14 @@ class HacksController < ApplicationController
# 更新代码
code_params = params[:hack_codes][:code] != @hack.code ? hack_code_params.merge(modify_time: Time.now) : hack_code_params
@hack.hack_codes.first.update_attributes!(code_params)
@hack.tag_discipline_containers.create!(tag_params) if tag_params
@hack.tag_discipline_containers.where.not(tag_discipline_id: params[:tags]).destroy_all
# 更新题库相关记录
if @hack.item_bank.present?
update_item_params = item_params.merge({difficulty: params[:hack][:difficult]})
@hack.item_bank.update!(update_item_params)
end
end
render_ok
rescue Exception => e
@ -127,9 +145,11 @@ class HacksController < ApplicationController
@hacks = hacks.includes(:hack_sets).page(page).per(limit)
end
def edit;end
def edit;
end
def new;end
def new;
end
def destroy
begin
@ -148,6 +168,7 @@ class HacksController < ApplicationController
end
private
# 实名认证老师,管理员与运营人员权限
def require_teacher_identity
unless current_user.certification_teacher? || admin_or_business?
@ -167,7 +188,11 @@ class HacksController < ApplicationController
end
def hack_params
params.require(:hack).permit(:name, :description, :difficult, :category, :open_or_not, :time_limit, :score)
params.require(:hack).permit(:name, :description, :difficult, :open_or_not, :time_limit, :score, :sub_discipline_id)
end
def item_params
params.require(:hack).permit(:name, :sub_discipline_id)
end
def hack_sets_params
@ -187,7 +212,7 @@ class HacksController < ApplicationController
end
def param_update_sets sets, all_sets_id
delete_set_ids = all_sets_id - sets.map{|set|set[:id]}
delete_set_ids = all_sets_id - sets.map {|set| set[:id]}
@hack.hack_sets.where(id: delete_set_ids).destroy_all
logger.info("#######sets:#{sets}")
sets.each do |set|
@ -215,7 +240,7 @@ class HacksController < ApplicationController
end
# 难度
if params[:difficult]
hacks = hacks.where(difficult: params[:difficult])
hacks = hacks.where(difficult: params[:difficult])
end
# 状态
if params[:status]

@ -1,14 +1,58 @@
class LibrariesController < ApplicationController
class ItemBanksController < ApplicationController
include PaginateHelper
before_action :require_login
before_action :find_item, except: [:index, :create]
before_action :edit_auth, only: [:update, :destroy, :set_public]
def index
default_sort('updated_at', 'desc')
@items = ItemBankQuery.call(params)
@items = paginate courses.includes(:school, :students, :attachments, :homework_commons, teacher: :user_extension)
items = ItemBankQuery.call(params)
@items_count = items.size
@items = paginate items.includes(:item_analysis, :user, :container)
@item_basket_ids = current_user.item_baskets.pluck(:item_bank_id)
end
def create
item = ItemBank.new(user: current_user)
ItemBanks::SaveItemService.call(item, form_params)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def edit
end
def update
ItemBanks::SaveItemService.call(@item, form_params)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def destroy
@item.destroy!
render_ok
end
def set_public
tip_exception(-1, "该试题已公开") if @item.public?
@item.update_attributes!(public: 1)
render_ok
end
private
def find_item
@item = ItemBank.find_by!(id: params[:id])
end
def edit_auth
current_user.admin_or_business? || @item.user == current_user
end
def form_params
params.permit(:discipline_id, :sub_discipline_id, :item_type, :difficulty, :name, :analysis, tag_discipline_id: [], choices: %i[choice_text is_answer])
end
end

@ -0,0 +1,90 @@
class ItemBasketsController < ApplicationController
before_action :require_login
before_action :validate_score, only: [:set_score, :batch_set_score]
helper_method :current_basket
def index
@item_baskets = current_user.item_baskets
@single_questions = @item_baskets.where(item_type: "SINGLE")
@multiple_questions = @item_baskets.where(item_type: "MULTIPLE")
@judgement_questions = @item_baskets.where(item_type: "JUDGMENT")
@program_questions = @item_baskets.where(item_type: "PROGRAM")
end
def basket_list
@single_questions_count = current_user.item_baskets.where(item_type: "SINGLE").count
@multiple_questions_count = current_user.item_baskets.where(item_type: "MULTIPLE").count
@judgement_questions_count = current_user.item_baskets.where(item_type: "JUDGMENT").count
@completion_questions_count = current_user.item_baskets.where(item_type: "COMPLETION").count
@subjective_questions_count = current_user.item_baskets.where(item_type: "SUBJECTIVE").count
@practical_questions_count = current_user.item_baskets.where(item_type: "PRACTICAL").count
@program_questions_count = current_user.item_baskets.where(item_type: "PROGRAM").count
end
def create
ItemBaskets::SaveItemBasketService.call(current_user, create_params)
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])
ActiveRecord::Base.transaction do
current_user.item_baskets.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.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
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
end
def adjust_position
same_items = current_user.item_baskets.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
if params[:position].to_i > current_basket.position
same_items.where("position > #{current_basket.position} and position <= #{params[:position].to_i}").update_all("position=position-1")
current_basket.update_attributes!(position: params[:position])
elsif params[:position].to_i < current_basket.position
same_items.where("position < #{current_basket.position} and position >= #{params[:position].to_i}").update_all("position=position+1")
current_basket.update_attributes!(position: params[:position])
else
return normal_status(-1, "排序无变化")
end
end
render_ok
end
private
def create_params
params.permit(item_ids: [])
end
def current_basket
@_current_basket = current_user.item_baskets.find_by!(id: params[:id])
end
def validate_score
tip_exception("分值不能为空") unless params[:score].present?
tip_exception("分值需大于0") unless params[:score].to_f > 0
end
end

@ -893,7 +893,8 @@ class ShixunsController < ApplicationController
content = upload_file.tempfile.read
author_name = current_user.real_name
author_email = current_user.git_mail
update_file_content(content, @repo_path, @path, author_email, author_name, "upload file by browser")
message = params[:message] || "upload file by browser"
update_file_content(content, @repo_path, @path, author_email, author_name, message)
render_ok
end
@ -901,14 +902,16 @@ class ShixunsController < ApplicationController
def upload_git_folder
author_name = current_user.real_name
author_email = current_user.git_mail
git_add_folder(@path, author_name, author_email, "upload folder by browser")
message = params[:message] || "upload folder by browser"
git_add_folder(@path, author_name, author_email, message)
render_ok
end
def delete_git_file
author_name = current_user.real_name
author_email = current_user.git_mail
git_delete_file(@path, author_name, author_email, "delete filer by browser")
message = params[:message] || "delete file by browser"
git_delete_file(@path, author_name, author_email, message)
render_ok
end

@ -0,0 +1,9 @@
class TagDisciplinesController < ApplicationController
before_action :require_login
def create
sub_discipline = SubDiscipline.find_by!(id: params[:sub_discipline_id])
tag_discipline = TagDiscipline.create!(name: params[:name].to_s.strip, sub_discipline: sub_discipline, user_id: current_user.id)
render_ok({tag_discipline_id: tag_discipline.id})
end
end

@ -0,0 +1,15 @@
class ExaminationBanks::SaveExamForm
include ActiveModel::Model
attr_accessor :discipline_id, :sub_discipline_id, :difficulty, :name, :duration, :tag_discipline_id
validates :discipline_id, presence: true
validates :sub_discipline_id, presence: true
validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true }
validates :name, presence: true, length: { maximum: 60 }
validate :validate_duration
def validate_duration
raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1
end
end

@ -0,0 +1,33 @@
class ItemBanks::SaveItemForm
include ActiveModel::Model
attr_accessor :discipline_id, :sub_discipline_id, :item_type, :difficulty, :name, :analysis, :tag_discipline_id, :choices
validates :discipline_id, presence: true
validates :sub_discipline_id, presence: true
validates :item_type, presence: true, inclusion: {in: %W(SINGLE MULTIPLE JUDGMENT COMPLETION SUBJECTIVE PRACTICAL PROGRAM)}
validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true }
validates :name, presence: true, length: { maximum: 1000 }
validates :analysis, length: { maximum: 1000 }
def validate!
super
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
raise("正确答案只能有一个")
elsif item_type == 1 && choices.pluck(:is_answer).select{|item| item == 1}.length <= 1
raise("多选题至少有两个正确答案")
end
end
class SubForm
include ActiveModel::Model
attr_accessor :choice_text, :is_answer
validates :choice_text, presence: true, length: { maximum: 100 }
validates :is_answer, presence: true, inclusion: {in: 0..1}, numericality: { only_integer: true }
end
end

@ -0,0 +1,16 @@
class Admins::ImportDisciplineExcel < BaseImportXlsx
DisciplineData = Struct.new(:discipline_name, :sub_discipline_name)
def read_each(&block)
sheet.each_row_streaming(pad_cells: true, offset: 2) do |row|
data = row.map(&method(:cell_value))[1..2]
block.call DisciplineData.new(*data)
end
end
private
def cell_value(obj)
obj&.cell_value
end
end

@ -0,0 +1,7 @@
class Discipline < ApplicationRecord
has_many :sub_disciplines, dependent: :destroy
has_many :shixun_sub_disciplines, -> { where("shixun = 1") }, class_name: "SubDiscipline"
has_many :subject_sub_disciplines, -> { where("subject = 1") }, class_name: "SubDiscipline"
has_many :question_sub_disciplines, -> { where("question = 1") }, class_name: "SubDiscipline"
end

@ -0,0 +1,17 @@
class ExaminationBank < ApplicationRecord
belongs_to :user
belongs_to :sub_discipline
has_many :tag_discipline_containers, as: :container, dependent: :destroy
has_many :tag_disciplines, through: :tag_discipline_containers
has_many :examination_items, dependent: :destroy
def question_count
examination_items.size
end
def total_score
examination_items.pluck(:score).sum
end
end

@ -0,0 +1,27 @@
class ExaminationItem < ApplicationRecord
enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 }
belongs_to :examination_bank, touch: true
belongs_to :item_bank, optional: true
has_many :examination_item_choices, dependent: :destroy
has_one :examination_item_analysis, dependent: :destroy
belongs_to :container, polymorphic: true, optional: true
def analysis
examination_item_analysis&.analysis
end
def item_choices
examination_item_choices
end
def public
0
end
def quotes
0
end
end

@ -0,0 +1,3 @@
class ExaminationItemAnalysis < ApplicationRecord
belongs_to :examination_item
end

@ -0,0 +1,3 @@
class ExaminationItemChoice < ApplicationRecord
belongs_to :examination_item
end

@ -15,8 +15,14 @@ class Hack < ApplicationRecord
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
# 消息
has_many :tidings, as: :container
# 知识点
has_many :tag_discipline_containers, as: :container, dependent: :destroy
belongs_to :user
belongs_to :sub_discipline
has_one :item_bank, as: :container, dependent: :destroy
has_one :examination_bank, as: :container, dependent: :destroy
scope :published, -> { where(status: 1) }
scope :unpublish, -> { where(status: 0) }
@ -51,4 +57,31 @@ class Hack < ApplicationRecord
user_id == user.id || user.admin_or_business?
end
# 复制fork
def fork
new_hack = Hack.new
new_hack.attributes = self.attributes.dup.except("id", "user_id", "status", "identifier", "comments_count", "praises_count",
"pass_num", "created_at", "updated_at", "hack_user_lastest_codes_count",
"open_or_not", "submit_num")
new_hack.user_id = User.current.id
new_hack.identifier = Util::UUID.generate_identifier(Hack, 8)
new_hack.fork_id = self.id
new_hack.save!
# 创建测试集与代码
hack_sets.each do |set|
new_hack.hack_sets.create!(input: set.input, output: set.output, position: set.position)
end
# 新建知识点
tag_discipline_containers.each do |tag|
new_hack.tag_discipline_containers.create!(tag_discipline_id: tag.tag_discipline_id)
end
hack_codes.each do |code|
new_hack.hack_codes.create!(code: code.code, language: code.language, modify_time: Time.now)
end
new_hack
end
end

@ -1,11 +1,20 @@
class ItemBank < ApplicationRecord
# difficulty: 1 简单 2 适中 3 困难
# item_type: 0 单选 1 多选 2 判断 3 填空 4 简答 5 实训 6 编程
enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 }
# item_type: 0 单选 1 多选 2 判断 3 填空 4 简答 5 实训 6 编程
belongs_to :user
belongs_to :sub_discipline
has_one :item_analysis, dependent: :destroy
has_many :item_choices, dependent: :destroy
has_many :item_baskets, dependent: :destroy
has_many :tag_discipline_containers, as: :container, dependent: :destroy
has_many :tag_disciplines, through: :tag_discipline_containers
belongs_to :container, polymorphic: true, optional: true
def analysis
item_analysis&.analysis
end
end

@ -1,4 +1,14 @@
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
end

@ -0,0 +1,9 @@
class SubDiscipline < ApplicationRecord
belongs_to :discipline
has_many :tag_disciplines, dependent: :destroy
has_one :hack
has_many :shixun_tag_disciplines, -> { where("shixun = 1") }, class_name: "TagDiscipline"
has_many :subject_tag_disciplines, -> { where("subject = 1") }, class_name: "TagDiscipline"
has_many :question_tag_disciplines, -> { where("question = 1") }, class_name: "TagDiscipline"
end

@ -0,0 +1,11 @@
class TagDiscipline < ApplicationRecord
belongs_to :sub_discipline
belongs_to :user, optional: true
has_many :tag_discipline_containers, dependent: :destroy
validates_presence_of :name
def discipline
sub_discipline&.discipline
end
end

@ -0,0 +1,5 @@
class TagDisciplineContainer < ApplicationRecord
belongs_to :tag_discipline
belongs_to :container, polymorphic: true, optional: true
end

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

@ -0,0 +1,32 @@
class ExaminationBankQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :updated_at, default_by: :updated_at, default_direction: :desc, default_table: 'examination_banks'
def initialize(params)
@params = params
end
def call
if params[:public].to_i == 1
exams = ExaminationBank.where(public: 1)
elsif params[:public].to_i == 0
exams = ExaminationBank.where(user_id: User.current.id)
end
if params[:tag_discipline_id].present?
exams = exams.joins(:tag_discipline_containers).where(tag_discipline_containers: {tag_discipline_id: params[:tag_discipline_id]})
elsif params[:sub_discipline_id].present?
exams = exams.where(sub_discipline_id: params[:sub_discipline_id])
elsif params[:discipline_id].present?
exams = exams.joins(:sub_discipline).where(sub_disciplines: {discipline_id: params[:discipline_id]})
end
exams = exams.where(difficulty: params[:difficulty].to_i) if params[:difficulty].present?
exams = exams.where("name like ?", "%#{params[:keyword].strip}%") if params[:keyword].present?
custom_sort(exams, params[:sort_by], params[:sort_direction])
end
end

@ -0,0 +1,37 @@
class ItemBankQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :updated_at, default_by: :updated_at, default_direction: :desc, default_table: 'item_banks'
def initialize(params)
@params = params
end
def call
if params[:public].to_i == 1
items = ItemBank.where(public: 1)
elsif params[:public].to_i == 0
items = ItemBank.where(user_id: User.current.id)
end
if params[:tag_discipline_id].present?
items = items.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])
elsif params[:discipline_id].present?
items = items.joins(:sub_discipline).where(sub_disciplines: {discipline_id: params[:discipline_id]})
end
if params[:oj_status].present? && params[:item_type] == "PROGRAM"
items = items.joins("join hacks on hacks.id=item_banks.container_id and item_banks.container_type='Hack'").where(hacks: {status: params[:oj_status]})
end
items = items.where(item_type: params[:item_type]) if params[:item_type].present?
items = items.where(difficulty: params[:difficulty].to_i) if params[:difficulty].present?
items = items.where("name like ?", "%#{params[:keyword].strip}%") if params[:keyword].present?
custom_sort(items, params[:sort_by], params[:sort_direction])
end
end

@ -0,0 +1,52 @@
class Admins::ImportDisciplineService < ApplicationService
Error = Class.new(StandardError)
attr_reader :file, :result
def initialize(file)
@file = file
@result = { success: 0, fail: [] }
end
def call
raise Error, '文件不存在' if file.blank?
excel = Admins::ImportDisciplineExcel.new(file)
excel.read_each(&method(:save_discipline))
result
rescue ApplicationImport::Error => ex
raise Error, ex.message
end
private
def save_discipline(data)
count = 0
discipline_name = data.discipline_name.to_s.strip
sub_discipline_name = data.sub_discipline_name.to_s.strip
return unless discipline_name.present?
discipline = Discipline.find_by(name: discipline_name)
if discipline.blank?
discipline = Discipline.create!(name: discipline_name)
count += 1
end
if sub_discipline_name.present?
sub_discipline = SubDiscipline.find_by(name: discipline_name, discipline: discipline)
if sub_discipline.blank?
SubDiscipline.create!(name: sub_discipline_name, discipline: discipline)
count += 1
end
end
result[:success] += count
rescue Exception => ex
fail_data = data.as_json
fail_data[:data] = fail_data.values.join(',')
fail_data[:message] = ex.message
result[:fail] << fail_data
end
end

@ -0,0 +1,30 @@
class ExaminationBanks::SaveExaminationBankService < ApplicationService
attr_reader :exam, :params
def initialize(exam, params)
@exam = exam
@params = params
end
def call
ExaminationBanks::SaveExamForm.new(params).validate!
exam.name = params[:name].to_s.strip
exam.difficulty = params[:difficulty]
exam.duration = params[:duration].present? ? params[:duration].to_i : nil
exam.sub_discipline_id = params[:sub_discipline_id]
exam.save!
# 知识点的创建
new_tag_discipline_ids = params[:tag_discipline_id] || []
old_tag_discipline_ids = exam.tag_discipline_containers.pluck(:tag_discipline_id)
delete_tag_discipline_ids = old_tag_discipline_ids - new_tag_discipline_ids
create_tag_discipline_ids = new_tag_discipline_ids - old_tag_discipline_ids
exam.tag_discipline_containers.where(tag_discipline_id: delete_tag_discipline_ids).destroy_all
create_tag_discipline_ids.each do |tag_id|
exam.tag_discipline_containers << TagDisciplineContainer.new(tag_discipline_id: tag_id)
end
exam
end
end

@ -0,0 +1,58 @@
class ItemBanks::SaveItemService < ApplicationService
attr_reader :item, :params
def initialize(item, params)
@item = item
@params = params
end
def call
new_record = item.new_record?
raise("不能更改题型") if !new_record && item.item_type != params[:item_type]
ItemBanks::SaveItemForm.new(params).validate!
ActiveRecord::Base.transaction do
item.item_type = params[:item_type] if new_record
item.difficulty = params[:difficulty]
item.sub_discipline_id = params[:sub_discipline_id]
item.name = params[:name].to_s.strip
item.save!
analysis = item.item_analysis || ItemAnalysis.new(item_bank_id: item.id)
analysis.analysis = params[:analysis].blank? ? nil : params[:analysis].strip
analysis.save
# 知识点的创建
new_tag_discipline_ids = params[:tag_discipline_id]
old_tag_discipline_ids = item.tag_discipline_containers.pluck(:tag_discipline_id)
delete_tag_discipline_ids = old_tag_discipline_ids - new_tag_discipline_ids
create_tag_discipline_ids = new_tag_discipline_ids - old_tag_discipline_ids
item.tag_discipline_containers.where(tag_discipline_id: delete_tag_discipline_ids).destroy_all
create_tag_discipline_ids.each do |tag_id|
item.tag_discipline_containers << TagDisciplineContainer.new(tag_discipline_id: tag_id)
end
# 选项的创建
if %W(SINGLE MULTIPLE JUDGMENT).include?(item.item_type)
old_choices = item.item_choices
new_choices = params[:choices]
new_choices.each_with_index do |choice, index|
choice_item = old_choices[index] || ItemChoice.new(item_bank_id: item.id)
choice_item.choice_text = choice[:choice_text]
choice_item.is_answer = choice[:is_answer]
choice_item.save!
end
if old_choices.length > new_choices.length
old_choices[new_choices.length..(old_choices.length-1)].each do |old_choice|
old_choice.destroy
end
end
end
end
item
end
end

@ -0,0 +1,47 @@
class ItemBaskets::SaveItemBasketService < ApplicationService
attr_reader :user, :params
def initialize(user, params)
@user = user
@params = params
end
def call
raise("请选择试题") if params[:item_ids].blank?
# 只能选用公共题库或是自己的题库
items = ItemBank.where(public: 1).or(ItemBank.where(user_id: user.id))
# 已选到过试题篮的不重复选用
item_ids = params[:item_ids] - user.item_baskets.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.score = item_score item.item_type
new_item.position = item_position item.item_type
new_item.save!
end
end
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
else
score =
case item_type
when "SINGLE", "MULTIPLE", "JUDGMENT"
5
when "PROGRAM"
10
else
5
end
end
score
end
def item_position item_type
user.item_baskets.where(item_type: item_type).last&.position.to_i + 1
end
end

@ -26,7 +26,14 @@ class CreateShixunService < ApplicationService
# 创建镜像
ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id)
# 创建主服务配置
ShixunServiceConfig.create!(:shixun_id => shixun.id, :mirror_repository_id => main_mirror.id)
config_params =
if shixun.is_jupyter?
{shixun_id: shixun.id, mirror_repository_id: main_mirror.id, cpu_limit: 2,
memory_limit: 1024, lower_cpu_limit: 0.2, request_limit: 100}
else
{shixun_id: shixun.id, mirror_repository_id: main_mirror.id}
end
ShixunServiceConfig.create!(config_params)
# 创建子镜像相关数据(实训镜像关联表,子镜像服务配置)
sub_mirrors.each do |sub|
ShixunMirrorRepository.create!(:shixun_id => shixun.id, :mirror_repository_id => sub.id)

@ -0,0 +1,2 @@
$.notify({ message: '删除成功' });
$(".discipline-item-<%= @discipline_id %>").remove();

@ -0,0 +1,2 @@
$('.admin-modal-container').html("<%= j( render partial: 'admins/disciplines/shared/edit_discipline_modal', locals: { discipline: @discipline } ) %>");
$('.modal.admin-edit-discipline-modal').modal('show');

@ -0,0 +1,17 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('课程方向', admins_disciplines_path) %>
<% end %>
<div class="box search-form-container discipline-list-form rig">
<div class="flex-1">
<%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-discipline-modal' } %>
</div>
<%= javascript_void_link '导入数据', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-import-discipline-modal'} %>
</div>
<div class="box admin-list-container discipline-list-container">
<%= render(partial: 'admins/disciplines/shared/list') %>
</div>
<%= render 'admins/disciplines/shared/create_discipline_modal' %>
<%= render partial: 'admins/disciplines/shared/import_discipline_modal' %>

@ -0,0 +1,28 @@
<div class="modal fade admin-create-discipline-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新建课程方向</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-create-discipline-form" data-url="<%= admins_disciplines_path %>">
<div class="form-group d-flex">
<label for="new_mirror_id" class="col-form-label">名称:</label>
<div class="w-75 d-flex flex-column">
<%= text_field_tag(:name, nil, class: 'form-control', placeholder: '请输入名称') %>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
<div class="modal fade admin-edit-discipline-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">编辑课程方向</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<%= simple_form_for([:admins, discipline], html: { class: 'admin-edit-discipline-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
<%= f.input :name, as: :string, label: '名称' %>
<div class="error text-danger"></div>
<% end %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,30 @@
<div class="modal fade admin-import-discipline-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">导入数据</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-import-discipline-form" enctype="multipart/form-data">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">文件</span>
</div>
<div class="custom-file">
<input type="file" name="file" class="upload-file-input" id="import-discipline-input" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
<label class="custom-file-label file-names" for="import-discipline-input">选择文件</label>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,33 @@
<table class="table table-hover text-center discipline-list-table">
<thead class="thead-light">
<tr>
<th width="6%">序号</th>
<th width="54%" class="text-left">课程方向</th>
<th width="8%">实践课程</th>
<th width="8%">实训</th>
<th width="8%">题库</th>
<th width="16%">操作</th>
</tr>
</thead>
<tbody>
<% if @disciplines.present? %>
<% @disciplines.each_with_index do |discipline, index| %>
<tr class="discipline-item discipline-item-<%= discipline.id %>">
<td><%= index + 1 %></td>
<td class="text-left">
<span><%= link_to discipline.name, admins_sub_disciplines_path(discipline_id: discipline), :title => discipline.name %></span>
</td>
<td><%= check_box_tag :subject,!discipline.subject,discipline.subject,remote:true,data:{id:discipline.id},class:"discipline-source-form" %></td>
<td><%= check_box_tag :shixun,!discipline.shixun,discipline.shixun,remote:true,data:{id:discipline.id},class:"discipline-source-form" %></td>
<td><%= check_box_tag :question,!discipline.question,discipline.question,remote:true,data:{id:discipline.id},class:"discipline-source-form" %></td>
<td>
<%= link_to '编辑', edit_admins_discipline_path(discipline), remote: true, class: 'action' %>
<%= delete_link '删除', admins_discipline_path(discipline, element: ".discipline-item-#{discipline.id}"), class: 'delete-discipline-action' %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>

@ -0,0 +1,2 @@
$('.modal.admin-edit-discipline-modal').modal("hide");
$(".discipline-list-container").html("<%= j(render :partial => 'admins/disciplines/shared/list') %>");

@ -38,6 +38,8 @@
<% end %>
</li>
<li><%= sidebar_item(admins_disciplines_path, '课程体系', icon: 'sitemap', controller: 'admins-disciplines') %></li>
<li>
<%= sidebar_item_group('#course-submenu', '课堂管理', icon: 'book') do %>
<li><%= sidebar_item(admins_course_lists_path, '课程列表', icon: 'list', controller: 'admins-course_lists') %></li>

@ -0,0 +1,2 @@
$.notify({ message: '删除成功' });
$(".sub-discipline-item-<%= @sub_discipline_id %>").remove();

@ -0,0 +1,2 @@
$('.admin-modal-container').html("<%= j( render partial: 'admins/sub_disciplines/shared/edit_sub_discipline_modal', locals: { sub_discipline: @sub_discipline } ) %>");
$('.modal.admin-edit-sub-discipline-modal').modal('show');

@ -0,0 +1,14 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('课程方向', admins_disciplines_path) %>
<% add_admin_breadcrumb(@discipline.name) %>
<% end %>
<div class="box search-form-container sub-discipline-list-form">
<%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-sub-discipline-modal' } %>
</div>
<div class="box admin-list-container sub-discipline-list-container">
<%= render(partial: 'admins/sub_disciplines/shared/list') %>
</div>
<%= render 'admins/sub_disciplines/shared/create_sub_discipline_modal' %>

@ -0,0 +1,28 @@
<div class="modal fade admin-create-sub-discipline-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新建课程</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-create-sub-discipline-form" data-url="<%= admins_sub_disciplines_path(discipline_id: @discipline) %>">
<div class="form-group d-flex">
<label for="new_mirror_id" class="col-form-label">名称:</label>
<div class="w-75 d-flex flex-column">
<%= text_field_tag(:name, nil, class: 'form-control', placeholder: '请输入名称') %>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
<div class="modal fade admin-edit-sub-discipline-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">编辑课程</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<%= simple_form_for([:admins, sub_discipline], html: { class: 'admin-edit-sub-discipline-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
<%= f.input :name, as: :string, label: '名称' %>
<div class="error text-danger"></div>
<% end %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,33 @@
<table class="table table-hover text-center sub-discipline-list-table">
<thead class="thead-light">
<tr>
<th width="6%">序号</th>
<th width="54%" class="text-left">课程</th>
<th width="8%">实践课程</th>
<th width="8%">实训</th>
<th width="8%">题库</th>
<th width="16%">操作</th>
</tr>
</thead>
<tbody>
<% if @sub_disciplines.present? %>
<% @sub_disciplines.each_with_index do |sub, index| %>
<tr class="sub-discipline-item sub-discipline-item-<%= sub.id %>">
<td><%= index + 1 %></td>
<td class="text-left">
<span><%= link_to sub.name, admins_tag_disciplines_path(sub_discipline_id: sub), :title => sub.name %></span>
</td>
<td><%= check_box_tag :subject,!sub.subject,sub.subject,disabled:!sub.discipline&.subject,remote:true,data:{id:sub.id},class:"sub-discipline-source-form" %></td>
<td><%= check_box_tag :shixun,!sub.shixun,sub.shixun,disabled:!sub.discipline&.shixun,remote:true,data:{id:sub.id},class:"sub-discipline-source-form" %></td>
<td><%= check_box_tag :question,!sub.question,sub.question,disabled:!sub.discipline&.question,remote:true,data:{id:sub.id},class:"sub-discipline-source-form" %></td>
<td>
<%= link_to '编辑', edit_admins_sub_discipline_path(sub), remote: true, class: 'action' %>
<%= delete_link '删除', admins_sub_discipline_path(sub, element: ".sub-discipline-item-#{sub.id}"), class: 'delete-sub-discipline-action' %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>

@ -0,0 +1,2 @@
$('.modal.admin-edit-sub-discipline-modal').modal("hide");
$(".sub-discipline-list-container").html("<%= j(render :partial => 'admins/sub_disciplines/shared/list') %>");

@ -0,0 +1,2 @@
$.notify({ message: '删除成功' });
$(".tag-discipline-item-<%= @tag_discipline_id %>").remove();

@ -0,0 +1,2 @@
$('.admin-modal-container').html("<%= j( render partial: 'admins/tag_disciplines/shared/edit_tag_discipline_modal', locals: { tag_discipline: @tag_discipline } ) %>");
$('.modal.admin-edit-tag-discipline-modal').modal('show');

@ -0,0 +1,15 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('课程方向', admins_disciplines_path) %>
<% add_admin_breadcrumb(@sub_discipline&.discipline&.name, admins_sub_disciplines_path(discipline_id: @sub_discipline&.discipline_id)) %>
<% add_admin_breadcrumb(@sub_discipline.name) %>
<% end %>
<div class="box search-form-container tag-discipline-list-form">
<%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-tag-discipline-modal' } %>
</div>
<div class="box admin-list-container tag-discipline-list-container">
<%= render(partial: 'admins/tag_disciplines/shared/list') %>
</div>
<%= render 'admins/tag_disciplines/shared/create_tag_discipline_modal' %>

@ -0,0 +1,28 @@
<div class="modal fade admin-create-tag-discipline-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新建知识点</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-create-tag-discipline-form" data-url="<%= admins_tag_disciplines_path(sub_discipline_id: @sub_discipline) %>">
<div class="form-group d-flex">
<label for="new_mirror_id" class="col-form-label">名称:</label>
<div class="w-75 d-flex flex-column">
<%= text_field_tag(:name, nil, class: 'form-control', placeholder: '请输入名称') %>
</div>
</div>
<div class="error text-danger"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
<div class="modal fade admin-edit-tag-discipline-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">编辑知识点</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<%= simple_form_for([:admins, tag_discipline], html: { class: 'admin-edit-tag-discipline-form' }, defaults: { wrapper_html: { class: 'offset-md-1 col-md-10' } }) do |f| %>
<%= f.input :name, as: :string, label: '名称' %>
<div class="error text-danger"></div>
<% end %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary submit-btn">确认</button>
</div>
</div>
</div>
</div>

@ -0,0 +1,48 @@
<table class="table table-hover text-center tag-discipline-list-table">
<thead class="thead-light">
<tr>
<th width="6%">序号</th>
<th width="42%" class="text-left">知识点</th>
<th width="12%">创建者</th>
<th width="8%">实践课程</th>
<th width="8%">实训</th>
<th width="8%">题库</th>
<th width="16%">操作</th>
</tr>
</thead>
<tbody>
<% if @tag_disciplines.present? %>
<% @tag_disciplines.each_with_index do |tag, index| %>
<tr class="tag-discipline-item tag-discipline-item-<%= tag.id %>">
<td><%= index + 1 %></td>
<td class="text-left"><%= tag.name %></td>
<td>
<% if tag.user.present? %>
<%= link_to tag.user.real_name, user_path(tag.user), :target => "_blank" %>
<% else %>
--
<% end %>
</td>
<td>
<% disabled = !(tag.sub_discipline&.subject && tag.discipline&.subject) %>
<%= check_box_tag :subject,!tag.subject,tag.subject,disabled:disabled,remote:true,data:{id:tag.id},class:"tag-discipline-source-form" %>
</td>
<td>
<% disabled = !(tag.sub_discipline&.shixun && tag.discipline&.shixun) %>
<%= check_box_tag :shixun,!tag.shixun,tag.shixun,disabled:disabled,remote:true,data:{id:tag.id},class:"tag-discipline-source-form" %>
</td>
<td>
<% disabled = !(tag.sub_discipline&.question && tag.discipline&.question) %>
<%= check_box_tag :question,!tag.question,tag.question,disabled:disabled,remote:true,data:{id:tag.id},class:"tag-discipline-source-form" %>
</td>
<td>
<%= link_to '编辑', edit_admins_tag_discipline_path(tag), remote: true, class: 'action' %>
<%= delete_link '删除', admins_tag_discipline_path(tag, element: ".tag-discipline-item-#{tag.id}"), class: 'delete-tag-discipline-action' %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>

@ -0,0 +1,2 @@
$('.modal.admin-edit-tag-discipline-modal').modal("hide");
$(".tag-discipline-list-container").html("<%= j(render :partial => 'admins/tag_disciplines/shared/list') %>");

@ -0,0 +1,35 @@
case params[:source]
when 'subject'
disciplines = Discipline.where(subject: 1).includes(subject_sub_disciplines: :subject_tag_disciplines)
json.disciplines disciplines do |dis|
json.(dis, :id, :name)
json.sub_disciplines dis.subject_sub_disciplines do |sub|
json.(sub, :id, :name)
json.tag_disciplines sub.subject_tag_disciplines do |tag|
json.(tag, :id, :name)
end
end
end
when 'shixun'
disciplines = Discipline.where(shixun: 1).includes(shixun_sub_disciplines: :shixun_tag_disciplines)
json.disciplines disciplines do |dis|
json.(dis, :id, :name)
json.sub_disciplines dis.shixun_sub_disciplines do |sub|
json.(sub, :id, :name)
json.tag_disciplines sub.shixun_tag_disciplines do |tag|
json.(tag, :id, :name)
end
end
end
when 'question'
disciplines = Discipline.where(question: 1).includes(question_sub_disciplines: :question_tag_disciplines)
json.disciplines disciplines do |dis|
json.(dis, :id, :name)
json.sub_disciplines dis.question_sub_disciplines do |sub|
json.(sub, :id, :name)
json.tag_disciplines sub.question_tag_disciplines do |tag|
json.(tag, :id, :name)
end
end
end
end

@ -0,0 +1,11 @@
json.exams @exams.each do |exam|
json.(exam, :id, :name, :difficulty, :quotes)
json.question_count exam.question_count
json.total_score exam.total_score
json.update_time exam.updated_at&.strftime("%Y-%m-%d %H:%M")
json.author do
json.login exam.user&.login
json.name exam.user&.full_name
end
end
json.exam_count @exams_count

@ -0,0 +1,51 @@
json.single_questions do
json.questions @single_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question}
end
json.questions_score @single_questions.map(&:score).sum
json.questions_count @single_questions.size
end
json.multiple_questions do
json.questions @multiple_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question}
end
json.questions_score @multiple_questions.map(&:score).sum
json.questions_count @multiple_questions.size
end
json.judgement_questions do
json.questions @judgement_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question}
end
json.questions_score @judgement_questions.map(&:score).sum
json.questions_count @judgement_questions.size
end
json.program_questions do
json.questions @program_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question}
end
json.questions_score @program_questions.map(&:score).sum
json.questions_count @program_questions.size
end
json.exam do
json.(@exam, :id, :name, :difficulty, :sub_discipline_id, :duration)
json.all_score @items.map(&:score).sum
json.all_questions_count @items.size
json.discipline do
json.(@exam.sub_discipline&.discipline, :id, :name)
end
json.sub_discipline do
json.(@exam.sub_discipline, :id, :name)
end
json.tag_disciplines @exam.tag_disciplines do |tag|
json.(tag, :id, :name)
end
end

@ -1,5 +1,8 @@
# 编程内容
json.(@hack, :name, :description, :language, :difficult, :category, :time_limit, :open_or_not, :status)
json.(@hack, :name, :description, :language, :difficult, :category, :time_limit, :open_or_not, :status, :sub_discipline_id)
# 课程
json.tag_discipline_id @hack.tag_discipline_containers.pluck(:tag_discipline_id)
# 代码
json.language @hack.language

@ -16,6 +16,7 @@ json.is_evaluation @is_evaluation ? @is_evaluation : false
json.work_public @homework.work_public
json.allow_late @homework.allow_late
if @user_course_identity < Course::STUDENT
json.calculation_time @homework.calculation_time if @homework.homework_type == "practice"

@ -0,0 +1,11 @@
json.(item, :name, :item_type, :difficulty, :public, :quotes)
json.analysis item.analysis
json.choices item.item_choices do |choice|
json.choice_text choice.choice_text
json.is_answer choice.is_answer
end
if item.item_type == "PROGRAM"
json.program_attr do
json.(item.container, :description, :language, :difficult, :status, :identifier)
end
end

@ -0,0 +1,15 @@
json.discipline do
json.(@item.sub_discipline&.discipline, :id, :name)
end
json.sub_discipline do
json.(@item.sub_discipline, :id, :name)
end
json.tag_disciplines @item.tag_disciplines do |tag|
json.(tag, :id, :name)
end
json.(@item, :id, :name, :item_type, :difficulty)
json.analysis @item.analysis
json.choices @item.item_choices do |choice|
json.choice_text choice.choice_text
json.is_answer choice.is_answer
end

@ -0,0 +1,11 @@
json.items @items.each do |item|
json.id item.id
json.partial! "item_banks/item", locals: {item: item}
json.update_time item.updated_at&.strftime("%Y-%m-%d %H:%M")
json.choosed @item_basket_ids.include?(item.id)
json.author do
json.login item.user&.login
json.name item.user&.full_name
end
end
json.items_count @items_count

@ -0,0 +1,7 @@
json.single_questions_count @single_questions_count
json.multiple_questions_count @multiple_questions_count
json.judgement_questions_count @judgement_questions_count
json.completion_questions_count @completion_questions_count
json.subjective_questions_count @subjective_questions_count
json.practical_questions_count @practical_questions_count
json.program_questions_count @program_questions_count

@ -0,0 +1,3 @@
json.status 0
json.questions_score @questions_score
json.all_score @all_score

@ -0,0 +1,38 @@
json.single_questions do
json.questions @single_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question.item_bank}
end
json.questions_score @single_questions.map(&:score).sum
json.questions_count @single_questions.size
end
json.multiple_questions do
json.questions @multiple_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question.item_bank}
end
json.questions_score @multiple_questions.map(&:score).sum
json.questions_count @multiple_questions.size
end
json.judgement_questions do
json.questions @judgement_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question.item_bank}
end
json.questions_score @judgement_questions.map(&:score).sum
json.questions_count @judgement_questions.size
end
json.program_questions do
json.questions @program_questions.each do |question|
json.(question, :id, :position, :score, :item_type)
json.partial! "item_banks/item", locals: {item: question.item_bank}
end
json.questions_score @program_questions.map(&:score).sum
json.questions_count @program_questions.size
end
json.all_score @item_baskets.map(&:score).sum
json.all_questions_count @item_baskets.size

@ -0,0 +1,3 @@
json.status 0
json.questions_score @questions_score
json.all_score @all_score

@ -55,10 +55,33 @@ Rails.application.routes.draw do
end
end
resources :disciplines, only: [:index]
resources :tag_disciplines, only: [:create]
resources :item_banks do
member do
post :set_public
end
end
resources :item_baskets do
collection do
get :basket_list
delete :delete_item_type
post :batch_set_score
end
member do
post :set_score
post :adjust_position
end
end
resources :examination_banks do
member do
post :set_public
end
end
resources :hacks, path: :problems, param: :identifier do
collection do
@ -1059,6 +1082,7 @@ Rails.application.routes.draw do
post :reset_login_times
end
end
resource :import_disciplines, only: [:create]
resource :import_users, only: [:create]
resource :import_course_members, only: [:create]
resources :user_statistics, only: [:index] do
@ -1255,6 +1279,10 @@ Rails.application.routes.draw do
resources :courses, only: [:index, :destroy, :update]
resources :projects, only: [:index, :destroy]
resources :disciplines, only: [:index, :create, :edit, :update, :destroy]
resources :sub_disciplines, only: [:index, :create, :edit, :update, :destroy]
resources :tag_disciplines, only: [:index, :create, :edit, :update, :destroy]
end
namespace :cooperative do

@ -0,0 +1,7 @@
class MigrateItemBankColumn < ActiveRecord::Migration[5.2]
def change
remove_column :item_banks, :curriculum_id
remove_column :item_banks, :curriculum_direction_id
add_column :item_banks, :sub_repertoire_id, :integer, index: true
end
end

@ -0,0 +1,7 @@
class ChangeItemBankPublicDefault < ActiveRecord::Migration[5.2]
def change
change_column_default :item_banks, :public, 0
change_column_default :item_banks, :quotes, 0
change_column_default :item_banks, :difficulty, 1
end
end

@ -0,0 +1,7 @@
class AddPositionScoreToBasket < ActiveRecord::Migration[5.2]
def change
add_column :item_baskets, :position, :integer, default: 0
add_column :item_baskets, :score, :float, default: 0
add_column :item_baskets, :item_type, :integer, default: 0
end
end

@ -0,0 +1,12 @@
class CreateDisciplines < ActiveRecord::Migration[5.2]
def change
create_table :disciplines do |t|
t.string :name
t.boolean :subject
t.boolean :shixun
t.boolean :question
t.timestamps
end
end
end

@ -0,0 +1,13 @@
class CreateSubDisciplines < ActiveRecord::Migration[5.2]
def change
create_table :sub_disciplines do |t|
t.references :discipline, index: true
t.string :name
t.boolean :subject
t.boolean :shixun
t.boolean :question
t.timestamps
end
end
end

@ -0,0 +1,13 @@
class CreateTagDisciplines < ActiveRecord::Migration[5.2]
def change
create_table :tag_disciplines do |t|
t.references :sub_discipline, index: true
t.string :name
t.boolean :subject
t.boolean :shixun
t.boolean :question
t.timestamps
end
end
end

@ -0,0 +1,6 @@
class MigrateItemBankTagColumn < ActiveRecord::Migration[5.2]
def change
remove_column :item_banks, :sub_repertoire_id
add_column :item_banks, :sub_discipline_id, :integer, index: true
end
end

@ -0,0 +1,13 @@
class CreateTagDisciplineContainers < ActiveRecord::Migration[5.2]
def change
create_table :tag_discipline_containers do |t|
t.references :tag_discipline, index: true
t.integer :container_id
t.string :container_type
t.timestamps
end
add_index :tag_discipline_containers, [:container_type, :container_id], name: "index_on_container"
end
end

@ -0,0 +1,15 @@
class MigrateDisciplinesDefault < ActiveRecord::Migration[5.2]
def change
change_column_default :disciplines, :subject, from: nil, to: 1
change_column_default :disciplines, :shixun, from: nil, to: 1
change_column_default :disciplines, :question, from: nil, to: 1
change_column_default :sub_disciplines, :subject, from: nil, to: 1
change_column_default :sub_disciplines, :shixun, from: nil, to: 1
change_column_default :sub_disciplines, :question, from: nil, to: 1
change_column_default :tag_disciplines, :subject, from: nil, to: 1
change_column_default :tag_disciplines, :shixun, from: nil, to: 1
change_column_default :tag_disciplines, :question, from: nil, to: 1
end
end

@ -0,0 +1,15 @@
class CreateExaminationBanks < ActiveRecord::Migration[5.2]
def change
create_table :examination_banks do |t|
t.string :name
t.integer :difficulty, default: 1
t.references :user, index: true
t.boolean :public, default: 0
t.integer :quotes, default: 0
t.references :sub_discipline, index: true
t.integer :duration
t.timestamps
end
end
end

@ -0,0 +1,15 @@
class CreateExaminationItems < ActiveRecord::Migration[5.2]
def change
create_table :examination_items do |t|
t.references :examination_bank, index: true
t.references :item_bank, index: true
t.string :name
t.integer :item_type, default: 0
t.integer :difficulty, default: 1
t.float :score, default: 0
t.integer :position, default: 0
t.timestamps
end
end
end

@ -0,0 +1,11 @@
class CreateExaminationItemChoices < ActiveRecord::Migration[5.2]
def change
create_table :examination_item_choices do |t|
t.references :examination_item, index: true
t.text :choice_text
t.boolean :is_answer, default: 0
t.timestamps
end
end
end

@ -0,0 +1,5 @@
class AddSubDisciplineIdForHacks < ActiveRecord::Migration[5.2]
def change
add_column :hacks, :sub_discipline_id, :integer
end
end

@ -0,0 +1,5 @@
class AddUserIdToTagDiscipline < ActiveRecord::Migration[5.2]
def change
add_column :tag_disciplines, :user_id, :integer
end
end

@ -0,0 +1,6 @@
class MigrateItemType < ActiveRecord::Migration[5.2]
def change
add_column :item_banks, :container_id, :integer
add_column :item_banks, :container_type, :string
end
end

@ -0,0 +1,7 @@
class AddForkIdToHacks < ActiveRecord::Migration[5.2]
def change
add_column :hacks, :fork_id, :integer, default: 0
add_column :examination_items, :container_id, :integer
add_column :examination_items, :container_type, :string
end
end

@ -0,0 +1,10 @@
class CreateExaminationItemAnalyses < ActiveRecord::Migration[5.2]
def change
create_table :examination_item_analyses do |t|
t.references :examination_item, index: true, unique: true
t.text :analysis
t.timestamps
end
end
end

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save