dev_aliyun_beta
SylorHuang 5 years ago
commit e55606426a

@ -13,6 +13,9 @@
//= require bootstrap-datepicker
//= require bootstrap.viewer
//= require lib/codemirror
//= require mode/shell/shell
//= require_tree ./i18n
//= require_tree ./admins
@ -32,11 +35,19 @@ $(document).on('turbolinks:load', function(){
// flash alert提示框自动关闭
if($('.admin-alert-container .alert').length > 0){
setTimeout(function(){
$('.admin-alert-container .alert').alert('close');
$('.admin-alert-container .alert:not(.alert-danger)').alert('close');
}, 2000);
setTimeout(function(){
$('.admin-alert-container .alert.alert-danger').alert('close');
}, 5000);
}
});
$(document).on("turbolinks:before-cache", function () {
$('[data-toggle="tooltip"]').tooltip('hide');
$('[data-toggle="popover"]').popover('hide');
});
// var progressBar = new Turbolinks.ProgressBar();
// $(document).on('ajax:send', function(event){

@ -0,0 +1,19 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-mirror-repositories-edit-page, body.admins-mirror-repositories-update-page').length > 0) {
var $form = $('form.edit-mirror');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"mirror_repository[type_name]": {
required: true
}
}
});
$form.submit(function(e){
if(!$form.valid()){ e.preventDefault(); }
});
}
});

@ -0,0 +1,4 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-mirror-repositories-index-page').length > 0) {
}
});

@ -0,0 +1,33 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-mirror-scripts-edit-page, body.admins-mirror-scripts-update-page, body.admins-mirror-scripts-new-page, body.admins-mirror-scripts-create-page').length > 0) {
var $form = $('form.script-form');
// codemirror编辑器
var scriptEditor = CodeMirror.fromTextArea(document.getElementById('mirror_script_script'), {
lineNumbers: true,
mode: 'shell',
theme: "default",
indentUnit: 4, //代码缩进为一个tab的距离
matchBrackets: true,
autoRefresh: true,
smartIndent: true,//智能换行
styleActiveLine: true,
lint: true
});
scriptEditor.setSize('auto', '600px');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
"mirror_script[script_type]": {
required: true
}
}
});
$form.submit(function(e){
if(!$form.valid()){ e.preventDefault(); }
});
}
});

@ -0,0 +1,32 @@
$(document).on('turbolinks:load', function() {
$('.admin-modal-container').on('show.bs.modal', '.modal.admin-choose-mirror-modal', function(){
var $modal = $('.modal.admin-choose-mirror-modal');
var $form = $modal.find('form.admin-choose-mirror-form');
var validateForm = function(){
var checkedValue = $form.find('input[name="mirror_number"]:checked').val();
if(checkedValue == undefined){
$modal.find('.error').html('必须选择一种镜像保存!');
return false;
}
return true;
}
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
var url = $form.attr('action');
if (validateForm()) {
$.ajax({
method: 'POST',
dataType: 'script',
url: url,
data: $form.serialize(),
}).done(function(){
$modal.modal('hide');
});
}
});
})
});

@ -0,0 +1,89 @@
$(document).on('turbolinks:load', function() {
var $modal = $('.modal.admin-replace-mirror-modal');
if ($modal.length > 0) {
var $form = $modal.find('form.admin-replace-mirror-form');
var $mirrorIdInput = $modal.find('.modal-body input[name="mirror_id"]');
var $mirrorSelect = $modal.find('.new-mirror-select');
var setMirror = function(id, name){
$mirrorIdInput.val(id);
$form.find('.mirror-id-container').html(id);
$form.find('.mirror-name-container').html(name);
}
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
new_mirror_id: {
required: true
},
},
messages: {
new_mirror_id: {
required: '请选择新镜像'
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function (event) {
var $link = $(event.relatedTarget);
var mirrorId = $link.data('id');
var mirrorName = $link.data('name');
setMirror(mirrorId, mirrorName);
$mirrorSelect.select2('val', ' ');
});
$modal.on('hide.bs.modal', function () {
setMirror('', '');
$mirrorSelect.select2('val', ' ');
$('#new_mirror_id-error').remove();
});
$mirrorSelect.select2({
theme: 'bootstrap4',
placeholder: '输入要合并的镜像名',
minimumInputLength: 1,
ajax: {
url: '/admins/mirror_repositories/for_select',
dataType: 'json',
data: function(params){
return { keyword: params.term };
},
processResults: function(data){
return { results: data.mirrors }
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$('#new_mirror_id-error').remove();
$('#new_mirror_id').val(item.id);
}
return item.name || item.text;
}
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'script',
url: url,
data: $form.serialize(),
}).done(function(){
$modal.modal('hide');
});
}
});
}
});

@ -0,0 +1,2 @@
// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.

@ -5,6 +5,7 @@
@import "select2-bootstrap4.min";
@import "bootstrap-datepicker";
@import "bootstrap-datepicker.standalone";
@import "lib/codemirror";
@import "admins/*";
@ -56,4 +57,9 @@ label.error {
right:20px;
position: absolute;
}
.position-r{position:relative;}
.position-r{position:relative;}
.font-12 { font-size: 12px !important; }
.font-14 { font-size: 14px !important; }
.font-16 { font-size: 16px !important; }
.font-18 { font-size: 18px !important; }

@ -106,5 +106,9 @@
padding: 0.5rem 2rem;
}
}
.CodeMirror {
border: 1px solid #ced4da;
}
}

@ -0,0 +1,3 @@
// Place all the styles related to the homework_banks controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/

@ -0,0 +1,11 @@
class Admins::ChooseMirrorRepositoriesController < Admins::BaseController
def new
@mirror = MirrorRepository.find(params[:mirror_id])
@new_mirror = MirrorOperationRecord.where(mirror_repository_id: @mirror.id, status: 1, user_id: -1).first
end
def create
mirror = MirrorRepository.find(params[:mirror_id])
Admins::ChooseMirrorService.call(mirror, current_user, params[:mirror_number])
end
end

