合并代码

dev_local_2
daiao 5 years ago
commit 6bd122723a

@ -96,3 +96,5 @@ gem 'searchkick'
gem 'aasm'
gem 'enumerize'
gem 'diffy'

@ -0,0 +1,86 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-laboratory-settings-show-page, body.admins-laboratory-settings-update-page').length > 0) {
var $container = $('.edit-laboratory-setting-container');
var $form = $container.find('.edit_laboratory');
$('.logo-item-left').on("change", 'input[type="file"]', function () {
var $fileInput = $(this);
var file = this.files[0];
var imageType = /image.*/;
if (file && file.type.match(imageType)) {
var reader = new FileReader();
reader.onload = function () {
var $box = $fileInput.parent();
$box.find('img').attr('src', reader.result).css('display', 'block');
$box.addClass('has-img');
};
reader.readAsDataURL(file);
} else {
}
});
createMDEditor('laboratory-footer-editor', { height: 200, placeholder: '请输入备案信息' });
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
errorPlacement:function(error,element){
if(element.parent().hasClass("input-group")){
element.parent().after(error);
}else{
element.after(error)
}
},
rules: {
identifier: {
required: true,
checkSite: true
},
name: {
required: true
}
}
});
$.validator.addMethod("checkSite",function(value,element,params){
var checkSite = /^(?=^.{3,255}$)[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$/;
return this.optional(element)||(checkSite.test(value + '.educoder.com'));
},"域名不合法!");
$form.on('click', '.submit-btn', function(){
$form.find('.submit-btn').attr('disabled', 'disabled');
$form.find('.error').html('');
var valid = $form.valid();
$('input[name="navbar[][name]"]').each(function(_, e){
var $ele = $(e);
if($ele.val() === undefined || $ele.val().length === 0){
$ele.addClass('danger text-danger');
valid = false;
} else {
$ele.removeClass('danger text-danger');
}
});
if(!valid) return;
$.ajax({
method: 'PATCH',
dataType: 'json',
url: $form.attr('action'),
data: new FormData($form[0]),
processData: false,
contentType: false,
success: function(data){
$.notify({ message: '保存成功' });
window.location.reload();
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
},
complete: function(){
$form.find('.submit-btn').attr('disabled', false);
}
});
})
}
});

@ -0,0 +1,164 @@
$(document).on('turbolinks:load', function() {
if ($('body.admins-laboratories-index-page').length > 0) {
var $searchContainer = $('.laboratory-list-form');
var $searchForm = $searchContainer.find('form.search-form');
var $list = $('.laboratory-list-container');
// ============== 新建 ===============
var $modal = $('.modal.admin-create-laboratory-modal');
var $form = $modal.find('form.admin-create-laboratory-form');
var $schoolSelect = $modal.find('.school-select');
$form.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
school_id: {
required: true
}
},
messages: {
school_id: {
required: '请选择所属单位'
}
}
});
// modal ready fire
$modal.on('show.bs.modal', function () {
$schoolSelect.select2('val', ' ');
});
// ************** 学校选择 *************
var matcherFunc = function(params, data){
if ($.trim(params.term) === '') {
return data;
}
if (typeof data.text === 'undefined') {
return null;
}
if (data.name && data.name.indexOf(params.term) > -1) {
var modifiedData = $.extend({}, data, true);
return modifiedData;
}
// Return `null` if the term should not be displayed
return null;
};
var defineSchoolSelect = function(schools) {
$schoolSelect.select2({
theme: 'bootstrap4',
placeholder: '请选择单位',
minimumInputLength: 1,
data: schools,
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.name;
},
templateSelection: function(item){
if (item.id) {
$('#school_id').val(item.id);
}
return item.name || item.text;
},
matcher: matcherFunc
});
}
$.ajax({
url: '/api/schools/for_option.json',
dataType: 'json',
type: 'GET',
success: function(data) {
defineSchoolSelect(data.schools);
}
});
$modal.on('click', '.submit-btn', function(){
$form.find('.error').html('');
if ($form.valid()) {
var url = $form.data('url');
$.ajax({
method: 'POST',
dataType: 'json',
url: url,
data: $form.serialize(),
success: function(){
$.notify({ message: '创建成功' });
$modal.modal('hide');
setTimeout(function(){
window.location.reload();
}, 500);
},
error: function(res){
var data = res.responseJSON;
$form.find('.error').html(data.message);
}
});
}
});
// ============= 添加管理员 ==============
var $addMemberModal = $('.admin-add-laboratory-user-modal');
var $addMemberForm = $addMemberModal.find('.admin-add-laboratory-user-form');
var $memberSelect = $addMemberModal.find('.laboratory-user-select');
var $laboratoryIdInput = $addMemberForm.find('input[name="laboratory_id"]')
$addMemberModal.on('show.bs.modal', function(event){
var $link = $(event.relatedTarget);
var laboratoryId = $link.data('laboratory-id');
$laboratoryIdInput.val(laboratoryId);
$memberSelect.select2('val', ' ');
});
$memberSelect.select2({
theme: 'bootstrap4',
placeholder: '请输入要添加的管理员姓名',
multiple: true,
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/admins/users',
dataType: 'json',
data: function(params){
return { name: params.term };
},
processResults: function(data){
return { results: data.users }
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return item.real_name;
},
templateSelection: function(item){
if (item.id) {
}
return item.real_name || item.text;
}
});
$addMemberModal.on('click', '.submit-btn', function(){
$addMemberForm.find('.error').html('');
var laboratoryId = $laboratoryIdInput.val();
var memberIds = $memberSelect.val();
if (laboratoryId && memberIds && memberIds.length > 0) {
$.ajax({
method: 'POST',
dataType: 'script',
url: '/admins/laboratories/' + laboratoryId + '/laboratory_user',
data: { user_ids: memberIds }
});
} else {
$addMemberModal.modal('hide');
}
});
}
});

@ -0,0 +1,99 @@
.admins-laboratories-index-page {
.laboratory-list-table {
.member-container {
.laboratory-user {
display: flex;
justify-content: center;
flex-wrap: wrap;
.laboratory-user-item {
display: flex;
align-items: center;
height: 22px;
line-height: 22px;
padding: 2px 5px;
margin: 2px 2px;
border: 1px solid #91D5FF;
background-color: #E6F7FF;
color: #91D5FF;
border-radius: 4px;
}
}
}
}
}
.admins-laboratory-settings-show-page, .admins-laboratory-settings-update-page {
.edit-laboratory-setting-container {
.logo-item {
display: flex;
&-img {
display: block;
width: 80px;
height: 80px;
}
&-upload {
cursor: pointer;
position: absolute;
top: 0;
width: 80px;
height: 80px;
background: #F5F5F5;
border: 1px solid #E5E5E5;
&::before {
content: '';
position: absolute;
top: 27px;
left: 39px;
width: 2px;
height: 26px;
background: #E5E5E5;
}
&::after {
content: '';
position: absolute;
top: 39px;
left: 27px;
width: 26px;
height: 2px;
background: #E5E5E5;
}
}
&-left {
position: relative;
width: 80px;
height: 80px;
&.has-img {
.logo-item-upload {
display: none;
}
&:hover {
.logo-item-upload {
display: block;
background: rgba(145, 145, 145, 0.8);
}
}
}
}
&-right {
display: flex;
flex-direction: column;
justify-content: space-between;
color: #777777;
font-size: 12px;
}
&-title {
color: #23272B;
font-size: 14px;
}
}
}
}

@ -0,0 +1,32 @@
class Admins::LaboratoriesController < Admins::BaseController
def index
params[:sort_by] = params[:sort_by].presence || 'id'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
laboratories = Admins::LaboratoryQuery.call(params)
@laboratories = paginate laboratories.preload(:school, :laboratory_users)
end
def create
Admins::CreateLaboratoryService.call(create_params)
render_ok
rescue Admins::CreateLaboratoryService::Error => ex
render_error(ex.message)
end
def destroy
current_laboratory.destroy!
render_delete_success
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:id])
end
def create_params
params.permit(:school_id)
end
end

@ -0,0 +1,20 @@
class Admins::LaboratorySettingsController < Admins::BaseController
def show
@laboratory = current_laboratory
end
def update
Admins::SaveLaboratorySettingService.call(current_laboratory, form_params)
render_ok
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
end
def form_params
params.permit(:identifier, :name, :nav_logo, :login_logo, :tab_logo, :footer, navbar: %i[name link hidden])
end
end

@ -0,0 +1,19 @@
class Admins::LaboratoryUsersController < Admins::BaseController
helper_method :current_laboratory
def create
Admins::AddLaboratoryUserService.call(current_laboratory, params.permit(user_ids: []))
current_laboratory.reload
end
def destroy
@laboratory_user = current_laboratory.laboratory_users.find_by(user_id: params[:user_id])
@laboratory_user.destroy! if @laboratory_user.present?
end
private
def current_laboratory
@_current_laboratory ||= Laboratory.find(params[:laboratory_id])
end
end

@ -92,6 +92,6 @@ class Admins::ShixunSettingsController < Admins::BaseController
end
def setting_params
params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:id,tag_repertoires:[])
params.permit(:use_scope,:excute_time,:close,:status,:can_copy,:webssh,:hidden,:homepage_show,:task_pass,:code_hidden,:page_no, :id,tag_repertoires:[])
end
end

@ -7,6 +7,7 @@ class ApplicationController < ActionController::Base
include ControllerRescueHandler
include GitHelper
include LoggerHelper
include LaboratoryHelper
protect_from_forgery prepend: true, unless: -> { request.format.json? }
@ -354,7 +355,7 @@ class ApplicationController < ActionController::Base
# 通关后,把最后一次成功的代码存到数据库
# type 0 创始内容, 1 最新内容
def game_passed_code(path, myshixun, game_id)
# 如果代码窗口是隐藏的或者是vnc实训,则不用保存通关代码
# 如果代码窗口是隐藏的,则不用保存代码
return if myshixun.shixun.hide_code || myshixun.shixun.vnc
file_content = git_fle_content myshixun.repo_path, path
unless file_content.present?

@ -0,0 +1,15 @@
module LaboratoryHelper
extend ActiveSupport::Concern
included do
helper_method :default_setting
end
def current_laboratory
@_current_laboratory ||= (Laboratory.find_by_subdomain(request.subdomain) || Laboratory.find(1))
end
def default_setting
@_default_setting ||= LaboratorySetting.find_by(laboratory_id: 1)
end
end

