Merge branches 'dev_aliyun' and 'dev_cxt' of https://bdgit.educoder.net/Hjqreturn/educoder into dev_cxt

dev_forge
杨树明 5 years ago
commit 4e4eb937be

@ -69,5 +69,80 @@ $(document).on('turbolinks:load', function() {
});
}
});
// 导入学生
var $importScoreModal = $('.modal.admin-import-competition-score-modal');
var $importScoreForm = $importScoreModal.find('form.admin-import-competition-score-form');
var $competitionIdInput = $importScoreForm.find('input[name="competition_id"]');
$importScoreModal.on('show.bs.modal', function(event){
resetFileInputFunc($importScoreModal.find('.upload-file-input'));
$importScoreModal.find('.file-names').html('选择文件');
$importScoreModal.find('.upload-file-input').trigger('click');
var $link = $(event.relatedTarget);
var competitionId = $link.data('competition-id');
$competitionIdInput.val(competitionId);
});
$importScoreModal.on('change', '.upload-file-input', function(e){
var file = $(this)[0].files[0];
$importScoreModal.find('.file-names').html(file ? file.name : '请选择文件');
});
var importUserFormValid = function(){
if($importScoreForm.find('input[name="file"]').val() == undefined || $importScoreForm.find('input[name="file"]').val().length == 0){
$importScoreForm.find('.error').html('请选择文件');
return false;
}
return true;
};
var buildResultMessage = function(data){
var messageHtml = "<div>导入结果:成功" + data.success + "条,失败"+ data.fail.length + "条</div>";
if(data.fail.length > 0){
messageHtml += '<table class="table"><thead class="thead-light"><tr><th>数据</th><th>失败原因</th></tr></thead><tbody>';
data.fail.forEach(function(item){
messageHtml += '<tr><td>' + item.data + '</td><td>' + item.message + '</td></tr>';
});
messageHtml += '</tbody></table>'
}
return messageHtml;
};
$importScoreModal.on('click', '.submit-btn', function(){
$importScoreForm.find('.error').html('');
if (importUserFormValid()) {
$('body').mLoading({ text: '正在导入...' });
$.ajax({
method: 'POST',
dataType: 'json',
url: '/admins/import_competition_scores',
data: new FormData($importScoreForm[0]),
processData: false,
contentType: false,
success: function(data){
$('body').mLoading('destroy');
$importScoreModal.modal('hide');
showMessageModal(buildResultMessage(data), function(){
window.location.reload();
});
},
error: function(res){
$('body').mLoading('destroy');
var data = res.responseJSON;
$importScoreForm.find('.error').html(data.message);
}
});
}
});
});