@ -0,0 +1,97 @@
class Admins::MirrorRepositoriesController < Admins::BaseController
before_action :check_shixun_mirrors!, only: [:index]
def index
mirrors = MirrorRepository.all
mirrors = mirrors.reorder(status: :desc, main_type: :desc, type_name: :asc)
@mirrors = paginate mirrors.includes(:mirror_scripts)
@error_mirror_names = MirrorRepository.where(status: 5).pluck(:name)
end
def new
@mirror = MirrorRepository.new
end
def create
@mirror = MirrorRepository.new
Admins::SaveMirrorRepositoryService.call(@mirror, current_user, form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_path(@mirror)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'new'
rescue Admins::SaveMirrorRepositoryService::Error => ex
flash.now[:danger] = ex.message
render 'new'
end
def edit
@mirror = current_mirror
end
def update
@mirror = current_mirror
Admins::SaveMirrorRepositoryService.call(current_mirror, current_user, form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_path(current_mirror)
rescue ActiveRecord::RecordInvalid
flash.now[:danger] = '保存失败'
render 'edit'
rescue Admins::SaveMirrorRepositoryService::Error => ex
flash.now[:danger] = ex.message
render 'edit'
end
def destroy
return render_js_error('该状态下不允许删除') unless current_mirror.deletable?
current_mirror.destroy!
render_delete_success
end
def for_select
mirrors = MirrorRepository.all
keyword = params[:keyword].to_s.strip
mirrors = mirrors.where('name LIKE ?', "%#{keyword}%") if keyword.present?
@mirrors = paginate mirrors
render_ok(count: @mirrors.total_count, mirrors: @mirrors.as_json(only: %i[id name]))
end
def merge
origin_mirror = MirrorRepository.find(params[:mirror_id])
mirror = MirrorRepository.find(params[:new_mirror_id])
ActiveRecord::Base.transaction do
origin_mirror.update!(name: mirror.name, mirrorID: mirror.mirrorID)
mirror.destroy!
end
end
private
def current_mirror
@_current_mirror ||= MirrorRepository.find(params[:id])
end
def form_params
columns = %i[type_name main_type time_limit resource_limit cpu_limit memory_limit description status]
params.require(:mirror_repository).permit(*columns)
end
def check_shixun_mirrors!
return
return unless request.format.html?
Admins::CheckShixunMirrorsService.call
rescue Admins::CheckShixunMirrorsService::Error => e
internal_server_error(e.message)
end
end

@ -0,0 +1,59 @@
class Admins::MirrorScriptsController < Admins::BaseController
helper_method :current_mirror
def index
scripts = current_mirror.mirror_scripts.order(updated_at: :desc)
@scripts = paginate scripts
end
def new
@script = current_mirror.mirror_scripts.new
end
def create
@script = current_mirror.mirror_scripts.new(form_params)
if @script.save
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script)
else
flash[:danger] = '保存失败'
render 'new'
end
end
def edit
@script = current_script
end
def update
@script = current_script
if @script.update(form_params)
flash[:success] = '保存成功'
redirect_to edit_admins_mirror_repository_mirror_script_path(current_mirror, @script)
else
flash[:danger] = '保存失败'
render 'edit'
end
end
def destroy
current_script.destroy!
render_delete_success
end
private
def current_script
@_current_script ||= current_mirror.mirror_scripts.find(params[:id])
end
def current_mirror
@_current_mirror ||= MirrorRepository.find(params[:mirror_repository_id])
end
def form_params
params.require(:mirror_script).permit(:script_type, :description, :script)
end
end

@ -416,7 +416,7 @@ class ApplicationController < ActionController::Base
# 实训主类别列表,自带描述
def shixun_main_type
list = []
mirrors = MirrorRepository.select([:id, :type_name]).published_main_mirror
mirrors = MirrorRepository.select([:id, :type_name, :description]).published_main_mirror
mirrors.try(:each) do |mirror|
list << {id: mirror.id, type_name: mirror.type_name, description: mirror.try(:description)}
end
@ -426,9 +426,9 @@ class ApplicationController < ActionController::Base
# 小类别列表
def shixun_small_type
list = []
mirrors = MirrorRepository.select([:id, :type_name]).published_small_mirror
mirrors = MirrorRepository.select([:id, :type_name, :description]).published_small_mirror
mirrors.try(:each) do |mirror|
list << {id: mirror.id, type_name: mirror.type_name}
list << {id: mirror.id, type_name: mirror.type_name, description: mirror.description}
end
list
end

@ -74,6 +74,7 @@ class ChallengesController < ApplicationController
ChallengeTag.create(:name => tag, :challenge_choose_id => @challenge_choose.id, :challenge_id => @challenge.id)
end
end
@challenge.update_column(:score, @challenge.challenge_chooses.sum(:score))
end
rescue Exception => e
raise ActiveRecord::Rollback
@ -95,6 +96,7 @@ class ChallengesController < ApplicationController
@challenge.update_column(:modify_time, Time.now)
end
@challenge_choose.update_attributes(chooce_params)
@challenge.update_column(:score, @challenge.challenge_chooses.sum(:score))
# 单选多选题的更新
category = @challenge_choose.standard_answer.length > 1 ? 2 : 1
@challenge_choose.update_column(:category, category)
@ -208,6 +210,9 @@ class ChallengesController < ApplicationController
# 测试集的
@shixun.myshixuns.update_all(:system_tip => 0)
end
if params[:challenge][:show_type].to_i == -1
@challenge.update_attributes(picture_path: nil, web_route: nil, expect_picture_path: nil, original_picture_path: nil)
end
# 关卡评测执行文件如果被修改,需要修改脚本内容
script = modify_shixun_script @shixun, @shixun.evaluate_script
@shixun.shixun_info.update_column(:evaluate_script, script)
@ -229,18 +234,21 @@ class ChallengesController < ApplicationController
# {...}, ...]
#}
def crud_answer
raise '参考答案不能为空' if params[:challenge_answer].empty?
raise '占比之和必须为100%' if params[:challenge_answer].map{|a| a[:score]}.sum != 100
ActiveRecord::Base.transaction do
@challenge.challenge_answers.destroy_all if @challenge.challenge_answers
params[:challenge_answer].each_with_index do |answer, index|
# 内容为空不保存
next if answer[:contents].blank?
ChallengeAnswer.create(name: answer[:name], contents: answer[:contents],
level: index+1, score: answer[:score], challenge_id: @challenge.id)
if @challenge.challenge_answers && params[:challenge_answer].blank?
@challenge.challenge_answers.destroy_all
else
raise '参考答案不能为空' if params[:challenge_answer].empty?
raise '占比之和必须为100%' if params[:challenge_answer].map{|a| a[:score]}.sum != 100
ActiveRecord::Base.transaction do
@challenge.challenge_answers.destroy_all if @challenge.challenge_answers
params[:challenge_answer].each_with_index do |answer, index|
# 内容为空不保存
next if answer[:contents].blank?
ChallengeAnswer.create(name: answer[:name], contents: answer[:contents],
level: index+1, score: answer[:score], challenge_id: @challenge.id)
end
end
end
end
# 查看参考答案接口

@ -1,36 +1,34 @@
module Admins::RenderHelper
extend ActiveSupport::Concern
def render_by_format(hash)
format = request.format.symbol
hash.key?(format) ? hash[format].call : hash[:html].call
end
def render_forbidden
respond_to do |format|
format.html { redirect_to '/403' }
format.json { super }
end
render_by_format(html: -> { current_user&.business? ? render('admins/shared/403') : redirect_to('/403') },
json: -> { render status: 403, json: { messages: I18n.t('error.forbidden') } } )
end
def render_not_found
respond_to do |format|
format.html { render 'admins/shared/404' }
format.js { render_js_error('资源未找到') }
format.json { render status: 404, json: { message: '资源未找到' } }
end
render_by_format(html: -> { render 'admins/shared/404' },
js: -> { render_js_error('资源未找到') },
json: -> { render status: 404, json: { message: '资源未找到' } })
end
def render_unprocessable_entity(message)
respond_to do |format|
format.html { render 'admins/shared/422' }
format.js { render_js_error(message) }
format.json { render status: 422, json: { message: message } }
end
render_by_format(html: -> { render 'admins/shared/422' },
js: -> { render_js_error(message) },
json: -> { render status: 422, json: { message: message } })
end
alias_method :render_error, :render_unprocessable_entity
def internal_server_error
respond_to do |format|
format.html { render 'admins/shared/500' }
format.js { render_js_error('系统错误') }
format.json { render status: 500, json: { message: '系统错误' } }
end
def internal_server_error(message = '系统错误')
@message = message
render_by_format(html: -> { render 'admins/shared/500' },
js: -> { render_js_error(message) },
json: -> { render status: 500, json: { message: message } })
end
def render_js_template(template, **opts)

@ -26,17 +26,19 @@ class CoursesController < ApplicationController
:base_info, :get_historical_courses, :create_group_by_importing_file,
:attahcment_category_list,:export_member_scores_excel, :duplicate_course,
:switch_to_teacher, :switch_to_assistant, :switch_to_student, :exit_course,
:informs, :update_informs, :new_informs, :online_learning, :update_task_position, :tasks_list, :join_excellent_course]
:informs, :update_informs, :online_learning, :update_task_position, :tasks_list,
:join_excellent_course, :export_couser_info, :export_member_act_score]
before_action :user_course_identity, except: [:join_excellent_course, :index, :create, :new, :apply_to_join_course,
:search_course_list, :get_historical_course_students, :mine, :search_slim, :board_list]
before_action :teacher_allowed, only: [:update, :destroy, :settings, :search_teacher_candidate,
:transfer_to_course_group, :delete_from_course,
:search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup, :add_teacher]
:transfer_to_course_group, :delete_from_course, :export_member_scores_excel,
:search_users, :add_students_by_search, :get_historical_courses, :add_teacher_popup,
:add_teacher, :export_couser_info, :export_member_act_score]
before_action :admin_allowed, only: [:set_invite_code_halt, :set_public_or_private, :change_course_admin,
:set_course_group, :create_group_by_importing_file, :update_informs, :new_informs,
:update_task_position, :tasks_list]
before_action :teacher_or_admin_allowed, only: [:graduation_group_list, :create_graduation_group, :join_graduation_group,
:change_course_teacher, :export_member_scores_excel, :course_group_list,
:change_course_teacher, :course_group_list,
:teacher_application_review, :apply_teachers, :delete_course_teacher]
before_action :validate_course_name, only: [:create, :update]
before_action :find_board, only: :board_list
@ -1081,44 +1083,64 @@ class CoursesController < ApplicationController
@courses= @courses.page(@page).per(@page_size)
end
# 导出课堂信息
def export_couser_info
set_export_cookies
course_info_to_xlsx @course
filename_ = "#{current_user.real_name}_#{@course.name}_课堂信息_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_course_info.xlsx.axlsx",
locals: {course_info: @course_info}
end
# 导出活跃度
def export_member_act_score
search = params[:search] ? "#{params[:search].strip}" : "" #用户名或学生学号id搜索
group_id = params[:group_id] #分班的班级id
@all_members = student_act_score group_id, search
if @all_members.size == 0
normal_status(-1,"课堂暂时没有学生")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
else
set_export_cookies
act_score_to_xlsx(@all_members)
filename_ = "#{current_user.real_name}_#{@course.name}_活跃度_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_act_score.xlsx.axlsx",
locals: {activity_level:@user_activity_level}
end
end
# 导出学生成绩
def export_member_scores_excel
ActiveRecord::Base.transaction do
begin
@all_members = @course.students
search = params[:search] ? "#{params[:search].strip}" : "" #用户名或学生学号id搜索
group_id = params[:group_id] #分班的班级id
# if group_id && group_id != "0" && group_id != "-1"
# @all_members = @course.students.course_find_by_ids("course_group_id",group_id)
# elsif group_id && group_id == "0" # 未分班
# @all_members = @course.course_members.ungroup_students
# else
# @all_members = @course.students
# end
# if name.present?
# @all_members = @all_members.joins(user: [:user_extension]).where('concat(users.lastname, users.firstname) like ? or user_extensions.student_id like ?',"%#{name}%","%#{name}%")
# end
@all_members = student_act_score group_id, search
@all_members = @all_members.where(course_group_id: group_id) unless group_id.blank?
unless search.blank?
@all_members = @all_members.joins(user: [:user_extension]).where('concat(users.lastname, users.firstname) like ? or user_extensions.student_id like ?',"%#{search}%","%#{search}%")
end
@c_homeworks = @course.homework_commons.homework_published.order("homework_commons.publish_time asc, homework_commons.created_at asc")
@c_exercises = @course.exercises.is_exercise_published.order("exercises.publish_time asc, exercises.created_at asc")
# @c_polls = @course.polls.publish_or_not.order("polls.publish_time asc, polls.created_at asc")
@c_tasks = @course.graduation_tasks.task_published.order("graduation_tasks.publish_time asc, graduation_tasks.created_at asc")
if @user_course_identity > Course::ASSISTANT_PROFESSOR
tip_exception(403,"无权限操作")
elsif @all_members.size == 0
if @all_members.length == 0
normal_status(-1,"课堂暂时没有学生")
elsif params[:export].present? && params[:export]
normal_status(0,"正在下载中")
normal_status(0,"正在下载中")
else
@c_homeworks = @course.homework_commons.homework_published.order("homework_commons.publish_time asc, homework_commons.created_at asc")
@c_exercises = @course.exercises.is_exercise_published.order("exercises.publish_time asc, exercises.created_at asc")
# @c_polls = @course.polls.publish_or_not.order("polls.publish_time asc, polls.created_at asc")
@c_tasks = @course.graduation_tasks.task_published.order("graduation_tasks.publish_time asc, graduation_tasks.created_at asc")
set_export_cookies
member_to_xlsx(@course, @all_members, @c_homeworks, @c_exercises, @c_tasks)
filename_ = "#{current_user.real_name}_#{@course.name}_全部成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
filename_ = "#{current_user.real_name}_#{@course.name}_成绩_#{Time.now.strftime('%Y%m%d_%H%M%S')}"
render xlsx: "#{format_sheet_name filename_.strip}",template: "courses/export_member_scores_excel.xlsx.axlsx",
locals: {course_info:@course_info, activity_level:@user_activity_level,
course_scores:@course_user_scores,shixun_works:@shixun_work_arrays,
locals: {course_scores:@course_user_scores,shixun_works:@shixun_work_arrays,
common_works:@common_work_arrays,group_works:@group_work_arrays,task_works:@task_work_arrays,
exercise_works:@exercise_work_arrays}
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception(e.message)
@ -1291,13 +1313,7 @@ class CoursesController < ApplicationController
# end
def student_act_score group_id, search
sql_select = %Q{SELECT cm.*,(
SELECT SUM(student_works.work_score)
FROM student_works,homework_commons
WHERE student_works.homework_common_id = homework_commons.id
AND homework_commons.course_id = #{@course.id}
AND student_works.user_id = cm.user_id
) AS score,
sql_select = %Q{SELECT cm.*,
(SELECT max(student_id) FROM user_extensions WHERE user_extensions.user_id = cm.user_id) AS student_id,
(SELECT count(messages.id) FROM messages join boards on messages.board_id = boards.id WHERE boards.course_id = #{@course.id}
AND messages.author_id = cm.user_id and messages.parent_id is null) AS message_num,
@ -1318,52 +1334,25 @@ class CoursesController < ApplicationController
FROM course_members cm}
if search.present? && group_id.present?
sql_select += %Q{ join users on cm.user_id = users.id
joins user_extensions ue on ue.user_id = users.id
join user_extensions ue on ue.user_id = users.id
WHERE cm.role = 4 and cm.course_id = #{@course.id} and cm.course_group_id = #{group_id} and
(concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%') ORDER BY score desc}
(concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%')}
elsif search.present?
sql_select += %Q{ join users on cm.user_id = users.id
joins user_extensions ue on ue.user_id = users.id
join user_extensions ue on ue.user_id = users.id
WHERE cm.role = 4 and
(concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%') ORDER BY score desc}
(concat(users.lastname, users.firstname) like '%#{search}%' or ue.student_id like '%#{search}%')}
elsif group_id.present?
sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id} and cm.course_group_id = #{group_id} ORDER BY score desc}
sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id} and cm.course_group_id = #{group_id}}
else
sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id} ORDER BY score desc}
sql_select += %Q{ WHERE cm.role = 4 and cm.course_id = #{@course.id}}
end
act_scores = CourseMember.find_by_sql(sql_select)
act_scores
end
def member_to_xlsx(course,all_members,homeworks,exercises,tasks)
#课堂的作业信息
shixun_homeworks = homeworks.search_homework_type(4) #全部实训作业
shixun_titles = shixun_homeworks.pluck(:name) + ["总得分"]
# 更新实训作业成绩
shixun_homeworks.includes(:homework_challenge_settings, :published_settings, :homework_commons_shixun).each do |homework|
homework.update_homework_work_score
end
shixun_homeworks = shixun_homeworks&.includes(score_student_works: :user)
common_homeworks = homeworks.search_homework_type(1) #全部普通作业
common_titles = common_homeworks.pluck(:name)+ ["总得分"]
common_homeworks = common_homeworks&.includes(score_student_works: :user)
group_homeworks = homeworks.search_homework_type(3) #全部分组作业
group_titles = group_homeworks.pluck(:name)+ ["总得分"]
group_homeworks = group_homeworks&.includes(score_student_works: :user)
task_titles = tasks.pluck(:name) + ["总得分"]
tasks = tasks&.includes(user: :user_extension, score_graduation_works: :user)
exercise_titles = exercises.pluck(:exercise_name) + ["总得分"]
exercises = exercises&.includes(user: :user_extension, score_exercise_users: :user)
total_user_score_array = [] #学生总成绩集合
def course_info_to_xlsx course
#课堂信息
@course_info = []
course_info_title = "课堂信息概要"
@ -1411,7 +1400,9 @@ class CoursesController < ApplicationController
end
course_group_info = [course_group_info_head,course_group_info_body]
@course_info += [course_info_title,course_main_info,course_group_info]
end
def act_score_to_xlsx all_members
#课堂活跃度
@user_activity_level = []
course_user_level = []
@ -1426,8 +1417,6 @@ class CoursesController < ApplicationController
user_stu_id = u.student_id.present? ? (u.student_id.to_s + "\t") : "--"
user_school = user.school_name
user_course_group = u.course_group_name
user_info_array = [user_login,user_name,user_mail,user_stu_id,user_school,user_course_group] #用户的信息集合
user_work_scores = []
#课堂活跃度统计
user_homeworks_num = u.homework_num.to_i #完成的作业数
@ -1446,23 +1435,69 @@ class CoursesController < ApplicationController
c_reply_num = user_reply_num
user_activity_levels = c_works_num + c_exercise_num + c_poll_num + c_file_num + c_message_num + c_reply_num + user_work_reply_num
user_ac_level = {
u_1: user_name,
u_2: user_login,
u_2_1: user_mail,
u_3: user_stu_id,
u_4: user_school,
u_5: user_course_group,
u_6: c_works_num,
u_7: c_exercise_num,
u_8: c_poll_num,
u_9: c_file_num,
u_10: c_message_num,
u_11: c_reply_num,
u_12: user_work_reply_num,
u_13: user_activity_levels
u_1: user_name,
u_2: user_login,
u_2_1: user_mail,
u_3: user_stu_id,
u_4: user_school,
u_5: user_course_group,
u_6: c_works_num,
u_7: c_exercise_num,
u_8: c_poll_num,
u_9: c_file_num,
u_10: c_message_num,
u_11: c_reply_num,
u_12: user_work_reply_num,
u_13: user_activity_levels
}
course_user_level.push(user_ac_level)
#.课堂活跃度统计的集合
course_user_level = course_user_level.sort_by {|k| k[:u_13]}.reverse
@user_activity_level = [course_activity_title,user_cell_head,course_user_level]
end
end
def member_to_xlsx(course,all_members,homeworks,exercises,tasks)
#课堂的作业信息
shixun_homeworks = homeworks.search_homework_type(4) #全部实训作业
shixun_titles = shixun_homeworks.pluck(:name) + ["总得分"]
# 更新实训作业成绩
shixun_homeworks.includes(:homework_challenge_settings, :published_settings, :homework_commons_shixun).each do |homework|
homework.update_homework_work_score
end
shixun_homeworks = shixun_homeworks&.includes(score_student_works: :user)
common_homeworks = homeworks.search_homework_type(1) #全部普通作业
common_titles = common_homeworks.pluck(:name)+ ["总得分"]
common_homeworks = common_homeworks&.includes(score_student_works: :user)
group_homeworks = homeworks.search_homework_type(3) #全部分组作业
group_titles = group_homeworks.pluck(:name)+ ["总得分"]
group_homeworks = group_homeworks&.includes(score_student_works: :user)
task_titles = tasks.pluck(:name) + ["总得分"]
tasks = tasks&.includes(user: :user_extension, score_graduation_works: :user)
exercise_titles = exercises.pluck(:exercise_name) + ["总得分"]
exercises = exercises&.includes(user: :user_extension, score_exercise_users: :user)
total_user_score_array = [] #学生总成绩集合
all_members.each do |u|
#用户的基本信息
user = u.user
user_login = user.login
user_name = user.real_name
user_mail = user.mail
user_stu_id = user.student_id.present? ? (user.student_id.to_s + "\t") : "--"
user_school = user.school_name
user_course_group = u.course_group_name
user_info_array = [user_name,user_login,user_mail,user_stu_id,user_school,user_course_group] #用户的信息集合
user_work_scores = []
#学生总成绩
shixun_score = 0.0 # 实训作业的总分
common_score = 0.0 #普通作业的总分
@ -1560,10 +1595,6 @@ class CoursesController < ApplicationController
total_user_score_array.push(user_work_scores) # 全部成员的集合
end
#1.课堂活跃度统计的集合
course_user_level = course_user_level.sort_by {|k| k[:u_12]}.reverse
@user_activity_level = [course_activity_title,user_cell_head,course_user_level]
#2.学生总成绩的集合
## 作业标题的集合
course_user_score_title = "学生总成绩"

@ -0,0 +1,388 @@
class ExerciseBankQuestionsController < ApplicationController
before_action :require_login, :check_auth #用户需登陆
before_action :get_exercise, only:[:create] #获取试卷
before_action :get_exercise_question, except: [:create] #获取试卷的问题及试卷
before_action :bank_admin #是否为老师
before_action :validate_params, only: [:create, :update] #传入参数的验证
def create
ActiveRecord::Base.transaction do
begin
question_options = {
:question_title => params[:question_title],
:question_type => params[:question_type].present? ? params[:question_type].to_i : 0, #默认为单选题
:question_number => @exercise.exercise_bank_questions.count + 1,
:question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0,
:shixun_id => params[:shixun_id].blank? ? "" : params[:shixun_id],
:is_ordered => params[:is_ordered] # 填空题的答案是否为一一对应关系,默认为true即为一一对应
}
@exercise_question = @exercise.exercise_bank_questions.new(question_options)
#插入问题时那么从插入的这个id以后的question_num都将要+1
if params[:insert_id].present?
insert_exercise = @exercise.exercise_bank_questions.find_by(id: params[:insert_id])
if insert_exercise.present? #如果该问题存在的话,意思是如果是第一题,那么就不存在插入
ques_num = insert_exercise.question_number.to_i
@exercise_question.question_number = ques_num + 1 #更新了问题的位置
@exercise.exercise_bank_questions.insert_question_ex(ques_num).update_all("question_number = question_number + 1")
end
end
if @exercise_question.save
#为选择题(包括单选和多选)的时候,创建问题选项
ques_type = @exercise_question.question_type
if ques_type <= Exercise::MULTIPLE
choices_array = params[:question_choices]
choices_count= choices_array.count
standard_answer = params[:standard_answers] #为数组格式,因为可能会有单选和多选,标准答案,已提前判断不能为空,
standard_answer = standard_answer.uniq.reject(&:blank?)
(1..choices_count).each do |c|
choice = choices_array[c-1] #每一个选项的内容
choice_option = {
:choice_position => c,
:choice_text => choice.strip
}
question_choices = @exercise_question.exercise_bank_choices.new(choice_option)
question_choices.save
end
#标准答案的存储,如:["1","2","3"..]等1对应A2对应B3对应C。。。
standard_answer.each do |a|
choice_id = a.to_i
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => choice_id #即为选择的位置参数
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
if standard_answer.count > 1 && ques_type == Exercise::SINGLE #当标准答案数大于1且不为多选时修改为多选
@exercise_question.update_attribute("question_type",Exercise::MULTIPLE)
elsif standard_answer.count == 1 && ques_type == Exercise::MULTIPLE
@exercise_question.update_attribute("question_type",Exercise::SINGLE)
end
end
elsif ques_type == Exercise::JUDGMENT #这个为判断题
choices_array = params[:question_choices] #判断的选项,对/错等等
choices_count= choices_array.count
(1..choices_count).each do |c|
choice = choices_array[c-1] #每一个选项的内容
choice_option = {
:choice_position => c,
:choice_text => choice.strip
}
question_choices = @exercise_question.exercise_bank_choices.create(choice_option)
question_choices.save
end
standard_answer = params[:standard_answers] #对应选项的id
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => standard_answer.first.to_i
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
elsif ques_type == Exercise::COMPLETION #填空题,每空的参考答案有多个,那么以位置对应
standard_answer = params[:standard_answers]
standard_answer.each do |a|
null_choice_id = a[:choice_id]
null_choice_text = a[:answer_text]
null_choice_text.each do |n|
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => null_choice_id,
:answer_text => n
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
end
end
elsif ques_type == Exercise::SUBJECTIVE #简答题
if params[:standard_answers].present? && params[:standard_answers].reject(&:blank?).count > 0
standard_answer = params[:standard_answers]
standard_answer.each do |a|
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:answer_text => a,
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
end
end
elsif ques_type == Exercise::PRACTICAL #实训题
shixun = Shixun.find_by(id: params[:shixun_id])
shixun_scores = params[:question_scores] #试卷有多个的分值有多个分数表,所以为分数的数组
shixun_name = params[:shixun_name] || shixun.name
question_score = 0
shixun.challenges.try(:each_with_index) do |challenge,index|
shixun_option = {
:challenge_id => challenge.id,
:shixun_id => shixun.id,
:exercise_bank_question_id => @exercise_question.id,
:position => (index + 1),
:question_score => shixun_scores[index].present? ? shixun_scores[index].to_f.round(1) : 5
}
ex_shixun_challenge = ExerciseBankShixunChallenge.create(shixun_option)
question_score += ex_shixun_challenge.question_score # 问题的分数,为各个关卡分数的总和
end
@exercise_question.update_attributes(:question_score => question_score, :shixun_name=> shixun_name)
end
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception("试卷问题创建失败!")
raise ActiveRecord::Rollback
end
end
end
def update
ActiveRecord::Base.transaction do
begin
# 更新试卷题目的内容
question_options = {
:question_title => params[:question_title],
:is_ordered => params[:is_ordered], # 填空题的答案是否为一一对应关系,默认为true即为一一对应
:question_score => params[:question_score].present? ? params[:question_score].to_f.round(1) : 5.0 #不可修改分数
}
choices_array = params[:question_choices]
stan_answer_params = params[:standard_answers]
standard_answer = stan_answer_params.present? ? stan_answer_params.uniq.reject(&:blank?) : []
@exercise_question.update_attributes(question_options)
#当选项存在时,可修改选项内容,但是不能更改选项的位置(即不能增删选项)
if choices_array.present?
ex_choices = @exercise_question.exercise_bank_choices
ex_choice_count = ex_choices.count
choice_array_count = choices_array.count
ex_choice_count_array = (1..ex_choice_count).to_a
choice_array_count_array = (1..choice_array_count).to_a
if ex_choice_count > choice_array_count #如果选项有减少的,那么只更新传入的,删除以前的
choice_array_count_array.each do |c|
choice = choices_array[c-1] #每一个选项的内容
exercise_choice = @exercise_question.exercise_bank_choices.find_choice_custom("choice_position",(c))
exercise_choice.update(choice_text:choice)
end
drop_ex_choice = @exercise_question.exercise_bank_choices.left_choice_choose("choice_position",(choice_array_count))
drop_ex_choice.destroy_all
else
ex_choice_count_array.each do |c|
choice = choices_array[c-1] #每一个选项的内容
exercise_choice = @exercise_question.exercise_bank_choices.find_choice_custom("choice_position",(c))
exercise_choice.update(choice_text:choice)
end
new_add_choice = choice_array_count_array - ex_choice_count_array #新传入的需新增
if new_add_choice.count > 0
new_add_choice.each do |i|
choice_option = {
:choice_position => i,
:choice_text => choices_array[i-1].strip
}
question_choices = @exercise_question.exercise_bank_choices.new(choice_option)
question_choices.save
end
end
end
end
#试卷未发布时,当标准答案存在时,可修改标准答案内容,可增删标准答案,否则只能修改标准答案,不能增删标准答案
@exercise_answers_array = @exercise_question.exercise_bank_standard_answers #问卷的全部标准答案
if standard_answer.present?
if @exercise_question.question_type <= Exercise::JUDGMENT #选择题/判断题,标准答案为一个或多个
exercise_standard_choices = @exercise_answers_array.pluck(:exercise_bank_choice_id) #问题以前的全部标准答案选项位置
if exercise_standard_choices.sort != standard_answer.sort #表示答案有更改的
standard_answer_change = true
common_standard_choices = standard_answer & exercise_standard_choices # 传入的标准答案的选项位置和以前的并集,即表示不用做更改的
old_left_standard_choices = exercise_standard_choices - common_standard_choices # 以前的差集共同的,剩余的表示需要删掉
new_left_standard_choices = standard_answer - common_standard_choices # 传入的标准答案差集共同的,剩余的表示需要新建
if old_left_standard_choices.count > 0
@exercise_answers_array.standard_by_ids(old_left_standard_choices).destroy_all
end
if new_left_standard_choices.count > 0 #新建标准答案
new_left_standard_choices.each do |s|
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => s.to_i #即为选择的位置参数
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
end
end
if standard_answer.count > 1 && @exercise_question.question_type == Exercise::SINGLE #当标准答案数大于1且不为多选时修改为多选
@exercise_question.update_attribute("question_type",Exercise::MULTIPLE)
elsif standard_answer.count == 1 && @exercise_question.question_type == Exercise::MULTIPLE
@exercise_question.update_attribute("question_type",Exercise::SINGLE)
end
end
elsif @exercise_question.question_type == Exercise::COMPLETION #填空题
old_ex_answer = @exercise_question.exercise_bank_standard_answers #当前问题的全部标准答案
old_ex_answer_choice_texts = old_ex_answer.pluck(:answer_text).uniq.sort
new_ex_answer_choice_texts = standard_answer.pluck(:answer_text).sum.uniq.sort
if old_ex_answer_choice_texts != new_ex_answer_choice_texts #填空题标准答案有更改时,才会更新标准答案
new_ex_answer_choice_ids = standard_answer.map {|a| a[:choice_id]}.uniq #新传入的答案数组序号
old_ex_answer_choice_ids = old_ex_answer.pluck(:exercise_bank_choice_id).uniq #全部的答案数组序号
standard_answer_change = true
#删除多余的选项
if old_ex_answer_choice_ids.count > new_ex_answer_choice_ids.count #有减少的填空
delete_ex_answer_choice_ids = old_ex_answer_choice_ids - new_ex_answer_choice_ids
old_ex_answer.standard_by_ids(delete_ex_answer_choice_ids).destroy_all
end
standard_answer.each do |aa|
null_choice_id = aa[:choice_id]
null_choice_text = aa[:answer_text]
null_choice_text_count = null_choice_text.count #当前传入的答案数量
null_choice_text_count_array = (1..null_choice_text_count).to_a
ex_answer_pre = old_ex_answer.standard_by_ids(null_choice_id) #当前问题的全部答案
ex_answer_pre_count = ex_answer_pre.count
ex_answer_pre_count_array = (1..ex_answer_pre_count).to_a
if old_ex_answer_choice_ids.include?(null_choice_id) #以前的填空题答案包含有现在的填空序号
if null_choice_text_count >= ex_answer_pre_count
new_add_choice = null_choice_text_count_array - ex_answer_pre_count_array
ex_answer_pre_count_array.each do |n|
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => null_choice_id,
:answer_text => null_choice_text[n-1]
}
ex_answer_pre[n-1].update(standard_option)
end
if new_add_choice.count > 0 #表示有新增的
new_add_choice.each do |i|
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => null_choice_id,
:answer_text => null_choice_text[i-1]
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
end
end
else
new_delete_choice = ex_answer_pre_count_array - null_choice_text_count_array
null_choice_text.each_with_index do |n,index|
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => null_choice_id,
:answer_text => n
}
ex_answer_pre[index].update(standard_option)
end
if new_delete_choice.count > 0 #表示填空题的答案有删减的
new_delete_choice.each do |d|
ex_answer_pre[d-1].destroy
end
end
end
else
null_choice_text.each do |n|
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:exercise_bank_choice_id => null_choice_id,
:answer_text => n
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
end
end
end
end
end
end
if @exercise_question.question_type == Exercise::SUBJECTIVE #主观题
main_standard_answer = standard_answer.present? ? standard_answer.first : nil
if @exercise_answers_array.present?
@exercise_answers_array.first.update_attribute("answer_text",main_standard_answer)
else
standard_option = {
:exercise_bank_question_id => @exercise_question.id,
:answer_text => main_standard_answer,
}
question_standard_answer = ExerciseBankStandardAnswer.new(standard_option)
question_standard_answer.save
end
elsif @exercise_question.question_type == Exercise::PRACTICAL
question_score = 0
shixun_name = params[:shixun_name] || @exercise_question.shixun_name
@exercise_question.exercise_shixun_challenges.each_with_index do |challenge, index|
challenge.question_score = params[:question_scores][index].to_f.round(1)
challenge.save
question_score += params[:question_scores][index].to_f.round(1)
end
@exercise_question.question_score = question_score
@exercise_question.shixun_name = shixun_name
@exercise_question.save
end
#当试卷已发布时(试卷的总状态),当标准答案修改时,如有已提交的学生,需重新计算分数.
if standard_answer_change && @exercise.exercise_status >= Exercise::PUBLISHED
# ex_users_committed = @exercise.exercise_users.exercise_user_committed
# if ex_users_committed.size > 0
# ex_users_committed.each do |ex_user|
# update_objective_score = update_single_score(@exercise_question,ex_user.user_id,standard_answer)
# if update_objective_score != 0
# objective_score = ex_user.objective_score
# new_objective_score = objective_score + update_objective_score
# total_score = ex_user.score + update_objective_score
# total_score = total_score < 0.0 ? 0.0 : total_score
# ex_user.update_attributes(objective_score:new_objective_score,score:total_score)
# end
# end
# end
normal_status(3,"修改了标准答案\n是否重新计算学生答题的成绩?")
else
normal_status(0,"试卷更新成功!")
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception("页面调用失败!")
raise ActiveRecord::Rollback
end
end
end
private
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
end
def get_exercise
@exercise = ExerciseBank.find_by!(id:params[:exercise_bank_id])
end
def get_exercise_question
@exercise_question = ExerciseBankQuestion.find_by!(id: params[:id])
@exercise = @exercise_question.exercise_bank
end
def validate_params
normal_status(-1,"题目不允许为空!") if (params[:question_title].blank? && params[:question_type].to_i != Exercise::PRACTICAL ) #除了实训题,其余题目必需有题干
normal_status(-1,"问题类型不允许为空!" ) if params[:question_type].blank?
normal_status(-1,"分值不允许为空!" ) if params[:question_score].blank? && params[:question_scores].blank? #分值的数组或参数必需存在一个
if params[:question_score].present? && params[:question_score].to_f <= 0.0 #问题类型存在,则分值不能为空,且必需大于0
normal_status(-1,"分值必需大于0")
elsif (params[:question_score].present? && params[:question_score].to_f.round(1) > 100.0) || (params[:question_scores].present? && (params[:question_scores].map{|a| a.to_f.round(1)}.max > 100.0))
normal_status(-1,"分值不能超过100分")
elsif params[:question_scores].present? && params[:question_scores].include?(0.0) #如果有负数,则自动取绝对值,#多个分数值,针对实训题的
normal_status(-1,"分值必需大于0")
elsif params[:standard_answers].present? && params[:question_choices].present? && (params[:standard_answers].count > params[:question_choices].count)
normal_status(-1,"标准答案数不能大于选项数!")
elsif [0,1,2,3].include?(params[:question_type].to_i) && (params[:standard_answers].blank? || params[:standard_answers].include?("")) #选择题/判断题/填空题 问题选项/标准答案不能为空,也不能包含空的内容
normal_status(-1,"标准答案不能为空!")
elsif params[:question_type].to_i == 2 && (params[:standard_answers].count > 1 || params[:question_choices].blank? || params[:question_choices].include?("")) #判断题的标准答案不能大于1个选项不能为空
normal_status(-1,"判断题选项不能为空/标准答案不能大于1个")
elsif params[:question_type].to_i <= 1 && (params[:question_choices].blank? || params[:question_choices].include?("") || params[:question_choices].count < 2) #选择题选项不能为空,且不能小于2
normal_status(-1,"选择题选项内容不能为空且不能少于2个")
elsif params[:question_type].to_i == 3 && (params[:standard_answers].blank? || params[:standard_answers].count > 5 ) #填空题选项最多为5个,且如果为1个的话不允许修改is_ordered
normal_status(-1,"填空题标准答案不能为空/不能超过5个")
elsif params[:question_type].to_i == 4 && params[:standard_answers].count > 2 #简单题参考答案最多为1个
normal_status(-1,"简答题的参考答案不能大于2个")
elsif params[:question_type].to_i == 5
if params[:shixun_id].blank? #实训题的id不能为空
normal_status(-1,"实训题id不能为空")
elsif params[:shixun_name].blank?
normal_status(-1,"实训题名称不能为空!")
end
end
end
end

@ -0,0 +1,60 @@
#encoding: UTF-8
class ExerciseBanksController < ApplicationController
before_action :require_login
before_action :find_bank
before_action :bank_admin, only: [:update]
def show
@exercise_questions = @bank.exercise_bank_questions&.includes(:exercise_bank_choices, :exercise_bank_shixun_challenges,
:exercise_bank_standard_answers).order("question_number ASC")
@exercise_ques_count = @exercise_questions.size # 全部的题目数
@exercise_ques_scores = @exercise_questions.pluck(:question_score).sum
#单选题的数量及分数
exercise_single_ques = @exercise_questions.find_by_custom("question_type", Exercise::SINGLE)
@exercise_single_ques_count = exercise_single_ques.size
@exercise_single_ques_scores = exercise_single_ques.pluck(:question_score).sum
#多选题的数量及分数
exercise_double_ques = @exercise_questions.find_by_custom("question_type", Exercise::MULTIPLE)
@exercise_double_ques_count = exercise_double_ques.size
@exercise_double_ques_scores = exercise_double_ques.pluck(:question_score).sum
# 判断题数量及分数
exercise_ques_judge = @exercise_questions.find_by_custom("question_type", Exercise::JUDGMENT)
@exercise_ques_judge_count = exercise_ques_judge.size
@exercise_ques_judge_scores = exercise_ques_judge.pluck(:question_score).sum
#填空题数量及分数
exercise_ques_null = @exercise_questions.find_by_custom("question_type", Exercise::COMPLETION)
@exercise_ques_null_count = exercise_ques_null.size
@exercise_ques_null_scores = exercise_ques_null.pluck(:question_score).sum
#简答题数量及分数
exercise_ques_main = @exercise_questions.find_by_custom("question_type", Exercise::SUBJECTIVE)
@exercise_ques_main_count = exercise_ques_main.size
@exercise_ques_main_scores = exercise_ques_main.pluck(:question_score).sum
#实训题数量及分数
exercise_ques_shixun = @exercise_questions.find_by_custom("question_type", Exercise::PRACTICAL)
@exercise_ques_shixun_count = exercise_ques_shixun.size
@exercise_ques_shixun_scores = exercise_ques_shixun.pluck(:question_score).sum
end
def update
tip_exception("试卷标题不能为空!") if params[:exercise_name].blank?
@bank.update_attributes!(name: params[:exercise_name], description: params[:exercise_description])
normal_status(0,"试卷更新成功!")
end
private
def find_bank
@bank = ExerciseBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
end
end

@ -171,17 +171,18 @@ class FilesController < ApplicationController
begin
attachment_ids.each do |attachment_id|
ori = Attachment.find_by_id(attachment_id)
@course.attachments.each do |att|
@exist = false
if att.id == ori.id || (!att.copy_from.nil? && !ori.copy_from.nil? && att.copy_from == ori.copy_from) || att.copy_from == ori.id || att.id == ori.copy_from
att.created_on = Time.now
att.save
@exist = true
break
end
end
next if @exist
# 同一个资源可以多次发送到课堂
# @course.attachments.each do |att|
# @exist = false
# if att.id == ori.id || (!att.copy_from.nil? && !ori.copy_from.nil? && att.copy_from == ori.copy_from) || att.copy_from == ori.id || att.id == ori.copy_from
# att.created_on = Time.now
# att.save
# @exist = true
# break
# end
# end
#
# next if @exist
attach_copied_obj = ori.copy
attach_copied_obj.container = @course
attach_copied_obj.created_on = Time.now

@ -739,7 +739,7 @@ class GamesController < ApplicationController
experience = 0
game_status = @game.status
had_done = @game.had_done
game_challenge = Challenge.select([:id, :score, :position, :shixun_id, :web_route]).find(@game.challenge_id)
game_challenge = Challenge.select([:id, :score, :position, :shixun_id, :web_route, :show_type]).find(@game.challenge_id)
if params[:resubmit].blank? # 非重新评测
if game_status == 2 # 通关
@ -765,9 +765,10 @@ class GamesController < ApplicationController
testset_detail max_query_index.to_i, game_challenge
# 处理生成图片类型文件
picture = (@game.picture_path.nil? ? 0 : @game.id)
picture = (game_challenge.show_type.to_i == -1 || @game.picture_path.nil?) ? 0 : @game.id
# 针对web类型的实训
web_route = game_challenge.try(:web_route)
mirror_name = @shixun.mirror_name
e_record = EvaluateRecord.where(:identifier => sec_key).first

@ -274,7 +274,7 @@ class GraduationWorksController < ApplicationController
@is_author = @work.user_id == current_user.id
@work_members = @task.task_type == 1 ? [] : @task.graduation_works.where.not(user_id: @work.user_id).
where(group_id: @work.group_id).includes(:user)
@attachments = @work.attachments.where.not(attachtype: 7)
@attachments = @work.attachments.where("attachtype != 7 or attachtype is null")
end
def comment_list

@ -0,0 +1,39 @@
class GtopicBanksController < ApplicationController
before_action :require_login
before_action :find_bank
before_action :bank_admin, only: [:edit, :update]
def show
@bank_attachments = @bank.attachments
end
def edit
@attachments = @bank.attachments
end
def update
ActiveRecord::Base.transaction do
@bank.update_attributes(gtopic_bank_params)
Attachment.associate_container(params[:attachment_ids], @bank.id, @bank.class) if params[:attachment_ids]
normal_status(0, "更新成功")
end
end
private
def find_bank
@bank = GtopicBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
end
def gtopic_bank_params
tip_exception("name参数不能为空") if params[:gtopic_bank][:name].blank?
tip_exception("description参数不能为空") if params[:gtopic_bank][:description].blank?
params.require(:gtopic_bank).permit(:name, :topic_type, :topic_source, :topic_property_first, :description,
:topic_property_second, :source_unit, :topic_repeat, :province, :city)
end
end

@ -0,0 +1,63 @@
class HomeworkBanksController < ApplicationController
before_action :require_login
before_action :find_bank
before_action :bank_params, only: [:update]
before_action :bank_admin, only: [:update, :destroy, :set_public]
def show
@bank_attachments = @bank.attachments.where(attachtype: 1)
@reference_attachments = @bank.attachments.where(attachtype: 2)
end
def update
ActiveRecord::Base.transaction do
@bank.update_attributes(name: params[:name], description: params[:description], reference_answer: params[:reference_answer])
# 作业描述的附件
Attachment.associate_container(params[:attachment_ids], @bank.id, @bank.class) if params[:attachment_ids]
# 作业参考答案的附件
Attachment.associate_container(params[:reference_attachment_ids], @bank.id, @bank.class, 2) if params[:reference_attachment_ids]
normal_status(0, "更新成功")
end
end
def destroy
ActiveRecord::Base.transaction do
@bank.homework_commons.update_all(homework_bank_id: nil)
@bank.destroy!
normal_status("删除成功")
end
end
def set_public
@bank.update_attributes(is_public: 1)
normal_status("更新成功")
end
private
def find_bank
@bank = HomeworkBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
end
def bank_params
tip_exception("name参数不能为空") if params[:homework_bank][:name].blank?
tip_exception("description参数不能为空") if params[:homework_bank][:description].blank?
if @bank.homework_type == 3
tip_exception("base_on_project参数不能为空") if params[:homework_bank][:base_on_project].nil?
tip_exception("min_num参数不能为空") if params[:homework_bank][:min_num].blank?
tip_exception("max_num参数不能为空") if params[:homework_bank][:max_num].blank?
tip_exception("最小人数不能小于1") if params[:homework_bank][:min_num].to_i < 1
tip_exception("最大人数不能小于最小人数") if params[:homework_bank][:max_num].to_i < params[:homework_bank][:min_num].to_i
end
params.require(:homework_bank).permit(:name, :description, :reference_answer) if @bank.homework_type == 1
params.require(:homework_bank).permit(:name, :description, :reference_answer, :min_num, :max_num, :base_on_project) if @bank.homework_type == 3
end
end

@ -1,6 +1,6 @@
class QuestionBanksController < ApplicationController
before_action :require_login, :check_auth
before_action :params_filter
before_action :params_filter, except: [:my_courses]
# 题库选用列表
# object_type: # normal 普通作业题库; group 分组作业题库; poll问卷题库 exercise试卷题库; gtask 毕设选题题库gtopic 毕设任务
@ -79,6 +79,30 @@ class QuestionBanksController < ApplicationController
end
end
def my_courses
@courses = current_user.manage_courses.where(is_delete: 0, is_end: 0)
end
def send_to_course
bank = current_bank
course = current_user.manage_courses.find_by(id: params[:course_id])
case @object_type
when 'HomeworkBank' # 作业
quote_homework_bank bank, course
when 'ExerciseBank'
if bank.container_type == 'Exercise' # 试卷
quote_exercise_bank bank, course
else # 问卷
quote_poll_bank bank, course
end
when 'GtaskBank'
quote_gtask_bank bank, course
when 'GtopicBank'
quote_gtopic_bank bank, course
end
normal_status("发送成功")
end
def destroy
bank = current_bank

@ -348,7 +348,8 @@ class ShixunsController < ApplicationController
sub_type.each do |mirror|
ShixunMirrorRepository.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
# 实训子镜像服务配置
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror)
name = MirrorRepository.find_by(id: mirror).try(:name) #查看镜像是否有名称,如果没有名称就不用服务配置
ShixunServiceConfig.create!(:shixun_id => @shixun.id, :mirror_repository_id => mirror) if name.present?
end
end
@ -422,7 +423,12 @@ class ShixunsController < ApplicationController
# 超级管理员和运营人员才能保存 中间层服务器pod信息的配置
if current_user.admin? || current_user.business?
@shixun.shixun_service_configs.destroy_all
@shixun.shixun_service_configs.create!(service_config_params[:shixun_service_configs])
service_config_params[:shixun_service_configs].each do |config|
logger.info("####{config[:mirror_repository_id]}")
name = MirrorRepository.find_by_id(config[:mirror_repository_id])&.name
# 不保存没有镜像的配置
@shixun.shixun_service_configs.create!(config) if name.present?
end
end
rescue Exception => e
uid_logger_error(e.message)