@ -12,10 +12,10 @@ class CoursesController < ApplicationController
end
before_action :require_login, except: [:index, :show, :students, :teachers, :board_list, :mine, :all_course_groups,
:left_banner, :top_banner, :informs, :online_learning]
:left_banner, :top_banner, :informs, :online_learning, :course_groups]
before_action :check_account, only: [:new, :create, :apply_to_join_course, :join_excellent_course]
before_action :check_auth, except: [:index, :show, :students, :teachers, :board_list, :mine, :all_course_groups,
:left_banner, :top_banner, :apply_to_join_course, :exit_course]
:left_banner, :top_banner, :apply_to_join_course, :exit_course, :course_groups]
before_action :set_course, only: [:show, :update, :destroy, :settings, :set_invite_code_halt,
:set_public_or_private, :search_teacher_candidate, :teachers, :apply_teachers,
:top_banner, :left_banner, :add_teacher_popup, :add_teacher,
@ -27,7 +27,8 @@ class CoursesController < ApplicationController
:attahcment_category_list,:export_member_scores_excel, :duplicate_course,
:switch_to_teacher, :switch_to_assistant, :switch_to_student, :exit_course,
:informs, :update_informs, :online_learning, :update_task_position, :tasks_list,
:join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs, :delete_informs]
:join_excellent_course, :export_couser_info, :export_member_act_score, :new_informs,
:delete_informs, :change_member_role, :course_groups, :join_course_group]
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,
@ -39,7 +40,7 @@ class CoursesController < ApplicationController
:set_course_group, :create_group_by_importing_file,
:update_task_position, :tasks_list]
before_action :teacher_or_admin_allowed, only: [:graduation_group_list, :create_graduation_group, :join_graduation_group,
:change_course_teacher, :course_group_list,
:change_course_teacher, :course_group_list, :change_member_role,
:teacher_application_review, :apply_teachers, :delete_course_teacher]
before_action :validate_course_name, only: [:create, :update]
before_action :find_board, only: :board_list
@ -340,8 +341,8 @@ class CoursesController < ApplicationController
@has_graduation_design = @course.course_modules.graduation_module_not_hidden.any?
sort = params[:sort] || "desc"
@order = params[:order].to_i
sort = params[:sort] || "asc"
@order = params[:order] ? params[:order].to_i : 1
if @order.present?
case @order
when 1
@ -547,6 +548,61 @@ class CoursesController < ApplicationController
end
end
# 修改角色
def change_member_role
tip_exception("请至少选择一个角色") if params[:roles].blank?
tip_exception("不能具有老师、助教两种角色") if params[:roles].include?("PROFESSOR") && params[:roles].include?("ASSISTANT_PROFESSOR")
tip_exception("管理员不能切换为助教或老师") if params[:user_id].to_i == @course.tea_id &&
(params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR"))
course_members = @course.course_members.where(user_id: params[:user_id])
tip_exception("非课堂成员不能修改角色") if course_members.blank?
ActiveRecord::Base.transaction do
# 第一次修改为教师或助教身份时直接创建数据
if params[:roles].include?("CREATOR")
teacher_member = course_members.where(role: %i[CREATOR]).take
elsif (params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")) && !course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = CourseMember.create!(course_id: @course.id, user_id: params[:user_id], role: params[:roles].include?("PROFESSOR") ? 2 : 3)
elsif course_members.exists?(role: %i[PROFESSOR ASSISTANT_PROFESSOR])
teacher_member = course_members.where(role: %i[PROFESSOR ASSISTANT_PROFESSOR]).take
if params[:roles].include?("PROFESSOR") || params[:roles].include?("ASSISTANT_PROFESSOR")
# 如果之前有老师身份且老师身份要调整时只需要修改role字段
if !params[:roles].include?(teacher_member.role) && params[:roles].include?("PROFESSOR")
teacher_member.PROFESSOR!
elsif !params[:roles].include?(teacher_member.role) && params[:roles].include?("ASSISTANT_PROFESSOR")
teacher_member.ASSISTANT_PROFESSOR!
end
teacher_member.save!
else
# 不含教师的参数时删除记录
teacher_member.destroy!
# CourseDeleteStudentNotifyJob.perform_later(@course.id, [teacher_member.user_id], current_user.id)
end
end
# 学生身份的处理
student_member = course_members.where(role: %i[STUDENT]).take
if params[:roles].include?("STUDENT") && student_member.blank?
correspond_teacher_exist = CourseMember.exists?(user_id: params[:user_id], is_active: 1, course_id: @course.id, role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR])
new_student = CourseMember.new(user_id: params[:user_id], course_id: @course.id, role: 4)
new_student.is_active = 0 if correspond_teacher_exist
new_student.save!
CourseAddStudentCreateWorksJob.perform_later(@course.id, [params[:user_id]])
# StudentJoinCourseNotifyJob.perform_later(current_user.id, course.id)
elsif !params[:roles].include?("STUDENT") && student_member.present?
# 删除学生身份时激活老师身份
teacher_member.update_attributes!(is_active: 1) if student_member.is_active && teacher_member.present?
student_member.destroy!
CourseDeleteStudentDeleteWorksJob.perform_later(@course.id, [params[:user_id]])
# CourseDeleteStudentNotifyJob.perform_later(@course.id, [params[:user_id]], current_user.id)
end
normal_status(0, "修改成功")
end
end
# 教师和助教角色转换的接口
def change_course_teacher
begin
@ -715,8 +771,8 @@ class CoursesController < ApplicationController
# 学生列表(包括各个子分班的学生列表)及搜索
def students
search = params[:search].present? ? params[:search].strip : nil
order = params[:order].present? ? params[:order].to_i : 0
sort = params[:sort].present? ? params[:sort] : "desc"
order = params[:order].present? ? params[:order].to_i : 1
sort = params[:sort].present? ? params[:sort] : "asc"
course_group_id = params[:course_group_id].present? ? params[:course_group_id].to_i : nil
@students = CourseMember.students(@course)
@ -766,6 +822,26 @@ class CoursesController < ApplicationController
end
end
# 分班列表
def course_groups
@course_groups = @course.course_groups
@course_groups = @course_groups.where("name like ?", "%#{params[:search]}%") unless params[:search].blank?
@all_group_count = @course_groups.size
@teachers = @course.teachers.includes(:user, :teacher_course_groups) if @user_course_identity < Course::NORMAL
@current_group_id = @course.students.where(user_id: current_user.id).take&.course_group_id if @user_course_identity == Course::STUDENT
end
# 学生自动加入分班
def join_course_group
tip_exception("学生才能加入分班") if @user_course_identity != Course::STUDENT
course_group = CourseGroup.find_by!(id: params[:course_group_id], course_id: @course.id)
member = CourseMember.find_by!(user_id: current_user.id, course_id: @course.id, role: 4)
if course_group && member
member.update_attributes!(course_group_id: course_group.id)
normal_status(0, "加入成功")
end
end
# 将学生批量移动到某个分班
def transfer_to_course_group
ActiveRecord::Base.transaction do

@ -1,17 +1,22 @@
class Ecs::CourseAchievementMethodsController < Ecs::CourseBaseController
def show
include_associations = { ec_course_achievement_methods: [:ec_course_evaluation, :ec_course_evaluation_subitems] }
include_associations = { ec_course_achievement_methods:
[:ec_course_evaluation, ec_achievement_evaluation_relates: :ec_course_evaluation_subitem] }
@course_targets = current_course.ec_course_targets.includes(include_associations)
end
def create
@course_target = Ecs::CreateCourseAchievementMethodsService.call(current_course_target, create_params)
Ecs::CreateCourseAchievementMethodsService.call(current_course_target, create_params)
render_ok
end
private
def create_params
params.permit(course_achievement_methods: %i[id course_evaluation_id course_evaluation_subitem_ids score percentage])
params.permit(course_achievement_methods: [
:id, :course_evaluation_id, :score, :percentage,
course_evaluation_relates: %i[:subitem_id position]
])
end
def current_course_target

@ -3,7 +3,8 @@ class Ecs::CourseManagersController < Ecs::CourseBaseController
before_action :check_major_manager_permission!, only: [:create, :destroy]
def create
@user = Ecs::CreateCourseManagerService.call(current_course, params[:user_id])
Ecs::CreateCourseManagerService.call(current_course, params[:user_ids])
render_ok
rescue Ecs::CreateCourseManagerService::Error => ex
render_error(ex.message)
end

@ -20,8 +20,8 @@ class Ecs::CourseTargetsController < Ecs::CourseBaseController
def with_achievement_methods
@course_targets = current_course.ec_course_targets
.includes(:ec_graduation_subitems,
ec_course_achievement_methods: [:ec_course_evaluation, :ec_course_evaluation_subitems])
.includes(ec_course_achievement_methods:
[:ec_course_evaluation, ec_achievement_evaluation_relates: :ec_course_evaluation_subitem])
end
private

@ -5,8 +5,9 @@ class Ecs::EvaluationsController < Ecs::CourseBaseController
render_ok(
course_targets: service.course_targets,
course_achievement: service.course_achievement,
course_rate: service.course_rate,
graduation_subitem_evaluations: service.graduation_subitem_evaluations,
score_levels_map: service.score_levels_map
score_levels: service.score_levels
)
end

@ -46,8 +46,8 @@ class ExercisesController < ApplicationController
@exercises = member_show_exercises.exists? ? member_show_exercises.unified_setting : []
else #已分班级的成员,可以查看统一设置和单独设置(试卷是发布在该班级)试卷
# 已发布 当前用户班级分组的 试卷id
not_exercise_ids = @course.exercise_group_settings.exercise_group_not_published.where("course_group_id = #{@member_group_id}").pluck(:exercise_id)
@exercises = member_show_exercises.where.not(id: not_exercise_ids)
publish_exercise_ids = @course.exercise_group_settings.exercise_group_published.where("course_group_id = #{@member_group_id}").pluck(:exercise_id)
@exercises = member_show_exercises.unified_setting.or(member_show_exercises.where(id: publish_exercise_ids))
end
else #用户未登陆或不是该课堂成员,仅显示统一设置的(已发布的/已截止的),如有公开,则不显示锁,不公开,则显示锁
@is_teacher_or = 0
@ -683,12 +683,34 @@ class ExercisesController < ApplicationController
end
end
# 详情页的立即发布弹框
def publish_groups
@current_user = current_user
# 可立即发布的分班:当前用户管理的分班去除已发布的分班
group_ids = @course.charge_group_ids(@current_user) - @exercise.exercise_group_settings.exercise_group_published.pluck(:course_group_id)
@course_groups = @course.course_groups.where(id: group_ids)
@group_settings = @exercise.exercise_group_settings.where(course_group_id: group_ids)
end
#首页批量或单独 立即发布,应是跳出弹窗,设置开始时间和截止时间。
def publish
group_ids = params[:group_ids]&.reject(&:blank?)
if params[:detail].blank?
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
else
group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time}
tip_exception("缺少截止时间参数") if group_end_times.blank?
tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length
group_end_times.each do |time|
tip_exception("分班截止时间不能早于当前时间") if time <= Time.now
tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && time > @course.end_date.end_of_day
end
end
ActiveRecord::Base.transaction do
begin
check_ids = Exercise.where(id: params[:check_ids])
@ -702,28 +724,30 @@ class ExercisesController < ApplicationController
.exercise_group_not_published.present? ? 1 : 0
end
if ex_status == 1 #如果试卷存在已发布的,或者是已截止的,那么则直接跳过
g_course = params[:group_ids] #表示是否传入分班参数,如果传入分班的参数,那么试卷的统一设置需修改
g_course = group_ids #表示是否传入分班参数,如果传入分班的参数,那么试卷的统一设置需修改
tiding_group_ids = g_course
if g_course
user_course_groups = @course.charge_group_ids(current_user)
if g_course.map(&:to_i).sort == user_course_groups.sort # 如果是设置为全部班级,则试卷不用分组,且试卷设定为统一设置,否则则分组设置
user_course_groups = @course.course_groups.pluck(:id)
if g_course.map(&:to_i).sort == user_course_groups.sort &&
((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?) # 如果是设置为全部班级,则试卷不用分组,且试卷设定为统一设置,否则则分组设置
exercise.exercise_group_settings.destroy_all
ex_unified = true
e_time = ex_end_time
e_time = params[:detail] ? group_end_times.max : ex_end_time
tiding_group_ids = []
else
ex_unified = false
g_course.each do |i|
g_course.each_with_index do |i, index|
exercise_group_setting = exercise.exercise_group_settings.find_in_exercise_group("course_group_id",i).first #根据课堂分班的id寻找试卷所在的班级
group_end_time = params[:detail] ? group_end_times[index] : ex_end_time
if exercise_group_setting #如果该试卷分组存在,则更新,否则新建
exercise_group_setting.update_attributes(publish_time:Time.now,end_time:ex_end_time)
exercise_group_setting.update_attributes(publish_time: Time.now, end_time: group_end_time)
else
p_course_group = {
:exercise_id => exercise.id,
:course_group_id => i,
:course_id => exercise.course.id,
:publish_time => Time.now,
:end_time => ex_end_time,
:end_time => group_end_time,
}
new_exercise_group = exercise.exercise_group_settings.new p_course_group
new_exercise_group.save
@ -954,7 +978,8 @@ class ExercisesController < ApplicationController
:status => nil,
:commit_status => 0,
:objective_score => 0.0,
:subjective_score => -1.0
:subjective_score => -1.0,
:commit_method => 0
}
redo_exercise_users = @exercise_users.exercise_commit_users(user_ids)
redo_exercise_users.update_all(redo_option)
@ -1077,7 +1102,19 @@ class ExercisesController < ApplicationController
def commit_exercise
ActiveRecord::Base.transaction do
begin
can_commit_exercise = false
user_left_time = nil
if @user_course_identity > Course::ASSISTANT_PROFESSOR #为学生时
if params[:commit_method].to_i == 2 #自动提交时
user_left_time = get_exercise_left_time(@exercise,current_user)
Rails.logger.info("######__________auto_commit_user_left_time_________################{user_left_time}")
if user_left_time.to_i <= 0
can_commit_exercise = true
end
else
can_commit_exercise = true
end
if can_commit_exercise
objective_score = calculate_student_score(@exercise,current_user)[:total_score]
subjective_score = @answer_committed_user.subjective_score
total_score_subjective_score = subjective_score < 0.0 ? 0.0 : subjective_score
@ -1088,11 +1125,17 @@ class ExercisesController < ApplicationController
:end_at => Time.now,
:objective_score => objective_score,
:score => total_score,
:subjective_score => subjective_score
:subjective_score => subjective_score,
:commit_method => @answer_committed_user&.commit_method.to_i > 0 ? @answer_committed_user&.commit_method.to_i : params[:commit_method].to_i
}
@answer_committed_user.update_attributes(commit_option)
CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id)
normal_status(0,"试卷提交成功!")
else
normal_status(-2,"#{user_left_time.to_i}")
end
else
normal_status(-1,"提交失败,当前用户不为课堂学生!")
end
rescue Exception => e
uid_logger_error(e.message)