@ -3,13 +3,24 @@
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require jquery.validate.min
//= require additional-methods.min
//= require bootstrap-notify
//= require select2
//= require common
//= require echarts
//= require ./i18n/jquery-validate-message-zh
//= require ./i18n/select2-i18n.zh-CN
//= require_tree ./colleges
Turbolinks.setProgressBarDelay(200);
// ******** select2 global config ********
$.fn.select2.defaults.set('theme', 'bootstrap4');
$.fn.select2.defaults.set('language', 'zh-CN');
$(document).on('turbolinks:load', function() {
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').popover();

@ -0,0 +1,28 @@
$(document).on('turbolinks:load', function() {
if ($('body.partners-customers-page').length > 0) {
var $customerContainer = $('.customer-list-container');
var partnerId = $customerContainer.find('.customer-list-body').data('id');
$customerContainer.on('change', '.manager-group-select', function(){
console.log('manager-group-select change', $(this).val());
var $select = $(this);
var customerId = $select.data('id');
var managerGroupId = $select.val();
$.ajax({
url: '/partners/' + partnerId + '/customer_manager_group.json',
method: 'POST',
dataType: 'json',
data: { customer_id: customerId, manager_group_id: managerGroupId },
success: function(){
showSuccessFlash();
$select.data('last', managerGroupId);
},
error: function(res){
showErrorNotify(res.responseJSON.message);
$select.val($select.data('last'));
}
})
})
}
});

@ -0,0 +1,125 @@
$(document).on('turbolinks:load', function() {
if ($('body.partners-partner-manager-groups-page').length > 0) {
var $container = $('.manager-group-list-container');
var partnerId = $container.find('.manager-group-list-body').data('id');
// ------- 新建编辑权限组弹窗 --------
var $managerGroupModal = $('.modal.partner-save-manager-group-modal');
var $managerGroupForm = $managerGroupModal.find('form.partner-save-manager-group-form');
var $managerGroupIdInput = $managerGroupForm.find('input[name="manager_group_id"]');
var $managerGroupNameInput = $managerGroupForm.find('input[name="manager_group_name"]');
$managerGroupForm.validate({
errorElement: 'span',
errorClass: 'danger text-danger',
rules: {
manager_group_name: {
required: true,
maxlength: 20
},
}
});
$managerGroupModal.on('show.bs.modal', function(event){
var $link = $(event.relatedTarget);
var managerGroupId = $link.data('id');
var managerGroupName = $link.data('name');
if(managerGroupId && managerGroupId !== ''){
$managerGroupModal.find('.modal-title').html('重命名');
$managerGroupIdInput.val(managerGroupId);
$managerGroupNameInput.val(managerGroupName)
} else {
$managerGroupModal.find('.modal-title').html('新建');
$managerGroupIdInput.val('');
$managerGroupNameInput.val('');
}
});
$managerGroupModal.on('hide.bs.modal', function(){
$managerGroupIdInput.val('');
$managerGroupNameInput.val('');
});
$managerGroupModal.on('click', '.submit-btn', function(){
$managerGroupForm.find('.error').html('');
var url = $managerGroupForm.data('url');
if ($managerGroupForm.valid()) {
$.ajax({
method: 'POST',
dataType: 'script',
url: url,
data: $managerGroupForm.serialize()
});
}
});
// ---------- 添加管理员弹窗 ------------
var $partnerManagerModal = $('.modal.partner-add-partner-manager-modal');
var $partnerManagerForm = $partnerManagerModal.find('form.partner-add-partner-manager-form');
var $managerGroupIdInput = $partnerManagerForm.find('input[name="manager_group_id"]');
var $userSelect = $partnerManagerForm.find('.partner-manager-select');
$userSelect.select2({
theme: 'bootstrap4',
placeholder: '请输入要添加的管理员姓名',
multiple: true,
closeOnSelect: false,
minimumInputLength: 1,
ajax: {
delay: 500,
url: '/api/users_for_partners',
dataType: 'json',
data: function(params){
return { name: params.term, partner_id: partnerId, page: params.page || 1, per_page: 20 };
},
processResults: function(data, params){
params.page = params.page || 1;
return {
results: data.users,
pagination: {
more: (params.page * 20) < data.count
}
};
}
},
templateResult: function (item) {
if(!item.id || item.id === '') return item.text;
return $("<span>" + item.real_name + " <span class='font-12'>" + item.school_name + ' ' + item.identity + "</span></span>");
},
templateSelection: function(item){
if (item.id) {
}
return item.real_name || item.text;
}
});
$partnerManagerModal.on('show.bs.modal', function(event){
var $link = $(event.relatedTarget);
var managerGroupId = $link.data('id');
$managerGroupIdInput.val(managerGroupId);
$userSelect.select2('val', ' ');
$partnerManagerModal.find('.error').html('');
});
$partnerManagerModal.on('click', '.submit-btn', function(){
$partnerManagerModal.find('.error').html('');
var managerGroupId = $managerGroupIdInput.val();
var userIds = $userSelect.val();
if (userIds && userIds.length > 0) {
$.ajax({
method: 'POST',
dataType: 'script',
url: '/partners/' + partnerId + '/partner_managers',
data: { user_ids: userIds, manager_group_id: managerGroupId }
});
} else {
$partnerManagerModal.modal('hide');
}
});
}
});

@ -88,6 +88,14 @@ function show_success_flash(message){
});
}
function showSuccessFlash(message){
$.notify({
message: message || '操作成功'
},{
type: 'success'
});
}
function showErrorNotify(message){
$.notify({
message: message || '操作失败'

@ -24,7 +24,7 @@
background: unset;
}
/* 内容表格 */
/* 内容表格 */
table {
table-layout: fixed;
@ -100,6 +100,14 @@
}
}
.admin-list-container {
overflow-x: scroll;
& > table {
min-width: 900px;
}
}
.global-error {
color: grey;
min-height: 300px;
@ -124,7 +132,6 @@
}
.batch-action-container {
margin-bottom: -15px;
padding: 10px 20px 0;
background: #fff;
}

@ -1,6 +1,8 @@
@import "bootstrap";
@import "font-awesome-sprockets";
@import "font-awesome";
@import "select2.min";
@import "select2-bootstrap4.min";
@import "common";
@ -10,4 +12,34 @@
.navbar-dark .navbar-nav .nav-link {
color: rgba(255, 255, 255, 1);
font-size: 16px;
}
.box {
padding: 20px;
border-radius: 5px;
background: #fff;
}
.custom-nav {
padding: 0 1rem;
display: flex;
border-bottom: 1px solid #EBEBEB;
&-item {
padding: 0 0.5rem;
}
&-link {
display: block;
margin-bottom: 2px;
padding: 0.8rem 0.5rem;
color: #495057;
font-size: 16px;
&.active {
margin-bottom: 0px;
color: #007bff;
border-bottom: 2px solid #007bff;
}
}
}

@ -0,0 +1,131 @@
.college-body-container {
padding: 20px;
flex: 1;
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-y: scroll;
& > .content {
flex: 1;
font-size: 14px;
.box {
padding: 20px;
border-radius: 5px;
background: #fff;
}
}
/* 面包屑 */
.breadcrumb {
padding-left: 5px;
font-size: 20px;
background: unset;
}
/* 内容表格 */
table {
table-layout: fixed;
td {
vertical-align: middle;
}
tr {
&.no-data {
&:hover {
color: darkgrey;
background: unset;
}
& > td {
text-align: center;
height: 300px;
}
}
}
}
.image-preview-container {
display: flex;
flex-direction: column;
align-items: center;
}
.action-container {
& > .action {
padding: 0 3px;
}
.more-action-dropdown {
.dropdown-item {
font-size: 14px;
}
}
}
/* 分页 */
.paginate-container {
margin-top: 20px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.paginate-total {
margin-bottom: 10px;
color: darkgrey;
}
.pagination {
margin-bottom: 0px;
}
}
/* 搜索表单 */
.search-form-container {
display: flex;
margin-bottom: 20px;
.search-form {
flex: 1;
* { font-size: 14px; }
select, input {
margin-right: 10px;
font-size: 14px;
}
}
}
.global-error {
color: grey;
min-height: 300px;
&-code {
font-size: 80px;
}
&-text {
font-size: 24px;
}
}
.nav-tabs {
.nav-link {
padding: 0.5rem 2rem;
}
}
.CodeMirror {
border: 1px solid #ced4da;
}
.batch-action-container {
padding: 10px 20px 0;
background: #fff;
}
}

@ -0,0 +1,5 @@
.partners-customers-page {
.customer-list-body {
min-height: 300px;
}
}

@ -0,0 +1,104 @@
.partners-partner-manager-groups-page {
.customer-list-form {
padding: 10px 20px;
align-items: center;
}
.manager-group-item {
margin-bottom: 20px;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
&-left {
flex: 1;
}
&-right {
.action {
}
}
}
}
.partner-manager {
&-body {
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
&-item {
padding: 5px 10px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.remove-partner-manager-action {
display: none;
position: absolute;
z-index: 100;
right: 10px;
top: 0;
font-size: 24px;
& > i {
color: #dc3545;
}
}
&:hover {
.remove-partner-manager-action {
display: block;
}
}
&-avatar {
cursor: pointer;
width: 80px;
height: 80px;
overflow: hidden;
border-radius: 50%;
position: relative;
& > img {
width: 80px;
height: 80px;
}
}
&.add-partner-manager-item {
.partner-manager-item-avatar {
background: #E4E4E4;
&:hover {
background: #D0D0D0;
}
&::before {
content: '';
position: absolute;
top: 39px;
left: 20px;
width: 40px;
height: 2px;
background: #fff;
}
&::after {
content: '';
position: absolute;
top: 20px;
left: 39px;
width: 2px;
height: 40px;
background: #fff;
}
}
}
}
}
}

@ -1,5 +1,7 @@
.colleges-statistics-page {
.college-body-container {
padding: 0;
.statistic-header {
width: 100%;
height: 240px;

@ -124,7 +124,6 @@
}
.batch-action-container {
margin-bottom: -15px;
padding: 10px 20px 0;
background: #fff;
}

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

@ -19,6 +19,13 @@ class Admins::LaboratoryShixunsController < Admins::BaseController
render_ok
end
def destroy
return render_js_error('不能删除自建实训', type: :notify) if current_laboratory_shixun.ownership?
current_laboratory_shixun.destroy!
render_delete_success
end
def homepage
current_laboratory_shixun.update!(homepage: true)
render_ok

@ -21,6 +21,15 @@ class Admins::LaboratorySubjectsController < Admins::BaseController
render_ok
end
def destroy
return render_js_error('不能删除自建课程', type: :notify) if current_laboratory_subject.ownership?
current_laboratory_subject.destroy!
render_delete_success
end
def homepage
current_laboratory_subject.update!(homepage: true)
render_ok

@ -1,5 +1,5 @@
class CollegesController < ApplicationController
include Admins::PaginateHelper
include PaginateHelper
layout 'college'
@ -154,7 +154,7 @@ class CollegesController < ApplicationController
return true if current_user.admin_or_business? # 超级管理员|运营
return true if current_college.is_a?(Department) && current_college.member?(current_user) # 部门管理员
return true if current_user.is_teacher? && current_user.school_id == current_school.id # 学校老师
return true if current_school.customer_id && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id)
return true if current_school.customers.exists? && current_user.partner&.partner_customers&.exists?(customer_id: current_school.customer_id)
false
end

@ -1,4 +1,6 @@
class Competitions::CompetitionStaffsController < Competitions::BaseController
skip_before_action :require_login
def show
end
end

@ -46,7 +46,7 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController
.where("exists(select 1 from homework_commons hcs where hcs.course_id = courses.id and hcs.publish_time is not null and hcs.publish_time < NOW() and hcs.homework_type = 4 and exists(#{subquery}))")
.joins('join course_members on course_members.course_id = courses.id and course_members.role in (1,2,3)')
.where(course_members: { user_id: @team_user_ids }).pluck(:id)
courses = Course.where(id: course_ids).joins(:practice_homeworks).where('homework_commons.publish_time < now()')
courses = Course.where(id: course_ids).joins(:practice_homeworks).where('homework_commons.publish_time < ?', end_time)
@courses = courses.select('courses.id, courses.name, courses.members_count, count(*) shixun_homework_count')
.group('courses.id').order('shixun_homework_count desc').having('shixun_homework_count > 0')

@ -123,26 +123,41 @@ class Competitions::CompetitionsController < Competitions::BaseController
@stage = @competition.competition_stages.take
end
@records = @competition.competition_teams.joins(:competition_scores).where(competition_scores: {competition_stage_id: @stage&.id.to_i})
@all_records = @competition.competition_teams.joins(:competition_scores).where(competition_scores: {competition_stage_id: @stage&.id.to_i})
.select("competition_teams.*, score, cost_time").order("score desc, cost_time desc")
current_team_ids = @competition.team_members.where(user_id: current_user.id).pluck(:competition_team_id).uniq
@user_ranks = @records.select{|com_team| current_team_ids.include?(com_team.id)}
@records = @records.where("score > 0")
@user_ranks = @all_records.select{|com_team| current_team_ids.include?(com_team.id)}
@records = @all_records.where("score > 0")
@record_ids = @records.pluck(:id)
if params[:format] == "xlsx"
@records = @records.includes(user: [user_extension: :school], team_members: :user)
respond_to do |format|
format.xlsx{
set_export_cookies
chart_to_xlsx(@records, @competition)
chart_to_xlsx(@all_records, @competition)
exercise_export_name = "#{@competition.name}比赛成绩"
render xlsx: "#{exercise_export_name.strip}",template: "competitions/competitions/chart_list.xlsx.axlsx",locals:
{table_columns: @competition_head_cells, chart_lists: @competition_cells_column}
}
end
else
@records = @records.includes(:team_members, user: :user_extension).limit(@competition.awards_count)
@records = @records.includes(:team_members, competition_prize_users: :competition_prize, user: :user_extension).limit(@competition.awards_count)
end
end
def export_extra_course_statistics
@competition = current_competition
@all_records = @competition.competition_teams.joins(:competition_scores, user: [user_extension: :school])
.select("competition_teams.*, competition_scores.score, cost_time").order("score desc, cost_time desc")
respond_to do |format|
format.xlsx{
extra_chart_to_xlsx(@all_records)
exercise_export_name = "#{@competition.name}比赛成绩"
render xlsx: "#{exercise_export_name.strip}",template: "competitions/competitions/chart_list.xlsx.axlsx",locals:
{table_columns: @competition_head_cells, chart_lists: @competition_cells_column}
}
end
end
@ -219,4 +234,67 @@ class Competitions::CompetitionsController < Competitions::BaseController
@competition_cells_column.push(row_cells_column)
end
end
def extra_chart_to_xlsx records
@competition_head_cells = []
@competition_cells_column = []
@competition_head_cells = %w(序号 姓名 id 学校 实训数 学习人数 被fork发布的学习人数 实训有效作品数 实训应用值 课堂数 学生数量 发布的实训作业数 有效作品数 课堂应用值 总分)
records.each_with_index do |record, index|
row_cells_column = []
row_cells_column << index + 1
record_user = record.user
row_cells_column << record_user.real_name
row_cells_column << record_user.login
row_cells_column << record_user.school_name
total_score = 0
shixun_count = 0
shixun_member_count = 0
shixun_fork_member_count = 0
shixun_valid_count = 0
shixun_score = 0
shixun_records = record.competition_course_records.where(type: 'CompetitionCourseShixunRecord')
shixun_records.each do |shixun|
shixun_count += 1
shixun_member_count += shixun.snapshot['myshixuns_count'].to_i
shixun_fork_member_count += shixun.snapshot['forked_myshixun_count'].to_i
shixun_valid_count += shixun.snapshot['valid_myshixun_count'].to_i
shixun_score += shixun.score.to_i
end
row_cells_column << shixun_count
row_cells_column << shixun_member_count
row_cells_column << shixun_fork_member_count
row_cells_column << shixun_valid_count
row_cells_column << shixun_score
total_score += shixun_score
course_count = 0
course_member_count = 0
course_homework_count = 0
course_valid_count = 0
course_score = 0
course_records = record.competition_course_records.where(type: 'CompetitionCourseCourseRecord')
course_records.each do |course|
course_count += 1
course_member_count += course.snapshot['members_count'].to_i
course_homework_count += course.snapshot['shixun_homework_count'].to_i
course_valid_count += course.snapshot['valid_myshixun_count'].to_i
course_score += course.score.to_i
end
row_cells_column << course_count
row_cells_column << course_member_count
row_cells_column << course_homework_count
row_cells_column << course_valid_count
row_cells_column << course_score
total_score += course_score
row_cells_column << total_score
@competition_cells_column.push(row_cells_column)
end
end
end

@ -2,7 +2,7 @@ class Competitions::PrizeLeaderAccountsController < Competitions::BaseController
before_action :require_prize_team_leader!
def update
Competitions::SavePrizeTeamAccountService.call(current_competition, current_user, update_params)
Competitions::SavePrizeTeamAccountService.call(current_competition, current_prize_user, update_params)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
@ -13,8 +13,8 @@ class Competitions::PrizeLeaderAccountsController < Competitions::BaseController
def require_prize_team_leader!
prize_user = current_competition.competition_prize_users.joins(:competition_prize)
.where(competition_prizes: { category: :bonus })
.find_by(leader: true, user_id: current_user.id)
return if prize_user.present?
.find_by(leader: true, user_id: current_prize_user.id)
return if prize_user.present? && (current_user.admin_or_business? || current_user.id == current_prize_user.id)
render_forbidden
end
@ -22,4 +22,8 @@ class Competitions::PrizeLeaderAccountsController < Competitions::BaseController
def update_params
params.permit(:bank, :second_bank, :card_no)
end
def current_prize_user
@_current_prize_user ||= User.find(params[:user_id])
end
end

@ -7,8 +7,8 @@ class Competitions::PrizesController < Competitions::BaseController
self_prizes = current_competition.competition_prize_users.where(user_id: current_prize_user.id).includes(:competition_team).order(:competition_prize_id)
@leader = self_prizes.any?{ |prize_user| prize_user.leader? && prize_user.competition_prize.category == 'bonus' } # 是否为队长并且有奖金奖励
if @leader
@bank_account = self_prizes.find(&:leader?).extra
if @leader || current_user.admin_or_business?
@bank_account = self_prizes.find(&:leader?)&.extra
@bank_account_editable = self_prizes.select(&:leader?).all?(&:pending?)
end

@ -15,6 +15,13 @@ class Cooperative::LaboratoryShixunsController < Cooperative::BaseController
end
def destroy
return render_js_error('不能删除自建实训', type: :notify) if current_laboratory_shixun.ownership?
current_laboratory_shixun.destroy!
render_delete_success
end
def homepage
current_laboratory_shixun.update!(homepage: true)
render_ok

@ -18,6 +18,13 @@ class Cooperative::LaboratorySubjectsController < Cooperative::BaseController
current_laboratory_subject.subject.update!(update_params)
end
def destroy
return render_js_error('不能删除自建实践课程', type: :notify) if current_laboratory_subject.ownership?
current_laboratory_subject.destroy!
render_delete_success
end
def homepage
current_laboratory_subject.update!(homepage: true)
render_ok

@ -1539,7 +1539,7 @@ class CoursesController < ApplicationController
end
def course_statistics course, max_exp, limit
max_rate = 20.0 / max_exp
max_rate = max_exp.nil? ? 0 : 20.0 / max_exp
sql_select = %Q{ SELECT a.*, (message_num*0.2 + message_reply_num*0.1 + resource_num*0.5 + homework_journal_num*0.1 + graduation_num +
homework_num + exercise_num + poll_num*0.7 + exercise_score * 0.7 + graduation_score * 0.7 + homework_score * 0.7 + exp*#{max_rate})

@ -458,15 +458,14 @@ class ExercisesController < ApplicationController
# 统一设置或者分班为0则更新试卷并删除试卷分组
if unified_setting || (course_group_ids.size == 0)
params_publish_time = params[:publish_time].blank? ? nil : params[:publish_time].to_time
params_end_time = nil
if params[:end_time].blank?
if params_publish_time.present?
params_end_time = params_publish_time + 30.days
end
else
params_end_time = params[:end_time].to_time
end
tip_exception("发布时间不能为空") if params[:publish_time].blank?
tip_exception("截止时间不能为空") if params[:end_time].blank?
tip_exception("截止时间不能早于发布时间") if params[:publish_time].to_time > params[:end_time].to_time
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && params[:end_time].to_time > @course.end_date.end_of_day
params_publish_time = params[:publish_time].to_time
params_end_time = params[:end_time].to_time
if (exercise_status != Exercise::UNPUBLISHED) && (@exercise.publish_time != params_publish_time)
normal_status(-1,"已发布/已截止,不允许修改发布时间")
@ -500,16 +499,16 @@ class ExercisesController < ApplicationController
old_exercise_groups = exercise_groups_ids - total_common_group #后来传入的分班里,没有了的班级,即需要删除
params_times.each do |t|
tip_exception("发布时间不能为空") if t[:publish_time].blank?
tip_exception("截止时间不能为空") if t[:end_time].blank?
tip_exception("截止时间不能早于发布时间") if t[:publish_time].to_time > t[:end_time].to_time
tip_exception("截止时间不能晚于课堂结束时间(#{@course.end_date.end_of_day.strftime("%Y-%m-%d %H:%M")}") if
@course.end_date.present? && t[:end_time].to_time > @course.end_date.end_of_day
course_id = t[:course_group_id]
exercise_publish_time = t[:publish_time].present? ? t[:publish_time].to_time : nil
exercise_end_time = nil
if t[:end_time].blank?
if exercise_publish_time.present?
exercise_end_time = exercise_publish_time + 30.days
end
else
exercise_end_time = t[:end_time].to_time
end
exercise_publish_time = t[:publish_time].to_time
exercise_end_time = t[:end_time].to_time
exercise_group = exercise_groups.find_in_exercise_group("course_group_id",course_id) #判断该分班是否存在
if exercise_group.present? && (exercise_group.first.publish_time < Time.now) && (exercise_publish_time != exercise_group.first.publish_time)
error_count += 1
@ -553,7 +552,7 @@ class ExercisesController < ApplicationController
:end_time => exercise_end_time
}
new_exercise_group = ExerciseGroupSetting.new(exercise_group_params)
new_exercise_group.save
new_exercise_group.save!
end
end
end
@ -602,7 +601,7 @@ class ExercisesController < ApplicationController
end
rescue Exception => e
uid_logger_error(e.message)
tip_exception("提交出现错误!")
tip_exception("试卷提交失败")
raise ActiveRecord::Rollback
end
end
@ -613,9 +612,9 @@ class ExercisesController < ApplicationController
exercise_user = @exercise.exercise_users.find_by!(user_id: params[:user_id])
tip_exception("已提交的作品请去评阅页进行调分") if exercise_user.commit_status == 1
if @exercise.subjective_score > 0
tip_exception("主观题成绩不能为空") if params[:subject_score].blank?
tip_exception("主观题成绩不能小于零") if params[:subject_score].to_f < 0
tip_exception("主观题成绩不能大于总分值:#{@exercise.subjective_score}") if params[:subject_score].to_f.round(1) > @exercise.subjective_score.round(1)
tip_exception("主观题成绩不能为空") if params[:subjective_score].blank?
tip_exception("主观题成绩不能小于零") if params[:subjective_score].to_f < 0
tip_exception("主观题成绩不能大于总分值:#{@exercise.subjective_score}") if params[:subjective_score].to_f.round(1) > @exercise.subjective_score.round(1)
end
if @exercise.objective_score > 0
@ -626,7 +625,7 @@ class ExercisesController < ApplicationController
ActiveRecord::Base.transaction do
start_at_time = exercise_user.start_at || Time.now
subjective_score = @exercise.subjective_score > 0 ? params[:subject_score].to_f.round(2) : 0
subjective_score = @exercise.subjective_score > 0 ? params[:subjective_score].to_f.round(2) : 0
objective_score = @exercise.objective_score > 0 ? params[:objective_score].to_f.round(2) : 0
score = subjective_score + objective_score
exercise_user.update_attributes!(start_at: start_at_time, end_at: Time.now, status: 1, commit_status: 1, score: score,
@ -1534,7 +1533,7 @@ class ExercisesController < ApplicationController
ex_group_settings = @exercise.exercise_group_settings
if ex_group_settings.present?
p_time_present = ex_group_settings.publish_time_no_null.map(&:publish_time).min
if p_time_present < Time.now
if p_time_present && p_time_present < Time.now
normal_status(-1,"设置失败,存在已发布的分班")
end
elsif params[:publish_time].blank?

@ -11,20 +11,7 @@ class HomeController < ApplicationController
end
# 目录分级
repertoires = Repertoire.includes(sub_repertoires: :tag_repertoires).order("updated_at asc")
@rep_list = []
repertoires.each do |rep|
sub_rep_list = []
rep.sub_repertoires.each do |sub_rep|
tag_rep_list = []
sub_rep.tag_repertoires.each do |tag_rep|
tag_rep_list << {tag_id: tag_rep.id, tag_name: tag_rep.name}
end
sub_rep_list << {sub_rep_id: sub_rep.id, sub_rep_name: sub_rep.name, tag_rep_list: tag_rep_list}
end
@rep_list << {rep_id: rep.id, rep_name: rep.name, sub_rep_list: sub_rep_list}
end
@rep_list = current_laboratory.shixun_repertoires
shixuns = current_laboratory.shixuns
subjects = current_laboratory.subjects

@ -20,7 +20,12 @@ class MessagesController < ApplicationController
sort = params[:sort].to_i == 1 ? 'asc' : 'desc'
sort_type = params[:sort_type] || 'time'
messages = @board.messages.root_nodes.by_keywords(params[:search])
if @board.parent_id == 0
messages = Message.where(board_id: @board.course.boards.pluck(:id))
else
messages = @board.messages
end
messages = messages.root_nodes.by_keywords(params[:search])
messages = messages.reorder('(sticky = 1) DESC') # 置顶
@ -31,7 +36,7 @@ class MessagesController < ApplicationController
else messages.order("created_on #{sort}")
end
messages = messages.includes(:author)
messages = messages.includes(:author, :board)
@messages = Kaminari.paginate_array(messages).page(@page).per(@page_size)
ids = @messages.map(&:id)

@ -0,0 +1,137 @@
class PartnersController < ApplicationController
include Base::PaginateHelper
include Admins::RenderHelper
layout 'college'
before_action :require_login, :check_partner_present!, :check_permission!
before_action :check_admin_manager_group_permission!, except: [:customers]
helper_method :current_partner, :manager_permission?
def customers
customers = CustomerQuery.call(current_partner, current_user, params)
@customers = paginate(customers.includes(:school))
load_customer_extra_statistic_data
end
def partner_manager_groups
@manager_groups = current_partner.partner_manager_groups.includes(users: :user_extension).to_a
end
def manager_group
name = params[:manager_group_name].to_s.strip
if params[:manager_group_id].present?
# 重命名
@manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
return render_error('不能修改管理者权限组名称') if @manager_group.admin?
@manager_group.update!(name: name)
else
# 新建
@manager_group = current_partner.partner_manager_groups.create!(name: name)
end
end
def remove_manager_group
manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
return render_error('不能删除管理者权限组') if manager_group.admin?
manager_group.destroy!
render_delete_success
end
def partner_managers
user_ids = Array.wrap(params[:user_ids])
@manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
ActiveRecord::Base.transaction do
User.where(id: user_ids).pluck(:id).each do |user_id|
next if current_partner.partner_managers.exists?(partner_manager_group: @manager_group, user_id: user_id)
current_partner.partner_managers.create!(partner_manager_group: @manager_group, user_id: user_id)
end
end
@manager_group.reload
end
def remove_partner_manager
partner_manager = current_partner.partner_managers.find(params[:manager_id])
return render_error('不能删除自己') if partner_manager.user_id == current_user.id && partner_manager.partner_manager_group.admin?
partner_manager.destroy!
render_delete_success
end
def customer_manager_group
customer = current_partner.customers.find(params[:customer_id])
if params[:manager_group_id].present?
manager_group = current_partner.partner_manager_groups.find(params[:manager_group_id])
customer.update!(partner_manager_group: manager_group)
else
customer.update!(partner_manager_group_id: nil)
end
render_ok
end
private
def current_partner
@_current_partner ||= Partner.find(params[:id].presence || params[:partner_id])
end
def check_partner_present!
return if current_partner.present?
redirect_to '/404'
end
def manager_permission?
admin_or_business? || current_user.partner_managers.exists?(partner_id: current_partner.id)
end
def check_permission!
return if manager_permission?
redirect_to '/403'
end
def check_admin_manager_group_permission!
return if admin_or_business?
return if current_partner.admin_partner_manager_group.partner_managers.exists?(user: current_user)
render_forbidden
end
def load_customer_extra_statistic_data
school_ids = @customers.map(&:school_id)
teacher_map = UserExtension.where(school_id: school_ids, identity: 0).group(:school_id).count
student_map = UserExtension.where(school_id: school_ids, identity: 1).group(:school_id).count
course_map = Course.where(school_id: school_ids, is_delete: 0).where.not(id: 1309).group(:school_id).count
shixun_map = Shixun.visible.joins('left join user_extensions on user_extensions.user_id = shixuns.user_id')
.where(user_extensions: { school_id: school_ids }).group('user_extensions.school_id').count
shixun_report_map = StudentWork.where(work_status: [1, 2]).where('myshixun_id != 0')
.joins('left join user_extensions on user_extensions.user_id = student_works.user_id')
.where(user_extensions: { school_id: school_ids })
.group('user_extensions.school_id').count
course_time_map = Course.where(school_id: school_ids, is_delete: 0)
.where.not(id: 1309).group(:school_id).maximum(:updated_at)
@customers.each do |customer|
customer._extra_data = {
teacher_count: teacher_map[customer.school_id],
student_count: student_map[customer.school_id],
course_count: course_map[customer.school_id],
shixun_count: shixun_map[customer.school_id],
shixun_report_count: shixun_report_map[customer.school_id],
course_time: course_time_map[customer.school_id]
}
end
end
end

@ -154,22 +154,7 @@ class ShixunsController < ApplicationController
## 获取顶部菜单
def menus
where_sql = ShixunTagRepertoire.where("shixun_tag_repertoires.tag_repertoire_id = tag_repertoires.id")
# 云上实验室过滤
unless current_laboratory.main_site?
where_sql = where_sql.joins('JOIN laboratory_shixuns ls ON ls.shixun_id = shixun_tag_repertoires.shixun_id')
end
where_sql = where_sql.select('1').to_sql
tags = TagRepertoire.where("EXISTS(#{where_sql})").distinct.includes(sub_repertoire: :repertoire)
@tags_map = tags.group_by(&:sub_repertoire)
@sub_reps_map = @tags_map.keys.group_by(&:repertoire)
# @repertoires = Repertoire.includes(sub_repertoires: [:tag_repertoires]).order("updated_at asc")
# respond_with @repertoires
render_json
@repertoires = current_laboratory.shixun_repertoires
end
## 实训详情

@ -11,7 +11,7 @@ class SubjectsController < ApplicationController
include SubjectsHelper
def index
@tech_system = Repertoire.where(nil).order("updated_at desc")
@tech_system = current_laboratory.subject_repertoires
select = params[:select] # 路径导航类型
reorder = params[:order] || "publish_time"
search = params[:search]

@ -17,6 +17,6 @@ class Users::AuthenticationAppliesController < Users::BaseAccountController
private
def create_params
params.permit(:name, :gender, :id_number, :upload_image, attachment_ids: [])
params.permit(:name, :show_realname, :gender, :id_number, :upload_image, attachment_ids: [])
end
end

@ -0,0 +1,27 @@
class UsersForPartnersController < ApplicationController
include Base::PaginateHelper
before_action :check_partner_manager_permission!
def index
params[:sort_by] = params[:sort_by].presence || 'created_on'
params[:sort_direction] = params[:sort_direction].presence || 'desc'
users = Admins::UserQuery.call(search_params)
@users = paginate users.includes(user_extension: :school)
end
private
def search_params
params.permit(:name, :sort_by, :sort_direction)
end
def check_partner_manager_permission!
partner = Partner.find(params[:partner_id])
return if admin_or_business?
return if partner.admin_partner_manager_group.partner_managers.exists?(user: current_user)
render_forbidden
end
end

@ -0,0 +1,29 @@
class Weapps::CourseStickiesController < Weapps::BaseController
# before_action :require_wechat_login!
def create
courses = params[:category] == "study" ? current_user.as_student_courses.started : current_user.manage_courses
courses = courses.order("course_members.sticky=1 desc, course_members.sticky_time desc, courses.created_at desc").first
return render_error("该课堂已置顶,请勿重复操作") if course_member.sticky && courses&.id.to_i == current_course.id
course_member.update!(sticky: 1, sticky_time: Time.now)
render_ok
end
def cancel_sticky
return render_error("该课堂未置顶,无法取消") unless course_member.sticky
course_member.update!(sticky: 0, sticky_time: nil)
render_ok
end
private
def current_course
@_current_course = Course.find params[:course_id]
end
def course_member
@_course_member = params[:category] == "study" ? current_course.students.find_by!(user_id: current_user.id) :
current_course.teachers.find_by!(user_id: current_user.id)
end
end

@ -0,0 +1,27 @@
class Weapps::CoursesController < Weapps::BaseController
# before_action :require_wechat_login!
def create
return render_error("只有老师身份才能创建课堂") unless current_user.is_teacher?
course = Course.new(tea_id: current_user.id)
Weapps::CreateCourseService.call(course, course_params)
render_ok
rescue ApplicationService::Error => ex
render_error(ex.message)
end
def edit
end
def update
end
private
def course_params
params.permit(:name, :course_list_name, :credit, course_module_types: [])
end
end

@ -1,15 +1,24 @@
class Weapps::HomesController < Weapps::BaseController
before_action :require_wechat_login!
def show
# banner
@carousels = WeappSettings::Carousel.only_online
# 广告
@advert = WeappSettings::Advert.only_online.first
# 热门实训
@shixuns = Shixun.where(homepage_show: true).includes(:tag_repertoires, :challenges).limit(4)
# 热门实践课程
@subjects = Subject.where(homepage_show: true).includes(:shixuns, :repertoire).limit(4)
# 我的课堂
@category = params[:category] && ["manage", "study"].include?(params[:category]) ? params[:category] : (current_user.is_teacher? ? "manage" : "study")
@courses = case @category
when 'study' then
current_user.as_student_courses.started
when 'manage' then
current_user.manage_courses
end
@courses = @courses.not_deleted.not_excellent
@course_count = @courses.count
order_str = "course_members.sticky=1 desc, course_members.sticky_time desc, courses.created_at desc"
@courses = paginate(@courses.order(order_str).includes(:teacher, :school))
@user = current_user
end
end

@ -1,7 +1,7 @@
class Users::ApplyAuthenticationForm
include ActiveModel::Model
attr_accessor :name, :id_number, :gender, :upload_image, :attachment_ids
attr_accessor :name, :show_realname, :id_number, :gender, :upload_image, :attachment_ids
validates :name, presence: true
validate :validate_ID_number

@ -0,0 +1,20 @@
class Weapps::CreateCourseForm
include ActiveModel::Model
attr_accessor :course
attr_accessor :name, :course_list_name, :credit, :course_module_types
validates :name, presence: true
validates :course_list_name, presence: true
validate :course_name_prefix
validate :check_course_modules
def course_name_prefix
raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0
end
def check_course_modules
raise '请至少添加一个课堂模块' if course_module_types.blank?
end
end

@ -47,7 +47,7 @@ module CompetitionsHelper
end
statistic_stages.each do |stage|
if stage.max_end_time && stage.max_end_time < Time.now
if (stage.max_end_time && stage.max_end_time < Time.now) || stage.max_end_time.nil?
stages << {id: stage.id, name: "#{stage.name}排行榜", rate: stage.score_rate, start_time: stage.min_start_time, end_time: stage.max_end_time}
end
end

@ -108,40 +108,28 @@ module ExercisesHelper
ques_less_title = nil
ex_answers = ex.exercise_answers
effictive_users = ex_answers.search_answer_users("user_id",user_ids)
effictive_users_count = effictive_users.where("exercise_choice_id is not null or (answer_text is not null and answer_text !='')").pluck(:user_id).uniq.size if ex.question_type != Exercise::COMPLETION
else
ques_title = ex.shixun.name
ques_less_title = ex.question_title
ex_answers = ex.exercise_shixun_answers
effictive_users = ex_answers.search_shixun_answers("user_id",user_ids)
effictive_users_count = effictive_users.pluck(:user_id).uniq.size
end
effictive_users_count = effictive_users.size #有效回答数可能有重复的用户id这里仅统计是否回答这个问题的全部人数
# effictive_users_count = effictive_users.size #有效回答数可能有重复的用户id这里仅统计是否回答这个问题的全部人数
#
if ex.question_type > Exercise::COMPLETION #当为主观题和实训题时,
ex_answered_scores = effictive_users.score_reviewed.pluck(:score).sum #该问题的全部得分
percent = (ex_total_score == 0.0 ? 0.0 : (ex_answered_scores / ex_total_score.to_f).round(3) * 100) #正确率
end
# if ex.question_type != Exercise::MULTIPLE
# ex_answered_scores = effictive_users.score_reviewed.pluck(:score).sum #该问题的全部得分
# percent = (ex_total_score == 0.0 ? 0.0 : (ex_answered_scores / ex_total_score.to_f).round(3) * 100) #正确率
# else
# multiple_score = 0
# user_ids.each do |user_id|
# ex_answer_score = ex_answers.select{|answer| answer.user_id == user_id}&.first&.score.to_f
# multiple_score += ex_answer_score
# end
# percent = (ex_total_score == 0.0 ? 0.0 : (multiple_score / ex_total_score.to_f).round(3) * 100) #正确率
# end
question_answer_infos = []
if ex.question_type <= Exercise::JUDGMENT #选择题和判断题
ex_choices = ex.exercise_choices
standard_answer = ex.exercise_standard_answers.pluck(:exercise_choice_id).sort #标准答案的位置
right_users_count = 0
#该问题的正确率
if ex.question_type == Exercise::MULTIPLE #多选题
if standard_answer.size == 1 #以前的多选题答案存在一个表里
standard_answer = standard_answer.first.to_s.split("").map(&:to_i)
end
@ -178,6 +166,17 @@ module ExercisesHelper
question_answer_infos.push(answer_option)
end
elsif ex.question_type == Exercise::COMPLETION #填空题
effictive_users_ids = effictive_users.where("answer_text is not null and answer_text !=''").pluck(:user_id).uniq
effictive_users_count = effictive_users_ids.size
user_wrong_count = 0
effictive_users_ids.each do |s|
user_score = effictive_users.where(user_id: s).pluck(:score).sum
if user_score.to_f < ex&.question_score.to_f
user_wrong_count = user_wrong_count + 1
end
end
# user_wrong_ids = effictive_users.where()
# user_wrong_count = (user_wrong_ids & effictive_users_ids).uniq.size
ex_ordered = ex.is_ordered
null_standard_answer = ex.exercise_standard_answers
null_stand_choice = null_standard_answer.pluck(:exercise_choice_id) #一个exercise_choice_id可能对应多个answer_text
@ -185,34 +184,48 @@ module ExercisesHelper
standard_answer_count = 0
each_null_score = null_stand_choice.size > 0 ? (ex&.question_score.to_f / null_stand_choice.uniq.size).round(3) : 0.0
all_user_count = 0
null_stand_choice.each_with_index do |s,index|
user_count = 0
s_choice_text = null_stand_text[index]
if ex_ordered #有序排列
user_count = user_count + effictive_users.select{|answer| answer.exercise_choice_id == s && answer.answer_text == s_choice_text}.size
else
user_count = user_count + effictive_users.select{|answer| answer.answer_text == s_choice_text }.size #回答了标准答案的用户
end
answer_percent = ((effictive_users_count == 0) ? 0.0 : (user_count / effictive_users_count.to_f ).round(3))
answer_option = {
if ex_ordered
all_user_answers = effictive_users.pluck(:answer_text)
null_stand_choice.each_with_index do |s,index|
s_choice_text = null_stand_text[index]
user_count = 0
# user_count = user_count + effictive_users.where("exercise_choice_id = ? and answer_text = ?",s,s_choice_text).pluck(:user_id).uniq.size
user_count = user_count + effictive_users.select{|answer| answer.exercise_choice_id == s && answer.answer_text == s_choice_text}.size
answer_percent = ((effictive_users_count == 0) ? 0.0 : (user_count / effictive_users_count.to_f ).round(3))
answer_option = {
:choice_position => index+1,
:choice_text => s_choice_text,
:choice_users_count => user_count,
:choice_percent => answer_percent.round(2).to_s,
:right_answer => true
}
question_answer_infos.push(answer_option)
all_user_count += user_count
standard_answer_count += 1
}
question_answer_infos.push(answer_option)
all_user_count += user_count
standard_answer_count += 1
all_user_answers = all_user_answers - [s]
end
else
# cycled_stand = {}
null_stand_text.uniq.each_with_index do |stand, index|
user_count = 0
user_count = user_count + effictive_users.where("answer_text = ?",stand).pluck(:user_id).uniq.size
answer_percent = ((effictive_users_count == 0) ? 0.0 : (user_count / effictive_users_count.to_f ).round(3))
answer_option = {
:choice_position => index+1,
:choice_text => stand,
:choice_users_count => user_count,
:choice_percent => answer_percent.round(2).to_s,
:right_answer => true
}
question_answer_infos.push(answer_option)
all_user_count += user_count
standard_answer_count += 1
end
end
answer_user_score = all_user_count * each_null_score
percent = (ex_total_score == 0.0 ? 0.0 : (answer_user_score / ex_total_score.to_f).round(3) * 100) #正确率
# percent = commit_user_ids > 0 ? (all_user_count / commit_user_ids.to_f).round(3)*100 : 0.0
user_wrong_count = (effictive_users_count - all_user_count )
if effictive_users_count > 0 && user_wrong_count >= 0
wrong_percent = (user_wrong_count / effictive_users_count.to_f ).round(3)
else
@ -479,17 +492,28 @@ module ExercisesHelper
end
end
else
st_answer_text = standard_answer_array.pluck(:answer_text).reject(&:blank?).map{|a| a.strip.downcase}
st_answer_text = standard_answer_array.pluck(:answer_text).reject(&:blank?).map{|a| a.strip.downcase}.uniq
answers_content.each do |u|
u_answer_text = u.answer_text.strip.downcase
if st_answer_text.include?(u_answer_text) #只要标准答案包含用户的答案,就有分数。同时,下一次循环时,就会删除该标准答案。防止用户的相同答案获分
u.update_column("score",q_score_2)
score2 = score2 + q_score_2
st_answer_text.delete(u_answer_text)
if st_answer_text.size == 1
if st_answer_text.first == u_answer_text
u.update_column("score",q_score_2)
score2 = score2 + q_score_2
else
u.update_column('score',-1.0)
score2 += 0.0
end
else
u.update_column('score',-1.0)
score2 += 0.0
if st_answer_text.include?(u_answer_text) #只要标准答案包含用户的答案,就有分数。同时,下一次循环时,就会删除该标准答案。防止用户的相同答案获分
u.update_column("score",q_score_2)
score2 = score2 + q_score_2
st_answer_text.delete(u_answer_text)
else
u.update_column('score',-1.0)
score2 += 0.0
end
end
end
end
else

@ -48,9 +48,10 @@ module ManageBackHelper
str.presence || default
end
def overflow_hidden_span(text, width: 300)
def overflow_hidden_span(text, width: 300, placement: nil)
opts = { class: 'd-inline-block text-truncate', style: "max-width: #{width}px" }
opts.merge!('data-toggle': 'tooltip', title: text) if text != '--'
opts.merge!('data-placement': placement) if placement
content_tag(:span, text, opts)
end

@ -12,7 +12,7 @@ module MyshixunsHelper
end
end
def view_answer_time game, user_id
game.game_answers.where(user_id: user_id).last&.view_time
def view_answer_time game
game.game_answers.where(user_id: game.user_id).last&.view_time
end
end

@ -13,7 +13,7 @@ module PollsHelper
end
def poll_votes_count(votes,user_ids)
votes.find_current_vote("user_id",user_ids.uniq).reject(&:blank?).size
votes.find_current_vote("user_id",user_ids.uniq).pluck(:user_id).uniq.reject(&:blank?).size
end
#公用tab页的相关信息

@ -0,0 +1,20 @@
class Admins::ImportCompetitionScoreExcel < BaseImportXlsx
Data = Struct.new(:competition_team_id, :score)
def read_each(&block)
sheet.each_row_streaming(pad_cells: true, offset: 1) do |row|
data = row.map(&method(:cell_value))[0..1]
block.call Data.new(*data)
end
end
private
def check_sheet_valid!
raise_import_error('请按照模板格式导入') if sheet.row(1).size != 2
end
def cell_value(obj)
obj&.cell_value&.to_s&.strip
end
end

@ -45,7 +45,7 @@ class LimitForbidControl::Base
end
def remain_times
allow_times - error_times
allow_times.to_i - error_times
end
def clear

@ -0,0 +1,2 @@
class CompetitionCourseCourseRecord < CompetitionCourseRecord
end

@ -0,0 +1,6 @@
class CompetitionCourseRecord < ApplicationRecord
belongs_to :competition
belongs_to :competition_team
serialize :snapshot, JSON
end

@ -0,0 +1,2 @@
class CompetitionCourseShixunRecord < CompetitionCourseRecord
end

@ -12,11 +12,16 @@ class CompetitionTeam < ApplicationRecord
has_many :teachers, -> { only_teachers }, class_name: 'TeamMember'
has_many :competition_prize_users, dependent: :destroy
has_many :competition_course_records, dependent: :destroy
def group_team_type?
team_type.zero?
end
def competition_prize
competition_prize_users.take&.competition_prize&.name
end
def personal_team_type?
team_type == 1
end

@ -90,7 +90,7 @@ class Course < ApplicationRecord
}
scope :started, -> { where("start_date is null or start_date <= '#{Date.today}'") }
acts_as_taggable
# acts_as_taggable
# 课程权限判断

@ -2,6 +2,7 @@ class Customer < ApplicationRecord
default_scope { order(created_at: :desc) }
belongs_to :school
belongs_to :partner_manager_group, optional: true
has_many :partner_customers, dependent: :destroy
has_many :partners, through: :partner_customers

@ -47,6 +47,42 @@ class Laboratory < ApplicationRecord
main_site? ? Subject.all : Subject.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
end
def shixun_repertoires
where_sql = ShixunTagRepertoire.where("shixun_tag_repertoires.tag_repertoire_id = tag_repertoires.id")
# 云上实验室过滤
unless main_site?
where_sql = where_sql.joins("JOIN laboratory_shixuns ls ON ls.shixun_id = shixun_tag_repertoires.shixun_id "\
"AND ls.laboratory_id = #{id}")
end
where_sql = where_sql.select('1').to_sql
tags = TagRepertoire.where("EXISTS(#{where_sql})").distinct.includes(sub_repertoire: :repertoire)
tags_map = tags.group_by(&:sub_repertoire)
sub_reps_map = tags_map.keys.group_by(&:repertoire)
sub_reps_map.keys.sort_by(&:updated_at).reverse.map do |repertoire|
repertoire_hash = repertoire.as_json(only: %i[id name])
repertoire_hash[:sub_repertoires] =
sub_reps_map[repertoire].sort_by(&:updated_at).reverse.map do |sub_repertoire|
sub_repertoire_hash = sub_repertoire.as_json(only: %i[id name])
sub_repertoire_hash[:tags] = tags_map[sub_repertoire].sort_by(&:updated_at).reverse.map { |tag| tag.as_json(only: %i[id name]) }
sub_repertoire_hash
end
repertoire_hash
end
end
def subject_repertoires
exist_sql = Subject.where('subjects.repertoire_id = repertoires.id')
unless main_site?
exist_sql = exist_sql.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
end
Repertoire.where("EXISTS(#{exist_sql.select('1').to_sql})").order(updated_at: :desc).distinct
end
# 是否为主站
def main_site?
id == 1

@ -1,7 +1,10 @@
class Partner < ApplicationRecord
belongs_to :school, optional: true
has_many :users
has_many :partner_customers, dependent: :destroy
has_many :customers, through: :partner_customers
has_many :partner_manager_groups, dependent: :destroy
has_one :admin_partner_manager_group, -> { where(admin: true) }, class_name: 'PartnerManagerGroup'
has_many :partner_managers, dependent: :destroy
end

@ -0,0 +1,5 @@
class PartnerManager < ApplicationRecord
belongs_to :user
belongs_to :partner
belongs_to :partner_manager_group
end

@ -0,0 +1,10 @@
class PartnerManagerGroup < ApplicationRecord
belongs_to :partner
has_many :customers, dependent: :nullify
has_many :partner_managers, dependent: :destroy
has_many :users, through: :partner_managers, source: :user
scope :without_admin, -> { where(admin: false) }
end

@ -14,7 +14,7 @@ class School < ApplicationRecord
has_many :courses
has_many :customers, dependent: :destroy
has_many :partners, dependent: :destroy
has_one :partner, dependent: :destroy
has_many :apply_add_departments, dependent: :destroy
has_many :user_extensions, dependent: :nullify

@ -18,6 +18,8 @@ module Searchable::Subject
def search_data
{
name: name,
status: status,
hidden: hidden,
description: Util.extract_content(description)[0..Searchable::MAXIMUM_LENGTH],
shixuns_count: shixuns_count,
myshixuns_count: member_count,

@ -293,7 +293,7 @@ class Shixun < ApplicationRecord
# 所属实践课程
def relation_path
subjects.where(hidden: 0).uniq
subjects.visible.unhidden.uniq
end
private

@ -146,7 +146,7 @@ class User < ApplicationRecord
has_many :videos, dependent: :destroy
# 客户管理
belongs_to :partner, optional: true
has_many :partner_managers, dependent: :destroy
# Groups and active users
scope :active, lambda { where(status: STATUS_ACTIVE) }

@ -0,0 +1,30 @@
class CustomerQuery < ApplicationQuery
attr_reader :partner, :user, :params
def initialize(partner, user, params)
@partner = partner
@user = user
@params = params
end
def call
customers = manager_group_scope
keyword = params[:keyword].to_s.strip.presence
customers = customers.joins(:school).where('schools.name LIKE ?', "%#{keyword}%") if keyword
customers
end
private
def manager_group_scope
# 超级管理员 或者 管理员
if user.admin_or_business? || partner.admin_partner_manager_group.partner_managers.exists?(user: user)
partner.customers
else
manager_group_ids = user.partner_managers.where(partner: partner).joins(:partner_manager_group).pluck('partner_manager_groups.id')
partner.customers.where(partner_manager_group_id: manager_group_ids)
end
end
end

@ -0,0 +1,59 @@
class Admins::ImportCompetitionScoreService < ApplicationService
Error = Class.new(StandardError)
attr_reader :file, :competition, :result
def initialize(file, competition)
@file = file
@competition = competition
@result = { success: 0, fail: [] }
end
def call
raise Error, '文件不存在' if file.blank?
# 创建所有战队的得分记录
create_all_records
excel = Admins::ImportCompetitionScoreExcel.new(file)
excel.read_each(&method(:update_competition_score)) # 更新单个战队成绩
result
rescue ApplicationImport::Error => ex
raise Error, ex.message
end
private
def update_competition_score(data)
team = competition.competition_scores.find_by(competition_team_id: data.competition_team_id)
raise "id为#{data.id}的战队不存在" if team.blank?
team.update!(score: data.score)
result[:success] += 1
rescue Exception => ex
fail_data = data.as_json
fail_data[:data] = fail_data.values.join(',')
fail_data[:message] = ex.message
result[:fail] << fail_data
end
def create_all_records
competition.competition_scores.destroy_all
stage = competition.competition_stages.first
attrs = %i[
competition_id competition_stage_id score cost_time user_id competition_team_id created_at updated_at
]
CompetitionScore.bulk_insert(*attrs) do |worker|
base_attr = { competition_id: competition.id, competition_stage_id: stage&.id.to_i,
score: 0, cost_time: 0 }
competition.competition_teams.each do |team|
worker.add(base_attr.merge(user_id: team.user_id).merge(competition_team_id: team.id))
end
end
end
end

@ -15,6 +15,7 @@ class Users::ApplyAuthenticationService < ApplicationService
user.lastname = params[:name].to_s.strip
user.firstname = ''
user.ID_number = params[:id_number].to_s.strip.presence
user.show_realname = params[:show_realname].to_s == 'true' if params[:show_realname].to_s.present?
ActiveRecord::Base.transaction do
user.authentication = false

@ -0,0 +1,30 @@
class Weapps::CreateCourseService < ApplicationService
attr_reader :course, :params
def initialize(course, params)
@course = course
@params = params
end
def call
Weapps::CreateCourseForm.new(form_params).validate!
ActiveRecord::Base.transaction do
course.name = params[:name].to_s.strip
course.school_id = course.teacher&.school_id
course.is_public = 0
course.credit = params[:credit].blank? ? nil : params[:credit]
course.save!
course.generate_invite_code
CourseMember.create!(course_id: course.id, user_id: course.tea_id, role: 1)
course.create_course_modules(params[:course_module_types])
end
end
private
def form_params
params.merge(course: course)
end
end

@ -6,7 +6,7 @@
<%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.auth-schools-new-add' } %>
</div>
<div class="box auth-schools-list-container">
<div class="box admin-list-container auth-schools-list-container">
<%= render(partial: 'admins/auth_schools/shared/list', locals: { schools: @schools }) %>
</div>

@ -14,7 +14,7 @@
<div class="input-group-prepend">
<span class="input-group-text">图片</span>
</div>
<div class="custom-file flex-row-reverse">
<div class="custom-file flex-row-reverse" style="overflow: hidden">
<input type="file" name="portal_image[image]" class="img-file-input" id="img-file-input" accept="image/*">
<label class="custom-file-label file-names" for="img-file-input">选择文件</label>
</div>

@ -59,6 +59,6 @@
</div>
</div>
<div class="box competition-prize-user-list-container">
<div class="box admin-list-container competition-prize-user-list-container">
<%= render(partial: 'admins/competition_prize_users/shared/list', locals: { prize_users: @prize_users }) %>
</div>

@ -17,7 +17,11 @@
<% if prize_user.leader? && prize_user.competition_prize.category == 'bonus' %>
<% bank_content = [prize_user.extra&.[]('bank'), prize_user.extra&.[]('second_bank'), prize_user.extra&.[]('card_no')].compact.join('<br>').presence || '无' %>
<%= javascript_void_link('查看银行账户', data: { toggle: 'tooltip', title: bank_content.html_safe, html: true, placement: 'left', trigger: 'click' }) %>
<% end %>
<% prize_module = prize_user.competition&.competition_modules.find_by(module_type: 'certificate') %>
<% if prize_module %>
<%= link_to('编辑', EduSetting.get("host_name").to_s + "/competitions/#{prize_user.competition&.identifier}?menu=#{prize_module&.id}&user_id=#{user.id}", target: "_blank") %>
<% end %>
<% end %>
<% if prize_user.pending? %>
<%= link_to('审批通过', approve_admins_competition_competition_prize_user_path(prize_user.competition, prize_user),

@ -25,9 +25,10 @@
</div>
</div>
<div class="box competitions-list-container">
<div class="box admin-list-container competitions-list-container">
<%= render partial: 'admins/competitions/shared/list', locals: { competitions: @competitions } %>
</div>
<%= render 'admins/competitions/shared/create_competition_modal' %>
<%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片' } %>
<%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片' } %>
<%= render partial: 'admins/competitions/shared/import_competition_score_modal' %>

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

@ -24,4 +24,8 @@
<% end %>
<%= link_to competition.published? ? "下架" : "上架", online_switch_admins_competition_path(competition), class: 'action online-action', method: :post, remote: true %>
<% if competition.mode != 1 %>
<%= javascript_void_link '导入成绩', class: 'action', data: { competition_id: competition.id, toggle: 'modal', target: '.admin-import-competition-score-modal'} %>
<% end %>
</td>

@ -12,7 +12,7 @@
<%= javascript_void_link('添加', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-select-school-modal' }) %>
</div>
<div class="box customer-list-container">
<div class="box admin-list-container customer-list-container">
<%= render 'admins/customers/shared/list', customers: @customers %>
</div>

@ -24,6 +24,6 @@
其它作业总数<span class="text-danger"><%= @other_homework_total %></span>个
</div>
<div class="box daily-school-statistic-list-container">
<div class="box admin-list-container daily-school-statistic-list-container">
<%= render partial: 'admins/daily_school_statistics/shared/list', locals: { statistics: @statistics } %>
</div>

@ -10,7 +10,7 @@
<% end %>
</div>
<div class="box department-applies-list-container">
<div class="box admin-list-container department-applies-list-container">
<%= render(partial: 'admins/department_applies/shared/list', locals: { applies: @depart_applies }) %>
</div>

@ -23,7 +23,7 @@
<%= javascript_void_link '新建部门', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-department-modal' } %>
</div>
<div class="box department-list-container">
<div class="box admin-list-container department-list-container">
<%= render partial: 'admins/departments/shared/list',
locals: { departments: @departments, users_count: @users_count, professional_auth_count: @professional_auth_count } %>
</div>

@ -7,7 +7,7 @@
</div>
<div class="box ec-templates-list-container">
<div class="box admin-list-container ec-templates-list-container">
<%= render(partial: 'admins/ec_templates/shared/list', locals: { templates: @templates }) %>
</div>

@ -14,14 +14,14 @@ wb.add_worksheet(name: '报名列表') do |sheet|
@personal ? "--" : team.teachers_info,
member_user.real_name,
member_user.identity,
member_user.phone,
member_user.phone.present? ? (member_user.phone.to_s + "\t") : "--",
member_user.mail,
member_user.student_id,
member_user.student_id.present? ? (member_user.student_id.to_s + "\t") : "--",
member_user.authentication ? "√" : "",
member_user.professional_certification ? "√" : "",
member_user.school_name,
member_user.school_province,
member.created_at.strftime('%Y-%m-%d %H:%M'),
team.created_at&.strftime('%Y-%m-%d %H:%M'),
rank
]
sheet.add_row(data)

@ -28,6 +28,6 @@
</div>
</div>
<div class="box competition-enroll-list-container">
<div class="box admin-list-container competition-enroll-list-container">
<%= render(partial: 'admins/enroll_lists/list', locals: { enroll_lists: @enroll_lists }) %>
</div>

@ -7,7 +7,7 @@
id: "-1", content: "", msg: "添加" } %>
</div>
<div class="box graduation-standards-list-container">
<div class="box admin-list-container graduation-standards-list-container">
<%= render(partial: 'admins/graduation_standards/shared/list', locals: { standards: @standards }) %>
</div>

@ -29,7 +29,7 @@
<%= javascript_void_link '批量同意', class: 'btn btn-outline-primary btn-sm batch-agree-btn' %>
</div>
<div class="box identity-authentication-list-container">
<div class="box admin-list-container identity-authentication-list-container">
<%= render(partial: 'admins/identity_authentications/shared/list', locals: { applies: @applies }) %>
</div>

@ -11,7 +11,7 @@
<%= javascript_void_link '新建', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-laboratory-modal' } %>
</div>
<div class="box laboratory-list-container">
<div class="box admin-list-container laboratory-list-container">
<%= render(partial: 'admins/laboratories/shared/list', locals: { laboratories: @laboratories }) %>
</div>

@ -38,7 +38,7 @@
<%= javascript_void_link('添加实训', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-add-laboratory-shixun-modal' }) %>
</div>
<div class="box laboratory-shixun-list-container" data-id="<%= current_laboratory.id %>">
<div class="box admin-list-container laboratory-shixun-list-container" data-id="<%= current_laboratory.id %>">
<%= render partial: 'admins/laboratory_shixuns/shared/list', locals: { laboratory_shixuns: @laboratory_shixuns } %>
</div>

@ -25,4 +25,8 @@
<%= link_to('去修改', admins_shixun_settings_path(id: laboratory_shixun.shixun_id)) %>
<%= javascript_void_link('首页展示', class: 'action homepage-show-action', data: { id: laboratory_shixun.id }, style: laboratory_shixun.homepage? ? 'display:none' : '') %>
<%= javascript_void_link('取消首页展示', class: 'action homepage-hide-action', data: { id: laboratory_shixun.id }, style: laboratory_shixun.homepage? ? '' : 'display:none') %>
<% unless laboratory_shixun.ownership? %>
<%= delete_link '删除', admins_laboratory_laboratory_shixun_path(current_laboratory, laboratory_shixun, element: ".laboratory-shixun-item-#{laboratory_shixun.id}") %>
<% end %>
</td>

@ -38,7 +38,7 @@
<%= javascript_void_link('添加课程', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-add-laboratory-subject-modal' }) %>
</div>
<div class="box laboratory-subject-list-container" data-id="<%= current_laboratory.id %>">
<div class="box admin-list-container laboratory-subject-list-container" data-id="<%= current_laboratory.id %>">
<%= render partial: 'admins/laboratory_subjects/shared/list', locals: { laboratory_subjects: @laboratory_subjects } %>
</div>

@ -36,6 +36,10 @@
<%= link_to('去修改', admins_subjects_path(id: laboratory_subject.subject_id)) %>
<%= javascript_void_link('首页展示', class: 'action homepage-show-action', data: { id: laboratory_subject.id }, style: laboratory_subject.homepage? ? 'display:none' : '') %>
<%= javascript_void_link('取消首页展示', class: 'action homepage-hide-action', data: { id: laboratory_subject.id }, style: laboratory_subject.homepage? ? '' : 'display:none') %>
<% unless laboratory_subject.ownership? %>
<%= delete_link '删除', admins_laboratory_laboratory_subject_path(current_laboratory, laboratory_subject, element: ".laboratory-subject-item-#{laboratory_subject.id}") %>
<% end %>
</td>
</tr>
<% end %>

@ -25,7 +25,7 @@
<% end %>
</div>
<div class="box library-applies-list-container">
<div class="box admin-list-container library-applies-list-container">
<%= render(partial: 'admins/library_applies/shared/list', locals: { applies: @library_applies }) %>
</div>

@ -2,6 +2,6 @@
<% add_admin_breadcrumb('本科专业目录') %>
<% end %>
<div class="box major-informations-list-container">
<div class="box admin-list-container major-informations-list-container">
<%= render(partial: 'admins/major_informations/shared/list', locals: { majors: @disciplines }) %>
</div>

@ -16,7 +16,7 @@
</div>
<% end %>
<div class="box mirror-repository-list-container">
<div class="box admin-list-container mirror-repository-list-container">
<%= render partial: 'admins/mirror_repositories/shared/list', locals: { mirrors: @mirrors } %>
</div>

@ -9,6 +9,6 @@
<%= link_to '新建', new_admins_mirror_repository_mirror_script_path(current_mirror), class: 'btn btn-primary' %>
</div>
<div class="box mirror-script-list-container">
<div class="box admin-list-container mirror-script-list-container">
<%= render partial: 'admins/mirror_scripts/shared/list', locals: { mirror: current_mirror, scripts: @scripts } %>
</div>

@ -9,6 +9,6 @@
<% end %>
</div>
<div class="box myshixun-list-container">
<div class="box admin-list-container myshixun-list-container">
<%= render(partial: 'admins/myshixuns/shared/list', locals: { myshixuns: @myshixuns, finish_game_count: @finish_game_count, total_score: @total_score }) %>
</div>

@ -11,7 +11,7 @@
<%= javascript_void_link('添加', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-select-school-modal' }) %>
</div>
<div class="box partner-list-container">
<div class="box admin-list-container partner-list-container">
<%= render 'admins/partners/shared/list', partners: @partners %>
</div>

@ -10,7 +10,9 @@
<% if partners.present? %>
<% partners.each do |partner| %>
<tr class="partner-item-<%= partner.id %>">
<td class="text-left"><%= partner.school&.name || partner.name %></td>
<td class="text-left">
<%= link_to partner.school&.name || partner.name, customers_partner_path(partner), target: '_blank' %>
</td>
<td><%= partner.created_at&.strftime('%Y-%m-%d %H:%M') %></td>
<td>
<%= link_to '查看', admins_partner_customers_path(partner), class: 'action' %>

@ -29,7 +29,7 @@
<%= javascript_void_link '批量同意', class: 'btn btn-outline-primary btn-sm batch-agree-btn' %>
</div>
<div class="box professional-authentication-list-container">
<div class="box admin-list-container professional-authentication-list-container">
<%= render(partial: 'admins/professional_authentications/shared/list', locals: { applies: @applies }) %>
</div>

@ -25,7 +25,7 @@
<% end %>
</div>
<div class="box project-package-applies-list-container">
<div class="box admin-list-container project-package-applies-list-container">
<%= render(partial: 'admins/project_package_applies/shared/list', locals: { applies: @package_applies}) %>
</div>

@ -44,6 +44,6 @@
</form>
</div>
<div class="box school-statistic-list-container">
<div class="box admin-list-container school-statistic-list-container">
<%= render partial: 'admins/school_statistics/shared/list', locals: { statistics: @statistics } %>
</div>

@ -11,6 +11,6 @@
<%#= javascript_void_link '新建单位', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-school-modal' } %>
</div>
<div class="box school-list-container">
<div class="box admin-list-container school-list-container">
<%= render partial: 'admins/schools/shared/list', locals: { schools: @schools } %>
</div>

@ -25,7 +25,7 @@
<% end %>
</div>
<div class="box shixun-authorization-list-container">
<div class="box admin-list-container shixun-authorization-list-container">
<%= render(partial: 'admins/shixun_authorizations/shared/list', locals: { applies: @applies, shixun_map: @shixun_map }) %>
</div>

@ -69,7 +69,7 @@
<% end %>
</div>
<div class="box shixun-settings-list-container">
<div class="box admin-list-container shixun-settings-list-container">
<%= render partial: 'admins/shixun_settings/shared/list', locals: { shixun_settings: @shixun_settings } %>
</div>

@ -27,6 +27,6 @@
<a href="javascript:void(0)" class="btn btn-primary" id="shixuns-export" data-disable-with = '导出中...'>导出</a>
</div>
<div class="box shixuns-list-container">
<div class="box admin-list-container shixuns-list-container">
<%= render partial: 'admins/shixuns/shared/list', locals: { shixuns: @shixuns } %>
</div>

@ -25,7 +25,7 @@
<% end %>
</div>
<div class="box subject-authorization-list-container">
<div class="box admin-list-container subject-authorization-list-container">
<%= render(partial: 'admins/subject_authorizations/shared/list',
locals: { applies: @applies, subject_map: @subject_map, challenge_count_map: @challenge_count_map }) %>
</div>

@ -34,7 +34,7 @@
<% end %>
</div>
<div class="box subject-list-container">
<div class="box admin-list-container subject-list-container">
<%= render partial: 'admins/subjects/shared/list', locals: { subjects: @subjects } %>
</div>

@ -22,6 +22,6 @@
<%= javascript_void_link '导出', class: 'btn btn-outline-primary export-action', 'data-url': export_admins_user_statistics_path(format: :xlsx) %>
</div>
<div class="box user-statistic-list-container">
<div class="box admin-list-container user-statistic-list-container">
<%= render partial: 'admins/user_statistics/shared/list', locals: { users: @users } %>
</div>

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

Loading…
Cancel
Save