@ -92,6 +92,7 @@ class StudentWorksController < ApplicationController
student_work.commit_time = Time.now
student_work.update_time = Time.now
student_work.commit_user_id = current_user.id
student_work.update_user_id = current_user.id
student_work.group_id = @homework.homework_type == "group" ? @homework.max_group_id : 0
#提交作品时,计算是否迟交
@ -112,7 +113,7 @@ class StudentWorksController < ApplicationController
homework_common_id: @homework.id, project_id: student_work.project_id,
late_penalty: student_work.late_penalty, work_status: student_work.work_status,
commit_time: Time.now, update_time: Time.now, group_id: student_work.group_id,
commit_user_id: current_user.id)
commit_user_id: current_user.id, update_user_id: current_user.id)
stu_work.save!
student_work.attachments.each do |attachment|
att = attachment.copy
@ -156,6 +157,7 @@ class StudentWorksController < ApplicationController
begin
@work.description = params[:description]
@work.update_time = Time.now
@work.update_user_id = current_user.id
# @work.commit_user_id = current_user.id
if @work.save!
Attachment.associate_container(params[:attachment_ids], @work.id, @work.class)
@ -172,7 +174,7 @@ class StudentWorksController < ApplicationController
# 原成员更新描述、更新时间以及附件
@homework.student_works.where(group_id: @work.group_id, user_id: (work_user_ids & params_user_ids)).each do |work|
# work.update_attributes(update_time: Time.now, description: @work.description, commit_user_id: current_user.id)
work.update_attributes(update_time: Time.now, description: @work.description)
work.update_attributes(update_time: Time.now, description: @work.description, update_user_id: current_user.id)
work.attachments.destroy_all
@work.attachments.each do |attachment|
att = attachment.copy
@ -192,7 +194,7 @@ class StudentWorksController < ApplicationController
@homework.student_works.where(group_id: @work.group_id, user_id: delete_user_ids).
update_all(work_status: 0, description: nil, late_penalty: 0, commit_time: nil, update_time: nil,
final_score: nil, teacher_score: nil, student_score: nil, teaching_asistant_score: nil,
work_score: nil, project_id: 0, group_id: 0, commit_user_id: nil)
work_score: nil, project_id: 0, group_id: 0, commit_user_id: nil, update_user_id: nil)
# 新增加的成员
(params_user_ids - work_user_ids).each do |user_id|
@ -200,7 +202,7 @@ class StudentWorksController < ApplicationController
stu_work.update_attributes(user_id: user_id, description: @work.description, homework_common_id: @homework.id,
project_id: @work.project_id, late_penalty: @work.late_penalty,
work_status: @work.work_status, commit_time: Time.now, update_time: Time.now,
group_id: @work.group_id, commit_user_id: @work.commit_user_id)
group_id: @work.group_id, commit_user_id: @work.commit_user_id, update_user_id: current_user.id)
@work.attachments.each do |attachment|
att = attachment.copy
att.author_id = attachment.author_id