@ -5,7 +5,7 @@ class HomeController < ApplicationController
images = PortalImage.where(status: true).order("position asc")
@images_url = []
images.each do |image|
@images_url << {path: image.link, image_url: "/images/avatars/PortalImage/#{image.id}"}
@images_url << {path: image.link, image_url: Util::FileManage.disk_file_url('PortalImage', image.id)}
end
# 目录分级

@ -159,8 +159,13 @@ class HomeworkCommonsController < ApplicationController
end
# 作品状态 0 未提交, 1 按时提交, 2 延迟提交
unless params[:work_status].blank?
@student_works = @student_works.where(work_status: params[:work_status])
if params[:work_status].present?
work_status = params[:work_status].map{|status| status.to_i}
all_student_works = @student_works.left_joins(:myshixun)
@student_works = all_student_works.where(work_status: work_status)
@student_works = @student_works.or(all_student_works.where(work_status: 0)).or(all_student_works.where(myshixuns: {status: 0})) if work_status.include?(3)
@student_works = @student_works.or(all_student_works.where(myshixuns: {status: 1})) if work_status.include?(4)
end
# 分班情况
@ -201,12 +206,12 @@ class HomeworkCommonsController < ApplicationController
page = params[:page] || 1
limit = params[:limit] || 20
@student_works = @student_works.page(page).per(limit)
@students = @course.students.where(user_id: @student_works.pluck(:user_id)).preload(:course_group)
if @homework.homework_type == "practice"
@student_works = @student_works.includes(:student_works_scores, user: :user_extension, myshixun: :games)
else
@student_works = @student_works.includes(:student_works_scores, :project, user: :user_extension)
end
@students = @course.students.preload(:course_group)
end
if params[:format] == "xlsx"
@ -1036,6 +1041,7 @@ class HomeworkCommonsController < ApplicationController
# 可立即发布的分班:当前用户管理的分班去除已发布的分班
group_ids = @course.charge_group_ids(@current_user) - @homework.homework_group_settings.group_published.pluck(:course_group_id)
@course_groups = @course.course_groups.where(id: group_ids)
@group_settings = @homework.homework_group_settings.where(course_group_id: group_ids)
else
tip_exception("没有可发布的分班")
end
@ -1043,16 +1049,28 @@ class HomeworkCommonsController < ApplicationController
def publish_homework
tip_exception("请至少选择一个分班") if params[:group_ids].blank? && @course.course_groups.size != 0
group_ids = params[:group_ids]&.reject(&:blank?)
if params[:detail].blank?
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
else
group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time}
tip_exception("缺少截止时间参数") if group_end_times.blank?
tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length
group_end_times.each do |time|
tip_exception("分班截止时间不能早于当前时间") if time <= Time.now
tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && time > @course.end_date.end_of_day
end
end
homeworks = @course.homework_commons.where(id: params[:homework_ids])
homeworks = homeworks.includes(:homework_group_settings, :homework_detail_manual)
charge_group_ids = @course.charge_group_ids(current_user)
publish_groups = charge_group_ids & params[:group_ids] if params[:group_ids]
publish_groups = charge_group_ids & group_ids if group_ids
# ActiveRecord::Base.transaction do
begin
@ -1062,18 +1080,26 @@ class HomeworkCommonsController < ApplicationController
if !params[:group_ids].blank?
# 全选即统一设置unified_setting为true
if @course.course_groups.where(id: publish_groups).size == @course.course_groups.size
if @course.course_groups.where(id: publish_groups).size == @course.course_groups.size &&
((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?)
homework.homework_group_settings.destroy_all
homework.unified_setting = true
homework.end_time = params[:end_time]
homework.end_time = params[:detail] ? group_end_times.max : params[:end_time]
else
homework.unified_setting = false
# 创建作业分班设置homework_group_setting
create_homework_group_settings(homework)
# 选中的分班设置的发布时间改为当前时间,截止时间改为传的截止时间参数
if params[:detail]
group_ids.each_with_index do |group_id, index|
homework.homework_group_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now,
end_time: group_end_times[index])
end
else
homework.homework_group_settings.where(course_group_id: publish_groups).update_all(publish_time: Time.now,
end_time: params[:end_time])
end
# 发消息
tiding_group_ids = publish_groups
end
@ -1086,7 +1112,7 @@ class HomeworkCommonsController < ApplicationController
# 截止时间的处理
if homework.end_time.nil?
homework.end_time = params[:end_time]
homework.end_time = params[:detail] ? group_end_times.max : params[:end_time]
elsif homework.max_group_end_time
homework.end_time = homework.max_group_end_time
end
@ -1101,12 +1127,22 @@ class HomeworkCommonsController < ApplicationController
create_homework_group_settings(homework)
none_publish_settings = homework.homework_group_settings.where(course_group_id: publish_groups).none_published
if params[:detail]
group_ids.each_with_index do |group_id, index|
none_publish_settings.find_by(course_group_id: group_id)&.update_attributes!(publish_time: Time.now,
end_time: group_end_times[index])
end
else
none_publish_settings.update_all(publish_time: Time.now, end_time: params[:end_time])
end
if homework.max_group_end_time
homework.end_time = homework.max_group_end_time
end
HomeworkCommonPushNotifyJob.perform_later(homework.id, none_publish_settings.pluck(:course_group_id))
end
if homework.end_time > Time.now && homework.homework_detail_manual.try(:comment_status) > 1
homework.homework_detail_manual.update_attribute("comment_status", 1)
end

@ -25,9 +25,9 @@ class MemosController < ApplicationController
!search.blank? ? "forum_id = #{forum_id} and root_id is null and subject like '%#{search}%'" :
"forum_id = #{forum_id} and root_id is null"
elsif !search.blank?
"forum_id in(3, 5) and root_id is null and subject like '%#{search}%'"
"forum_id in(3, 5, 16) and root_id is null and subject like '%#{search}%'"
else
"forum_id in(3, 5) and root_id is null"
"forum_id in(3, 5, 16) and root_id is null"
end
if tag_repertoire_id

@ -326,7 +326,7 @@ class MyshixunsController < ApplicationController
if edu_css.present?
css_path = edu_css.split(",")
css_path.each do |path|
file_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
file_content = git_fle_content(@repo_path, path)["content"]
file_content = tran_base64_decode64(file_content) unless file_content.blank?
@contents = @contents.sub(/EDUCODERCSS/, "<style>#{file_content}</style>")
end
@ -335,7 +335,7 @@ class MyshixunsController < ApplicationController
if edu_js.present?
js_path = edu_js.split(",")
js_path.each do |path|
file_content = GitService.file_content(repo_path: @repo_path, path: path)["content"]
file_content = git_fle_content(@repo_path, path)["content"]
file_content = tran_base64_decode64(file_content) unless file_content.blank?
@contents = @contents.sub(/EDUCODERJS/, "<script>#{file_content}</script>")
end

@ -2,10 +2,10 @@ class PollsController < ApplicationController
# before_action :check_poll_status 问卷的发消息和定时任务没有做
before_action :require_login, :check_auth,except: [:index]
before_action :find_course, except: [:show,:poll_setting,:commit_setting,:edit,:update,:start_answer,:commit_poll,
:commit_result,:poll_lists,:cancel_publish,:cancel_publish_modal,:common_header]
:commit_result,:poll_lists,:cancel_publish,:cancel_publish_modal,:common_header,:publish_groups]
before_action :get_poll_and_course, only: [:show,:poll_setting,:commit_setting,:edit,:update,:start_answer,
:commit_poll,:commit_result,:poll_lists,:cancel_publish,
:cancel_publish_modal,:common_header]
:cancel_publish_modal,:common_header, :publish_groups]
before_action :user_course_identity
before_action :is_course_teacher, except: [:index,:start_answer,:poll_setting,:commit_poll,:commit_result,:poll_lists,:common_header] #判断是否为课堂老师
before_action :check_user_status
@ -242,12 +242,35 @@ class PollsController < ApplicationController
end
end
end
# 详情页的立即发布弹框
def publish_groups
@current_user = current_user
# 可立即发布的分班:当前用户管理的分班去除已发布的分班
group_ids = @course.charge_group_ids(@current_user) - @poll.poll_group_settings.poll_group_published.pluck(:course_group_id)
@course_groups = @course.course_groups.where(id: group_ids)
@group_settings = @poll.poll_group_settings.where(course_group_id: group_ids)
end
#首页批量或单独 立即发布,应是跳出弹窗,设置开始时间和截止时间。
def publish
group_ids = params[:group_ids]&.reject(&:blank?)
if params[:detail].blank?
tip_exception("缺少截止时间参数") if params[:end_time].blank?
tip_exception("截止时间不能早于当前时间") if params[:end_time] <= strf_time(Time.now)
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time] > strf_time(@course.end_date.end_of_day)
else
group_end_times = params[:group_end_times].reject(&:blank?).map{|time| time.to_time}
tip_exception("缺少截止时间参数") if group_end_times.blank?
tip_exception("截止时间和分班参数的个数不一致") if group_end_times.length != group_ids.length
group_end_times.each do |time|
tip_exception("分班截止时间不能早于当前时间") if time <= Time.now
tip_exception("分班截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && time > @course.end_date.end_of_day
end
end
ActiveRecord::Base.transaction do
begin
check_ids = Poll.where(id: params[:check_ids])
@ -259,26 +282,28 @@ class PollsController < ApplicationController
pl_status = poll.poll_group_settings.find_in_poll_group("course_group_id",params[:group_ids]).poll_group_not_published.present? ? 1 : 0 #立即发布针对分组设置的全部未发布的班级才生效
end
if pl_status == 1 #如果问卷存在已发布的,或者是已截止的,那么则直接跳过
g_course = params[:group_ids] #表示是否传入分班参数,如果传入分班的参数那么poll的统一设置需修改
g_course = group_ids #表示是否传入分班参数,如果传入分班的参数那么poll的统一设置需修改
if g_course
user_course_groups = @course.charge_group_ids(current_user)
if g_course.map(&:to_i).sort == user_course_groups.sort # 如果是设置为全部班级,则问卷不用分组,且问卷设定为统一设置,否则则分组设置
user_course_groups = @course.course_groups.pluck(:id)
if g_course.map(&:to_i).sort == user_course_groups.sort &&
((params[:detail] && group_end_times.min == group_end_times.max) || params[:detail].blank?) # 如果是设置为全部班级,则问卷不用分组,且问卷设定为统一设置,否则则分组设置
poll.poll_group_settings.destroy_all
poll_unified = true
e_time = ex_end_time
e_time = params[:detail] ? group_end_times.max : ex_end_time
else
poll_unified = false
g_course.each do |i|
g_course.each_with_index do |i, index|
poll_group_setting = poll.poll_group_settings.find_in_poll_group("course_group_id",i).first #根据课堂分班的id寻找问卷所在的班级
group_end_time = params[:detail] ? group_end_times[index] : ex_end_time
if poll_group_setting #如果该问卷分组存在,则更新,否则新建
poll_group_setting.update_attributes(publish_time:Time.now,end_time:ex_end_time)
poll_group_setting.update_attributes(publish_time: Time.now, end_time: group_end_time)
else
p_course_group = {
:poll_id => poll.id,
:course_group_id => i,
:course_id => poll.course.id,
:publish_time => Time.now,
:end_time => ex_end_time,
:end_time => group_end_time,
}
new_poll_group = poll.poll_group_settings.new p_course_group
new_poll_group.save
@ -692,7 +717,7 @@ class PollsController < ApplicationController
else
unified_setting = @poll.unified_setting
end
show_result = params[:show_result].to_i
show_result = params[:show_result]
un_anonymous = params[:un_anonymous] ? true : false
# 统一设置或者分班为0则更新问卷并删除问卷分组
if unified_setting || (course_group_ids.size == 0)
@ -1318,7 +1343,7 @@ class PollsController < ApplicationController
poll_ques_titles = poll_questions.pluck(:question_title).map {|k| ActionController::Base.helpers.strip_tags(k) if k.present?}
poll_un_anony = poll.un_anonymous
if poll_un_anony #是否匿名默认为false
user_info = %w(登陆名 真实姓名 邮箱 学号)
user_info = %w(登陆名 真实姓名 邮箱 学号 学员单位)
else
user_info = []
end
@ -1392,7 +1417,8 @@ class PollsController < ApplicationController
user_login = u_user.login
user_name = u_user.real_name.present? ? u_user.real_name : "--"
user_student_id = u_user.student_id.present? ? u_user.student_id : "--"
user_cell += [user_login,user_name, u_user.mail, user_student_id]
user_school_name = u_user.school_name.present? ? u_user.school_name : "--"
user_cell += [user_login,user_name, u_user.mail, user_student_id, user_school_name]
end
all_user_cell = user_cell + user_answer_array
user_commit.push(all_user_cell)

@ -90,23 +90,25 @@ class QuestionBanksController < ApplicationController
def send_to_course
banks = @object_type.classify.constantize.where(id: params[:object_id])
course = current_user.manage_courses.find_by!(id: params[:course_id])
task_ids = []
banks.each do |bank|
case @object_type
when 'HomeworkBank' # 作业
quote_homework_bank bank, course
task = quote_homework_bank bank, course
when 'ExerciseBank'
if bank.container_type == 'Exercise' # 试卷
quote_exercise_bank bank, course
task = quote_exercise_bank bank, course
else # 问卷
quote_poll_bank bank, course
task = quote_poll_bank bank, course
end
when 'GtaskBank'
quote_gtask_bank bank, course
task = quote_gtask_bank bank, course
when 'GtopicBank'
quote_gtopic_bank bank, course
task = quote_gtopic_bank bank, course
end
task_ids << task.id if task
end
normal_status("发送成功")
render :json => {task_ids: task_ids, status: 0, message: "发送成功"}
end
def destroy

@ -0,0 +1,5 @@
class SettingsController < ApplicationController
def show
@laboratory = current_laboratory
end
end

@ -2,6 +2,7 @@ class ShixunsController < ApplicationController
include ShixunsHelper
include ApplicationHelper
include ElasticsearchAble
include CoursesHelper
before_action :require_login, :check_auth, except: [:download_file, :index, :menus, :show, :show_right, :ranking_list,
:discusses, :collaborators, :fork_list, :propaedeutics]

@ -0,0 +1,5 @@
class TemplatesController < ApplicationController
def show
@template = EcTemplate.find_by_name!(params[:name])
end
end

@ -2,10 +2,10 @@ class Wechats::JsSdkSignaturesController < ApplicationController
def create
timestamp = Time.now.to_i
noncestr = ('A'..'z').to_a.sample(8).join
signature = Util::Wechat.js_sdk_signature(params[:url], noncestr, timestamp)
signature = Wechat::OfficialAccount.js_sdk_signature(params[:url], noncestr, timestamp)
render_ok(appid: Util::Wechat.appid, timestamp: timestamp, noncestr: noncestr, signature: signature)
rescue Util::Wechat::Error => ex
render_ok(appid: Wechat::OfficialAccount.appid, timestamp: timestamp, noncestr: noncestr, signature: signature)
rescue Wechat::Error => ex
render_error(ex.message)
end
end

@ -1,5 +1,17 @@
module CoursesHelper
def member_manager group, teachers
str = ""
members = teachers.select{|teacher| teacher.teacher_course_groups.pluck(:course_group_id).include?(group.id) || teacher.teacher_course_groups.size == 0}
str = members.uniq.size == teachers.size ? "全部教师" : members.map{|member| member.user.real_name}.join("")
str
# teachers.each do |member|
# if member.teacher_course_groups.exists?(course_group_id: group.id) || member.teacher_course_groups.size == 0
# str << member.user.real_name
# end
# end
end
# 是否有切换为学生的入口
def switch_student_role is_teacher, course, user
is_teacher && course.course_members.where(user_id: user.id, role: %i(STUDENT)).exists?

@ -148,7 +148,7 @@ module ExercisesHelper
:choice_position => c.choice_position,
:choice_text => c.choice_text,
:choice_users_count => answer_users_count,
:choice_percent => answer_percent.round(1).to_s,
:choice_percent => answer_percent.round(2).to_s,
:right_answer => right_answer
}
question_answer_infos.push(answer_option)
@ -174,7 +174,7 @@ module ExercisesHelper
:choice_position => index+1,
:choice_text => s_choice_text,
:choice_users_count => user_count,
:choice_percent => answer_percent.round(1).to_s,
:choice_percent => answer_percent.round(2).to_s,
:right_answer => true
}
question_answer_infos.push(answer_option)
@ -194,7 +194,7 @@ module ExercisesHelper
:choice_position => (standard_answer_count + 1),
:choice_text => "wrong",
:choice_users_count => user_wrong_count,
:choice_percent => wrong_percent.round(1).to_s,
:choice_percent => wrong_percent.round(2).to_s,
:right_answer => false
}
question_answer_infos.push(wrong_answer_position)
@ -220,7 +220,7 @@ module ExercisesHelper
:choice_position => index+1,
:choice_text => index+1,
:choice_users_count => s,
:choice_percent => score_percent.round(1).to_s,
:choice_percent => score_percent.round(2).to_s,
:right_answer => right_answer
}
question_answer_infos.push(answer_option)
@ -246,7 +246,7 @@ module ExercisesHelper
:choice_position => index+1,
:choice_text => index+1,
:choice_users_count => s,
:choice_percent => score_percent.round(1).to_s,
:choice_percent => score_percent.round(2).to_s,
:right_answer => right_answer
}
shixun_chas.push(answer_option)
@ -256,7 +256,7 @@ module ExercisesHelper
:cha_name => c.challenge.subject,
:cha_position => c.position,
:cha_details => shixun_chas,
:cha_percent => game_percent.round(1).to_s
:cha_percent => game_percent.round(2).to_s
}
question_answer_infos.push(shixun_new_chas)
end
@ -266,7 +266,7 @@ module ExercisesHelper
:ques_less_title => ques_less_title, #副标题,仅实训题才有
:type => ex.question_type,
:position => ex.question_number,
:percent => percent.round(1).to_s,
:percent => percent.round(2).to_s,
:ques_effictive_counts => effictive_users_count,
:ques_details => question_answer_infos
}

@ -222,6 +222,14 @@ module HomeworkCommonsHelper
[{ id: 0 ,name: "未评", count: homework.uncomment_count(user_id)}, {id: 1, name: "已评", count: homework.comment_count(user_id)}]
end
# 作品状态
def practice_homework_status homework, member
[{id: 3, name: "未通关", count: homework.un_complete_count(member)},
{id: 4, name: "已通关", count: homework.complete_count(member)},
{id: 1, name: "按时完成", count: homework.finished_count(member)},
{id: 2, name: "延时完成", count: homework.delay_finished_count(member)}]
end
# 作品状态
def homework_status homework, member
[{id: 0, name: "未提交", count: homework.unfinished_count(member)},

@ -1,6 +1,6 @@
module MemosHelper
def forum_list
[{id: 5, name: "技术分享"}, {id: 3, name: "操作指南"}]
[{id: 5, name: "技术分享"}, {id: 3, name: "操作指南"}, {id: 16, name: "通知公告"}]
end
end

@ -0,0 +1,12 @@
class CreateDiffRecordJob < ApplicationJob
queue_as :default
def perform(user_id, obj_id, obj_klass, column_name, before, after)
user = User.find_by(id: user_id)
obj = obj_klass.constantize.find_by(id: obj_id)
return if user.blank? || obj.blank?
CreateDiffRecordService.call(user, obj, column_name, before, after)
end
end

@ -19,7 +19,8 @@ class EndExerciseCalculateJob < ApplicationJob
:end_at => Time.now,
:objective_score => objective_score,
:score => total_score,
:subjective_score => user_sub_score
:subjective_score => user_sub_score,
:commit_method => user&.commit_method.to_i > 0 ? user&.commit_method.to_i : 4
}
user.update_attributes(commit_option)
end

@ -48,8 +48,8 @@ module Util
return if str.blank?
case type
when :phone then "#{str[0..2]}***#{str[-4..-1]}"
when :email then "#{str[0..2]}***#{str[str.rindex('@')..-1]}"
when :phone then "#{str[0..2]}***#{str[-3..-1]}"
when :email then "#{str[0]}***#{str[(str.rindex('@')-1)..-1]}"
else "#{str[0..2]}***#{str[-3..-1]}"
end
end