@ -101,8 +101,9 @@ class SubjectsController < ApplicationController
@tags = ChallengeTag.where(challenge_id: challenge_ids).pluck(:name).uniq
# 用户获取的实训标签
# @user_tags = @subject.shixuns.map(&:user_tags_name).flatten.uniq
@user_tags = user_shixun_tags challenge_ids, @user.id
@my_subject_progress = @subject.my_subject_progress
# 用户进展
user_subject_progress(challenge_ids)
end
def new
@ -269,7 +270,7 @@ class SubjectsController < ApplicationController
page = params[:page] || 1
member_ids = @subject.subject_members.map(&:user_id).join(',')
condition = "%#{params[:search].strip}%".gsub(" ","")
@users = User.where("id not in (?) and status = 1 and LOWER(concat(lastname, firstname, login, mail)) LIKE ?", member_ids, "#{condition}")
@users = User.where("id not in (?) and status = 1 and LOWER(concat(lastname, ifnull(firstname, ''), login)) LIKE ?", member_ids, "#{condition}")
@users = @users.page(page).per(10)
@users = @users.includes(:user_extension)
@ -436,4 +437,33 @@ class SubjectsController < ApplicationController
tip_exception("403", "")
end
end
# 用户进展和获取的标签
def user_subject_progress challenge_ids
pass_games = Game.select(:id, :cost_time, :challenge_id).where(status: 2, user_id: current_user.id, challenge_id: challenge_ids) if current_user.logged?
@all_score = Challenge.where(id: challenge_ids).sum(:score)
# 如果没有通关的,没必要再继续统计了
if pass_games.blank?
@my_score = 0
@learned = 0
@time = 0
@user_tags = []
else
pass_challenge_ids = pass_games.map(&:challenge_id).uniq # 按道理是不用去重的,但是历史数据与重复
subject_challenge_count = @subject.shixuns.sum(:challenges_count)
# 用户通关获得的标签
@user_tags = ChallengeTag.where(challenge_id: pass_challenge_ids).pluck(:name)
# 用户学习进度
@learned =
subject_challenge_count == 0 ? 0 :
((pass_challenge_ids.size.to_f / subject_challenge_count).round(2) * 100).to_i
# 用户通关分数
@my_score = Challenge.where(id: pass_challenge_ids).pluck(:score).sum
@time = pass_games.map(&:cost_time).sum
end
end
end