@ -10,24 +10,35 @@ module Util::FileManage
File.join(Rails.root, "public", "images", relative_path)
end
def disk_filename(source_type, source_id,image_file=nil)
File.join(storage_path, "#{source_type}", "#{source_id}")
def disk_filename(source_type, source_id, suffix=nil)
File.join(storage_path, "#{source_type}", "#{source_id}#{suffix}")
end
def exist?(source_type, source_id)
File.exist?(disk_filename(source_type, source_id))
def source_disk_filename(source, suffix=nil)
disk_filename(source.class.name, source.id, suffix)
end
def exists?(source)
File.exist?(disk_filename(source.class, source.id))
def exist?(source_type, source_id, suffix=nil)
File.exist?(disk_filename(source_type, source_id, suffix))
end
def disk_file_url(source_type, source_id)
File.join('/images', relative_path, "#{source_type}", "#{source_id}")
def exists?(source, suffix=nil)
File.exist?(disk_filename(source.class, source.id, suffix))
end
def source_disk_file_url(source)
File.join('/images', relative_path, "#{source.class}", "#{source.id}")
def disk_file_url(source_type, source_id, suffix = nil)
t = ctime(source_type, source_id, suffix)
File.join('/images', relative_path, "#{source_type}", "#{source_id}#{suffix}") + "?t=#{t}"
end
def source_disk_file_url(source, suffix=nil)
disk_file_url(source.class, source.id, suffix)
end
def ctime(source_type, source_id, suffix)
return nil unless exist?(source_type, source_id, suffix)
File.ctime(disk_filename(source_type, source_id, suffix)).to_i
end
def disk_auth_filename(source_type, source_id, type)
@ -39,7 +50,7 @@ module Util::FileManage
end
def auth_file_url(source_type, source_id, type)
File.join('/images', relative_path, source_type, "#{source_id}#{type}")
disk_file_url(source_type, source_id, type)
end
def real_name_auth_file_url(source_id)

@ -1,75 +0,0 @@
module Util::Wechat
BASE_SITE = 'https://api.weixin.qq.com'.freeze
Error = Class.new(StandardError)
class << self
attr_accessor :appid, :secret
def js_sdk_signature(url, noncestr, timestamp)
data = { jsapi_ticket: jsapi_ticket, noncestr: noncestr, timestamp: timestamp, url: url }
str = data.map { |k, v| "#{k}=#{v}" }.join('&')
Digest::SHA1.hexdigest(str)
end
def access_token
# 7200s 有效时间
Rails.cache.fetch(access_token_cache_key, expires_in: 100.minutes) do
result = request(:get, '/cgi-bin/token', appid: appid, secret: secret, grant_type: 'client_credential')
result['access_token']
end
end
def refresh_access_token
Rails.cache.delete(access_token_cache_key)
access_token
end
def jsapi_ticket
# 7200s 有效时间
Rails.cache.fetch(jsapi_ticket_cache_key, expires_in: 100.minutes) do
result = request(:get, '/cgi-bin/ticket/getticket', access_token: access_token, type: 'jsapi')
result['ticket']
end
end
def refresh_jsapi_ticket
Rails.cache.delete(jsapi_ticket_cache_key)
jsapi_ticket
end
def access_token_cache_key
"#{base_cache_key}/access_token"
end
def jsapi_ticket_cache_key
"#{base_cache_key}/jsapi_ticket"
end
def base_cache_key
"wechat/#{appid}"
end
private
def request(method, url, **params)
Rails.logger.error("[wechat] request: #{method} #{url} #{params.inspect}")
client = Faraday.new(url: BASE_SITE)
response = client.public_send(method, url, params)
result = JSON.parse(response.body)
Rails.logger.error("[wechat] response:#{response.status} #{result.inspect}")
if response.status != 200
raise Error, result.inspect
end
if result['errcode'].present? && result['errcode'].to_i.nonzero?
raise Error, result.inspect
end
result
end
end
end

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

@ -0,0 +1,11 @@
class Wechat::App
class << self
attr_accessor :appid, :secret
delegate :access_token, :jscode2session, to: :client
def client
@_client ||= Wechat::Client.new(appid, secret)
end
end
end

@ -0,0 +1,74 @@
class Wechat::Client
BASE_SITE = 'https://api.weixin.qq.com'.freeze
attr_reader :appid, :secret
def initialize(appid, secret)
@appid = appid
@secret = secret
end
def access_token
# 7200s 有效时间
Rails.cache.fetch(access_token_cache_key, expires_in: 100.minutes) do
result = request(:get, '/cgi-bin/token', appid: appid, secret: secret, grant_type: 'client_credential')
result['access_token']
end
end
def refresh_access_token
Rails.cache.delete(access_token_cache_key)
access_token
end
def jsapi_ticket
# 7200s 有效时间
Rails.cache.fetch(jsapi_ticket_cache_key, expires_in: 100.minutes) do
result = request(:get, '/cgi-bin/ticket/getticket', access_token: access_token, type: 'jsapi')
result['ticket']
end
end
def refresh_jsapi_ticket
Rails.cache.delete(jsapi_ticket_cache_key)
jsapi_ticket
end
def jscode2session(code)
request(:get, '/sns/jscode2session', appid: appid, secret: secret, js_code: code, grant_type: 'authorization_code')
end
def access_token_cache_key
"#{base_cache_key}/access_token"
end
def jsapi_ticket_cache_key
"#{base_cache_key}/jsapi_ticket"
end
def base_cache_key
"wechat/#{appid}"
end
private
def request(method, url, **params)
Rails.logger.error("[wechat] request: #{method} #{url} #{params.except(:secret).inspect}")
client = Faraday.new(url: BASE_SITE)
response = client.public_send(method, url, params)
result = JSON.parse(response.body)
Rails.logger.error("[wechat] response:#{response.status} #{result.inspect}")
if response.status != 200
raise Wechat::Error.parse(result)
end
if result['errcode'].present? && result['errcode'].to_i.nonzero?
raise Wechat::Error.parse(result)
end
result
end
end

@ -0,0 +1,14 @@
class Wechat::Error < StandardError
attr_reader :code
def initialize(code, message)
super message
@code = code
end
class << self
def parse(result)
new(result['errcode'], result['errmsg'])
end
end
end

@ -0,0 +1,17 @@
class Wechat::OfficialAccount
class << self
attr_accessor :appid, :secret
delegate :access_token, :jsapi_ticket, to: :client
def js_sdk_signature(url, noncestr, timestamp)
data = { jsapi_ticket: jsapi_ticket, noncestr: noncestr, timestamp: timestamp, url: url }
str = data.map { |k, v| "#{k}=#{v}" }.join('&')
Digest::SHA1.hexdigest(str)
end
def client
@_client ||= Wechat::Client.new(appid, secret)
end
end
end

@ -28,6 +28,8 @@ class Challenge < ApplicationRecord
scope :fields_for_list, -> { select([:id, :subject, :st, :score, :position, :shixun_id]) }
after_commit :create_diff_record
def next_challenge
position = self.position + 1
Challenge.where(:position => position, :shixun_id => self.shixun).first
@ -126,4 +128,11 @@ class Challenge < ApplicationRecord
end
# 关卡评测文件
private
def create_diff_record
return unless task_pass_previously_changed?
CreateDiffRecordJob.perform_later(User.current.id, id, 'Challenge', 'task_pass', task_pass_before_last_save, task_pass)
end
end

@ -0,0 +1,8 @@
class DiffRecord < ApplicationRecord
belongs_to :user
belongs_to :container, polymorphic: true
has_one :diff_record_content, dependent: :destroy
delegate :content, to: :diff_record_content
end

@ -0,0 +1,3 @@
class DiffRecordContent < ApplicationRecord
belongs_to :diff_record
end

@ -6,7 +6,7 @@ class EcCourse < ApplicationRecord
has_many :ec_graduation_subitem_course_targets, through: :ec_course_targets
# 课程负责教师
has_many :ec_course_users
has_many :course_managers, through: :ec_course_users, class_name: 'User'
has_many :course_managers, through: :ec_course_users, source: :user
# 课程考核标准
has_many :ec_course_evaluations, dependent: :destroy
# 课程等级
@ -21,7 +21,7 @@ class EcCourse < ApplicationRecord
has_many :courses, through: :ec_major_courses
# 课程目标达成方法
# has_many :ec_course_achievement_methods
has_many :ec_course_achievement_methods
accepts_nested_attributes_for :ec_course_targets, :ec_score_levels, allow_destroy: true
end

@ -16,4 +16,23 @@ class EcCourseEvaluation < ApplicationRecord
def imported?
import_status?
end
def evaluation_relates
# 总成绩支撑只有课程考核标准的名称, 分项成绩支撑是课程考核标准名称与考核分项的笛卡尔积
if status == 1
return evaluation_count.times.map { |index| { id: -1, name: "#{name}#{index + 1}", position: index + 1 } }
end
if is_course_type?
ec_course_evaluation_subitems.map.with_index { |item, index| { id: item.id, name: item.name, position: index + 1 } }
else
data = []
ec_course_evaluation_subitems.each do |item|
evaluation_count.times do |i|
data << { id: item.id, name: "#{name}#{i + 1}#{item.name}", position: i + 1 }
end
end
data
end
end
end

@ -131,7 +131,7 @@ class Exercise < ApplicationRecord
status = 4
else
if user.present? && user.student_of_course?(course) #当为学生的时候,需根据分班来判断试卷状态
ex_time = get_exercise_times(user_id,false)
ex_time = get_exercise_times(user.id,false)
pb_time = ex_time[:publish_time]
ed_time = ex_time[:end_time]
if pb_time.present? && ed_time.present? && pb_time <= Time.now && ed_time > Time.now

@ -1,4 +1,5 @@
class ExerciseUser < ApplicationRecord
# commit_method 0 为默认, 1为学生的手动提交2为倒计时结束后自动提交3为试卷定时截止的自动提交, 4为教师手动的立即截止
belongs_to :user
belongs_to :exercise

@ -1,2 +1,3 @@
class Forum < ApplicationRecord
has_many :memos, dependent: :destroy
end

@ -136,7 +136,7 @@ class HomeworkCommon < ApplicationRecord
# 作业查看最新成绩
def update_score identity
identity < Course::NORMAL && publish_time.present? && publish_time < Time.now && !course.is_end
identity < Course::NORMAL && publish_time.present? && publish_time < Time.now && !end_or_late_none_group
end
# 作业能否立即发布
@ -240,6 +240,16 @@ class HomeworkCommon < ApplicationRecord
self.teacher_works(member).delay_finished.count
end
# 未通关数
def un_complete_count member
teacher_works(member).count - complete_count(member)
end
# 通关数
def complete_count member
Myshixun.where(id: self.teacher_works(member).pluck(:myshixun_id), status: 1).count
end
# 分组作业的最大分组id
def max_group_id
self.student_works.has_committed.maximum(:group_id).to_i + 1

@ -0,0 +1,26 @@
class Laboratory < ApplicationRecord
belongs_to :school, optional: true
has_many :laboratory_users, dependent: :destroy
has_many :users, through: :laboratory_users, source: :user
has_one :laboratory_setting, dependent: :destroy
validates :identifier, uniqueness: { case_sensitive: false }, allow_nil: true
def site
rails_env = EduSetting.get('rails_env')
suffix = rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net'
identifier ? "#{identifier}#{suffix}" : ''
end
def self.find_by_subdomain(subdomain)
return if subdomain.blank?
rails_env = EduSetting.get('rails_env')
subdomain = subdomain.slice(0, subdomain.size - rails_env.size - 1) if subdomain.end_with?(rails_env) # winse.dev => winse
find_by_identifier(subdomain)
end
end

@ -0,0 +1,54 @@
class LaboratorySetting < ApplicationRecord
belongs_to :laboratory
serialize :config, JSON
%i[name navbar footer].each do |method_name|
define_method method_name do
config&.[](method_name.to_s)
end
define_method "#{method_name}=" do |value|
self.config ||= {}
config.[]=(method_name.to_s, value)
end
end
def login_logo_url
logo_url('login')
end
def nav_logo_url
logo_url('nav')
end
def tab_logo_url
logo_url('tab')
end
def default_navbar
self.class.default_config[:navbar]
end
private
def logo_url(type)
return nil unless Util::FileManage.exists?(self, type)
Util::FileManage.source_disk_file_url(self, type)
end
def self.default_config
{
name: nil,
navbar: [
{ 'name' => '实践课程', 'link' => '/paths', 'hidden' => false },
{ 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false },
{ 'name' => '实现项目', 'link' => '/shixuns', 'hidden' => false },
{ 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false },
{ 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false },
{ 'name' => '交流问答', 'link' => '/forums', 'hidden' => false },
],
footer: nil
}
end
end

@ -0,0 +1,4 @@
class LaboratoryUser < ApplicationRecord
belongs_to :laboratory
belongs_to :user
end

@ -5,7 +5,7 @@ class MirrorRepository < ApplicationRecord
scope :published_mirror, -> { where(status: 1) }
scope :published_mirror, -> { where(status: [1,2,3,5]) }
scope :published_main_mirror, -> { published_mirror.where(main_type: 1) }
scope :published_small_mirror, -> { published_mirror.where(main_type: 0) }

@ -1,5 +1,6 @@
class Shixun < ApplicationRecord
include Searchable::Shixun
attr_accessor :page_no #管理员页面 实训配置更新状态时需要接受page_no参数
# status: 0编辑 1申请发布 2正式发布 3关闭 -1软删除
# hide_code 隐藏代码窗口

@ -2,4 +2,13 @@ class ShixunInfo < ApplicationRecord
belongs_to :shixun
validates_uniqueness_of :shixun_id
validates_presence_of :shixun_id
after_commit :create_diff_record
private
def create_diff_record
return unless description_previously_changed?
CreateDiffRecordJob.perform_later(User.current.id, id, 'ShixunInfo', 'description', description_before_last_save, description)
end
end

@ -0,0 +1,23 @@
class Admins::LaboratoryQuery < ApplicationQuery
include CustomSortable
attr_reader :params
sort_columns :id, default_by: :id, default_direction: :desc
def initialize(params)
@params = params
end
def call
laboratories = Laboratory.all
keyword = strip_param(:keyword)
if keyword.present?
like_sql = 'schools.name LIKE :keyword OR laboratories.identifier LIKE :keyword'
laboratories = laboratories.joins(:school).where(like_sql, keyword: "%#{keyword}%")
end
custom_sort laboratories, params[:sort_by], params[:sort_direction]
end
end

@ -0,0 +1,19 @@
class Admins::AddLaboratoryUserService < ApplicationService
attr_reader :laboratory, :params
def initialize(laboratory, params)
@laboratory = laboratory
@params = params
end
def call
columns = %i[]
LaboratoryUser.bulk_insert(*columns) do |worker|
Array.wrap(params[:user_ids]).compact.each do |user_id|
next if laboratory.laboratory_users.exists?(user_id: user_id)
worker.add(laboratory_id: laboratory.id, user_id: user_id)
end
end
end
end

@ -0,0 +1,20 @@
class Admins::CreateLaboratoryService < ApplicationService
Error = Class.new(StandardError)
attr_reader :params
def initialize(params)
@params = params
end
def call
raise Error, '单位不能为空' if params[:school_id].blank?
raise Error, '该单位已存在' if Laboratory.exists?(school_id: params[:school_id])
ActiveRecord::Base.transaction do
laboratory = Laboratory.create!(school_id: params[:school_id])
laboratory.create_laboratory_setting!
end
end
end

@ -14,7 +14,7 @@ class Admins::DragPortalImageService < ApplicationService
images = PortalImage.all
ActiveRecord::Base.transaction do
if after.blank? # 移动至末尾
if after.blank? || move.id == after.id # 移动至末尾
total = images.count
images.where('position > ?', move.position).update_all('position = position - 1')
@ -26,8 +26,8 @@ class Admins::DragPortalImageService < ApplicationService
images.where('position >= ? AND position < ?', after.position, move.position).update_all('position = position + 1')
move.update!(position: after.position)
else # 后移
images.where('position > ? AND position <= ?', move.position, after.position).update_all('position = position - 1')
move.update!(position: after.position)
images.where('position > ? AND position < ?', move.position, after.position).update_all('position = position - 1')
move.update!(position: after.position - 1)
end
end
end

@ -0,0 +1,51 @@
class Admins::SaveLaboratorySettingService < ApplicationService
attr_reader :laboratory, :laboratory_setting, :params
def initialize(laboratory, params)
@params = params
@laboratory = laboratory
@laboratory_setting = laboratory.laboratory_setting
end
def call
ActiveRecord::Base.transaction do
laboratory.identifier = strip params[:identifier]
laboratory_setting.name = strip params[:name]
laboratory_setting.navbar = navbar_config
laboratory_setting.footer = strip params[:footer]
laboratory.save!
laboratory_setting.save!
deal_logo_file
end
laboratory
end
private
def navbar_config
params[:navbar].map do |nav|
hash = {}
hash[:name] = strip nav[:name]
hash[:link] = strip nav[:link]
hash[:hidden] = nav[:hidden].to_s == 0
hash
end
end
def deal_logo_file
save_logo_file(params[:nav_logo], 'nav')
save_logo_file(params[:login_logo], 'login')
save_logo_file(params[:tab_logo], 'tab')
end
def save_logo_file(file, type)
return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile)
file_path = Util::FileManage.source_disk_filename(laboratory_setting, type)
File.delete(file_path) if File.exist?(file_path) # 删除之前的文件
Util.write_file(file, file_path)
end
end

@ -1,3 +1,9 @@
class ApplicationService
include Callable
private
def strip(str)
str.to_s.strip.presence
end
end

@ -0,0 +1,46 @@
class CreateDiffRecordService < ApplicationService
attr_reader :user, :obj, :column_name, :after, :before
def initialize(user, obj, column_name, before, after)
@user = user
@obj = obj
@before = before
@after = after
@column_name = column_name
end
def call
ActiveRecord::Base.transaction do
diff_record = DiffRecord.create!(user: user, container: obj, column_name: column_name)
diff_record.create_diff_record_content!(content: diff_content)
end
end
private
def diff_content
content = ''
arr = []
index = 0
fragment_size = 1
Diffy::Diff.new(before, after).each do |line|
unless line =~ /^[\+-]/
if arr.empty? && index < fragment_size
content += line
index += 1
else
index = 0
arr << line
arr.shift if arr.size > fragment_size
end
next
end
content += arr.join('') if arr.present?
content += line
arr.clear
end
content
end
end

@ -31,8 +31,8 @@ class Ecs::CreateCourseAchievementMethodsService < ApplicationService
# 创建新的评价方法时,全部为新建
if item[:id].blank? || course_target.ec_course_achievement_methods.find_by(id: item[:id]).blank?
item[:ec_achievement_evaluation_relates_attributes] =
Array(*item[:course_evaluation_subitem_ids]).uniq.map do |subitem_id|
{ ec_course_evaluation_subitem_id: subitem_id.to_i }
item[:course_evaluation_relates].map do |relate|
{ ec_course_evaluation_subitem_id: relate[:subitem_id].to_i, position: relate[:position] }
end
return
end
@ -41,22 +41,22 @@ class Ecs::CreateCourseAchievementMethodsService < ApplicationService
relates = achievement_method.ec_achievement_evaluation_relates
# 获取传入的 subitem id数组和已存在的 subitem id数组
old_subitem_ids = relates.map(&:ec_course_evaluation_subitem_id).uniq
new_subitem_ids = Array(*item[:course_evaluation_subitem_ids]).map(&:to_i).uniq
old_data = relates.map { |e| [e.ec_course_evaluation_subitem_id, e.position] }
new_data = item[:course_evaluation_relates].map { |e| [e[:subitem_id, e[:position]]] }
# 分别得到需要移除的 subitem ID数组和需要创建的 subitem ID数组
destroy_subitem_ids = old_subitem_ids - new_subitem_ids
create_subitem_ids = new_subitem_ids - old_subitem_ids
destroy_data = old_data - new_data
create_data = new_data - old_data
# 生成需要创建关系的 subitem id 数据
create_attributes = create_subitem_ids.map { |item_id| { ec_course_evaluation_subitem_id: item_id } }
create_attributes = create_data.map { |arr| { ec_course_target_id: course_target.id, ec_course_evaluation_subitem_id: arr[0], position: arr[1] } }
# 处理需要更新或者删除的记录
exists_attributes = relates.map do |relate|
if destroy_subitem_ids.include?(relate.ec_course_evaluation_subitem_id)
if destroy_data.include?([relate.ec_course_evaluation_subitem_id, relate.position])
{ id: relate.id, _destroy: true }
else
relate.as_json(only: %i[id ec_course_evaluation_subitem_id ec_course_achievement_method_id])
relate.as_json(only: %i[id ec_course_target_id ec_course_evaluation_subitem_id ec_course_achievement_method_id position])
end
end

@ -3,27 +3,29 @@ class Ecs::CreateCourseManagerService < ApplicationService
COURSE_MANAGER_COUNT_LIMIT = 2 # 课程管理员数量限制
attr_reader :ec_course, :user_id
attr_reader :ec_course, :user_ids
def initialize(ec_course, user_id)
def initialize(ec_course, user_ids)
@ec_course = ec_course
@user_id = user_id
@user_ids = user_ids
end
def call
user = User.find_by(id: params[:user_id])
raise Error, '该用户不存在' if user.blank?
users_count = User.where(id: user_ids).count
raise Error, '用户不存在' if users_count != user_ids.size
if ec_course.ec_course_users.exists?(user_id: user.id)
raise Error, '用户已经是该课程的管理员'
if ec_course.ec_course_users.exists?(user_id: user_ids)
raise Error, '用户已经是该课程的管理员'
end
if ec_course.ec_course_users.count >= COURSE_MANAGER_COUNT_LIMIT
raise Error, '该课程管理员数量已达上限'
if ec_course.ec_course_users.count + user_ids.size > COURSE_MANAGER_COUNT_LIMIT
raise Error, "课程管理员数量过多(最多#{COURSE_MANAGER_COUNT_LIMIT}"
end
ec_course.ec_course_users.create!(user: user)
user
ActiveRecord::Base.transaction do
user_ids.each do |user_id|
ec_course.ec_course_users.create!(user_id: user_id)
end
end
end
end

@ -4,6 +4,7 @@ class Ecs::QueryCourseEvaluationService < ApplicationService
def initialize(ec_course)
@ec_course = ec_course
@_course_achievement = 0
@_course_rate = 0
end
def course_targets
@ -16,6 +17,12 @@ class Ecs::QueryCourseEvaluationService < ApplicationService
@_course_achievement.round(2)
end
def course_rate
course_targets
@_course_rate.round(2)
end
def graduation_subitem_evaluations
student_scores = ec_course.ec_course_student_scores.joins(ec_student_score_targets: :ec_course_target).group(:ec_course_target_id)
student_scores = student_scores.select('AVG(score) as average_score, ec_course_target_id')
@ -58,10 +65,10 @@ class Ecs::QueryCourseEvaluationService < ApplicationService
end
end
def score_levels_map
@_score_levels_map ||= begin
def score_levels
@_score_levels ||= begin
index = 0
ec_course.ec_score_levels.each_with_object({}) do |level, obj|
ec_course.ec_score_levels.map do |level|
hash = level.as_json(only: %i[id position score level])
hash[:description] =
@ -72,7 +79,7 @@ class Ecs::QueryCourseEvaluationService < ApplicationService
end
index += 1
obj[level.id.to_s] = hash
hash
end
end
end
@ -96,6 +103,7 @@ class Ecs::QueryCourseEvaluationService < ApplicationService
# 计算总评成绩
@_course_achievement += data[:average_score].to_f * course_target.weight.to_f
@_course_rate += course_target.weight.to_f
# 计算学生成绩分布区间
student_count = 0