@ -0,0 +1,48 @@
class TaskBanksController < ApplicationController
before_action :require_login
before_action :find_bank
before_action :bank_admin, only: [:update]
def show
@bank_attachments = @bank.attachments
end
def update
ActiveRecord::Base.transaction do
begin
@bank.update_attributes(gtask_bank_params)
Attachment.associate_container(params[:attachment_ids], @bank.id, @bank.class) if params[:attachment_ids]
normal_status(0, "更新成功")
rescue Exception => e
uid_logger(e.message)
tip_exception(e.message)
raise ActiveRecord::Rollback
end
end
end
private
def find_bank
@bank = GtaskBank.find_by!(id: params[:id])
tip_exception(403, "无权限") unless (current_user.certification_teacher? && (@bank.is_public || @bank.user_id == current_user.id)) || current_user.admin?
end
def bank_admin
tip_exception(403, "无权限") unless (current_user.certification_teacher? && @bank.user_id == current_user.id) || current_user.admin?
end
def gtask_bank_params
tip_exception("name参数不能为空") if params[:gtask_bank][:name].blank?
tip_exception("description参数不能为空") if params[:gtask_bank][:description].blank?
if @bank.homework_type == 3
tip_exception("base_on_project参数不能为空") if params[:gtask_bank][:base_on_project].nil?
tip_exception("min_num参数不能为空") if params[:gtask_bank][:min_num].blank?
tip_exception("max_num参数不能为空") if params[:gtask_bank][:max_num].blank?
tip_exception("最小人数不能小于1") if params[:gtask_bank][:min_num].to_i < 1
tip_exception("最大人数不能小于最小人数") if params[:gtask_bank][:max_num].to_i < params[:gtask_bank][:min_num].to_i
end
params.require(:gtask_bank).permit(:name, :description) if @bank.task_type == 1
params.require(:gtask_bank).permit(:name, :description, :min_num, :max_num, :base_on_project) if @bank.task_type == 2
end
end

@ -36,8 +36,8 @@ class Users::QuestionBanksController < Users::BaseController
.group('graduation_tasks.gtask_bank_id').count
when 'gtopic' then
StudentGraduationTopic.joins(:graduation_topic)
.where(gtopic_banks: { gtopic_bank_id: question_bank_ids }).where('status != 0')
.group('gtopic_banks.gtopic_bank_id').count
.where(graduation_topics: { gtopic_bank_id: question_bank_ids }).where('student_graduation_topics.status = 1')
.group('graduation_topics.gtopic_bank_id').count
end
end

@ -0,0 +1,23 @@
module Admins::MirrorRepositoriesHelper
def mirror_type_tag(mirror)
case mirror.main_type
when '1' then '<i class="fa fa-star text-success font-16" aria-hidden="true" data-toggle="tooltip" data-title="主类别"></i>'.html_safe
when '0' then '<i class="fa fa-star text-secondary font-16" aria-hidden="true" data-toggle="tooltip" data-title="子类别"></i>'.html_safe
end
end
def mirror_status_tag(mirror)
case mirror.status
when 0
'<i class="fa fa-check-circle text-secondary font-16" data-toggle="tooltip" data-title="未发布"></i>'.html_safe
when 1
'<i class="fa fa-check-circle text-success font-16" data-toggle="tooltip" data-title="已发布"></i>'.html_safe
when 2, 3
'<i class="fa fa-exclamation-circle text-danger font-16" data-toggle="tooltip" data-title="被修改"></i>'.html_safe
when 4
'<i class="fa fa-times-circle text-danger font-18" data-toggle="tooltip" data-title="被删除"></i>'.html_safe
when 5
'<i class="fa fa-exclamation-circle text-warning font-16" data-toggle="tooltip" data-title="子节点异常"></i>'.html_safe
end
end
end

@ -0,0 +1,2 @@
module ExerciseBankQuestionsHelper
end

@ -0,0 +1,2 @@
module HomeworkBanksHelper
end

@ -48,7 +48,7 @@ module HomeworkCommonsHelper
end
when 2, 5, 6
status << "评阅中"
time = "评阅剩余时间:" + (course.end_date.present? ? how_much_time(course.end_date.end_of_day) : "")
time = course.end_date.present? ? ("评阅剩余时间:" + how_much_time(course.end_date.end_of_day)) : ""
time_status = 5
end

@ -40,16 +40,18 @@ class Challenge < ApplicationRecord
## 选择题总分
def choose_score
self.challenge_chooses.pluck(:score).sum
self.score
#self.challenge_chooses.pluck(:score).sum
end
# 关卡总分
def all_score
if self.st == 1
self.choose_score
else
self.score
end
self.score
# if self.st == 1
# self.choose_score
# else
# self.score
# end
end
# 开启挑战

@ -18,4 +18,7 @@ class ExerciseBank < ApplicationRecord
scope :exercise_bank_search, lambda { |keywords|
where("name LIKE ?", "%#{keywords}%") unless keywords.blank?}
validates :name, length: { maximum: 60, too_long: "60 characters is the maximum allowed" }
validates :description, length: { maximum: 100, too_long: "100 characters is the maximum allowed" }
end

@ -1,4 +1,7 @@
class ExerciseBankChoice < ApplicationRecord
belongs_to :exercise_bank_question
has_many :exercise_bank_standard_answers
scope :find_choice_custom, lambda {|k,v| where("#{k} = ?",v)} #根据传入的参数查找问题
scope :left_choice_choose, lambda {|k,v| where("#{k} > ?",v)} #根据传入的参数查找问题
end

@ -6,12 +6,19 @@ class ExerciseBankQuestion < ApplicationRecord
has_many :exercise_bank_standard_answers, :dependent => :destroy
#attr_accessible :question_number, :question_score, :question_title, :question_type
scope :insert_question_ex, lambda {|k| where("question_number > ?",k)}
scope :find_by_custom, lambda {|k,v| where("#{k} = ?",v)} #根据传入的参数查找问题
scope :left_question_choose, lambda {|k,v| where("#{k} > ?",v)} #根据传入的参数查找问题
scope :find_objective_questions, -> {where("question_type != ?",4)} #查找全部客观题
def question_type_name
case self.question_type
when 1
when 0
"单选题"
when 2
when 1
"多选题"
when 2
"判断题"
when 3
"填空题"
when 4
@ -20,4 +27,14 @@ class ExerciseBankQuestion < ApplicationRecord
"实训题"
end
end
#获取问题的全部标准答案
def get_standard_answer_ids
exercise_bank_standard_answers.pluck(:exercise_bank_choice_id)
end
def get_standard_answer_text
exercise_bank_standard_answers.pluck(:answer_text)
end
end

@ -2,4 +2,5 @@ class ExerciseBankStandardAnswer < ApplicationRecord
belongs_to :exercise_bank_question
belongs_to :exercise_bank_choice
#attr_accessible :answer_text
scope :standard_by_ids, lambda { |s| where(exercise_bank_choice_id: s) }
end

@ -31,7 +31,7 @@ class Game < ApplicationRecord
# 根据得分比例来算实际得分(试卷、实训作业)
def real_score score
((final_score < 0 ? 0 : final_score).to_f / challenge.all_score) * score
((final_score < 0 ? 0 : final_score).to_f / challenge.score) * score
end
# 判断实训是否全部通关

@ -0,0 +1,7 @@
# status: 0 创建镜像; 1 修改镜像ID 2 修改镜像name 3 删除镜像 4.从主节点同步镜像到子节点(子节点发生异常), 5. 修改镜像别名, 6. 修改镜像的状态
# user_id: -1时证明是非人为因素造成中间层异常导致
class MirrorOperationRecord < ActiveRecord::Base
default_scope { order(created_at: :desc) }
belongs_to :mirror_repository
end

@ -8,4 +8,8 @@ class MirrorRepository < ApplicationRecord
scope :published_mirror, -> { where(status: 1) }
scope :published_main_mirror, -> { published_mirror.where(main_type: 1) }
scope :published_small_mirror, -> { published_mirror.where(main_type: 0) }
def deletable?
status != 1 && !shixun_mirror_repositories.exists?
end
end

@ -219,11 +219,7 @@ class Shixun < ApplicationRecord
# 实训关卡的总分(由于大部分是实践题,因此没关联查choose表)
# 提前加载问题由于选择题比较少所以几乎不会触发选择题的查询所以没必要提前载入choose_score
def all_score
sum = 0
challenges.each do |challenge|
sum += challenge.st == 0 ? challenge.score : challenge.choose_score
end
sum
self.challenges.pluck(:score).sum
end
### fork 数量

@ -2,6 +2,7 @@ class StudentWork < ApplicationRecord
#学生提交作品表 #work_status :0 未提交 1 已提交 2 迟交
belongs_to :user
belongs_to :commit_user, class_name: 'User', foreign_key: :commit_user_id, optional: true
belongs_to :update_user, class_name: 'User', foreign_key: :update_user_id, optional: true
belongs_to :homework_common
belongs_to :myshixun, optional: true
has_many :student_works_evaluation_distributions, dependent: :destroy

@ -0,0 +1,89 @@
class Admins::CheckShixunMirrorsService < ApplicationService
Error = Class.new(StandardError)
def call
bridge_images
ActiveRecord::Base.transaction do
check_sync_mirrors!
check_mirrors!
end
end
private
def mirrors
bridge_images['images']
end
def sync_mirrors
bridge_images['imagesNotSync']
end
def check_mirrors!
return if mirrors.blank?
image_names = []
mirrors.each do |data|
mirror = JSON.parse(data)
name_repository = MirrorRepository.find_by(name: mirror['imageName'])
id_repository = MirrorRepository.find_by(mirrorID: mirror['imageID'])
image_names << mirror['imageName']
if name_repository.blank? && id_repository.present? # 镜像名称被修改
id_repository.update_column(:status, 2)
MirrorOperationRecord.create!(mirror_repository_id: id_repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 2, user_id: -1)
elsif name_repository.blank? # 镜像不存在、创建镜像
new_repository = MirrorRepository.create!(mirrorID: mirror['imageID'], name: mirror['imageName'])
MirrorOperationRecord.create!(mirror_repository_id: new_repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 0, user_id: -1)
elsif name_repository.mirrorID != mirror['imageID'] # 镜像ID被修改
name_repository.update_column(:status, 2)
MirrorOperationRecord.create!(mirror_repository_id: name_repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 1, user_id: -1)
end
end
# 判断中间层镜像是否被删除
MirrorRepository.find_each do |mirror|
next if mirror&.name.blank? || image_names.index(mirror.name)
mirror.update_column(:status, 4)
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, mirror_id: mirror&.mirrorID,
mirror_name: mirror.name, status: 3, user_id: -1)
end
end
def check_sync_mirrors!
return if sync_mirrors.blank?
sync_mirrors.each do |data|
mirror = JSON.parse(data)
repository = MirrorRepository.find_by(name: mirror['imageName'])
next if repository.blank? || repository.status != 1
repository.update_column(:status, 5)
MirrorOperationRecord.create!(mirror_repository_id: repository.id, mirror_id: mirror['imageID'],
mirror_name: mirror['imageName'], status: 4, user_id: -1)
end
end
def bridge_images
@_bridge_images ||= begin
url = EduSetting.get('cloud_bridge')
res = Faraday.get(url)
raise Error, '拉取镜像信息异常' if res && res['code'].nonzero?
res
rescue => e
Rails.logger.error("get response failed ! #{e.message}")
raise Error, '实训云平台繁忙繁忙等级84'
end
end
end

@ -0,0 +1,21 @@
class Admins::ChooseMirrorService < ApplicationService
attr_reader :mirror, :user, :number
def initialize(mirror, user, mirror_number)
@mirror = mirror
@user = user
@number = mirror_number
end
def call
if mirror.mirrorID == number
mirror.update_column(:status, 1)
return
end
old_number = mirror.mirrorID
mirror.update!(mirrorID: number, status: 1)
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, mirror_id: number, mirror_name: mirror.name,
status: 1, user_id: user.id, old_tag: old_number, new_tag: mirror.mirrorID)
end
end

@ -0,0 +1,37 @@
class Admins::SaveMirrorRepositoryService < ApplicationService
Error = Class.new(StandardError)
attr_reader :mirror, :user, :params
def initialize(mirror, user, params)
@mirror = mirror
@user = user
@params = params
end
def call
mirror.assign_attributes(params)
raise Error, '镜像别名重复' if MirrorRepository.where.not(id: mirror.id).exists?(type_name: params[:type_name])
ActiveRecord::Base.transaction do
record_operation! if mirror.persisted?
mirror.save!
end
end
private
def record_operation!
if mirror.type_name_changed?
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, status: 5,
user_id: user.id, old_tag: mirror.type_name_in_database,
new_tag: mirror.type_name)
elsif mirror.status_changed?
MirrorOperationRecord.create!(mirror_repository_id: mirror.id, status: 5,
user_id: user.id, old_tag: mirror.status_in_database,
new_tag: mirror.status)
end
end
end

@ -0,0 +1,5 @@
$.notify({ message: '操作成功' },{ type: 'success' });
setTimeout(function(){
window.location.reload();
}, 500)

@ -0,0 +1,2 @@
$('.admin-modal-container').html("<%= j( render partial: 'admins/mirror_repositories/shared/choose_mirror_modal', locals: { mirror: @mirror, new_mirror: @new_mirror } ) %>");
$('.modal.admin-choose-mirror-modal').modal('show');

@ -0,0 +1,8 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path)
add_admin_breadcrumb('镜像详情')
end
%>
<%= render partial: 'admins/mirror_repositories/shared/form', locals: { mirror: @mirror, form_action: 'update' } %>

@ -0,0 +1,23 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理') %>
<% end %>
<div class="box search-form-container mirror-repository-list-form">
<form class="flex-1"></form>
<%= link_to '新建', new_admins_mirror_repository_path, class: 'btn btn-primary' %>
</div>
<% if @error_mirror_names.present? %>
<div class="box pb-0">
以下镜像异常:
<% @error_mirror_names.each do |mirror_name| %>
<span class="ml-2 text-danger"><%= mirror_name %></span>
<% end %>
</div>
<% end %>
<div class="box mirror-repository-list-container">
<%= render partial: 'admins/mirror_repositories/shared/list', locals: { mirrors: @mirrors } %>
</div>
<%= render 'admins/mirror_repositories/shared/replace_mirror_modal' %>

@ -0,0 +1 @@
$('.mirror-repository-list-container').html("<%= j( render partial: 'admins/mirror_repositories/shared/list', locals: { mirrors: @mirrors } ) %>");

@ -0,0 +1,5 @@
$.notify({ message: '操作成功' },{ type: 'success' });
setTimeout(function(){
window.location.reload();
}, 500)

@ -0,0 +1,8 @@
<%
define_admin_breadcrumbs do
add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path)
add_admin_breadcrumb('新建镜像')
end
%>
<%= render partial: 'admins/mirror_repositories/shared/form', locals: { mirror: @mirror, form_action: 'create' } %>