@ -25,9 +25,9 @@ class Users::ApplyAuthenticationService < ApplicationService
user.apply_user_authentication.create!(auth_type: 1, status: 0)
move_image_file! unless params[:upload_image].to_s == 'false'
end
sms_notify_admin
end
user
end

@ -37,13 +37,9 @@ class Users::ApplyProfessionalAuthService < ApplicationService
user.apply_user_authentication.create!(auth_type: 2, status: 0)
move_image_file! unless params[:upload_image].to_s == 'false'
end
sms_cache = Rails.cache.read("apply_pro_certification")
if sms_cache.nil?
sms_notify_admin
Rails.cache.write("apply_pro_certification", 1, expires_in: 5.minutes)
end
end
end
private
@ -61,7 +57,11 @@ class Users::ApplyProfessionalAuthService < ApplicationService
end
def sms_notify_admin
sms_cache = Rails.cache.read('apply_pro_certification')
if sms_cache.nil?
Educoder::Sms.notify_admin(send_type: 'apply_pro_certification')
Rails.cache.write('apply_pro_certification', 1, expires_in: 5.minutes)
end
rescue => ex
Util.logger_error(ex)
end

@ -66,7 +66,8 @@ class ExercisePublishTask
:end_at => Time.now,
:objective_score => s_score,
:score => total_score,
:subjective_score => subjective_score
:subjective_score => subjective_score,
:commit_method => exercise_user&.commit_method.to_i > 0 ? exercise_user&.commit_method.to_i : 3
}
exercise_user.update_attributes(commit_option)
end
@ -108,7 +109,8 @@ class ExercisePublishTask
:end_at => Time.now,
:objective_score => s_score,
:score => total_score,
:subjective_score => subjective_score
:subjective_score => subjective_score,
:commit_method => exercise_user&.commit_method.to_i > 0 ? exercise_user&.commit_method.to_i : 3
}
exercise_user.update_attributes(commit_option)
end