@ -0,0 +1,42 @@
<div class="modal fade admin-choose-mirror-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_tag(admins_choose_mirror_repositories_path(mirror_id: mirror.id), method: :post, class: 'admin-choose-mirror-form') do %>
<div class="form-group row">
<div class="col-md-2"></div>
<div class="col-md-5">ID</div>
<div class="col-md-5">名称</div>
</div>
<div class="form-group row">
<div class="col-md-2">旧镜像</div>
<div class="col-md-5 form-check">
<input class="form-check-input" type="radio" name="mirror_number" id="old-mirror-check" value="<%= mirror.mirrorID %>">
<label class="form-check-label" for="old-mirror-check"><%= mirror.mirrorID %></label>
</div>
<div class="col-md-5"><%= mirror.name %></div>
</div>
<div class="form-group row">
<div class="col-md-2">新镜像</div>
<div class="col-md-5 form-check">
<input class="form-check-input" type="radio" name="mirror_number" id="new-mirror-check" value="<%= new_mirror.mirror_id %>">
<label class="form-check-label" for="new-mirror-check"><%= new_mirror.mirror_id %></label>
</div>
<div class="col-md-5"><%= new_mirror.mirror_name %></div>
</div>
<div class="mt-2 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,42 @@
<div class="box edit-mirror-repository-container">
<%= simple_form_for([:admins, mirror], url: { action: form_action }, html: { class: 'edit-mirror col-md-12' }, defaults: { wrapper_html: { class: 'col-md-4' } }) do |f| %>
<% unless mirror.new_record? %>
<div class="row">
<%= f.input :mirrorID, label: '镜像ID', input_html: { readonly: true, class: 'form-control-plaintext' } %>
<%= f.input :name, label: '镜像名称', input_html: { readonly: true, class: 'form-control-plaintext' } %>
</div>
<% end %>
<div class="row">
<%= f.input :type_name, as: :string, label: '镜像别名 *' %>
<div class="form-group select optional col-md-4">
<%= f.label :main_type, label: '类别' %>
<%= f.select :main_type, [['主类别', 1],['小类别', 0]], {}, class: 'form-control optional' %>
</div>
</div>
<div class="row">
<%= f.input :time_limit, as: :integer, label: '评测时限(S)' %>
<%= f.input :resource_limit, as: :integer, label: '磁盘限制(K)' %>
</div>
<div class="row">
<%= f.input :cpu_limit, as: :integer, label: 'CPU限制(核)' %>
<%= f.input :memory_limit, as: :integer, label: '内存限制(M)' %>
</div>
<div class="row">
<%= f.input :description, as: :text, label: '描述', wrapper_html: { class: 'col-md-8' } %>
</div>
<div class="row">
<%= f.input :status, as: :radio_buttons, label: '状态', collection: [%w(未发布 0), %w(已发布 1)], wrapper_html: { class: 'col-md-4' } %>
</div>
<div class="row">
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4', 'data-disable-with': '保存中...' %>
<%= link_to '取消', admins_mirror_repositories_path, class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -0,0 +1,54 @@
<table class="table table-hover text-center mirror-repository-list-table">
<thead class="thead-light">
<tr>
<th width="6%">ID</th>
<th width="6%">类别</th>
<th width="12%" class="text-left">镜像别名</th>
<th width="16%" class="text-left">镜像名称</th>
<th width="22%" class="text-left">镜像描述</th>
<th width="14%">修改时间</th>
<th width="6%">脚本</th>
<th width="6%">状态</th>
<th width="12%">操作</th>
</tr>
</thead>
<tbody>
<% if mirrors.present? %>
<% mirrors.each do |mirror| %>
<tr class="mirror-repository-item-<%= mirror.id %>">
<td><%= mirror.id %></td>
<td><%= mirror_type_tag(mirror) %></td>
<td class="text-left"><%= display_text(mirror.type_name) %></td>
<td class="text-left"><%= overflow_hidden_span mirror.name, width: 150 %></td>
<td class="text-left"><%= overflow_hidden_span mirror.description, width: 240 %></td>
<td><%= mirror.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<% if mirror.main_type == "1" %>
<%= link_to admins_mirror_repository_mirror_scripts_path(mirror) do %>
<i class="fa fa-file-text <%= mirror.mirror_scripts.blank? ? 'text-danger' : 'text-success' %>" aria-hidden="true" data-toggle="tooltip" data-title="查看脚本"></i>
<% end %>
<% end %>
</td>
<td><%= mirror_status_tag mirror %></td>
<td class="action-container">
<%= link_to '编辑', edit_admins_mirror_repository_path(mirror), class: 'action edit-action' %>
<% if mirror.status == 2 %>
<%= link_to '同步', new_admins_choose_mirror_repository_path(mirror_id: mirror.id), remote: true, class: 'action sync-action' %>
<% end %>
<%= javascript_void_link '替换', class: 'action replace-action', data: { toggle: 'modal', target: '.admin-replace-mirror-modal', id: mirror.id, name: mirror.name } %>
<% if mirror.deletable? %>
<%= delete_link '删除', admins_mirror_repository_path(mirror, element: ".mirror-repository-item-#{mirror.id}"), class: 'delete-mirror-repository-action' %>
<% end %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: mirrors } %>

@ -0,0 +1,33 @@
<div class="modal fade admin-replace-mirror-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-replace-mirror-form" data-url="<%= merge_admins_mirror_repositories_path %>">
<%= hidden_field_tag(:mirror_id, nil) %>
<div class="form-group d-flex">
<div>被替换镜像:</div>
<div class="ml-2">ID<span class="mx-1 text-info mirror-id-container"></span></div>
<div class="ml-2">名称:<span class="mx-1 text-info mirror-name-container"></span></div>
</div>
<div class="form-group d-flex">
<label for="new_mirror_id" class="col-form-label">选择新镜像:</label>
<div class="d-flex flex-column-reverse w-75">
<select id="new_mirror_id" name="new_mirror_id" class="form-control new-mirror-select"></select>
</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,8 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %>
<% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %>
<% add_admin_breadcrumb('脚本列表', admins_mirror_repository_mirror_scripts_path(current_mirror)) %>
<% add_admin_breadcrumb('编辑脚本') %>
<% end %>
<%= render partial: 'admins/mirror_scripts/shared/form', locals: { mirror: current_mirror, script: @script, form_action: 'update' } %>

@ -0,0 +1,14 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %>
<% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %>
<% add_admin_breadcrumb('脚本列表') %>
<% end %>
<div class="box search-form-container mirror-script-list-form">
<form class="flex-1"></form>
<%= link_to '新建', new_admins_mirror_repository_mirror_script_path(current_mirror), class: 'btn btn-primary' %>
</div>
<div class="box mirror-script-list-container">
<%= render partial: 'admins/mirror_scripts/shared/list', locals: { mirror: current_mirror, scripts: @scripts } %>
</div>

@ -0,0 +1,8 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('镜像管理', admins_mirror_repositories_path) %>
<% add_admin_breadcrumb(current_mirror.type_name, edit_admins_mirror_repository_path(current_mirror)) %>
<% add_admin_breadcrumb('脚本列表', admins_mirror_repository_mirror_scripts_path(current_mirror)) %>
<% add_admin_breadcrumb('新建脚本') %>
<% end %>
<%= render partial: 'admins/mirror_scripts/shared/form', locals: { mirror: current_mirror, script: @script, form_action: 'create' } %>

@ -0,0 +1,12 @@
<div class="box edit-mirror-script-container">
<%= simple_form_for([:admins, mirror, script], url: { action: form_action }, html: { class: 'script-form' }) do |f| %>
<%= f.input :script_type, label: '名称', input_html: { class: 'col-md-6' } %>
<%= f.input :description, as: :text, label: '说明', input_html: { class: 'col-md-6' } %>
<%= f.input :script, as: :text, label: '评测脚本' %>
<div class="form-row">
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4', 'data-disable-with': '保存中...' %>
<%= link_to '取消', admins_mirror_repository_mirror_scripts_path(mirror), class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -0,0 +1,32 @@
<table class="table table-hover text-center mirror-script-list-table">
<thead class="thead-light">
<tr>
<th width="10%">ID</th>
<th width="20%" class="text-left">名称</th>
<th width="34%" class="text-left">说明</th>
<th width="16%">更新时间</th>
<th width="20%">操作</th>
</tr>
</thead>
<tbody>
<% if scripts.present? %>
<% scripts.each do |script| %>
<tr class="mirror-script-item-<%= script.id %>">
<td><%= script.id %></td>
<td class="text-left"><%= overflow_hidden_span script.script_type, width: 200 %></td>
<td class="text-left"><%= overflow_hidden_span script.description, width: 400 %></td>
<td><%= script.updated_at.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">
<%= link_to '编辑', edit_admins_mirror_repository_mirror_script_path(mirror, script), class: 'action edit-action' %>
<%= delete_link '删除', admins_mirror_repository_mirror_script_path(mirror, script, element: ".mirror-script-item-#{script.id}"), class: 'delete-mirror-script-action' %>
</td>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: scripts } %>

@ -0,0 +1,6 @@
<div class="d-flex flex-column align-items-center justify-content-center global-error">
<div class="global-error-code">
<span>403</span>
</div>
<div class="global-error-text">未授权</div>
</div>

@ -2,5 +2,5 @@
<div class="global-error-code">
<span>500</span>
</div>
<div class="global-error-text">系统错误</div>
<div class="global-error-text"><%= @message %></div>
</div>

@ -1,5 +1,5 @@
<div class="paginate-container">
<% if objects.size.nonzero? %>
<% if objects && objects.size.nonzero? %>
<div class="paginate-total"><%= page_entries_info objects %></div>
<% end %>
<%= paginate objects, views_prefix: 'admins', remote: true %>

@ -20,6 +20,12 @@
<% end %>
</li>
<li>
<%= sidebar_item_group('#shixun-submenu', '实训管理', icon: 'window-restore') do %>
<li><%= sidebar_item(admins_mirror_repositories_path, '镜像管理', icon: 'cubes', controller: 'admins-mirror_repositories') %></li>
<% end %>
</li>
<!-- <li>-->
<%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %>
<!-- <li><%#= sidebar_item('#', '课程列表', icon: 'calendar', controller: '') %></li>-->

@ -131,7 +131,7 @@
<div class="form-row mt-4">
<%= f.button :submit, value: '保存', class: 'btn-primary mr-3 px-4' %>
<%= link_to '取消', 'javascript:history.go(-1)', class: 'btn btn-secondary px-4' %>
<%= link_to '取消', admins_users_path, class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -1,2 +1,2 @@
json.status 1
json.message "创建参考答案成功"
json.message "操作成功"

@ -13,7 +13,7 @@ if @challenges.present?
json.position challenge.position
json.st challenge.st
json.name challenge.subject
json.score challenge.all_score
json.score challenge.score
json.passed_count challenge.user_passed_count
json.playing_count challenge.playing_count
json.name_url shixun_challenge_path(challenge, shixun_identifier: @shixun.identifier)

@ -0,0 +1,29 @@
wb = xlsx_package.workbook
wb.use_shared_strings = true
wb.styles do |s|
no_wrap_sz = s.add_style :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: false,:horizontal => :center,:vertical => :center }
sz_all = s.add_style :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center }
row_cell = s.add_style :bg_color=> "FAEBDC",:border => { :style => :thin, :color =>"000000" },alignment: {wrap_text: true,:horizontal => :center,:vertical => :center }
blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 25,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center}
#课堂信息摘要
wb.add_worksheet(name:course_info[0]) do |sheet|
sheet.sheet_view.show_grid_lines = false
course_main_info = course_info[1]
course_group_info = course_info[2]
group_info_d = course_group_info[0]
group_info_detail = course_group_info[1]
course_main_info.each do |c|
sheet.add_row c, :style => sz_all #用户id
end
sheet["A1:A7"].each { |c| c.style = row_cell }
sheet.add_row [],:style => sz_all
if group_info_detail.count > 0
sheet.add_row group_info_d, :style => blue_cell
group_info_detail.each do |group|
sheet.add_row group, :style => sz_all #用户id
end
sheet.column_info.second.width = 40
end
end #add_worksheet
end

@ -0,0 +1,25 @@
wb = xlsx_package.workbook
wb.use_shared_strings = true
wb.styles do |s|
no_wrap_sz = s.add_style :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: false,:horizontal => :center,:vertical => :center }
sz_all = s.add_style :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center }
row_cell = s.add_style :bg_color=> "FAEBDC",:border => { :style => :thin, :color =>"000000" },alignment: {wrap_text: true,:horizontal => :center,:vertical => :center }
blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 25,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center}
#课堂活跃度统计
wb.add_worksheet(name:activity_level[0]) do |sheet|
sheet.sheet_view.show_grid_lines = false
sheet_title = activity_level[1]
sheet_content = activity_level[2]
sheet.add_row sheet_title, :height => 25,:style => blue_cell
if sheet_content.count > 0
sheet_content.each_with_index do |c,index|
c_1 = (index+1)
c_2 = [c_1] + c.values
sheet.add_row c_2, :height => 25, :style => sz_all #用户id
end
sheet.column_widths *([20]*sheet.column_info.count)
sheet.column_info.first.width = 8
end
end #add_worksheet
end

@ -6,44 +6,6 @@ wb.styles do |s|
row_cell = s.add_style :bg_color=> "FAEBDC",:border => { :style => :thin, :color =>"000000" },alignment: {wrap_text: true,:horizontal => :center,:vertical => :center }
blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 25,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {wrap_text: true,:horizontal => :center,:vertical => :center}
#课堂信息摘要
wb.add_worksheet(name:course_info[0]) do |sheet|
sheet.sheet_view.show_grid_lines = false
course_main_info = course_info[1]
course_group_info = course_info[2]
group_info_d = course_group_info[0]
group_info_detail = course_group_info[1]
course_main_info.each do |c|
sheet.add_row c, :style => sz_all #用户id
end
sheet["A1:A7"].each { |c| c.style = row_cell }
sheet.add_row [],:style => sz_all
if group_info_detail.count > 0
sheet.add_row group_info_d, :style => blue_cell
group_info_detail.each do |group|
sheet.add_row group, :style => sz_all #用户id
end
sheet.column_info.second.width = 40
end
end #add_worksheet
#课堂活跃度统计
wb.add_worksheet(name:activity_level[0]) do |sheet|
sheet.sheet_view.show_grid_lines = false
sheet_title = activity_level[1]
sheet_content = activity_level[2]
sheet.add_row sheet_title, :height => 25,:style => blue_cell
if sheet_content.count > 0
sheet_content.each_with_index do |c,index|
c_1 = (index+1)
c_2 = [c_1] + c.values
sheet.add_row c_2, :height => 25, :style => sz_all #用户id
end
sheet.column_widths *([20]*sheet.column_info.count)
sheet.column_info.first.width = 8
end
end #add_worksheet
#学生总成绩
wb.add_worksheet(name:course_scores[0]) do |sheet|
sheet.sheet_view.show_grid_lines = false