@ -0,0 +1,19 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('云上实验室') %>
<% end %>
<div class="box search-form-container laboratory-list-form">
<%= form_tag(admins_laboratories_path(unsafe_params), method: :get, class: 'form-inline search-form flex-1', remote: true) do %>
<%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-6 col-md-4 ml-3', placeholder: '学校名称/二级域名前缀检索') %>
<%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %>
<% end %>
<%= javascript_void_link '新建', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-laboratory-modal' } %>
</div>
<div class="box laboratory-list-container">
<%= render(partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>
</div>
<%= render 'admins/laboratories/shared/create_laboratory_modal' %>
<%= render 'admins/laboratories/shared/add_laboratory_user_modal' %>

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

@ -0,0 +1,30 @@
<div class="modal fade admin-add-laboratory-user-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-add-laboratory-user-form">
<%= hidden_field_tag(:laboratory_id, nil) %>
<div class="form-group d-flex">
<label class="col-form-label">管理员:</label>
<div class="d-flex flex-column-reverse w-75">
<select id="user_ids" name="user_ids" class="form-control laboratory-user-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,28 @@
<div class="modal fade admin-create-laboratory-modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">新建云上实验室</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form class="admin-create-laboratory-form" data-url="<%= admins_laboratories_path %>">
<div class="form-group d-flex">
<label for="school_id" class="col-form-label">选择单位:</label>
<div class="d-flex flex-column-reverse w-75">
<select id="school_id" name="school_id" class="form-control school-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,40 @@
<% school = laboratory.school %>
<td class="text-left"><%= school&.name || 'EduCoder主站' %></td>
<td class="text-left">
<% if laboratory.identifier %>
<%= link_to laboratory.site, "https://#{laboratory.site}", target: '_blank' %>
<% else %>
--
<% end %>
</td>
<td>
<% if school && school.identifier.present? %>
<%= link_to school.identifier.to_s, statistics_college_path(school.identifier), target: '_blank' %>
<% else %>
--
<% end %>
</td>
<td class="member-container">
<div class="laboratory-user">
<% laboratory.users.each do |user| %>
<span class="laboratory-user-item laboratory-user-item-<%= user.id %>">
<%= link_to user.real_name, "/users/#{user.login}", target: '_blank', data: { toggle: 'tooltip', title: '个人主页' } %>
<%= link_to(admins_laboratory_laboratory_user_path(laboratory, user_id: user.id),
method: :delete, remote: true, class: 'ml-1 delete-laboratory-user-action',
data: { confirm: '确认删除吗?' }) do %>
<i class="fa fa-close"></i>
<% end %>
</span>
<% end %>
</div>
</td>
<td><%= laboratory.created_at.strftime('%Y-%m-%d %H:%M') %></td>
<td class="action-container">
<%= link_to '定制', admins_laboratory_laboratory_setting_path(laboratory) %>
<% if school.present? && laboratory.id != 1 %>
<%= javascript_void_link '添加管理员', class: 'action', data: { laboratory_id: laboratory.id, toggle: 'modal', target: '.admin-add-laboratory-user-modal' } %>
<%= delete_link '删除', admins_laboratory_path(laboratory, element: ".laboratory-item-#{laboratory.id}"), class: 'delete-laboratory-action' %>
<% end %>
</td>

@ -0,0 +1,25 @@
<table class="table table-hover text-center laboratory-list-table">
<thead class="thead-light">
<tr>
<th width="20%" class="text-left">单位名称</th>
<th width="16%" class="text-left">域名</th>
<th width="10%">统计链接</th>
<th width="22%">管理员</th>
<th width="14%"><%= sort_tag('创建时间', name: 'id', path: admins_laboratories_path) %></th>
<th width="20%">操作</th>
</tr>
</thead>
<tbody>
<% if laboratories.present? %>
<% laboratories.each do |laboratory| %>
<tr class="laboratory-item laboratory-item-<%= laboratory.id %>">
<%= render 'admins/laboratories/shared/laboratory_item', laboratory: laboratory %>
</tr>
<% end %>
<% else %>
<%= render 'admins/shared/no_data_for_table' %>
<% end %>
</tbody>
</table>
<%= render partial: 'admins/shared/paginate', locals: { objects: laboratories } %>

@ -0,0 +1,131 @@
<% define_admin_breadcrumbs do %>
<% add_admin_breadcrumb('云上实验室', admins_laboratories_path) %>
<% add_admin_breadcrumb('单位定制') %>
<% end %>
<div class="box edit-laboratory-setting-container">
<%= simple_form_for(@laboratory, url: admins_laboratory_laboratory_setting_path(@laboratory), method: 'patch', html: { enctype: 'multipart/form-data' }) do |f| %>
<% setting = @laboratory.laboratory_setting %>
<div class="form-group px-2 setting-item">
<div class="setting-item-head"><h6>网站域名设置</h6></div>
<div class="dropdown-divider"></div>
<div class="pl-0 py-3 setting-item-body">
<div class="input-group col-12 col-md-4 px-0">
<div class="input-group-prepend">
<span class="input-group-text">https://</span>
</div>
<%= text_field_tag :identifier, @laboratory.identifier,
maxlength: 15, class: 'form-control font-16',
'onKeyUp': 'value=value.replace(/[^\w\.\-\/]/ig,"").toLowerCase()',
style: 'text-transform:lowercase'%>
<div class="input-group-append">
<% rails_env = EduSetting.get('rails_env') %>
<span class="input-group-text font-14" id="site-prefix"><%= rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net' %></span>
</div>
</div>
<%# if @laboratory.errors && @laboratory.errors.key?(:identifier) %>
<!-- <span id="identifier-error" class="danger text-danger">二级域名已被使用</span>-->
<%# end %>
</div>
</div>
<div class="form-group px-2 setting-item">
<div class="setting-item-head"><h6>网站名称设置</h6></div>
<div class="dropdown-divider"></div>
<div class="pl-0 py-3 setting-item-body">
<%= text_field_tag :name, setting.name, placeholder: '输入20个字以内的网站名称', maxlength: 20, class: 'form-control col-12 col-md-4' %>
</div>
</div>
<div class="form-group px-2 setting-item">
<div class="setting-item-head"><h6>Logo设置</h6></div>
<div class="dropdown-divider"></div>
<div class="pl-0 py-3 row setting-item-body">
<div class="col-12 col-md-4 logo-item">
<% nav_logo_img = setting.nav_logo_url %>
<div class="logo-item-left mr-3 <%= nav_logo_img ? 'has-img' : '' %>">
<img class="logo-item-img nav-logo-img" src="<%= nav_logo_img %>" style="<%= nav_logo_img.present? ? '' : 'display: none' %>"/>
<%= file_field_tag(:nav_logo, accept: 'image/png,image/jpg,image/jpeg', style: 'display: none', value: params[:nav_logo]) %>
<label for="nav_logo" class="logo-item-upload" data-toggle="tooltip" data-title="选择图片"></label>
</div>
<div class="logo-item-right">
<div class="logo-item-title flex-1">网站导航logo</div>
<div>格式PNG、JPG</div>
<div>尺寸高度38px以内宽等比例缩放</div>
</div>
</div>
<div class="col-12 col-md-4 logo-item">
<% login_logo_img = setting.login_logo_url %>
<div class="logo-item-left mr-3 <%= login_logo_img ? 'has-img' : '' %>">
<img class="logo-item-img login-logo-img" src="<%= login_logo_img %>" style="<%= login_logo_img.present? ? '' : 'display: none' %>"/>
<%= file_field_tag(:login_logo, accept: 'image/png,image/jpg,image/jpeg', style: 'display: none', value: params[:login_logo]) %>
<label for="login_logo" class="logo-item-upload" data-toggle="tooltip" data-title="选择图片"></label>
</div>
<div class="logo-item-right">
<div class="logo-item-title flex-1">登录页面logo</div>
<div>格式PNG、JPG</div>
<div>尺寸高度90px以内宽等比例缩放</div>
</div>
</div>
<div class="col-12 col-md-4 logo-item">
<% tab_logo_img = setting.tab_logo_url %>
<div class="logo-item-left mr-3 <%= tab_logo_img ? 'has-img' : '' %>">
<img class="logo-item-img tab-logo-img" src="<%= tab_logo_img %>" style="<%= tab_logo_img.present? ? '' : 'display: none' %>"/>
<%= file_field_tag(:tab_logo, accept: 'image/x-icon', style: 'display: none', value: params[:tab_logo]) %>
<label for="tab_logo" class="logo-item-upload" data-toggle="tooltip" data-title="选择图片"></label>
</div>
<div class="logo-item-right">
<div class="logo-item-title flex-1">浏览器导航栏logo</div>
<div>格式ico</div>
<div>尺寸16*16 32*32 48*48 64*64 </div>
</div>
</div>
</div>
</div>
<div class="form-group px-2 setting-item">
<div class="setting-item-head"><h6>导航设置</h6></div>
<div class="dropdown-divider"></div>
<div class="pl-0 py-3 setting-item-body">
<table class="table">
<thead class="thead-light">
<tr>
<th width="35%">导航名称</th>
<th width="50%">导航链接</th>
<th width="15%" class="text-center">是否展示</th>
</tr>
</thead>
<tbody>
<% (setting.navbar || setting.default_navbar).each do |nav| %>
<tr>
<td><%= text_field_tag('navbar[][name]', nav['name'], id: nil, class: 'form-control') %></td>
<td><%= text_field_tag('navbar[][link]', nav['link'], id: nil, class: 'form-control') %></td>
<td class="text-center">
<%= check_box_tag('navbar[][hidden]', 0, !nav['hidden'], id: nil, class: 'font-16') %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
<div class="form-group px-2 setting-item">
<div class="setting-item-head"><h6>底部备案信息设置</h6></div>
<div class="dropdown-divider"></div>
<div class="pl-0 my-3 setting-item-body" id="laboratory-footer-editor">
<%= text_area_tag(:footer, setting.footer, placeholder: '请输入备案信息', class: 'form-control', style: 'display: none;') %>
</div>
</div>
<div class="error my-2 danger text-danger"></div>
<div class="form-group mt-4">
<%= javascript_void_link '保存', class: 'btn btn-primary mr-3 px-4 submit-btn' %>
<%= link_to '取消', admins_laboratories_path, class: 'btn btn-secondary px-4' %>
</div>
<% end %>
</div>

@ -0,0 +1,4 @@
$('.modal.admin-add-laboratory-user-modal').modal('hide');
$.notify({ message: '操作成功' });
$('.laboratory-list-table .laboratory-item-<%= current_laboratory.id %>').html("<%= j(render partial: 'admins/laboratories/shared/laboratory_item', locals: { laboratory: current_laboratory }) %>")

@ -0,0 +1,2 @@
$.notify({ message: '操作成功' });
$('.laboratory-list-container .laboratory-item-<%= current_laboratory.id %> .laboratory-user-item-<%= @laboratory_user.user_id %>').remove();

@ -7,7 +7,7 @@
<th width="14%" class="text-left">单位名称</th>
<th width="6%">地区</th>
<th width="6%">城市</th>
<th width="16%">详细地址</th>
<th width="16%" class="text-left">详细地址</th>
<th width="6%"><%= sort_tag('用户数', name: 'users_count', path: admins_schools_path) %></th>
<th width="6%">部门数</th>
<th width="12%"><%= sort_tag('创建时间', name: 'created_at', path: admins_schools_path) %></th>

@ -33,23 +33,13 @@
<%= sidebar_item_group('#schools-submenu', '单位管理', icon: 'building') do %>
<li><%= sidebar_item(admins_schools_path, '单位列表', icon: 'university', controller: 'admins-schools') %></li>
<li><%= sidebar_item(admins_departments_path, '部门列表', icon: 'sitemap', controller: 'admins-departments') %></li>
<li><%= sidebar_item(admins_laboratories_path, '云上实验室', icon: 'cloud', controller: 'admins-laboratories') %></li>
<% end %>
</li>
<!-- <li>-->
<%#= sidebar_item_group('#course-submenu', '课堂+', icon: 'mortar-board') do %>
<!-- <li><%#= sidebar_item('#', '课程列表', icon: 'calendar', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '课堂列表', icon: 'book', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '实训作业', icon: 'file-code-o', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '项目列表', icon: 'sitemap', controller: '') %></li>-->
<%# end %>
<!-- </li>-->
<li>
<%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
<li><%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %></li>
<!-- <li><%#= sidebar_item('#', '试用授权列表', icon: 'id-card-o', controller: '') %></li>-->
<!-- <li><%#= sidebar_item('#', '自动授权列表', icon: 'id-card', controller: '') %></li>-->
<% end %>
</li>

@ -1,12 +1,7 @@
<% if @teachers.present? %>
<% @teachers.each_with_index do |teacher, index| %>
<tr>
<td class="pl20 pr20">
<% if index < 3 %>
<img src="/images/educoder/competition/<%= index + 1 %>.png" width="18px" height="22px" class="mt8"/></td>
<% else %>
<%= index + 1 %>
<% end %>
<td class="pl20 pr20"><%= index + 1 %></td>
<td class="color-dark"><a href="<%= user_path(teacher['login']) %>" target="_blank" class="task-hide" style="max-width: 84px;"><%= teacher['real_name'] %></a></td>
<td><%= teacher['course_count'] %></td>
<td><%= teacher['shixun_work_count'] %></td>

@ -0,0 +1,11 @@
json.course_groups @course_groups.each do |group|
json.(group, :id, :course_members_count, :name)
json.invite_code group.invite_code if @user_course_identity < Course::STUDENT
json.member_manager member_manager(group, @teachers) if @user_course_identity < Course::NORMAL
end
if @user_course_identity == Course::STUDENT
json.current_group_id @current_group_id
end
json.none_group_member_count @course.none_group_count
json.group_count @all_group_count

@ -1,12 +1,17 @@
json.students do
json.array! @students do |student|
json.user_id student.user_id
json.login student.user.try(:login)
# json.login student.user.try(:login)
json.name student.user.try(:real_name)
json.name_link user_path(student.user)
json.student_id student.user.try(:student_id)
json.course_group_name student.course_group.try(:name)
json.course_member_id student.id
if @user_course_identity < Course::ASSISTANT_PROFESSOR && !params[:course_group_id].present?
json.member_roles student.user.course_role(@course)
end
json.user_phone student.user.hidden_phone
json.user_mail student.user.hidden_mail
end
end
json.students_count @students_count

@ -16,6 +16,9 @@ json.teacher_list do
end
json.graduation_group teacher.graduation_group.try(:name)
json.graduation_group_id teacher.graduation_group.try(:id)
if @user_course_identity < Course::ASSISTANT_PROFESSOR
json.member_roles teacher.user.course_role(@course)
end
end
end
json.teacher_list_size @teacher_list_size

@ -25,4 +25,3 @@ json.excellent @course.excellent
if @course.is_end == 0
json.days_remaining (@course.end_date.to_date - Time.now.to_date).to_i
end

@ -1,3 +1,32 @@
json.course_targets @course_targets,
partial: 'ecs/course_targets/shared/ec_course_target_with_achievement_methods',
as: :ec_course_target
json.course_targets do
json.array! @course_targets do |course_target|
json.extract! course_target, :id, :position, :content
json.course_achievement_methods do
json.array! course_target.ec_course_achievement_methods do |achievement_method|
evaluation = achievement_method.ec_course_evaluation
json.extract! achievement_method, :id, :score, :percentage
json.course_evaluation do
json.partial! 'ecs/course_evaluations/shared/ec_course_evaluation_only', ec_course_evaluation: evaluation
end
json.course_evaluation_relates do
json.array! achievement_method.ec_achievement_evaluation_relates do |relate|
json.extract! relate, :id, :position, :ec_course_evaluation_subitem_id
subitem = relate.ec_course_evaluation_subitem
if evaluation.is_course_type?
json.name subitem&.name
else
json.name subitem&.name ? "#{evaluation.name}#{relate.position}:#{subitem&.name}" : "#{evaluation.name}#{relate.position}"
end
end
end
end
end
end
end

@ -1 +1,7 @@
json.course_evaluations @course_evaluations, partial: 'ecs/course_evaluations/shared/ec_course_evaluation_slim', as: :ec_course_evaluation
json.course_evaluations do
json.array! @course_evaluations do |course_evaluation|
json.partial! 'ecs/course_evaluations/shared/ec_course_evaluation_slim', ec_course_evaluation: course_evaluation
json.evaluation_relates course_evaluation.evaluation_relates
end
end

@ -1 +0,0 @@
json.partial! 'ecs/shared/user', user: @user

@ -1,2 +1,29 @@
json.course_targets do
json.array! @course_targets do |course_target|
json.extract! course_target, :id, :position, :content
json.course_targets @course_targets, partial: 'ecs/course_targets/shared/ec_course_target_with_achievement_methods', as: :ec_course_target
json.course_achievement_methods do
json.array! course_target.ec_course_achievement_methods do |achievement_method|
evaluation = achievement_method.ec_course_evaluation
json.extract! achievement_method, :id, :score, :percentage
json.course_evaluation do
json.partial! 'ecs/course_evaluations/shared/ec_course_evaluation_only', ec_course_evaluation: evaluation
end
json.course_evaluation_relates do
json.array! achievement_method.ec_achievement_evaluation_relates do |relate|
json.extract! relate, :id, :position, :ec_course_evaluation_subitem_id
subitem = relate.ec_course_evaluation_subitem
if evaluation.is_course_type?
json.name subitem&.name
else
json.name subitem&.name ? "#{evaluation.name}#{relate.position}:#{subitem&.name}" : "#{evaluation.name}#{relate.position}"
end
end
end
end
end
end
end

@ -8,6 +8,7 @@ json.user_group_name ex_user_info[:user_group_name]
json.student_id ex_user_info[:student_id]
json.commit_status ex_user_info[:commit_status]
json.end_at ex_user_info[:end_at]
json.commit_method exercise_user&.commit_method.to_i
if subjective_type == 1
json.objective_score ex_user_info[:ex_object_score]
json.subjective_score ex_user_info[:ex_subject_score]

@ -9,6 +9,7 @@ json.exercise_answer_user do
json.user_id ex_answerer.id
json.login ex_answerer.login
if exercise_user.present?
json.commit_method exercise_user&.commit_method.to_i
json.start_at exercise_user.start_at
json.score exercise_user.score.present? ? exercise_user.score.round(1).to_s : "0.0"
end

@ -1,6 +1,6 @@
json.course_is_end @course.is_end # true表示已结束false表示未结束
json.extract! @exercise, :id,:exercise_name,:exercise_description,:show_statistic
json.time @user_left_time
json.time (@user_left_time.to_i / 60)
json.exercise_status @ex_status

@ -0,0 +1,6 @@
json.course_groups @course_groups do |group|
json.id group.id
json.name group.name
json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time
end
json.end_time @exercise.end_time

@ -3,3 +3,4 @@ json.task_status task_curr_status(graduation, course)[:status]
json.task_name graduation.name
json.task_id graduation.id
json.status graduation.status
json.end_time graduation.end_time

@ -1,4 +1,6 @@
json.course_groups @course_groups do |group|
json.id group.id
json.name group.name
json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time
end
json.end_time @homework.end_time

@ -23,7 +23,7 @@ if @user_course_identity < Course::STUDENT
if @homework.homework_type != "practice"
json.teacher_comment teacher_comment @homework, @current_user.id
end
json.task_status homework_status @homework, @member
json.task_status @homework.homework_type != "practice" ? homework_status(@homework, @member) : practice_homework_status(@homework, @member)
json.course_group_info course_group_info @course, @current_user.id
elsif @user_course_identity == Course::STUDENT

@ -0,0 +1,6 @@
json.course_groups @course_groups do |group|
json.id group.id
json.name group.name
json.end_time @group_settings.select{|group_setting| group_setting.course_group_id == group.id}.first&.end_time
end
json.end_time @poll.end_time

@ -0,0 +1,12 @@
json.setting do
setting = @laboratory.laboratory_setting
json.name setting.name || default_setting.name
json.nav_logo_url setting.nav_logo_url || default_setting.nav_logo_url
json.login_logo_url setting.login_logo_url || default_setting.login_logo_url
json.tab_logo_url setting.tab_logo_url || default_setting.tab_logo_url
json.navbar setting.navbar || default_setting.navbar
json.footer setting.footer || default_setting.footer
end

@ -1,3 +1,4 @@
json.status 1
json.message "发送成功"
json.course_id @course.id
json.first_category_url module_url(@course.none_hidden_course_modules.first, @course)

@ -0,0 +1,3 @@
json.template do
json.partial! 'attachments/attachment_simple', attachment: @template.attachments.last
end

@ -1,5 +1,7 @@
json.extract! observed_user, :id, :nickname, :phone, :mail, :show_realname
json.extract! observed_user, :id, :nickname, :show_realname
json.phone observed_user.hidden_phone
json.mail observed_user.hidden_mail
json.avatar_url url_to_avatar(observed_user)
user = ActiveDecorator::Decorator.instance.decorate(observed_user)
json.name user.name

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

Loading…
Cancel
Save