@ -0,0 +1,72 @@
json.question_id question.id
q_positon = question.question_number
if ques_position.present?
q_positon = ques_position
end
json.q_position q_positon
json.question_title question.question_title
json.question_type question.question_type
json.question_score question.question_score.round(1).to_s
if question.question_type <= 2 #当为选择题或判断题时,只显示选项的位置
standard_answers_array = question.get_standard_answer_ids
exercise_choices = choices.order("choice_position ASC")
json.question_choices exercise_choices.each do |a|
#TODO: 旧版本来一个题只有一个标准答案的新版又做成了一个题有多个标准答案exercise_choice_id存放的是标准答案的位置..
standard_answer_b = standard_answers_array.join("").include?(a.choice_position.to_s)
choice_text = a.choice_text
if question.question_type == 2
if choice_text == "对"
choice_text = "正确"
end
if choice_text == "错"
choice_text = "错误"
end
end
json.choice_id a.id
# json.choice_text (edit_type.present? || question.question_type == 2) ? a.choice_text : "#{(index+65).chr}.#{a.choice_text}"
json.choice_text choice_text
json.choice_position a.choice_position
json.standard_boolean standard_answer_b
end
json.standard_answer standard_answers_array
if question.question_type == 2 #返回答案的文字
standard_text = standard_answers_array.first.to_i == 1 ? "正确" : "错误"
else
if question.question_type == 1 && standard_answers_array.size == 1
standard_answers_array = standard_answers_array.first.to_s.split("")
end
array_text_answer = []
standard_answers_array.each{|a| array_text_answer.push((a.to_i+64).chr)}
standard_text = array_text_answer.join("")
end
json.standard_answer_show standard_text
elsif question.question_type == 3 #当为填空题时
standard_answers_count = question.exercise_bank_standard_answers
standard_answer_ex = standard_answers_count.pluck(:exercise_bank_choice_id).uniq
json.is_ordered question.is_ordered
json.multi_count standard_answer_ex.size #有几个填空
json.standard_answer do
json.array! standard_answer_ex.each do |a|
s_answer_text = standard_answers_count.standard_by_ids(a).pluck(:answer_text)
json.choice_id a
json.answer_text s_answer_text
end
end
elsif question.question_type == 4 #简答题时
json.standard_answer question.exercise_bank_standard_answers.pluck(:answer_text)
elsif question.question_type == 5
json.shixun_id question.shixun_id
json.shixun_name question.shixun_name.present? ? question.shixun_name : ""
json.shixun_identifier question.shixun.identifier
json.shixun do
json.array! shixun_challenges do |s|
json.challenge_id s.challenge_id
json.challenge_position s.position
json.challenge_name s.challenge.subject
json.challenge_score s.question_score.round(1).to_s
end
end
end

@ -0,0 +1,16 @@
json.exercise do
json.extract! @bank, :id, :name, :description, :is_public
end
json.partial! "exercises/exercise_scores"
json.exercise_questions do
json.array! @exercise_questions do |q|
json.partial! "exercise_bank_questions/exercise_bank_questions",
question: q,
choices:q.exercise_bank_choices,
shixun_challenges: q.exercise_bank_shixun_challenges,
ques_position:nil,
edit_type:nil
end
end

@ -54,7 +54,6 @@ if question.question_type <= 2 #当为选择题或判断题时,只显示选
standard_answers_array = standard_answers_array.first.to_s.split("")
end
array_text_answer = []
standard_answers_array.each{|a| array_text_answer.push((a.to_i+64).chr)}
standard_text = array_text_answer.join("")
end

@ -7,6 +7,7 @@ if @exercises_count > 0
second_left = get_exercise_left_time(exercise,@current_user_)
json.time second_left.present? ? (second_left / 60) : nil
end
json.author exercise.user.real_name
ex_index = exercise_index_show(exercise,@course,@is_teacher_or,@current_user_)
json.exercise_status ex_index[:ex_status]
json.lock_status ex_index[:lock_icon]

@ -0,0 +1,15 @@
json.topic_type topic_type
json.topic_source topic_source
json.topic_property_first topic_property_first
json.topic_property_second topic_property_second
json.topic_repeat topic_repeat
json.selected_data do
json.(@bank, :name, :topic_type, :topic_source, :topic_property_first,
:description, :topic_property_second, :source_unit, :topic_repeat,
:province, :city, :is_public)
end
json.attachments @attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end

@ -0,0 +1,6 @@
json.(@bank, :id, :name, :description, :is_public, :topic_type, :topic_source, :topic_property_first, :topic_property_second,
:source_unit, :topic_repeat, :province, :city)
json.attachment_list @bank_attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end

@ -0,0 +1,9 @@
json.(@bank, :id, :name, :description, :homework_type, :is_public, :min_num, :max_num, :base_on_project, :reference_answer)
json.attachments @bank_attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end
json.reference_attachments @reference_attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end

@ -17,6 +17,7 @@ json.homeworks @homework_commons.each do |homework|
json.status_time curr_status[:time]
json.time_status curr_status[:time_status]
json.allow_late homework.allow_late
json.author homework.user.real_name
# 只有在主目录才显示
json.upper_category_name homework.course_second_category&.name unless params[:category]

@ -5,6 +5,7 @@ if @polls_count > 0
json.array! @polls do |poll|
poll_index_array = poll_index_show(poll,@course,@is_teacher_or,@current_user_)
json.extract! poll, :id, :polls_name,:is_public,:created_at
json.author poll.user.real_name
json.polls_status poll_index_array[:polls_status]
json.lock_status poll_index_array[:lock_icon]
json.publish_time poll_index_array[:publish_time] # 问卷的发布时间

@ -0,0 +1,4 @@
json.courses @courses do |course|
json.course_id course.id
json.course_name course.name
end

@ -5,7 +5,8 @@ json.is_evaluation @is_evaluation
json.author_name @is_evaluation ? "匿名" : @work.user.real_name
json.is_leader_work @work.user_id == @work.commit_user_id if @homework.homework_type == "group"
json.is_author @is_author
json.update_user_name @is_evaluation ? "匿名" : @work.commit_user.try(:real_name)
json.commit_user_name @is_evaluation ? "匿名" : @work.commit_user.try(:real_name)
json.update_user_name @is_evaluation ? "匿名" : @work.update_user.try(:real_name)
json.update_atta @homework.late_duration && @is_author

@ -14,8 +14,8 @@ end
# 我的进展
json.progress do
json.my_score @subject.my_subject_score
json.all_score @subject.all_score
json.learned @subject.my_subject_progress
json.time @subject.my_consume_time
json.my_score @my_score
json.all_score @all_score
json.learned @learned
json.time @time
end

@ -0,0 +1,14 @@
json.(@bank, :id, :name, :description, :task_type, :is_public)
# 附件
json.attachments @bank_attachments do |attachment|
json.partial! "attachments/attachment_simple", locals: {attachment: attachment}
end
# 分组要求
if @bank.task_type == 2
json.group_info do
json.max_number @bank.max_num
json.min_number @bank.min_num
json.base_on_project @bank.base_on_project
end
end

@ -1 +1 @@
admins-users: admins-users
admins-mirror_scripts: 'admins-mirror_repositories'

@ -7,8 +7,10 @@ Rails.application.config.assets.version = '1.0'
# Rails.application.config.assets.paths << Emoji.images_path
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
Rails.application.config.assets.paths << Rails.root.join('vendor/assets')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
Rails.application.config.assets.precompile += %w( admin.js admin.css )

@ -343,6 +343,8 @@ Rails.application.routes.draw do
get 'base_info'
get 'attahcment_category_list'
get 'export_member_scores_excel' #导出课堂信息
get 'export_couser_info'
get 'export_member_act_score'
post 'switch_to_teacher'
post 'switch_to_assistant'
post 'switch_to_student'
@ -569,14 +571,6 @@ Rails.application.routes.draw do
resource :poll_votes,only:[:create,:destroy]
end
resources :question_banks do
collection do
get :bank_list
post :save_banks
end
end
resources :exercises do
member do
get :choose_shixun
@ -631,6 +625,26 @@ Rails.application.routes.draw do
end
end
resources :question_banks do
collection do
get :bank_list
post :save_banks
get :my_courses
post :send_to_course
end
end
resources :homework_banks do
member do
post :set_public
end
end
resources :gtopic_banks
resources :task_banks
resources :exercise_banks
resources :attachments
resources :schools do
@ -800,6 +814,15 @@ Rails.application.routes.draw do
end
resources :shixuns, only: [:index,:destroy]
resources :shixun_settings, only: [:index,:update]
resources :mirror_repositories, only: [:index, :new, :create, :edit, :update, :destroy] do
collection do
post :merge
get :for_select
end
resources :mirror_scripts, only: [:index, :new, :create, :edit, :update, :destroy]
end
resources :choose_mirror_repositories, only: [:new, :create]
end
#git 认证回调

@ -0,0 +1,10 @@
class ModifyChallnegeScoreForChoose < ActiveRecord::Migration[5.2]
def change
challenges = Challenge.where(st: 1)
challenges.find_each do |c|
puts(c.id)
score = c.challenge_chooses.sum(:score)
c.update_column(:score, score)
end
end
end

@ -0,0 +1,6 @@
class AddUpdateUserIdToStudentWorks < ActiveRecord::Migration[5.2]
def change
add_column :student_works, :update_user_id, :integer
StudentWork.update_all("update_user_id = commit_user_id")
end
end

@ -0,0 +1,5 @@
class AddIsOrderedToExerciseBankQuestion < ActiveRecord::Migration[5.2]
def change
add_column :exercise_bank_questions, :is_ordered, :boolean, :default => true
end
end

File diff suppressed because one or more lines are too long

@ -17816,6 +17816,607 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
.datepicker.datepicker-inline td {
padding: 4px 5px;
}
/* BASICS */
/* line 3, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
direction: ltr;
}
/* PADDING */
/* line 13, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
padding: 4px 0;
/* Vertical padding around content */
}
/* line 16, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
padding: 0 4px;
/* Horizontal padding of content */
}
/* line 21, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white;
/* The little square between H and V scrollbars */
}
/* GUTTER */
/* line 27, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
/* line 33, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
/* line 41, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker {
color: black;
}
/* line 42, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker-subtle {
color: #999;
}
/* CURSOR */
/* line 46, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
/* line 52, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
/* line 55, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
/* line 60, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
/* line 63, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor-mark {
background-color: rgba(20, 255, 20, 0.5);
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
/* line 69, vendor/assets/codemirror/lib/codemirror.css */
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-webkit-keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
/* Can style cursor different in overwrite (non-insert) mode */
/* line 96, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab {
display: inline-block;
text-decoration: inherit;
}
/* line 98, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rulers {
position: absolute;
left: 0;
right: 0;
top: -50px;
bottom: 0;
overflow: hidden;
}
/* line 103, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0;
bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
/* line 111, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-header {
color: blue;
}
/* line 112, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-quote {
color: #090;
}
/* line 113, vendor/assets/codemirror/lib/codemirror.css */
.cm-negative {
color: #d44;
}
/* line 114, vendor/assets/codemirror/lib/codemirror.css */
.cm-positive {
color: #292;
}
/* line 115, vendor/assets/codemirror/lib/codemirror.css */
.cm-header, .cm-strong {
font-weight: bold;
}
/* line 116, vendor/assets/codemirror/lib/codemirror.css */
.cm-em {
font-style: italic;
}
/* line 117, vendor/assets/codemirror/lib/codemirror.css */
.cm-link {
text-decoration: underline;
}
/* line 118, vendor/assets/codemirror/lib/codemirror.css */
.cm-strikethrough {
text-decoration: line-through;
}
/* line 120, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-keyword {
color: #708;
}
/* line 121, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-atom {
color: #219;
}
/* line 122, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-number {
color: #164;
}
/* line 123, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-def {
color: #00f;
}
/* line 128, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-2 {
color: #05a;
}
/* line 129, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {
color: #085;
}
/* line 130, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-comment {
color: #a50;
}
/* line 131, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string {
color: #a11;
}
/* line 132, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string-2 {
color: #f50;
}
/* line 133, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-meta {
color: #555;
}
/* line 134, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-qualifier {
color: #555;
}
/* line 135, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-builtin {
color: #30a;
}
/* line 136, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-bracket {
color: #997;
}
/* line 137, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-tag {
color: #170;
}
/* line 138, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-attribute {
color: #00c;
}
/* line 139, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-hr {
color: #999;
}
/* line 140, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-link {
color: #00c;
}
/* line 142, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-error {
color: #f00;
}
/* line 143, vendor/assets/codemirror/lib/codemirror.css */
.cm-invalidchar {
color: #f00;
}
/* line 145, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-composing {
border-bottom: 2px solid;
}
/* Default styles for common addons */
/* line 149, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-matchingbracket {
color: #0b0;
}
/* line 150, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-nonmatchingbracket {
color: #a22;
}
/* line 151, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-matchingtag {
background: rgba(255, 150, 0, 0.3);
}
/* line 152, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-activeline-background {
background: #e8f2ff;
}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
/* line 159, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
/* line 165, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll {
overflow: scroll !important;
/* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px;
margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none;
/* Prevent dragging from highlighting the element */
position: relative;
}
/* line 175, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
/* line 183, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
/* line 188, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar {
right: 0;
top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
/* line 193, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-hscrollbar {
bottom: 0;
left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
/* line 198, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler {
right: 0;
bottom: 0;
}
/* line 201, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-filler {
left: 0;
bottom: 0;
}
/* line 205, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
position: absolute;
left: 0;
top: 0;
min-height: 100%;
z-index: 3;
}
/* line 210, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
/* line 217, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
/* line 223, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-background {
position: absolute;
top: 0;
bottom: 0;
z-index: 4;
}
/* line 228, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
/* line 233, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
.CodeMirror-gutter-wrapper ::selection {
background-color: transparent;
}
/* line 234, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
/* line 236, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
cursor: text;
min-height: 1px;
/* prevents collapsing before first draw */
}
/* line 240, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
/* Reset some styles that the rest of the page might have set */
border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
/* line 260, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-wrap pre.CodeMirror-line,
.CodeMirror-wrap pre.CodeMirror-line-like {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
/* line 267, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linebackground {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 0;
}
/* line 273, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linewidget {
position: relative;
z-index: 2;
padding: 0.1px;
/* Force widget margins to stay inside of the container */
}
/* line 281, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rtl pre {
direction: rtl;
}
/* line 283, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
/* line 288, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
box-sizing: content-box;
}
/* line 297, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
/* line 305, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
/* line 309, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure pre {
position: static;
}
/* line 311, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
/* line 316, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-dragcursors {
visibility: visible;
}
/* line 320, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
/* line 324, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-selected {
background: #d9d9d9;
}
/* line 325, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused .CodeMirror-selected {
background: #d7d4f0;
}
/* line 326, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-crosshair {
cursor: crosshair;
}
/* line 327, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
background: #d7d4f0;
}
/* line 328, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
/* line 330, vendor/assets/codemirror/lib/codemirror.css */
.cm-searching {
background-color: #ffa;
background-color: rgba(255, 255, 0, 0.4);
}
/* Used to force a border model for a node */
/* line 336, vendor/assets/codemirror/lib/codemirror.css */
.cm-force-border {
padding-right: .1px;
}
@media print {
/* Hide the cursor when printing */
/* line 340, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
/* line 346, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab-wrap-hack:after {
content: '';
}
/* Help users use markselection to safely style text background */
/* line 349, vendor/assets/codemirror/lib/codemirror.css */
span.CodeMirror-selectedtext {
background: none;
}
/* line 1, app/assets/stylesheets/admins/common.scss */
.admin-body-container {
padding: 20px;
@ -17952,6 +18553,11 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
padding: 0.5rem 2rem;
}
/* line 110, app/assets/stylesheets/admins/common.scss */
.admin-body-container .CodeMirror {
border: 1px solid #ced4da;
}
/* line 2, app/assets/stylesheets/admins/daily_school_statistics.scss */
.admins-daily-school-statistics-index-page .daily-school-statistic-list-container {
text-align: center;
@ -18359,7 +18965,7 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
text-align: center;
}
/* line 11, app/assets/stylesheets/admin.scss */
/* line 12, app/assets/stylesheets/admin.scss */
body {
width: 100vw;
height: 100vh;
@ -18373,33 +18979,53 @@ body {
background: #efefef;
}
/* line 23, app/assets/stylesheets/admin.scss */
/* line 24, app/assets/stylesheets/admin.scss */
a:hover {
text-decoration: unset;
}
/* line 28, app/assets/stylesheets/admin.scss */
/* line 29, app/assets/stylesheets/admin.scss */
textarea.danger, input.danger {
border-color: #dc3545 !important;
}
/* line 32, app/assets/stylesheets/admin.scss */
/* line 33, app/assets/stylesheets/admin.scss */
label.error {
color: #dc3545 !important;
}
/* line 38, app/assets/stylesheets/admin.scss */
/* line 39, app/assets/stylesheets/admin.scss */
.simple_form .form-group .collection_radio_buttons {
margin-bottom: 0px;
}
/* line 42, app/assets/stylesheets/admin.scss */
/* line 43, app/assets/stylesheets/admin.scss */
.simple_form .form-group .form-check-inline {
height: calc(1.5em + 0.75rem + 2px);
}
/* line 48, app/assets/stylesheets/admin.scss */
/* line 49, app/assets/stylesheets/admin.scss */
.flex-1 {
-webkit-box-flex: 1;
flex: 1;
}
/* line 53, app/assets/stylesheets/admin.scss */
.font-12 {
font-size: 12px !important;
}
/* line 54, app/assets/stylesheets/admin.scss */
.font-14 {
font-size: 14px !important;
}
/* line 55, app/assets/stylesheets/admin.scss */
.font-16 {
font-size: 16px !important;
}
/* line 56, app/assets/stylesheets/admin.scss */
.font-18 {
font-size: 18px !important;
}

@ -17816,6 +17816,607 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
.datepicker.datepicker-inline td {
padding: 4px 5px;
}
/* BASICS */
/* line 3, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
/* Set height, width, borders, and global font properties here */
font-family: monospace;
height: 300px;
color: black;
direction: ltr;
}
/* PADDING */
/* line 13, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
padding: 4px 0;
/* Vertical padding around content */
}
/* line 16, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
padding: 0 4px;
/* Horizontal padding of content */
}
/* line 21, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white;
/* The little square between H and V scrollbars */
}
/* GUTTER */
/* line 27, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
white-space: nowrap;
}
/* line 33, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linenumber {
padding: 0 3px 0 5px;
min-width: 20px;
text-align: right;
color: #999;
white-space: nowrap;
}
/* line 41, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker {
color: black;
}
/* line 42, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-guttermarker-subtle {
color: #999;
}
/* CURSOR */
/* line 46, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
border-left: 1px solid black;
border-right: none;
width: 0;
}
/* Shown when moving in bi-directional text */
/* line 52, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-secondarycursor {
border-left: 1px solid silver;
}
/* line 55, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor .CodeMirror-cursor {
width: auto;
border: 0 !important;
background: #7e7;
}
/* line 60, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor div.CodeMirror-cursors {
z-index: 1;
}
/* line 63, vendor/assets/codemirror/lib/codemirror.css */
.cm-fat-cursor-mark {
background-color: rgba(20, 255, 20, 0.5);
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
/* line 69, vendor/assets/codemirror/lib/codemirror.css */
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-webkit-keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
@keyframes blink {
0% {
}
50% {
background-color: transparent;
}
100% {
}
}
/* Can style cursor different in overwrite (non-insert) mode */
/* line 96, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab {
display: inline-block;
text-decoration: inherit;
}
/* line 98, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rulers {
position: absolute;
left: 0;
right: 0;
top: -50px;
bottom: 0;
overflow: hidden;
}
/* line 103, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-ruler {
border-left: 1px solid #ccc;
top: 0;
bottom: 0;
position: absolute;
}
/* DEFAULT THEME */
/* line 111, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-header {
color: blue;
}
/* line 112, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-quote {
color: #090;
}
/* line 113, vendor/assets/codemirror/lib/codemirror.css */
.cm-negative {
color: #d44;
}
/* line 114, vendor/assets/codemirror/lib/codemirror.css */
.cm-positive {
color: #292;
}
/* line 115, vendor/assets/codemirror/lib/codemirror.css */
.cm-header, .cm-strong {
font-weight: bold;
}
/* line 116, vendor/assets/codemirror/lib/codemirror.css */
.cm-em {
font-style: italic;
}
/* line 117, vendor/assets/codemirror/lib/codemirror.css */
.cm-link {
text-decoration: underline;
}
/* line 118, vendor/assets/codemirror/lib/codemirror.css */
.cm-strikethrough {
text-decoration: line-through;
}
/* line 120, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-keyword {
color: #708;
}
/* line 121, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-atom {
color: #219;
}
/* line 122, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-number {
color: #164;
}
/* line 123, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-def {
color: #00f;
}
/* line 128, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-2 {
color: #05a;
}
/* line 129, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {
color: #085;
}
/* line 130, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-comment {
color: #a50;
}
/* line 131, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string {
color: #a11;
}
/* line 132, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-string-2 {
color: #f50;
}
/* line 133, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-meta {
color: #555;
}
/* line 134, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-qualifier {
color: #555;
}
/* line 135, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-builtin {
color: #30a;
}
/* line 136, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-bracket {
color: #997;
}
/* line 137, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-tag {
color: #170;
}
/* line 138, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-attribute {
color: #00c;
}
/* line 139, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-hr {
color: #999;
}
/* line 140, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-link {
color: #00c;
}
/* line 142, vendor/assets/codemirror/lib/codemirror.css */
.cm-s-default .cm-error {
color: #f00;
}
/* line 143, vendor/assets/codemirror/lib/codemirror.css */
.cm-invalidchar {
color: #f00;
}
/* line 145, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-composing {
border-bottom: 2px solid;
}
/* Default styles for common addons */
/* line 149, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-matchingbracket {
color: #0b0;
}
/* line 150, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror span.CodeMirror-nonmatchingbracket {
color: #a22;
}
/* line 151, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-matchingtag {
background: rgba(255, 150, 0, 0.3);
}
/* line 152, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-activeline-background {
background: #e8f2ff;
}
/* STOP */
/* The rest of this file contains styles related to the mechanics of
the editor. You probably shouldn't touch them. */
/* line 159, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror {
position: relative;
overflow: hidden;
background: white;
}
/* line 165, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll {
overflow: scroll !important;
/* Things will break if this is overridden */
/* 30px is the magic margin used to hide the element's real scrollbars */
/* See overflow: hidden in .CodeMirror */
margin-bottom: -30px;
margin-right: -30px;
padding-bottom: 30px;
height: 100%;
outline: none;
/* Prevent dragging from highlighting the element */
position: relative;
}
/* line 175, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-sizer {
position: relative;
border-right: 30px solid transparent;
}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actual scrolling happens, thus preventing shaking and
flickering artifacts. */
/* line 183, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
}
/* line 188, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-vscrollbar {
right: 0;
top: 0;
overflow-x: hidden;
overflow-y: scroll;
}
/* line 193, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-hscrollbar {
bottom: 0;
left: 0;
overflow-y: hidden;
overflow-x: scroll;
}
/* line 198, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scrollbar-filler {
right: 0;
bottom: 0;
}
/* line 201, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-filler {
left: 0;
bottom: 0;
}
/* line 205, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutters {
position: absolute;
left: 0;
top: 0;
min-height: 100%;
z-index: 3;
}
/* line 210, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter {
white-space: normal;
height: 100%;
display: inline-block;
vertical-align: top;
margin-bottom: -30px;
}
/* line 217, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper {
position: absolute;
z-index: 4;
background: none !important;
border: none !important;
}
/* line 223, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-background {
position: absolute;
top: 0;
bottom: 0;
z-index: 4;
}
/* line 228, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-elt {
position: absolute;
cursor: default;
z-index: 4;
}
/* line 233, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
.CodeMirror-gutter-wrapper ::selection {
background-color: transparent;
}
/* line 234, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-gutter-wrapper ::-moz-selection {
background-color: transparent;
}
/* line 236, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-lines {
cursor: text;
min-height: 1px;
/* prevents collapsing before first draw */
}
/* line 240, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like {
/* Reset some styles that the rest of the page might have set */
border-radius: 0;
border-width: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
margin: 0;
white-space: pre;
word-wrap: normal;
line-height: inherit;
color: inherit;
z-index: 2;
position: relative;
overflow: visible;
-webkit-tap-highlight-color: transparent;
-webkit-font-variant-ligatures: contextual;
font-variant-ligatures: contextual;
}
/* line 260, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-wrap pre.CodeMirror-line,
.CodeMirror-wrap pre.CodeMirror-line-like {
word-wrap: break-word;
white-space: pre-wrap;
word-break: normal;
}
/* line 267, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linebackground {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 0;
}
/* line 273, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-linewidget {
position: relative;
z-index: 2;
padding: 0.1px;
/* Force widget margins to stay inside of the container */
}
/* line 281, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-rtl pre {
direction: rtl;
}
/* line 283, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-code {
outline: none;
}
/* Force content-box sizing for the elements where we expect it */
/* line 288, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-scroll,
.CodeMirror-sizer,
.CodeMirror-gutter,
.CodeMirror-gutters,
.CodeMirror-linenumber {
box-sizing: content-box;
}
/* line 297, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure {
position: absolute;
width: 100%;
height: 0;
overflow: hidden;
visibility: hidden;
}
/* line 305, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-cursor {
position: absolute;
pointer-events: none;
}
/* line 309, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-measure pre {
position: static;
}
/* line 311, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-cursors {
visibility: hidden;
position: relative;
z-index: 3;
}
/* line 316, vendor/assets/codemirror/lib/codemirror.css */
div.CodeMirror-dragcursors {
visibility: visible;
}
/* line 320, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused div.CodeMirror-cursors {
visibility: visible;
}
/* line 324, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-selected {
background: #d9d9d9;
}
/* line 325, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-focused .CodeMirror-selected {
background: #d7d4f0;
}
/* line 326, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-crosshair {
cursor: crosshair;
}
/* line 327, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection {
background: #d7d4f0;
}
/* line 328, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection {
background: #d7d4f0;
}
/* line 330, vendor/assets/codemirror/lib/codemirror.css */
.cm-searching {
background-color: #ffa;
background-color: rgba(255, 255, 0, 0.4);
}
/* Used to force a border model for a node */
/* line 336, vendor/assets/codemirror/lib/codemirror.css */
.cm-force-border {
padding-right: .1px;
}
@media print {
/* Hide the cursor when printing */
/* line 340, vendor/assets/codemirror/lib/codemirror.css */
.CodeMirror div.CodeMirror-cursors {
visibility: hidden;
}
}
/* See issue #2901 */
/* line 346, vendor/assets/codemirror/lib/codemirror.css */
.cm-tab-wrap-hack:after {
content: '';
}
/* Help users use markselection to safely style text background */
/* line 349, vendor/assets/codemirror/lib/codemirror.css */
span.CodeMirror-selectedtext {
background: none;
}
/* line 1, app/assets/stylesheets/admins/common.scss */
.admin-body-container {
padding: 20px;
@ -17952,6 +18553,11 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
padding: 0.5rem 2rem;
}
/* line 110, app/assets/stylesheets/admins/common.scss */
.admin-body-container .CodeMirror {
border: 1px solid #ced4da;
}
/* line 2, app/assets/stylesheets/admins/daily_school_statistics.scss */
.admins-daily-school-statistics-index-page .daily-school-statistic-list-container {
text-align: center;
@ -18359,7 +18965,7 @@ form.was-validated select:valid ~ .select2-container--bootstrap4 .select2-select
text-align: center;
}
/* line 11, app/assets/stylesheets/admin.scss */
/* line 12, app/assets/stylesheets/admin.scss */
body {
width: 100vw;
height: 100vh;
@ -18373,36 +18979,56 @@ body {
background: #efefef;
}
/* line 23, app/assets/stylesheets/admin.scss */
/* line 24, app/assets/stylesheets/admin.scss */
a:hover {
text-decoration: unset;
}
/* line 28, app/assets/stylesheets/admin.scss */
/* line 29, app/assets/stylesheets/admin.scss */
textarea.danger, input.danger {
border-color: #dc3545 !important;
}
/* line 32, app/assets/stylesheets/admin.scss */
/* line 33, app/assets/stylesheets/admin.scss */
label.error {
color: #dc3545 !important;
}
/* line 38, app/assets/stylesheets/admin.scss */
/* line 39, app/assets/stylesheets/admin.scss */
.simple_form .form-group .collection_radio_buttons {
margin-bottom: 0px;
}
/* line 42, app/assets/stylesheets/admin.scss */
/* line 43, app/assets/stylesheets/admin.scss */
.simple_form .form-group .form-check-inline {
height: calc(1.5em + 0.75rem + 2px);
}
/* line 48, app/assets/stylesheets/admin.scss */
/* line 49, app/assets/stylesheets/admin.scss */
.flex-1 {
-webkit-box-flex: 1;
flex: 1;
}
/* line 53, app/assets/stylesheets/admin.scss */
.font-12 {
font-size: 12px !important;
}
/* line 54, app/assets/stylesheets/admin.scss */
.font-14 {
font-size: 14px !important;
}
/* line 55, app/assets/stylesheets/admin.scss */
.font-16 {
font-size: 16px !important;
}
/* line 56, app/assets/stylesheets/admin.scss */
.font-18 {
font-size: 18px !important;
}
@charset "UTF-8";
/* line 1, app/assets/stylesheets/admins/common.scss */
.admin-body-container {
@ -18539,6 +19165,11 @@ label.error {
.admin-body-container .nav-tabs .nav-link {
padding: 0.5rem 2rem;
}
/* line 110, app/assets/stylesheets/admins/common.scss */
.admin-body-container .CodeMirror {
border: 1px solid #ced4da;
}
/* line 2, app/assets/stylesheets/admins/daily_school_statistics.scss */
.admins-daily-school-statistics-index-page .daily-school-statistic-list-container {
text-align: center;

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

Loading…
Cancel
Save