diff --git a/app/assets/javascripts/admins/competitions/index.js b/app/assets/javascripts/admins/competitions/index.js new file mode 100644 index 000000000..c476dbf24 --- /dev/null +++ b/app/assets/javascripts/admins/competitions/index.js @@ -0,0 +1,73 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-competitions-index-page').length > 0) { + $('.modal.admin-upload-file-modal').on('upload:success', function(e, data){ + var $imageElement = $('.competition-image-' + data.source_id); + $imageElement.attr('src', data.url); + $imageElement.show(); + $imageElement.next().html('重新上传'); + }); + } + + $(".admin-competition-list-form").on("change", '.competitions-hot-select', function () { + var s_value = $(this).get(0).checked ? 1 : 0; + var json = {}; + json["hot"] = s_value; + $.ajax({ + url: "/admins/competitions/hot_setting", + type: "POST", + dataType:'json', + data: json, + success: function(){ + $.notify({ message: '操作成功' }); + } + }); + }); + + // ============== 新增竞赛 =============== + var $modal = $('.modal.admin-create-competition-modal'); + var $form = $modal.find('form.admin-create-competition-form'); + var $competitionNameInput = $form.find('input[name="competition_name"]'); + + $form.validate({ + errorElement: 'span', + errorClass: 'danger text-danger', + rules: { + competition_name: { + required: true + } + } + }); + + // modal ready fire + $modal.on('show.bs.modal', function () { + $competitionNameInput.val(''); + }); + + $modal.on('click', '.submit-btn', function(){ + $form.find('.error').html(''); + + if ($form.valid()) { + var url = $form.data('url'); + + $.ajax({ + method: 'POST', + dataType: 'json', + url: url, + data: $form.serialize(), + success: function(){ + $.notify({ message: '创建成功' }); + $modal.modal('hide'); + + setTimeout(function(){ + window.location.reload(); + }, 500); + }, + error: function(res){ + var data = res.responseJSON; + $form.find('.error').html(data.message); + } + }); + } + }); +}); + diff --git a/app/assets/javascripts/admins/enroll_lists/index.js b/app/assets/javascripts/admins/enroll_lists/index.js new file mode 100644 index 000000000..04bd95070 --- /dev/null +++ b/app/assets/javascripts/admins/enroll_lists/index.js @@ -0,0 +1,9 @@ +$(document).on('turbolinks:load', function() { + if($('body.admins-enroll-lists-index-page').length > 0){ + let search_form = $(".search-form"); + //导出 + $(".competition-enroll-list-form").on("click","#enroll-lists-export",function () { + window.location.href = "/admins/competitions/"+$(this).attr("data-competition-id")+"/enroll_lists.xls?" + search_form.serialize(); + }); + } +}); \ No newline at end of file diff --git a/app/assets/javascripts/admins/modals/admin-upload-file-modal.js b/app/assets/javascripts/admins/modals/admin-upload-file-modal.js index cf1333381..a53173959 100644 --- a/app/assets/javascripts/admins/modals/admin-upload-file-modal.js +++ b/app/assets/javascripts/admins/modals/admin-upload-file-modal.js @@ -4,14 +4,17 @@ $(document).on('turbolinks:load', function() { var $form = $modal.find('form.admin-upload-file-form') var $sourceIdInput = $modal.find('input[name="source_id"]'); var $sourceTypeInput = $modal.find('input[name="source_type"]'); + var $suffixInput = $modal.find('input[name="suffix"]'); $modal.on('show.bs.modal', function(event){ var $link = $(event.relatedTarget); var sourceId = $link.data('sourceId'); var sourceType = $link.data('sourceType'); + var suffix = $link.data('suffix'); $sourceIdInput.val(sourceId); $sourceTypeInput.val(sourceType); + if(suffix != undefined){ $suffixInput.val(suffix); } $modal.find('.upload-file-input').trigger('click'); }); @@ -48,6 +51,7 @@ $(document).on('turbolinks:load', function() { contentType: false, success: function(data){ $.notify({ message: '上传成功' }); + $modal.find('.file-names').html(''); $modal.trigger('upload:success', data); $modal.modal('hide'); }, diff --git a/app/assets/javascripts/admins/shixun_settings/index.js b/app/assets/javascripts/admins/shixun_settings/index.js index f99574673..8b3eee505 100644 --- a/app/assets/javascripts/admins/shixun_settings/index.js +++ b/app/assets/javascripts/admins/shixun_settings/index.js @@ -34,10 +34,17 @@ $(document).on('turbolinks:load', function() { }); $('.modal.admin-upload-file-modal').on('upload:success', function(e, data){ - var $imageElement = $('.shixun-image-' + data.source_id); - $imageElement.attr('src', data.url); - $imageElement.show(); - $imageElement.next().html('重新上传'); + if(data.suffix == '_weapp'){ + var $imageElement = $('.shixun-weapp-image-' + data.source_id); + $imageElement.attr('src', data.url); + $imageElement.show(); + $imageElement.next().html('重新上传'); + } else { + var $imageElement = $('.shixun-image-' + data.source_id); + $imageElement.attr('src', data.url); + $imageElement.show(); + $imageElement.next().html('重新上传'); + } }) } }); diff --git a/app/assets/javascripts/admins/user_statistics/index.js b/app/assets/javascripts/admins/user_statistics/index.js new file mode 100644 index 000000000..3ffb288ac --- /dev/null +++ b/app/assets/javascripts/admins/user_statistics/index.js @@ -0,0 +1,73 @@ +$(document).on('turbolinks:load', function() { + if ($('body.admins-user-statistics-index-page').length > 0) { + var $form = $('.user-statistic-list-form'); + + // ************** 学校选择 ************* + 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) { + $form.find('.school-select').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) { + $form.find('#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); + } + }); + + // 清空 + $form.on('click', '.clear-btn', function(){ + $form.find('select[name="date"]').val(''); + $form.find('.school-select').val('').trigger('change'); + $form.find('input[type="submit"]').trigger('click'); + }) + + + // 导出 + $('.export-action').on('click', function(){ + var form = $(".user-statistic-list-form .search-form") + var exportLink = $(this); + var date = form.find("select[name='date']").val(); + var schoolId = form.find('input[name="school_id"]').val(); + + var url = exportLink.data("url").split('?')[0] + "?date=" + date + "&school_id=" + schoolId; + window.open(url); + }); + } +}); \ No newline at end of file diff --git a/app/controllers/admins/competitions_controller.rb b/app/controllers/admins/competitions_controller.rb index 3b9b63243..2e2dbd4e2 100644 --- a/app/controllers/admins/competitions_controller.rb +++ b/app/controllers/admins/competitions_controller.rb @@ -11,18 +11,34 @@ class Admins::CompetitionsController < Admins::BaseController ids = @competitions.map(&:id) @member_count_map = TeamMember.where(competition_id: ids).group(:competition_id).count + @competition_hot = ModuleSetting.exists?(module_type: "Competition", property: "hot") respond_to do |format| format.js format.html end end + def create + name = params[:competition_name].to_s.strip + Competition.create!(name: name) + render_ok + end + + def hot_setting + if params[:hot].to_i == 1 && !ModuleSetting.exists?(module_type: "Competition", property: "hot") + ModuleSetting.create!(module_type: "Competition", property: "hot") + elsif params[:hot].to_i == 0 && ModuleSetting.exists?(module_type: "Competition", property: "hot") + ModuleSetting.where(module_type: "Competition", property: "hot").destroy_all + end + render_ok + end + def publish - @competition.update_attributes!(:published_at, Time.now) + @competition.update_attributes!(published_at: Time.now) end def unpublish - @competition.update_attributes!(:published_at, nil) + @competition.update_attributes!(published_at: nil) end def online_switch @@ -33,10 +49,6 @@ class Admins::CompetitionsController < Admins::BaseController end end - def enroll_list - - end - private def find_competition diff --git a/app/controllers/admins/enroll_lists_controller.rb b/app/controllers/admins/enroll_lists_controller.rb new file mode 100644 index 000000000..ccac6a72d --- /dev/null +++ b/app/controllers/admins/enroll_lists_controller.rb @@ -0,0 +1,25 @@ +class Admins::EnrollListsController < Admins::BaseController + + def index + @competition = current_competition + params[:sort_by] = params[:sort_by].presence || 'created_at' + params[:sort_direction] = params[:sort_direction].presence || 'desc' + enroll_lists = Admins::CompetitionEnrollListQuery.call(@competition, params) + + @params_page = params[:page] || 1 + @enroll_lists = paginate enroll_lists.preload(competition_team: [:user, :teachers], user: { user_extension: :school }) + @personal = @competition.personal? + + respond_to do |format| + format.js + format.html + format.xls + end + end + + private + def current_competition + @_current_competition ||= Competition.find(params[:competition_id]) + end + +end \ No newline at end of file diff --git a/app/controllers/admins/files_controller.rb b/app/controllers/admins/files_controller.rb index b269f8e27..cb8b66119 100644 --- a/app/controllers/admins/files_controller.rb +++ b/app/controllers/admins/files_controller.rb @@ -6,7 +6,12 @@ class Admins::FilesController < Admins::BaseController Util.write_file(@file, file_path) - render_ok(source_id: params[:source_id], source_type: params[:source_type].to_s, url: file_url + "?t=#{Random.rand}") + render_ok( + source_id: params[:source_id], + source_type: params[:source_type].to_s, + suffix: params[:suffix].presence, + url: file_url + ) rescue StandardError => ex logger_error(ex) render_error('上传失败') @@ -33,14 +38,14 @@ class Admins::FilesController < Admins::BaseController @_file_path ||= begin case params[:source_type].to_s when 'Shixun' then - Util::FileManage.disk_filename('Shixun', params[:source_id]) + Util::FileManage.disk_filename('Shixun', params[:source_id], params[:suffix].presence) else - Util::FileManage.disk_filename(params[:source_type].to_s, params[:source_id].to_s) + Util::FileManage.disk_filename(params[:source_type].to_s, params[:source_id].to_s, params[:suffix].presence) end end end def file_url - Util::FileManage.disk_file_url(params[:source_type].to_s, params[:source_id].to_s) + Util::FileManage.disk_file_url(params[:source_type].to_s, params[:source_id].to_s, params[:suffix].presence) end end \ No newline at end of file diff --git a/app/controllers/admins/user_statistics_controller.rb b/app/controllers/admins/user_statistics_controller.rb new file mode 100644 index 000000000..71dc3096a --- /dev/null +++ b/app/controllers/admins/user_statistics_controller.rb @@ -0,0 +1,19 @@ +class Admins::UserStatisticsController < Admins::BaseController + def index + default_sort('finish_shixun_count', 'desc') + + total_count, users = Admins::UserStatisticQuery.call(params) + + @users = paginate users, total_count: total_count + end + + def export + default_sort('finish_shixun_count', 'desc') + + params[:per_page] = 10000 + _count, @users = Admins::UserStatisticQuery.call(params) + + filename = ['用户实训情况', Time.zone.now.strftime('%Y%m%d%H%M%S')].join('-') << '.xlsx' + render xlsx: 'export', filename: filename + end +end \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d94325a5b..df3e9c96e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -623,4 +623,13 @@ class ApplicationController < ActionController::Base end user end + + # 记录热门搜索关键字 + def record_search_keyword + keyword = params[:keyword].to_s.strip + return if keyword.blank? || keyword.size <= 1 + return unless HotSearchKeyword.available? + + HotSearchKeyword.add(keyword) + end end diff --git a/app/controllers/competitions/competition_teams_controller.rb b/app/controllers/competitions/competition_teams_controller.rb index e03810b61..6a4dbfd34 100644 --- a/app/controllers/competitions/competition_teams_controller.rb +++ b/app/controllers/competitions/competition_teams_controller.rb @@ -1,5 +1,65 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController + before_action :tech_mode, only: [:shixun_detail, :course_detail] + + def shixun_detail + start_time = current_competition.competition_mode_setting&.start_time || current_competition.start_time + end_time = current_competition.competition_mode_setting&.end_time || current_competition.end_time + + team_user_ids = @team.team_members.pluck(:user_id) + shixuns = Shixun.where(user_id: team_user_ids, status: 2) + .where('shixuns.created_at > ? && shixuns.created_at <= ?', start_time, end_time) + shixuns = shixuns.joins('left join shixuns forked_shixuns on forked_shixuns.fork_from = shixuns.id and forked_shixuns.status = 2') + shixuns = shixuns.select('shixuns.id, shixuns.identifier, shixuns.user_id, shixuns.myshixuns_count, shixuns.name, shixuns.fork_from, sum(forked_shixuns.myshixuns_count) forked_myshixun_count') + @shixuns = shixuns.group('shixuns.id').order('shixuns.myshixuns_count desc').includes(:user) + + shixun_ids = @shixuns.map(&:id) + @myshixun_count_map = get_valid_myshixun_count(shixun_ids) + @original_myshixun_count_map = @myshixun_count_map.clone + # forked shixun valid myshixun count + forked_shixun_map = Shixun.where(status: 2, fork_from: shixun_ids).select('id, fork_from') + forked_shixun_map = forked_shixun_map.each_with_object({}) { |sx, obj| obj[sx.id] = sx.fork_from } + @forked_myshixun_count_map = get_valid_myshixun_count(forked_shixun_map.keys) + @forked_myshixun_count_map.each { |k, v| @myshixun_count_map[forked_shixun_map[k]] += v } + + @course_count_map = get_valid_course_count(shixun_ids) + @forked_map = get_valid_course_count(forked_shixun_map.keys) + @forked_course_count_map = {} + @forked_map.each do |forked_id, course_count| + @forked_course_count_map[forked_shixun_map[forked_id]] ||= 0 + @forked_course_count_map[forked_shixun_map[forked_id]] += course_count + end + @forked_shixun_map = forked_shixun_map + end + + def course_detail + start_time = current_competition.competition_mode_setting&.start_time || current_competition.start_time + end_time = current_competition.competition_mode_setting&.end_time || current_competition.end_time + @team_user_ids = @team.team_members.pluck(:user_id) + + student_count_subquery = CourseMember.where('course_id = courses.id AND role = 4').select('count(*)').to_sql + subquery = StudentWork.where('homework_common_id = hcs.id') + .select('sum(compelete_status !=0 ) as finish, count(*) as total') + .having('total != 0 and finish >= (total / 2)').to_sql + course_ids = Course.where('courses.created_at > ?', start_time) + .where('courses.created_at <= ?', end_time) + .where("(#{student_count_subquery}) >= 3") + .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 = 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') + + course_ids = @courses.map(&:id) + @course_myshixun_map = Myshixun.joins(student_works: :homework_common) + .where(homework_commons: { course_id: course_ids }) + .where('exists(select 1 from games where games.myshixun_id = myshixuns.id and games.status = 2)') + .group('homework_commons.course_id').count + @course_shixun_count_map = get_valid_shixun_count(course_ids) + end + def index + @personal = current_competition.personal? admin_or_business? ? all_competition_teams : user_competition_teams end @@ -67,4 +127,37 @@ class Competitions::CompetitionTeamsController < Competitions::BaseController def save_params params.permit(:name, teacher_ids: [], member_ids: []) end + + def tech_mode + # render_not_found if current_competition.mode != 3 + @team = current_competition.competition_teams.find_by(id: params[:id]) + end + + def get_valid_myshixun_count(ids) + Myshixun.where(shixun_id: ids) + .where('exists(select 1 from games where games.myshixun_id = myshixuns.id and games.status = 2)') + .group('shixun_id').count + end + + def get_valid_course_count(ids) + percentage_sql = StudentWork.where('homework_common_id = homework_commons.id and homework_commons.publish_time is not null and homework_commons.publish_time < NOW()') + .select('sum(compelete_status !=0 ) as finish, count(*) as total') + .having('total != 0 and finish >= (total / 2)').to_sql + + Course.joins(practice_homeworks: :homework_commons_shixun) + .where('shixun_id in (?)', ids) + .where("exists (#{percentage_sql})") + .group('shixun_id').count + end + + def get_valid_shixun_count(ids) + percentage_sql = StudentWork.where('homework_common_id = homework_commons.id and homework_commons.publish_time is not null and homework_commons.publish_time < NOW()') + .select('sum(compelete_status !=0 ) as finish, count(*) as total') + .having('total != 0 and finish >= (total / 2)').to_sql + Shixun.joins(homework_commons_shixuns: :homework_common) + .where(homework_commons: { homework_type: 4 }) + .where('course_id in (?)', ids) + .where("exists (#{percentage_sql})") + .group('course_id').count + end end diff --git a/app/controllers/competitions/competitions_controller.rb b/app/controllers/competitions/competitions_controller.rb index 77159fdaf..a17564737 100644 --- a/app/controllers/competitions/competitions_controller.rb +++ b/app/controllers/competitions/competitions_controller.rb @@ -1,6 +1,10 @@ class Competitions::CompetitionsController < Competitions::BaseController + include CompetitionsHelper + skip_before_action :require_login before_action :allow_visit, except: [:index] + before_action :require_admin, only: [:update, :update_inform] + before_action :chart_visible, only: [:charts, :chart_rules] def index # 已上架 或者 即将上架 @@ -25,10 +29,108 @@ class Competitions::CompetitionsController < Competitions::BaseController end def show + @competition = current_competition + end + + def update + @competition.update_attributes!(introduction: params[:introduction]) + normal_status("更新成功") end def common_header + @competition = current_competition + @competition_modules = @competition.unhidden_competition_modules + @user = current_user + end + + def informs + status = params[:status] || 1 + @informs = current_competition.informs.where(status: status) + end + + def update_inform + tip_exception("标题和内容不能为空") if params[:name].blank? || params[:description].blank? + tip_exception("status参数有误") if params[:status].blank? || ![1, 2].include?(params[:status].to_i) + ActiveRecord::Base.transaction do + if params[:inform_id] + inform = current_competition.informs.find_by!(id: params[:inform_id]) + inform.update_attributes!(name: params[:name], description: params[:description]) + else + inform = current_competition.informs.create!(name: params[:name], description: params[:description], status: params[:status]) + end + Attachment.associate_container(params[:attachment_ids], inform.id, inform.class) if params[:attachment_ids] + normal_status("更新成功") + end + end + + def md_content + @md_content = CompetitionModuleMdContent.find_by!(id: params[:md_content_id]) + end + + def update_md_content + tip_exception("标题和内容不能为空") if params[:name].blank? || params[:content].blank? + ActiveRecord::Base.transaction do + if params[:md_content_id] + md_content = CompetitionModuleMdContent.find_by!(id: params[:md_content_id]) + md_content.update_attributes!(name: params[:name], content: params[:content]) + else + md_content = CompetitionModuleMdContent.create!(name: params[:name], content: params[:content]) + end + Attachment.associate_container(params[:attachment_ids], md_content.id, md_content.class) if params[:attachment_ids] + normal_status("更新成功") + end + end + + def chart_rules + @competition = current_competition + @stages = @competition.competition_stages + @rule_contents = @competition.chart_rules + end + + def update_chart_rules + tip_exception("内容不能为空") if params[:content].blank? + @competition = current_competition + @stage = @competition.competition_stages.find_by!(id: params[:stage_id]) if params[:stage_id] + chart_rule = @competition.chart_rules.where(competition_stage_id: @stage&.id).first + if chart_rule + chart_rule.update_attributes!(content: params[:content]) + else + @competition.chart_rules.create!(competition_stage_id: @stage&.id, content: params[:content]) + end + normal_status("更新成功") + end + def charts + @competition = current_competition + if params[:stage_id] + @stage = @competition.competition_stages.find_by(id: params[:stage_id]) + end + + if @competition.identifier == "gcc-annotation-2018" + @records = @competition.competition_teams.joins(:competition_scores) + .select("competition_teams.*, score, cost_time").order("score desc, cost_time desc") + else + @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") + end + + 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") + 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) + 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(:user, :team_members).limit(@competition.awards_count) + end end private @@ -38,9 +140,60 @@ class Competitions::CompetitionsController < Competitions::BaseController end def allow_visit - unless current_competition.published? || admin_or_business? - render_forbidden - return + render_forbidden unless current_competition.published? || admin_or_business? + end + + def chart_visible + chart_module = current_competition.competition_modules.find_by(name: "排行榜") + render_forbidden unless (chart_module.present? && !chart_module.hidden) || admin_or_business? + end + + # 竞赛成绩导出 + def chart_to_xlsx records, competition + @competition_head_cells = [] + @competition_cells_column = [] + + max_staff = competition.competition_staffs.sum(:maximum) + if max_staff < 2 # 个人赛 + @competition_head_cells = %w(序号 姓名 学校 学号) + else # 战队赛 + @competition_head_cells = %w(序号 战队名 指导老师 队员 学校) + end + + statistic_stages = competition.competition_stages.where("score_rate > 0") + statistic_stages.each do |stage| + @competition_head_cells += ["#{stage.name}得分", "#{stage.name}用时"] + end + @competition_head_cells += ["总得分", "总用时"] if statistic_stages.size > 1 + competition_scores = competition.competition_scores + + records.each_with_index do |record, index| + row_cells_column = [] + row_cells_column << index + 1 + record_user = record.user + if max_staff < 2 + row_cells_column << record_user.real_name + row_cells_column << record_user.school_name + row_cells_column << record_user.student_id + else + row_cells_column << record.name + row_cells_column << record.teachers_name + row_cells_column << record.members_name + row_cells_column << chart_school_str(record.team_members.select{|member| !member.is_teacher}.pluck(:user_id)) + end + + statistic_stages.each do |stage| + stage_score = competition_scores.select{|score| score.competition_stage_id == stage.id && score.competition_team_id == record.id}.first + row_cells_column << stage_score&.score.to_f.round(2) + row_cells_column << com_spend_time(stage_score&.cost_time.to_i) + end + + if statistic_stages.size > 1 + row_cells_column << record&.score.to_f.round(2) + row_cells_column << com_spend_time(record&.cost_time.to_i) + end + + @competition_cells_column.push(row_cells_column) end end end \ No newline at end of file diff --git a/app/controllers/concerns/base/paginate_helper.rb b/app/controllers/concerns/base/paginate_helper.rb index a10d7eb2e..7a81da2b9 100644 --- a/app/controllers/concerns/base/paginate_helper.rb +++ b/app/controllers/concerns/base/paginate_helper.rb @@ -1,6 +1,11 @@ module Base::PaginateHelper extend ActiveSupport::Concern + def default_sort(sort_by, direction) + params[:sort_by] = params[:sort_by].presence || sort_by + params[:sort_direction] = params[:sort_direction].presence || direction + end + def offset (page - 1) * per_page end diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index d71b72618..99356946f 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -1129,7 +1129,7 @@ class ExercisesController < ApplicationController :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) + @answer_committed_user.update_attributes!(commit_option) CommitExercsieNotifyJobJob.perform_later(@exercise.id, current_user.id) normal_status(0,"试卷提交成功!") else diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb index 67a2e0ee8..78f665b3d 100644 --- a/app/controllers/games_controller.rb +++ b/app/controllers/games_controller.rb @@ -11,7 +11,7 @@ class GamesController < ApplicationController include ApplicationHelper def show - uid_logger_dubug("--games show start") + uid_logger("--games show start") # 防止评测中途ajaxE被取消;3改成0是为了处理首次进入下一关的问题 update_game_parameter(@game) @@ -60,7 +60,7 @@ class GamesController < ApplicationController praise_count: praise_count, user_praise: user_praise, time_limit: time_limit, tomcat_url: edu_setting('cloud_tomcat_php'), is_teacher: is_teacher, myshixun_manager: myshixun_manager, git_url: (@shixun.vnc ? repo_url(@myshixun.repo_path) : "")} - if @shixun.vnc && @st == 0 + if @shixun.vnc get_vnc_link(@game) end @@ -439,7 +439,7 @@ class GamesController < ApplicationController path = params[:path] || path status = params[:status].to_i path = path.try(:strip) - uid_logger_dubug("--rep_content: path is #{path}") + uid_logger("--rep_content: path is #{path}") begin @content = git_fle_content(@myshixun.repo_path, path) || "" rescue Exception => e @@ -455,7 +455,7 @@ class GamesController < ApplicationController # 监测版本库HEAD是否存在,不存在则取最新的HEAD uri = "#{shixun_tomcat}/bridge/game/check" res = uri_post uri, rep_params - uid_logger_dubug("repo_content to bridge: res is #{res}") + uid_logger("repo_content to bridge: res is #{res}") # res值:0 表示正常;-1表示有错误;-2表示代码版本库没了 # if status == 0 && res @@ -507,10 +507,11 @@ class GamesController < ApplicationController else {updated_at: Time.now} end + logger.info("#############myshixuns_update: ##{myshixuns_update}") @myshixun.update_attributes!(myshixuns_update) gitUrl = repo_ip_url @myshixun.repo_path - uid_logger_dubug("#############giturl: ##{gitUrl}") + logger.info("#############giturl: ##{gitUrl}") gitUrl = Base64.urlsafe_encode64(gitUrl) shixun_tomcat = edu_setting('cloud_bridge') @@ -536,7 +537,7 @@ class GamesController < ApplicationController testSet << test_cases end - uid_logger_dubug("##############testSet: #{testSet}") + logger.info("##############testSet: #{testSet}") testCases = Base64.urlsafe_encode64(testSet.to_json) unless testSet.blank? # 评测类型: 0,1,2 用于webssh的评测, 3用于vnc @@ -559,11 +560,11 @@ class GamesController < ApplicationController # 私密仓库的设置 secret_rep = @shixun.shixun_secret_repository - uid_logger_dubug("############secret_rep: #{secret_rep}") + logger.info("############secret_rep: #{secret_rep}") if secret_rep&.repo_name secretGitUrl = repo_ip_url secret_rep.repo_path br_params.merge!({secretGitUrl: Base64.urlsafe_encode64(secretGitUrl), secretDir: secret_rep.secret_dir_path}) - uid_logger_dubug("#######br_params:#{br_params}") + logger.info("#######br_params:#{br_params}") end # 中间层交互 @@ -579,6 +580,7 @@ class GamesController < ApplicationController # 选择题评测 def choose_build + Rails.logger.error("#################{params}") # 选择题如果通关了,则不让再评测 if @game.status == 2 raise Educoder::TipException.new("您已通过该关卡") @@ -621,7 +623,7 @@ class GamesController < ApplicationController end # 批量插入评测结果 - uid_logger_dubug("#------------chooice score: #{score}") + uid_logger("#------------chooice score: #{score}") sql = "INSERT INTO outputs (game_id, test_set_position, actual_output, result, query_index, created_at, updated_at) VALUES" + str ActiveRecord::Base.connection.execute sql @@ -681,10 +683,10 @@ class GamesController < ApplicationController resubmit_identifier = @game.resubmit_identifier # 如果没有超时并且正在评测中 # 判断评测中的状态有两种:1、如果之前没有通关的,只需判断status为1即可;如果通过关,则判断game的resubmit_identifier是否更新 - uid_logger_dubug("################game_status: #{@game.status}") - uid_logger_dubug("################params[:resubmit]: #{params[:resubmit]}") - uid_logger_dubug("################resubmit_identifier: #{resubmit_identifier}") - uid_logger_dubug("################time_out: #{params[:time_out]}") + uid_logger("################game_status: #{@game.status}") + uid_logger("################params[:resubmit]: #{params[:resubmit]}") + uid_logger("################resubmit_identifier: #{resubmit_identifier}") + uid_logger("################time_out: #{params[:time_out]}") sec_key = params[:sec_key] if (params[:time_out] == "false") && ((params[:resubmit].blank? && @game.status == 1) || (params[:resubmit].present? && (params[:resubmit] != resubmit_identifier))) @@ -694,7 +696,7 @@ class GamesController < ApplicationController render :json => { running_code_status: running_code_status, running_code_message: running_code_message } end - uid_logger_dubug("##### resubmit_identifier is #{resubmit_identifier}") + uid_logger("##### resubmit_identifier is #{resubmit_identifier}") port = params[:port] score = 0 experience = 0 @@ -743,7 +745,7 @@ class GamesController < ApplicationController end end - uid_logger_dubug("game is #{@game.id}, record id is #{e_record.try(:id)}, time is**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") + uid_logger("game is #{@game.id}, record id is #{e_record.try(:id)}, time is**** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") # 记录前端总耗时 record_consume_time = e_record.try(:pod_execute) max_mem = e_record.try(:max_mem) @@ -789,7 +791,7 @@ class GamesController < ApplicationController begin shixun_tomcat = edu_setting('cloud_bridge') uri = "#{shixun_tomcat}/bridge/webssh/delete" - uid_logger_dubug("#{current_user} => cloese_webssh digest is #{digest}") + Rails.logger.info("#{current_user} => cloese_webssh digest is #{digest}") params = {:tpiID => myshixun_id, :digestKey => digest_key, :identifier => @game.identifier} res = uri_post uri, params if res && res['code'].to_i != 0 @@ -828,7 +830,7 @@ class GamesController < ApplicationController @allowed_hidden_testset = @identity < User::EDU_GAME_MANAGER || @game.test_sets_view #解锁的用户 if max_query_index > 0 - uid_logger_dubug("max_query_index is #{max_query_index} game id is #{@game.id}, challenge_id is #{challenge.id}") + uid_logger("max_query_index is #{max_query_index} game id is #{@game.id}, challenge_id is #{challenge.id}") @qurey_test_sets = TestSet.find_by_sql("SELECT o.code, o.actual_output, o.out_put, o.result, o.test_set_position, o.ts_time, o.ts_mem, o.query_index, t.is_public, t.input, t.output, o.compile_success FROM outputs o, games g, challenges c, test_sets t where g.id=#{@game.id} and c.id=#{challenge.id} and o.query_index=#{max_query_index} @@ -879,7 +881,7 @@ class GamesController < ApplicationController end # actual_output为空表示暂时没有评测答题,不允许查看 actual_output = output.try(:actual_output).try(:strip) - has_answer << choose.answer if choose.answer.present? + #has_answer << choose.answer if choose.answer.present? # 标准答案处理,错误的不让用户查看,用-1替代 standard_answer = (actual_output.blank? || !output.try(:result)) ? -1 : choose.standard_answer result = output.try(:result) @@ -890,7 +892,7 @@ class GamesController < ApplicationController @chooses << sin_choose test_sets << sin_test_set end - @has_answer = has_answer.presence # 选择题永远都有答案 + @has_answer = true # 选择题永远都有答案 @choose_test_cases = {:had_submmit => had_submmit, :challenge_chooses_count => challenge_chooses_count, :choose_correct_num => choose_correct_num, :test_sets => test_sets} end diff --git a/app/controllers/homework_commons_controller.rb b/app/controllers/homework_commons_controller.rb index 60cf2d6c5..168d05299 100644 --- a/app/controllers/homework_commons_controller.rb +++ b/app/controllers/homework_commons_controller.rb @@ -1058,8 +1058,8 @@ class HomeworkCommonsController < ApplicationController 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 + tip_exception("缺少分班截止时间参数") if params[:group_end_times].blank? 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 diff --git a/app/controllers/hot_keywords_controller.rb b/app/controllers/hot_keywords_controller.rb new file mode 100644 index 000000000..e0c536e42 --- /dev/null +++ b/app/controllers/hot_keywords_controller.rb @@ -0,0 +1,7 @@ +class HotKeywordsController < ApplicationController + def index + keywords = [] + keywords = HotSearchKeyword.hot(8) if HotSearchKeyword.available? + render_ok(keywords: keywords) + end +end \ No newline at end of file diff --git a/app/controllers/myshixuns_controller.rb b/app/controllers/myshixuns_controller.rb index a9cc76296..4e587513a 100644 --- a/app/controllers/myshixuns_controller.rb +++ b/app/controllers/myshixuns_controller.rb @@ -261,7 +261,7 @@ class MyshixunsController < ApplicationController uid_logger_dubug("-- game build: file update #{@sec_key}, record id is #{record.id}, time is **** #{Time.now.strftime("%Y-%m-%d %H:%M:%S.%L")}") end # 隐藏代码文件 和 VNC的都不需要走版本库 - unless @hide_code || @myshixun.shixun&.vnc_evaluate + unless @hide_code || (@myshixun.shixun&.vnc_evaluate && params[:evaluate].present?) # 远程版本库文件内容 last_content = GitService.file_content(repo_path: @repo_path, path: path)["content"] content = if @myshixun.mirror_name.select {|a| a.include?("MachineLearning") || a.include?("Python")}.present? && params[:content].present? diff --git a/app/controllers/searchs_controller.rb b/app/controllers/searchs_controller.rb index 1ea1c5d05..130e8a5cd 100644 --- a/app/controllers/searchs_controller.rb +++ b/app/controllers/searchs_controller.rb @@ -1,9 +1,12 @@ class SearchsController < ApplicationController + after_action :record_search_keyword, only: [:index] + def index @results = SearchService.call(search_params) end private + def search_params params.permit(:keyword, :type, :page, :per_page) end diff --git a/app/controllers/shixuns_controller.rb b/app/controllers/shixuns_controller.rb index 98af25b7d..05c3500ee 100644 --- a/app/controllers/shixuns_controller.rb +++ b/app/controllers/shixuns_controller.rb @@ -232,10 +232,12 @@ class ShixunsController < ApplicationController # 同步私密版本库 if @shixun.shixun_secret_repository - repo_name = "#{current_user.login}/secret_#{@shixun.identifier}" + # 源仓库的的私密版本库地址 + repo_name = @shixun.shixun_secret_repository.repo_name + # 新生成的地址 fork_repository_name = "#{current_user.login}/secret_#{@new_shixun.identifier}" ShixunSecretRepository.create!(shixun_id: @new_shixun.id, - repo_name: "#{repo_name}", + repo_name: "#{fork_repository_name}", secret_dir_path: @shixun.shixun_secret_repository.secret_dir_path) GitService.fork_repository(repo_path: "#{repo_name}.git", fork_repository_path: (fork_repository_name + ".git")) end @@ -262,6 +264,11 @@ class ShixunsController < ApplicationController :mirror_repository_id => config.mirror_repository_id) end + # 同步高校限制 + @shixun.shixun_schools.each do |school| + ShixunSchool.create!(shixun_id: @new_shixun.id, school_id: school.school_id) + end + # fork版本库 logger.info("###########fork_repo_path: ######{@repo_path}") project_fork(@new_shixun, @repo_path, current_user.login) @@ -330,7 +337,10 @@ class ShixunsController < ApplicationController end rescue Exception => e uid_logger_error("copy shixun failed ##{e.message}") - g.delete_project(gshixungshixun.id) if gshixun.try(:id).present? # 异常后,如果已经创建了版本库需要删除该版本库 + # 删除版本库 + # 删除私密版本库 + GitService.delete_repository(repo_path: "#{fork_repository_name}.git") if @new_shixun.shixun_secret_repository&.repo_name + GitService.delete_repository(repo_path: @new_shixun.repo_path) if @new_shixun.repo_path tip_exception("实训Fork失败") raise ActiveRecord::Rollback end diff --git a/app/controllers/weapps/code_sessions_controller.rb b/app/controllers/weapps/code_sessions_controller.rb index b5886efd8..687605fc4 100644 --- a/app/controllers/weapps/code_sessions_controller.rb +++ b/app/controllers/weapps/code_sessions_controller.rb @@ -5,16 +5,23 @@ class Weapps::CodeSessionsController < Weapps::BaseController result = Wechat::Weapp.jscode2session(params[:code]) - # 已授权,绑定过账号 + # 能根据 code 拿到 unionid open_user = OpenUsers::Wechat.find_by(uid: result['unionid']) if open_user.present? && open_user.user successful_authentication(open_user.user) set_session_unionid(result['unionid']) logged = true else - # 新用户 + # 根据 code没拿到 unionid user_info = Wechat::Weapp.decrypt(result['session_key'], params[:encrypted_data], params[:iv]) + # 老用户,已绑定 + open_user = OpenUsers::Wechat.find_by(uid: user_info['unionId']) + if open_user.present? && open_user.user + successful_authentication(open_user.user) + logged = true + end + set_session_unionid(user_info['unionId']) end @@ -22,5 +29,7 @@ class Weapps::CodeSessionsController < Weapps::BaseController set_weapp_session_key(result['session_key']) # weapp session_key写入缓存 后续解密需要 render_ok(openid: result['openid'], logged: logged) + rescue Wechat::Error => ex + render_error(ex.message) end end \ No newline at end of file diff --git a/app/controllers/weapps/searchs_controller.rb b/app/controllers/weapps/searchs_controller.rb new file mode 100644 index 000000000..b4960e7b1 --- /dev/null +++ b/app/controllers/weapps/searchs_controller.rb @@ -0,0 +1,13 @@ +class Weapps::SearchsController < Weapps::BaseController + after_action :record_search_keyword, only: [:index] + + def index + @results = Weapps::SearchQuery.call(search_params) + end + + private + + def search_params + params.permit(:keyword, :type, :page, :per_page) + end +end \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index dbdcaea40..1d684350c 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -142,6 +142,14 @@ module ApplicationHelper end end + # 主页banner图 + def banner_img(source_type) + if File.exist?(disk_filename(source_type, "banner")) + ctime = File.ctime(disk_filename(source_type, "banner")).to_i + File.join("images/avatars", ["#{source_type}", "banner"]) + "?t=#{ctime}" + end + end + def disk_filename(source_type,source_id,image_file=nil) File.join(storage_path, "#{source_type}", "#{source_id}") end diff --git a/app/helpers/competitions_helper.rb b/app/helpers/competitions_helper.rb new file mode 100644 index 000000000..9e3e7f62a --- /dev/null +++ b/app/helpers/competitions_helper.rb @@ -0,0 +1,54 @@ +module CompetitionsHelper + def chart_school_str user_ids + chart_school_name = "" + chart_school_name = School.where(id: UserExtension.where(user_id: user_ids).pluck(:school_id).uniq).pluck(:name).join("、") + end + + # 耗时:小时、分、秒 00:00:00 + # 小于1秒钟则不显示 + def com_spend_time time + hour = time / (60*60) + min = time % (60*60) / 60 + sec = time % (60*60) % 60 + hour_str = "00" + min_str = "00" + sec_str = "00" + if hour >= 1 && hour < 10 + hour_str = "0#{hour}" + elsif hour >= 10 + hour_str = "#{hour}" + end + + if min >= 1 && min < 10 + min_str = "0#{min}" + elsif min >= 10 + min_str = "#{min}" + end + + if sec >= 1 && sec < 10 + sec_str = "0#{sec}" + elsif sec >= 10 + sec_str = "#{sec}" + end + + time = "#{hour_str} : #{min_str} : #{sec_str}" + return time + end + + def chart_stages competition + stages = [] + statistic_stages = competition.competition_stages.where("rate > 0") + if competition.end_time && competition.end_time < Time.now && statistic_stages.size > 1 + stages << {id: nil, name: "总排行榜", rate: 1.0, start_time: competition.start_time, end_time: competition.end_time} + end + + statistic_stages.each do |stage| + if stage.max_end_time && stage.max_end_time < Time.now + 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 + + stages = stages.sort { |a, b| b[:end_time] <=> a[:end_time] } if stages.size > 0 + return stages + end +end \ No newline at end of file diff --git a/app/helpers/exercises_helper.rb b/app/helpers/exercises_helper.rb index 395d67913..eadbfa05c 100644 --- a/app/helpers/exercises_helper.rb +++ b/app/helpers/exercises_helper.rb @@ -414,7 +414,7 @@ module ExercisesHelper score2 = 0.0 #填空题 score5 = 0.0 #实训题 ques_stand = [] #问题是否正确 - exercise_questions = exercise.exercise_questions.includes(:exercise_answers,:exercise_shixun_answers,:exercise_standard_answers,:exercise_shixun_challenges) + exercise_questions = exercise.exercise_questions.includes(:exercise_standard_answers,:exercise_shixun_challenges) exercise_questions&.each do |q| begin if q.question_type != 5 diff --git a/app/libs/hot_search_keyword.rb b/app/libs/hot_search_keyword.rb index f026142cb..f79d1641e 100644 --- a/app/libs/hot_search_keyword.rb +++ b/app/libs/hot_search_keyword.rb @@ -3,6 +3,7 @@ class HotSearchKeyword class << self def add(keyword) return if keyword.blank? + Rails.logger.info("[Hot Keyword] #{keyword} score increment ~") Rails.cache.data.zincrby(redis_key, 1, keyword) end diff --git a/app/libs/wechat/client.rb b/app/libs/wechat/client.rb index 5ac0a28f2..43d5b0732 100644 --- a/app/libs/wechat/client.rb +++ b/app/libs/wechat/client.rb @@ -53,13 +53,13 @@ class Wechat::Client private def request(method, url, **params) - Rails.logger.error("[wechat] request: #{method} #{url} #{params.except(:secret).inspect}") + Rails.logger.info("[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}") + Rails.logger.info("[wechat] response:#{response.status} #{result.inspect}") if response.status != 200 raise Wechat::Error.parse(result) diff --git a/app/libs/wechat/weapp.rb b/app/libs/wechat/weapp.rb index 755c9351a..9684206cd 100644 --- a/app/libs/wechat/weapp.rb +++ b/app/libs/wechat/weapp.rb @@ -34,7 +34,7 @@ class Wechat::Weapp data = cipher.update(encrypted_data) << cipher.final result = JSON.parse(data[0...-data.last.ord]) - raise Wechat::Error, '解密错误' if result.dig('watermark', 'appid') != appid + raise Wechat::Error.new(-1, '解密错误') if result.dig('watermark', 'appid') != appid result end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index a876440c3..ded4119cd 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,9 +1,15 @@ class ApplicationRecord < ActiveRecord::Base include NumberDisplayHelper + attr_accessor :_extra_data + self.abstract_class = true def format_time(time) time.present? ? time.strftime('%Y-%m-%d %H:%M') : '' end + + def display_extra_data(key) + _extra_data&.[](key) + end end diff --git a/app/models/chart_rule.rb b/app/models/chart_rule.rb new file mode 100644 index 000000000..de8fceaf1 --- /dev/null +++ b/app/models/chart_rule.rb @@ -0,0 +1,6 @@ +class ChartRule < ApplicationRecord + belongs_to :competition + belongs_to :competition_stage, optional: true + + validates :content, length: { maximum: 1000 } +end diff --git a/app/models/competition.rb b/app/models/competition.rb index 9055adefc..684db8fa5 100644 --- a/app/models/competition.rb +++ b/app/models/competition.rb @@ -9,12 +9,19 @@ class Competition < ApplicationRecord has_many :competition_teams, dependent: :destroy has_many :team_members, dependent: :destroy + has_many :chart_rules, dependent: :destroy + + has_many :competition_scores, dependent: :destroy has_many :competition_staffs, dependent: :destroy has_one :teacher_staff, -> { where(category: :teacher) }, class_name: 'CompetitionStaff' has_one :member_staff, -> { where.not(category: :teacher) }, class_name: 'CompetitionStaff' + has_one :competition_mode_setting, dependent: :destroy + + has_many :informs, as: :container, dependent: :destroy + has_many :attachments, as: :container, dependent: :destroy - has_many :attachments, as: :container + has_many :competition_awards, dependent: :destroy after_create :create_competition_modules @@ -48,7 +55,7 @@ class Competition < ApplicationRecord # 是否为个人赛 def personal? - competition_staffs.maximum(:maximum) == 1 + competition_staffs.maximum(:maximum) == 1 || max_num == 1 end # 报名是否结束 @@ -81,10 +88,20 @@ class Competition < ApplicationRecord member_staff.mutiple_limited? end + def max_min_stage_time + CompetitionStageSection.find_by_sql("SELECT MAX(end_time) as max_end_time, MIN(start_time) as min_start_time, + competition_stage_id FROM competition_stage_sections WHERE competition_id = #{id} + GROUP BY competition_stage_id order by competition_stage_id") + end + + def awards_count + competition_awards.pluck(:num)&.sum > 0 ? competition_awards.pluck(:num)&.sum : 20 + end + private def create_competition_modules - CompetitionModule.bulk_insert(*%i[competition_id name position]) do |worker| + CompetitionModule.bulk_insert(*%i[competition_id name position created_at updated_at]) do |worker| %w(首页 报名 通知公告 排行榜 资料下载).each_with_index do |name, index| worker.add(competition_id: id, name: name, position: index + 1) end diff --git a/app/models/competition_award.rb b/app/models/competition_award.rb new file mode 100644 index 000000000..715d82ee2 --- /dev/null +++ b/app/models/competition_award.rb @@ -0,0 +1,3 @@ +class CompetitionAward < ApplicationRecord + belongs_to :competition +end diff --git a/app/models/competition_mode_setting.rb b/app/models/competition_mode_setting.rb index b6bafa7c3..e8d6c17d7 100644 --- a/app/models/competition_mode_setting.rb +++ b/app/models/competition_mode_setting.rb @@ -1,3 +1,4 @@ class CompetitionModeSetting < ApplicationRecord - belongs_to :course + belongs_to :course, optional: true + belongs_to :competition end diff --git a/app/models/competition_module.rb b/app/models/competition_module.rb index be73bf3c1..9579533da 100644 --- a/app/models/competition_module.rb +++ b/app/models/competition_module.rb @@ -4,4 +4,21 @@ class CompetitionModule < ApplicationRecord belongs_to :competition has_one :competition_module_md_content, dependent: :destroy + + def module_url + case name + when "首页", "赛制介绍" + "/competitions/#{competition.identifier}" + when "通知公告" + "/competitions/#{competition.identifier}/informs?status=1" + when "参赛手册" + "/competitions/#{competition.identifier}/informs?status=2" + when "排行榜" + "/competitions/#{competition.identifier}/charts" + when "报名" + "/competitions/#{competition.identifier}/competition_teams" + else + url || "/competitions/#{competition.identifier}/md_content?md_content_id=#{competition_module_md_content&.id}" + end + end end diff --git a/app/models/competition_score.rb b/app/models/competition_score.rb new file mode 100644 index 000000000..ce7bad427 --- /dev/null +++ b/app/models/competition_score.rb @@ -0,0 +1,6 @@ +class CompetitionScore < ApplicationRecord + belongs_to :user + belongs_to :competition + belongs_to :competition_stage, optional: true + belongs_to :competition_team, optional: true +end diff --git a/app/models/competition_stage.rb b/app/models/competition_stage.rb index 60d4b1644..dec777b39 100644 --- a/app/models/competition_stage.rb +++ b/app/models/competition_stage.rb @@ -3,5 +3,15 @@ class CompetitionStage < ApplicationRecord has_many :competition_stage_sections, dependent: :destroy has_many :competition_entries, dependent: :destroy + has_many :competition_scores, dependent: :destroy + has_one :chart_rule, dependent: :destroy + + def min_start_time + competition_stage_sections.where("start_time is not null").pluck(:start_time).min + end + + def max_end_time + competition_stage_sections.where("end_time is not null").pluck(:end_time).max + end end \ No newline at end of file diff --git a/app/models/competition_team.rb b/app/models/competition_team.rb index 625b29421..6c2b99859 100644 --- a/app/models/competition_team.rb +++ b/app/models/competition_team.rb @@ -7,6 +7,7 @@ class CompetitionTeam < ApplicationRecord has_many :team_members, dependent: :destroy has_many :users, through: :team_members, source: :user + has_many :competition_scores, dependent: :destroy has_many :members, -> { without_teachers }, class_name: 'TeamMember' has_many :teachers, -> { only_teachers }, class_name: 'TeamMember' @@ -30,4 +31,21 @@ class CompetitionTeam < ApplicationRecord self.code = code code end + + def teachers_info + info = "" + teachers.each do |teacher| + teacher_info = "#{teacher.user.real_name}/#{teacher.user.school_name}" + info += info == "" ? teacher_info : "、#{teacher_info}" + end + info + end + + def teachers_name + teachers.map{|teacher| teacher.user.real_name}.join(",") + end + + def members_name + members.map{|member| member.user.real_name}.join(",") + end end \ No newline at end of file diff --git a/app/models/inform.rb b/app/models/inform.rb index 5caf80c5f..d486b6f11 100644 --- a/app/models/inform.rb +++ b/app/models/inform.rb @@ -3,4 +3,6 @@ class Inform < ApplicationRecord validates :name, length: { maximum: 60 } validates :description, length: { maximum: 5000 } + + has_many :attachments, as: :container, dependent: :destroy end diff --git a/app/models/laboratory_setting.rb b/app/models/laboratory_setting.rb index 32848dca2..ca62f5c91 100644 --- a/app/models/laboratory_setting.rb +++ b/app/models/laboratory_setting.rb @@ -43,7 +43,7 @@ class LaboratorySetting < ApplicationRecord navbar: [ { 'name' => '实践课程', 'link' => '/paths', 'hidden' => false }, { 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false }, - { 'name' => '实现项目', 'link' => '/shixuns', 'hidden' => false }, + { 'name' => '实训项目', 'link' => '/shixuns', 'hidden' => false }, { 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false }, { 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false }, { 'name' => '交流问答', 'link' => '/forums', 'hidden' => false }, diff --git a/app/models/module_setting.rb b/app/models/module_setting.rb new file mode 100644 index 000000000..36fc45e6f --- /dev/null +++ b/app/models/module_setting.rb @@ -0,0 +1,2 @@ +class ModuleSetting < ApplicationRecord +end diff --git a/app/models/shixun.rb b/app/models/shixun.rb index e0feb80ce..38df5f0d4 100644 --- a/app/models/shixun.rb +++ b/app/models/shixun.rb @@ -27,6 +27,7 @@ class Shixun < ApplicationRecord has_one :first_shixun_tag_repertoire, class_name: 'ShixunTagRepertoire' has_one :first_tag_repertoire, through: :first_shixun_tag_repertoire, source: :tag_repertoire + has_many :homework_commons_shixuns, class_name: 'HomeworkCommonsShixun' #实训的关卡 has_many :exercise_shixun_challenges, :dependent => :destroy diff --git a/app/models/user.rb b/app/models/user.rb index 103f9e0b5..135b85e25 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -237,6 +237,11 @@ class User < ApplicationRecord professional_certification end + # 学校所在的地区 + def school_province + user_extension&.school&.province || '' + end + # 用户的学校名称 def school_name user_extension&.school&.name || '' diff --git a/app/queries/admins/competition_enroll_list_query.rb b/app/queries/admins/competition_enroll_list_query.rb new file mode 100644 index 000000000..64f5dc7f8 --- /dev/null +++ b/app/queries/admins/competition_enroll_list_query.rb @@ -0,0 +1,40 @@ +class Admins::CompetitionEnrollListQuery < ApplicationQuery + include CustomSortable + + attr_reader :competition, :params + + sort_columns :created_at, :competition_team_id, default_by: :created_at, default_direction: :desc + + def initialize(competition, params) + @competition = competition + @params = params + end + + def call + members = competition.team_members + only_teacher = competition.competition_staffs.count == 1 && competition.competition_staffs.first.category == 'teacher' + members = members.where("team_members.is_teacher = 0") unless only_teacher # 只有老师报名时才显示老师,此时老师作为队员 + + + school = params[:school].to_s.strip + if school.present? + school_ids = School.where("schools.name like ?", "%#{school}%").pluck(:id) + school_ids = school_ids.size == 0 ? "(-1)" : "(" + school_ids.join(",") + ")" + members = members.joins(user: :user_extension).where("user_extensions.school_id in #{school_ids}") + end + + location = params[:location].to_s.strip + if location.present? + members = members.joins(user: { user_extension: :school }).where("schools.province like ?", "%#{location}%") + end + + # 关键字模糊查询 + keyword = params[:keyword].to_s.strip + if keyword.present? + members = members.joins(:competition_team) + .where('competition_teams.name LIKE :keyword', keyword: "%#{keyword}%") + end + + custom_sort(members, params[:sort_by], params[:sort_direction]) + end +end \ No newline at end of file diff --git a/app/queries/admins/user_statistic_query.rb b/app/queries/admins/user_statistic_query.rb new file mode 100644 index 000000000..d1811e5ab --- /dev/null +++ b/app/queries/admins/user_statistic_query.rb @@ -0,0 +1,138 @@ +class Admins::UserStatisticQuery < ApplicationQuery + include CustomSortable + + attr_reader :params + + sort_columns :study_challenge_count, :finish_challenge_count, :study_shixun_count, :finish_shixun_count, + default_by: :finish_shixun_count, default_direction: :desc + + def initialize(params) + @params = params + end + + def call + users = User.where(type: 'User').group(:id) + + users = users.joins(:user_extension).where(user_extensions: { school_id: params[:school_id] }) if params[:school_id].present? + + total = users.count.count + + # 根据排序字段进行查询 + users = query_by_sort_column(users, params[:sort_by]) + users = custom_sort(users, params[:sort_by], params[:sort_direction]) + + users = users.includes(user_extension: [:school, :department]) + users = users.limit(page_size).offset(offset).to_a + # 查询并组装其它数据 + users = package_other_data(users) + + [total, users] + end + + private + + def package_other_data(users) + ids = users.map(&:id) + + study_myshixun = Myshixun.where(user_id: ids) + finish_myshixun = Myshixun.where(user_id: ids, status: 1) + study_challenge = Game.joins(:myshixun).where(myshixuns: { user_id: ids }).where(status: [0, 1, 2]) + finish_challenge = Game.joins(:myshixun).where(myshixuns: { user_id: ids }).where(status: 2) + + if time_range.present? + study_myshixun = study_myshixun.where(updated_at: time_range) + finish_myshixun = finish_myshixun.where(updated_at: time_range) + study_challenge = study_challenge.where(updated_at: time_range) + finish_challenge = finish_challenge.where(updated_at: time_range) + end + + study_myshixun_map = study_myshixun.group(:user_id).count + finish_myshixun_map = finish_myshixun.group(:user_id).count + study_challenge_map = study_challenge.group(:user_id).count + finish_challenge_map = finish_challenge.group(:user_id).count + + users.each do |user| + user._extra_data = { + study_shixun_count: study_myshixun_map.fetch(user.id, 0), + finish_shixun_count: finish_myshixun_map.fetch(user.id, 0), + study_challenge_count: study_challenge_map.fetch(user.id, 0), + finish_challenge_count: finish_challenge_map.fetch(user.id, 0), + } + end + + users + end + + def query_by_sort_column(users, sort_by_column) + base_query_column = 'users.*' + + case sort_by_column.to_s + when 'study_shixun_count' then + users = + if time_range.present? + users.joins("LEFT JOIN myshixuns ON myshixuns.user_id = users.id "\ + "AND myshixuns.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'") + else + users.left_joins(:myshixuns) + end + + users.select("#{base_query_column}, COUNT(*) study_shixun_count") + when 'finish_shixun_count' then + users = + if time_range.present? + users.joins("LEFT JOIN myshixuns ON myshixuns.user_id = users.id AND myshixuns.status = 1 AND "\ + "myshixuns.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'") + else + users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id AND myshixuns.status = 1') + end + + users.select("#{base_query_column}, COUNT(*) finish_shixun_count") + when 'study_challenge_count' then + users = + if time_range.present? + users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id') + .joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id "\ + "AND games.status IN (0,1,2) AND games.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'") + else + users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id') + .joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id AND games.status IN (0,1,2)") + end + + users.select("#{base_query_column}, COUNT(*) study_challenge_count") + when 'finish_challenge_count' then + users = + if time_range.present? + users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id') + .joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id "\ + "AND games.status = 2 AND games.updated_at BETWEEN '#{time_range.min}' AND '#{time_range.max}'") + else + users.joins('LEFT JOIN myshixuns ON myshixuns.user_id = users.id') + .joins("LEFT JOIN games ON games.myshixun_id = myshixuns.id AND games.status = 2") + end + + users.select("#{base_query_column}, COUNT(*) finish_challenge_count") + else + users + end + end + + def time_range + @_time_range ||= begin + case params[:date] + when 'weekly' then 1.weeks.ago..Time.now + when 'monthly' then 1.months.ago..Time.now + when 'quarterly' then 3.months.ago..Time.now + when 'yearly' then 1.years.ago..Time.now + else '' + end + end + end + + def page_size + params[:per_page].to_i.zero? ? 20 : params[:per_page].to_i + end + + def offset + (params[:page].to_i.zero? ? 0 : params[:page].to_i - 1) * page_size + end +end diff --git a/app/queries/weapps/search_query.rb b/app/queries/weapps/search_query.rb new file mode 100644 index 000000000..665480073 --- /dev/null +++ b/app/queries/weapps/search_query.rb @@ -0,0 +1,37 @@ +class Weapps::SearchQuery < ApplicationQuery + include ElasticsearchAble + + attr_reader :params + + def initialize(params) + @params = params + end + + def call + modal_name.search(keyword, search_options) + end + + private + + def search_options + hash = { + fields: [:name], + page: page, + per_page: per_page + } + hash.merge(where: { status: 2 }) if modal_name == Shixun + + hash + end + + def modal_name + @_modal_name ||= begin + case params[:type].to_s + when 'subject' then Subject + when 'shixun' then Shixun + when 'course' then Course + else Subject + end + end + end +end \ No newline at end of file diff --git a/app/tasks/exercise_publish_task.rb b/app/tasks/exercise_publish_task.rb index a7b533d05..4e34320ea 100644 --- a/app/tasks/exercise_publish_task.rb +++ b/app/tasks/exercise_publish_task.rb @@ -69,7 +69,7 @@ class ExercisePublishTask :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) + exercise_user.update_attributes!(commit_option) end rescue Exception => e Rails.logger.info("rescue errors ___________________________#{e}") diff --git a/app/views/admins/competition_settings/show.html.erb b/app/views/admins/competition_settings/show.html.erb index e69de29bb..6272687e4 100644 --- a/app/views/admins/competition_settings/show.html.erb +++ b/app/views/admins/competition_settings/show.html.erb @@ -0,0 +1,16 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('竞赛列表', admins_competitions_path) + add_admin_breadcrumb(@competition.name) + end +%> + +
+
+ 基础设置 +
+
+ +
+
+ diff --git a/app/views/admins/competitions/index.html.erb b/app/views/admins/competitions/index.html.erb index b97e26f94..41e061fc1 100644 --- a/app/views/admins/competitions/index.html.erb +++ b/app/views/admins/competitions/index.html.erb @@ -2,8 +2,32 @@ <% add_admin_breadcrumb('竞赛列表', admins_competitions_path) %> <% end %> +
+
+
+
+ <% imageExists = File.exist?(disk_filename("Competition", "banner")) %> + <% imageUrl = imageExists ? '/' + banner_img("Competition") + "?#{Time.now.to_i}" : '' %> + 竞赛主页banner + <%= image_tag(imageUrl, width: 150, height: 50, class: "preview-image competition-image-banner mr-1", data: { toggle: 'tooltip', title: '点击预览' }) %> + <%= javascript_void_link imageExists ? '重新上传' : '上传图片', class: 'action upload-competition-image-action', data: { source_id: "banner", source_type: 'Competition', toggle: 'modal', target: '.admin-upload-file-modal' } %> +
+ +
+ +
+
+ + <%= javascript_void_link '新增', class: 'btn btn-primary', data: { toggle: 'modal', target: '.admin-create-competition-modal' } %> +
+
+
<%= render partial: 'admins/competitions/shared/list', locals: { competitions: @competitions } %>
+<%= render 'admins/competitions/shared/create_competition_modal' %> <%= render partial: 'admins/shared/modal/upload_file_modal', locals: { title: '上传图片' } %> \ No newline at end of file diff --git a/app/views/admins/competitions/index.js.erb b/app/views/admins/competitions/index.js.erb new file mode 100644 index 000000000..e0985ce5e --- /dev/null +++ b/app/views/admins/competitions/index.js.erb @@ -0,0 +1 @@ +$('.competitions-list-container').html("<%= j( render partial: 'admins/competitions/shared/list', locals: { competitions: @competitions } ) %>"); \ No newline at end of file diff --git a/app/views/admins/competitions/online_switch.js.erb b/app/views/admins/competitions/online_switch.js.erb index e69de29bb..f67122e46 100644 --- a/app/views/admins/competitions/online_switch.js.erb +++ b/app/views/admins/competitions/online_switch.js.erb @@ -0,0 +1,4 @@ +var page_no = $("#competition-item-<%= @competition.id %>").children(":first").html(); +$("#competition-item-<%= @competition.id %>").html("<%= j render partial: "admins/competitions/shared/td", locals: {competition: @competition, page_no: 1} %>"); +$("#competition-item-<%= @competition.id %>").children(":first").html(page_no); +show_success_flash(); \ No newline at end of file diff --git a/app/views/admins/competitions/publish.js.erb b/app/views/admins/competitions/publish.js.erb index e69de29bb..f67122e46 100644 --- a/app/views/admins/competitions/publish.js.erb +++ b/app/views/admins/competitions/publish.js.erb @@ -0,0 +1,4 @@ +var page_no = $("#competition-item-<%= @competition.id %>").children(":first").html(); +$("#competition-item-<%= @competition.id %>").html("<%= j render partial: "admins/competitions/shared/td", locals: {competition: @competition, page_no: 1} %>"); +$("#competition-item-<%= @competition.id %>").children(":first").html(page_no); +show_success_flash(); \ No newline at end of file diff --git a/app/views/admins/competitions/shared/_create_competition_modal.html.erb b/app/views/admins/competitions/shared/_create_competition_modal.html.erb new file mode 100644 index 000000000..94d12ba32 --- /dev/null +++ b/app/views/admins/competitions/shared/_create_competition_modal.html.erb @@ -0,0 +1,28 @@ + \ No newline at end of file diff --git a/app/views/admins/competitions/shared/_list.html.erb b/app/views/admins/competitions/shared/_list.html.erb index 8f215d54c..6e9620a39 100644 --- a/app/views/admins/competitions/shared/_list.html.erb +++ b/app/views/admins/competitions/shared/_list.html.erb @@ -18,7 +18,7 @@ <% competitions.each_with_index do |competition, index| %> <% page_no = list_index_no(@params_page.to_i, index) %> - <%= render partial: "admins/competitions/shared/td",locals: {competition: competition, page_no: page_no} %> + <%= render partial: "admins/competitions/shared/td", locals: {competition: competition, page_no: page_no} %> <% end %> <% else %> diff --git a/app/views/admins/competitions/shared/_td.html.erb b/app/views/admins/competitions/shared/_td.html.erb index d05974c86..394a41d79 100644 --- a/app/views/admins/competitions/shared/_td.html.erb +++ b/app/views/admins/competitions/shared/_td.html.erb @@ -1,13 +1,13 @@ <%= page_no %> - <%= link_to competition.name, enroll_list_admins_competition_path(competition), :target => "_blank", :title => competition.name %> + <%= link_to competition.name, admins_competition_enroll_lists_path(competition), :title => competition.name %> <%= competition.sub_title %> <%= competition.mode_type %> <%= @member_count_map&.fetch(competition.id, 0) || competition.team_members.count %> <%= competition.teacher_staff_num %> <%= competition.member_staff_num %> - + <% imageExists = File.exist?(disk_filename("Competition", competition.id)) %> <% imageUrl = imageExists ? '/' + url_to_avatar(competition) + "?#{Time.now.to_i}" : '' %> <%= image_tag(imageUrl, width: 60, height: 40, class: "preview-image competition-image-#{competition.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: imageExists ? '' : 'display:none') %> @@ -15,7 +15,7 @@ <%= competition.created_at.strftime('%Y-%m-%d %H:%M') %> - <%= link_to '配置', admins_competition_competition_setting_path(competition), class: 'action edit-action' %> + <%= link_to '配置', admins_competition_competition_settings_path(competition), class: 'action edit-action' %> <% if !competition.status? && competition.published_at.blank? %> <%= link_to '发布', publish_admins_competition_path(competition), class: 'action publish-action', method: :post, remote: true %> diff --git a/app/views/admins/competitions/unpublish.js.erb b/app/views/admins/competitions/unpublish.js.erb index e69de29bb..f67122e46 100644 --- a/app/views/admins/competitions/unpublish.js.erb +++ b/app/views/admins/competitions/unpublish.js.erb @@ -0,0 +1,4 @@ +var page_no = $("#competition-item-<%= @competition.id %>").children(":first").html(); +$("#competition-item-<%= @competition.id %>").html("<%= j render partial: "admins/competitions/shared/td", locals: {competition: @competition, page_no: 1} %>"); +$("#competition-item-<%= @competition.id %>").children(":first").html(page_no); +show_success_flash(); \ No newline at end of file diff --git a/app/views/admins/enroll_lists/_list.html.erb b/app/views/admins/enroll_lists/_list.html.erb new file mode 100644 index 000000000..2b86a3507 --- /dev/null +++ b/app/views/admins/enroll_lists/_list.html.erb @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + <% if enroll_lists.present? %> + <% enroll_lists.each_with_index do |member, index| %> + + <% team = member.competition_team %> + <% page_no = list_index_no(@params_page.to_i, index) %> + + + + + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
序号<%= sort_tag('战队ID', name: 'competition_team_id', path: admins_competition_enroll_lists_path(@competition)) %>战队名称创建者队员姓名职业学号队员学校地区指导老师<%= sort_tag('报名时间', name: 'created_at', path: admins_competition_enroll_lists_path(@competition)) %>
<%= page_no %><%= member.competition_team_id %><%= @personal ? "--" : team.name %><%= team.user.real_name %><%= member.user.real_name %><%= member.user.identity %><%= member.user.student_id %><%= member.user.school_name %><%= member.user.school_province %><%= @personal ? "--" : team.teachers_info %><%= member.created_at.strftime('%Y-%m-%d %H:%M') %>
+ +<%= render partial: 'admins/shared/paginate', locals: { objects: enroll_lists } %> \ No newline at end of file diff --git a/app/views/admins/enroll_lists/index.html.erb b/app/views/admins/enroll_lists/index.html.erb new file mode 100644 index 000000000..95787d54c --- /dev/null +++ b/app/views/admins/enroll_lists/index.html.erb @@ -0,0 +1,33 @@ +<% + define_admin_breadcrumbs do + add_admin_breadcrumb('竞赛列表', admins_competitions_path) + add_admin_breadcrumb(@competition.name) + end +%> + +
+ + +
+ <%= form_tag(admins_competition_enroll_lists_path(unsafe_params), method: :get, class: 'form-inline search-form mt-3 flex-1 d-flex', remote: true) do %> + <%= text_field_tag(:keyword, params[:keyword], class: 'form-control col-sm-2 ml-3', placeholder: '战队名称检索') %> + <%= text_field_tag(:school, params[:school], class: 'form-control col-sm-2 ml-3', placeholder: '队员学校名称检索') %> + <%= text_field_tag(:location, params[:location], class: 'form-control col-sm-2 ml-3', placeholder: '队员地区检索') %> + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + <%= link_to "清除", admins_competition_enroll_lists_path(@competition), class: "btn btn-default",'data-disable-with': '清除中...' %> + <% end %> + + 导出 +
+
+ +
+ <%= render(partial: 'admins/enroll_lists/list', locals: { enroll_lists: @enroll_lists }) %> +
\ No newline at end of file diff --git a/app/views/admins/enroll_lists/index.js.erb b/app/views/admins/enroll_lists/index.js.erb new file mode 100644 index 000000000..18edd97f2 --- /dev/null +++ b/app/views/admins/enroll_lists/index.js.erb @@ -0,0 +1 @@ +$('.competition-enroll-list-container').html("<%= j( render(partial: 'admins/enroll_lists/list', locals: { enroll_lists: @enroll_lists }) ) %>"); \ No newline at end of file diff --git a/app/views/admins/shared/_sidebar.html.erb b/app/views/admins/shared/_sidebar.html.erb index 694e9d1ff..4d6986bc6 100644 --- a/app/views/admins/shared/_sidebar.html.erb +++ b/app/views/admins/shared/_sidebar.html.erb @@ -39,6 +39,7 @@
  • <%= sidebar_item_group('#user-submenu', '用户', icon: 'user') do %>
  • <%= sidebar_item(admins_users_path, '用户列表', icon: 'user', controller: 'admins-users') %>
  • +
  • <%= sidebar_item(admins_user_statistics_path, '用户实训情况', icon: 'area-chart', controller: 'admins-user_statistics') %>
  • <% end %> diff --git a/app/views/admins/shared/modal/_upload_file_modal.html.erb b/app/views/admins/shared/modal/_upload_file_modal.html.erb index 036f1122a..a32058cd3 100644 --- a/app/views/admins/shared/modal/_upload_file_modal.html.erb +++ b/app/views/admins/shared/modal/_upload_file_modal.html.erb @@ -11,6 +11,7 @@
    <%= hidden_field_tag(:source_type, nil) %> <%= hidden_field_tag(:source_id, nil) %> + <%= hidden_field_tag(:suffix, nil) %>
    文件 diff --git a/app/views/admins/shixun_settings/shared/_list.html.erb b/app/views/admins/shixun_settings/shared/_list.html.erb index 0235e5ef8..7591fe323 100644 --- a/app/views/admins/shixun_settings/shared/_list.html.erb +++ b/app/views/admins/shixun_settings/shared/_list.html.erb @@ -1,12 +1,12 @@ - - + + diff --git a/app/views/admins/shixun_settings/shared/_td.html.erb b/app/views/admins/shixun_settings/shared/_td.html.erb index f72dc8a38..ddbbdff02 100644 --- a/app/views/admins/shixun_settings/shared/_td.html.erb +++ b/app/views/admins/shixun_settings/shared/_td.html.erb @@ -1,4 +1,3 @@ - +
    序号 ID 实训名称 技术平台 权限 技术体系上传图片上传图片小程序封面 创建者 关闭 复制<%= page_no %> <%= shixun.identifier %> @@ -21,6 +20,13 @@ <%= image_tag(imageUrl, width: 60, height: 40, class: "preview-image shixun-image-#{shixun.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: imageExists ? '' : 'display:none') %> <%= javascript_void_link imageExists ? '重新上传' : '上传图片', class: 'action upload-shixun-image-action', data: { source_id: shixun.id, source_type: 'Shixun', toggle: 'modal', target: '.admin-upload-file-modal' } %> + <% weappImageExists = Util::FileManage.exists?(shixun, '_weapp') %> + <% imageUrl = weappImageExists ? Util::FileManage.source_disk_file_url(shixun, '_weapp') : '' %> + <%= image_tag(imageUrl, width: 60, height: 40, class: "preview-image shixun-weapp-image-#{shixun.id}", data: { toggle: 'tooltip', title: '点击预览' }, style: weappImageExists ? '' : 'display:none') %> + <%= raw '
    ' if weappImageExists %> + <%= javascript_void_link weappImageExists ? '重新上传' : '上传图片', class: 'action upload-shixun-weapp-image-action', data: { source_id: shixun.id, source_type: 'Shixun', suffix: '_weapp', toggle: 'modal', target: '.admin-upload-file-modal' } %> +
    <%= link_to shixun.owner.try(:real_name),"/users/#{shixun.owner.login}",target:'_blank' %> <% if shixun.status.to_i < 3 %> diff --git a/app/views/admins/user_statistics/export.xlsx.axlsx b/app/views/admins/user_statistics/export.xlsx.axlsx new file mode 100644 index 000000000..b66e62a99 --- /dev/null +++ b/app/views/admins/user_statistics/export.xlsx.axlsx @@ -0,0 +1,16 @@ +wb = xlsx_package.workbook +wb.add_worksheet(name: '用户实训情况') do |sheet| + sheet.add_row %w(姓名 单位部门 学习关卡数 完成关卡数 学习实训数 完成实训数) + + @users.each do |user| + data = [ + user.real_name, + [user.school_name.presence, user.department_name.presence].compact.join(' - '), + user.display_extra_data(:study_challenge_count), + user.display_extra_data(:finish_challenge_count), + user.display_extra_data(:study_shixun_count), + user.display_extra_data(:finish_shixun_count) + ] + sheet.add_row(data) + end +end \ No newline at end of file diff --git a/app/views/admins/user_statistics/index.html.erb b/app/views/admins/user_statistics/index.html.erb new file mode 100644 index 000000000..31f968eab --- /dev/null +++ b/app/views/admins/user_statistics/index.html.erb @@ -0,0 +1,27 @@ +<% define_admin_breadcrumbs do %> + <% add_admin_breadcrumb('用户实训情况') %> +<% end %> + +
    + <%= form_tag(admins_user_statistics_path, method: :get, class: 'form-inline search-form flex-1', remote: true) do %> +
    + + <% data_arrs = [['不限', ''], ['最近一周', 'weekly'], ['最近一个月', 'monthly'], ['最近三个月', 'quarterly'], ['最近一年', 'yearly']] %> + <%= select_tag(:date, options_for_select(data_arrs, params[:date]), class: 'form-control') %> +
    + +
    + + <%= hidden_field_tag(:school_id, params[:school_id]) %> + <%= select_tag :school_name, options_for_select([''], params[:school_id]), class: 'form-control school-select flex-1' %> +
    + + <%= submit_tag('搜索', class: 'btn btn-primary ml-3', 'data-disable-with': '搜索中...') %> + + <% end %> + <%= javascript_void_link '导出', class: 'btn btn-outline-primary export-action', 'data-url': export_admins_user_statistics_path(format: :xlsx) %> +
    + +
    + <%= render partial: 'admins/user_statistics/shared/list', locals: { users: @users } %> +
    \ No newline at end of file diff --git a/app/views/admins/user_statistics/index.js.erb b/app/views/admins/user_statistics/index.js.erb new file mode 100644 index 000000000..c3d2cc9de --- /dev/null +++ b/app/views/admins/user_statistics/index.js.erb @@ -0,0 +1 @@ +$('.user-statistic-list-container').html("<%= j( render partial: 'admins/user_statistics/shared/list', locals: { users: @users } ) %>"); \ No newline at end of file diff --git a/app/views/admins/user_statistics/shared/_list.html.erb b/app/views/admins/user_statistics/shared/_list.html.erb new file mode 100644 index 000000000..1e1b14ea3 --- /dev/null +++ b/app/views/admins/user_statistics/shared/_list.html.erb @@ -0,0 +1,34 @@ + + + + + + + + + + + + + <% if users.present? %> + <% users.each do |user| %> + + + + + + + + + <% end %> + <% else %> + <%= render 'admins/shared/no_data_for_table' %> + <% end %> + +
    姓名单位部门<%= sort_tag('学习关卡数', name: 'study_challenge_count', path: admins_user_statistics_path) %><%= sort_tag('完成关卡数', name: 'finish_challenge_count', path: admins_user_statistics_path) %><%= sort_tag('学习实训数', name: 'study_shixun_count', path: admins_user_statistics_path) %><%= sort_tag('完成实训数', name: 'finish_shixun_count', path: admins_user_statistics_path) %>
    + <%= link_to "/users/#{user.login}", target: '_blank' do %> + <%= overflow_hidden_span user.real_name, width: 100 %> + <% end %> + <%= display_text [user.school_name.presence, user.department_name.presence].compact.join(' - ') %><%= user.display_extra_data(:study_challenge_count) %><%= user.display_extra_data(:finish_challenge_count) %><%= user.display_extra_data(:study_shixun_count) %><%= user.display_extra_data(:finish_shixun_count) %>
    + +<%= render partial: 'admins/shared/paginate', locals: { objects: users } %> \ No newline at end of file diff --git a/app/views/competitions/competition_teams/course_detail.json.jbuilder b/app/views/competitions/competition_teams/course_detail.json.jbuilder new file mode 100644 index 000000000..ce37d9568 --- /dev/null +++ b/app/views/competitions/competition_teams/course_detail.json.jbuilder @@ -0,0 +1,28 @@ +total_students_count = 0 +total_shixun_homework_count = 0 +total_course_score = 0 + +json.courses @courses.each do |course| + students_count = course.students.count + total_students_count += students_count + total_shixun_homework_count += course['shixun_homework_count'].to_i + + score = 500 + 5 * @course_shixun_count_map.fetch(course.id, 0) * @course_myshixun_map.fetch(course.id, 0) + total_course_score += score + + teacher = course.teachers.where(user_id: @team_user_ids).first.user + json.creator teacher&.real_name + json.creator_login teacher&.login + json.course_name course.name + json.course_id course.id + json.students_count students_count + json.shixun_homework_count course['shixun_homework_count'] + json.valid_count @course_myshixun_map.fetch(course.id, 0) + json.score score +end + +json.total_course_count @courses.size +json.total_students_count total_students_count +json.total_shixun_homework_count total_shixun_homework_count +json.total_valid_count @course_myshixun_map.values.reduce(:+) +json.total_course_score total_course_score \ No newline at end of file diff --git a/app/views/competitions/competition_teams/index.json.jbuilder b/app/views/competitions/competition_teams/index.json.jbuilder index b641b28e6..d44fcdf1c 100644 --- a/app/views/competitions/competition_teams/index.json.jbuilder +++ b/app/views/competitions/competition_teams/index.json.jbuilder @@ -1,4 +1,5 @@ json.count @count +json.personal @personal json.competition_teams do json.array! @teams.each do |team| json.extract! team, :id, :name, :invite_code diff --git a/app/views/competitions/competition_teams/shixun_detail.json.jbuilder b/app/views/competitions/competition_teams/shixun_detail.json.jbuilder new file mode 100644 index 000000000..abeb8c85c --- /dev/null +++ b/app/views/competitions/competition_teams/shixun_detail.json.jbuilder @@ -0,0 +1,41 @@ +total_myshixun_count = 0 +total_forked_myshixun_count = 0 +total_shixun_score = 0 + +json.shixuns @shixuns.each do |shixun| + total_myshixun_count += shixun.myshixuns_count + total_forked_myshixun_count += shixun['forked_myshixun_count'].to_i + + valid_course_count = @course_count_map.fetch(shixun.id, 0) + valid_student_count = @original_myshixun_count_map.fetch(shixun.id, 0) + score = + if shixun.fork_from.blank? + 500 + 50 * valid_course_count + 10 * valid_student_count + else + 100 + 10 * valid_course_count + 5 * valid_student_count + end + + @forked_shixun_map.each do |shixun_id, fork_from_id| + next if fork_from_id != shixun.id + + score += 100 + 10 * @forked_map.fetch(shixun_id, 0) + 5 * @forked_myshixun_count_map.fetch(shixun_id, 0) + end + + total_shixun_score += score + + json.creator shixun.user.real_name + json.creator_login shixun.user.login + json.shixun_name shixun.name + json.shixun_identifier shixun.identifier + json.forked shixun.fork_from.present? + json.myshixuns_count shixun.myshixuns_count + json.forked_myshixun_count shixun['forked_myshixun_count'].to_i + json.valid_count @myshixun_count_map.fetch(shixun.id, 0) + json.score score +end + +json.shixun_count @shixuns.size +json.total_myshixun_count total_myshixun_count +json.total_forked_myshixun_count total_forked_myshixun_count +json.total_valid_count @myshixun_count_map.values.reduce(:+) +json.total_shixun_score total_shixun_score \ No newline at end of file diff --git a/app/views/competitions/competitions/chart_list.xlsx.axlsx b/app/views/competitions/competitions/chart_list.xlsx.axlsx new file mode 100644 index 000000000..2186b6ffd --- /dev/null +++ b/app/views/competitions/competitions/chart_list.xlsx.axlsx @@ -0,0 +1,18 @@ + +wb = xlsx_package.workbook +# wb.use_autowidth = false +wb.styles do |s| + sz_all = s.add_style :border => { :style => :thin, :color =>"000000" },:alignment => {:horizontal => :center} + blue_cell = s.add_style :bg_color => "FAEBDC", :sz => 10,:height => 20,:b => true, :border => { :style => :thin, :color =>"000000" },:alignment => {:horizontal => :center} + wb.add_worksheet(:name => "比赛成绩") do |sheet| + sheet.sheet_view.show_grid_lines = false + sheet.add_row table_columns, :style => blue_cell + if chart_lists.count > 0 + chart_lists.each do |user| + sheet.add_row user, :height => 20,:style => sz_all + end #each_widh_index + end + sheet.column_widths *([20]*sheet.column_info.count) + sheet.column_info.first.width = 12 + end #add_worksheet +end \ No newline at end of file diff --git a/app/views/competitions/competitions/chart_rules.json.jbuilder b/app/views/competitions/competitions/chart_rules.json.jbuilder new file mode 100644 index 000000000..bf1d04689 --- /dev/null +++ b/app/views/competitions/competitions/chart_rules.json.jbuilder @@ -0,0 +1,5 @@ +json.rule_contents @rule_contents.each do |rule| + json.(rule, :id, :content, :competition_stage_id) +end + +json.stages chart_stages @competition \ No newline at end of file diff --git a/app/views/competitions/competitions/charts.json.jbuilder b/app/views/competitions/competitions/charts.json.jbuilder new file mode 100644 index 000000000..f59f377f7 --- /dev/null +++ b/app/views/competitions/competitions/charts.json.jbuilder @@ -0,0 +1,21 @@ +json.user_ranks @user_ranks.each do |user_rank| + rank = @records.map(&:id).index(user_rank.id) + rank = rank.present? ? (rank+1) : 0 + json.rank rank == 0 ? "--" : user_rank.rank + json.team_name user_rank.name + json.user_name user_rank.user.real_name + json.cost_time rank == 0 && user_rank.cost_time ? "--" : com_spend_time(user_rank.cost_time) + json.score rank == 0 ? "--" : user_rank.score.round(2) +end + +json.teams @records.each do |record| + record_user = record.user + json.team_name record.name + json.record_user_name record_user.real_name + json.user_image url_to_avatar(record_user) + json.user_login record_user.login + school_name = chart_school_str record.team_members.select{|member| !member.is_teacher}.pluck(:user_id) + json.school_name school_name + json.score record&.score&.round(2) + json.spend_time record.cost_time ? com_spend_time(record.cost_time) : "--" +end \ No newline at end of file diff --git a/app/views/competitions/competitions/common_header.json.jbuilder b/app/views/competitions/competitions/common_header.json.jbuilder new file mode 100644 index 000000000..882d1d1c0 --- /dev/null +++ b/app/views/competitions/competitions/common_header.json.jbuilder @@ -0,0 +1,24 @@ +json.extract! @competition, :id, :name, :sub_title, :identifier, :bonus, :mode +json.visits_count @competition.visits +member_count = @competition.team_members.count +json.member_count member_count.zero? ? 268 : member_count + +json.start_time @competition.start_time&.strftime("%Y-%m-%d") +json.end_time @competition.end_time&.strftime("%Y-%m-%d") +json.enroll_end_time @competition.enroll_end_time&.strftime("%Y-%m-%d %H:%M:%S") + +json.published @competition.published? +json.nearly_published @competition.published_at.present? + +json.competition_modules @competition_modules do |com_module| + json.(com_module, :name, :position) + json.module_url com_module.module_url +end + +json.stages + +if @competition.mode == 1 + json.course_id @competition.competition_mode_setting&.course_id + json.member_of_course @user.member_of_course?(@competition.competition_mode_setting&.course) +end + diff --git a/app/views/admins/competitions/enroll_list.html.erb b/app/views/competitions/competitions/enroll.json.jbuilder similarity index 100% rename from app/views/admins/competitions/enroll_list.html.erb rename to app/views/competitions/competitions/enroll.json.jbuilder diff --git a/app/views/competitions/competitions/index.json.jbuilder b/app/views/competitions/competitions/index.json.jbuilder index 4b8390a7d..8e99d754d 100644 --- a/app/views/competitions/competitions/index.json.jbuilder +++ b/app/views/competitions/competitions/index.json.jbuilder @@ -1,7 +1,7 @@ json.count @count json.competitions do json.array! @competitions.each do |competition| - json.extract! competition, :id, :identifier, :name, :sub_title, :bonus, :description + json.extract! competition, :id, :identifier, :name, :sub_title, :bonus, :description, :mode json.visits_count competition.visits member_count = @member_count_map&.fetch(competition.id, 0) || competition.team_members.count @@ -12,18 +12,18 @@ json.competitions do json.nearly_published competition.published_at.present? json.single_stage (@stage_count_map&.fetch(competition.id, 0) || competition.competition_stages.count) == 1 - json.start_time competition.display_start_time - json.end_time competition.display_end_time - json.enroll_end_time competition.display_enroll_end_time + json.start_time competition.start_time&.strftime("%Y-%m-%d") + json.end_time competition.end_time&.strftime("%Y-%m-%d") + json.enroll_end_time competition.enroll_end_time&.strftime("%Y-%m-%d %H:%M:%S") - section = competition.current_stage_section - if section - json.current_stage do - - json.name section.competition_stage.name - json.start_time section.display_start_time - json.end_time section.display_end_time - end - end + # section = competition.current_stage_section + # if section + # json.current_stage do + # + # json.name section.competition_stage.name + # json.start_time section.display_start_time + # json.end_time section.display_end_time + # end + # end end end \ No newline at end of file diff --git a/app/views/competitions/competitions/informs.json.jbuilder b/app/views/competitions/competitions/informs.json.jbuilder new file mode 100644 index 000000000..1b40c93e7 --- /dev/null +++ b/app/views/competitions/competitions/informs.json.jbuilder @@ -0,0 +1,6 @@ +json.informs @informs.each do |inform| + json.(inform, :id, :name, :description) + json.attachments inform.attachments do |atta| + json.partial! "attachments/attachment_simple", locals: {attachment: atta} + end +end \ No newline at end of file diff --git a/app/views/competitions/competitions/md_content.json.jbuilder b/app/views/competitions/competitions/md_content.json.jbuilder new file mode 100644 index 000000000..45325cecb --- /dev/null +++ b/app/views/competitions/competitions/md_content.json.jbuilder @@ -0,0 +1,4 @@ +json.(@md_content, :id, :name, :content) +json.attachments @md_content.attachments do |atta| + json.partial! "attachments/attachment_simple", locals: {attachment: atta} +end \ No newline at end of file diff --git a/app/views/competitions/competitions/show.json.jbuilder b/app/views/competitions/competitions/show.json.jbuilder index 18216ee92..f96949398 100644 --- a/app/views/competitions/competitions/show.json.jbuilder +++ b/app/views/competitions/competitions/show.json.jbuilder @@ -1,36 +1,2 @@ -competition = current_competition -json.extract! competition, :id, :name, :sub_title, :identifier - -json.start_time competition.display_start_time -json.end_time competition.display_end_time -json.enroll_end_time competition.display_enroll_end_time - -json.images do - json.array! competition.attachments, partial: 'attachments/attachment_simple', as: :attachment -end - -json.competition_stages do - stages = competition.competition_stages.includes(competition_stage_sections: :competition_entries) - json.array! stages.each do |stage| - json.extract! stage, :id, :name - - json.sections do - json.array! stage.competition_stage_sections.each do |section| - json.extract! section, :id, :name - - decorator_section = ActiveDecorator::Decorator.instance.decorate(section) - json.start_time decorator_section.display_start_time - json.end_time decorator_section.display_end_time - - is_start = section.start_time > Time.now - json.entries do - json.array! section.competition_entries.each do |entry| - json.extract! entry, :id, :name - - json.url is_start ? entry.url : '' - end - end - end - end - end -end \ No newline at end of file +json.extract! @competition, :id, :introduction +json.image_url url_to_avatar(@competition) \ No newline at end of file diff --git a/app/views/weapps/homes/show.json.jbuilder b/app/views/weapps/homes/show.json.jbuilder index d2a451030..eb5b238ac 100644 --- a/app/views/weapps/homes/show.json.jbuilder +++ b/app/views/weapps/homes/show.json.jbuilder @@ -6,7 +6,22 @@ json.images do end json.shixuns do - json.partial! 'shixuns/shixun', locals: { shixuns: @shixuns } + json.array! @shixuns do |shixun| + json.id shixun.id + json.identifier shixun.identifier + json.name shixun.name + json.status shixun.status + json.power (current_user.shixun_permission(shixun)) # 现在首页只显示已发布的实训 + # REDO: 局部缓存 + json.tag_name @tag_name_map&.fetch(shixun.id, nil) || shixun.tag_repertoires.first.try(:name) + json.myshixuns_count shixun.myshixuns_count + json.stu_num shixun.myshixuns_count + json.score_info shixun.averge_star + json.challenges_count shixun.challenges_count + #json.exp shixun.all_score + json.level level_to_s(shixun.trainee) + json.pic Util::FileManage.source_disk_file_url(shixun, '_weapp') + end end json.subjects do diff --git a/app/views/weapps/searchs/index.json.jbuilder b/app/views/weapps/searchs/index.json.jbuilder new file mode 100644 index 000000000..25a1a6a34 --- /dev/null +++ b/app/views/weapps/searchs/index.json.jbuilder @@ -0,0 +1,9 @@ +json.count @results.total_count +json.results do + json.array! @results.with_highlights(multiple: true) do |obj, highlights| + json.merge! obj.to_searchable_json + json.type obj.class.name.downcase + + json.title highlights.delete(:name)&.join('...') || obj.searchable_title + end +end \ No newline at end of file diff --git a/config/admins/sidebar.yml b/config/admins/sidebar.yml index 71e77e218..b722f2505 100644 --- a/config/admins/sidebar.yml +++ b/config/admins/sidebar.yml @@ -1,3 +1,4 @@ admins-mirror_scripts: 'admins-mirror_repositories' admins-laboratory_settings: 'admins-laboratories' -admins-carousels: 'admins-laboratories' \ No newline at end of file +admins-carousels: 'admins-laboratories' +admins-competition_settings: 'admins-competitions' \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index c395614c8..6013eaf3f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -784,18 +784,27 @@ Rails.application.routes.draw do resources :repertoires, only: [:index] scope module: :competitions do - resources :competitions, only: [:index, :show] do + resources :competitions, only: [:index, :show, :update] do resources :competition_modules, only: [:index, :show, :update] resource :competition_staff resources :competition_teams, only: [:index, :show] do post :join, on: :collection post :leave, on: :member + get :course_detail, on: :member + get :shixun_detail, on: :member end resources :teachers, only: [:index] resources :students, only: [:index] member do get :common_header + get :informs + post :update_inform + get :md_content + post :update_md_content + get :charts + get :chart_rules + post :update_chart_rules end end end @@ -837,12 +846,16 @@ Rails.application.routes.draw do get '/auth/wechat/callback', to: 'oauth/wechat#create' resource :bind_user, only: [:create] + resources :hot_keywords, only: [:index] + namespace :weapps do resource :home, only: [:show] resource :session, only: [:create] resource :register, only: [:create] resource :code_session, only: [:create] resource :verify, only: [:create] + + resources :searchs, only: [:index] end end @@ -895,7 +908,9 @@ Rails.application.routes.draw do end resource :import_users, only: [:create] resource :import_course_members, only: [:create] - + resources :user_statistics, only: [:index] do + get :export, on: :collection + end resources :library_applies, only: [:index] do member do post :agree @@ -997,14 +1012,18 @@ Rails.application.routes.draw do end end - resources :competitions, only: [:index, :destroy] do + resources :competitions, only: [:index, :destroy, :create] do member do post :publish post :unpublish post :online_switch - get :enroll_list end - resource :competition_setting, only: [:show, :update] + collection do + post :hot_setting + end + + resources :competition_settings, only: [:index, :update] + resources :enroll_lists, only: [:index] end end diff --git a/db/migrate/20191017063840_add_competition_id_to_mode_setting.rb b/db/migrate/20191017063840_add_competition_id_to_mode_setting.rb new file mode 100644 index 000000000..ef68d395b --- /dev/null +++ b/db/migrate/20191017063840_add_competition_id_to_mode_setting.rb @@ -0,0 +1,7 @@ +class AddCompetitionIdToModeSetting < ActiveRecord::Migration[5.2] + def change + add_column :competition_mode_settings, :competition_id, :integer + + add_index :competition_mode_settings, :competition_id + end +end diff --git a/db/migrate/20191017092449_migrate_competition_score.rb b/db/migrate/20191017092449_migrate_competition_score.rb new file mode 100644 index 000000000..095f403c5 --- /dev/null +++ b/db/migrate/20191017092449_migrate_competition_score.rb @@ -0,0 +1,29 @@ +class MigrateCompetitionScore < ActiveRecord::Migration[5.2] + def change + CompetitionModule.where(name: "排行榜", hidden: 0).each do |com_module| + competition = com_module.competition + if competition.present? + puts competition.id + if competition.identifier == 'hn' || competition.identifier == 'gcc-task-2019' + p_rate = 0.2 + f_rate = 0.8 + else + p_rate = 0.15 + f_rate = 0.85 + end + pre_stage = competition.competition_stages.where(:name => "预赛").first + final_stage = competition.competition_stages.where(:name => "决赛").first + + competition.competition_teams.each do |team| + f_score = team.competition_scores.where(:competition_stage_id => final_stage.try(:id)).first + # 预赛记录 + p_score = team.competition_scores.where(:competition_stage_id => pre_stage.try(:id)).first + s_score = (f_score.try(:score).to_f * f_rate + p_score.try(:score).to_f * p_rate).try(:round, 2) + s_spend_time = f_score.try(:cost_time).to_i + p_score.try(:cost_time).to_i + CompetitionScore.create(:user_id => team.user_id, :competition_team_id => team.id, :competition_id => competition.id, + :competition_stage_id => 0, :score => s_score, :cost_time => s_spend_time) + end + end + end + end +end diff --git a/db/migrate/20191018022821_create_competition_awards.rb b/db/migrate/20191018022821_create_competition_awards.rb new file mode 100644 index 000000000..9da6068ce --- /dev/null +++ b/db/migrate/20191018022821_create_competition_awards.rb @@ -0,0 +1,12 @@ +class CreateCompetitionAwards < ActiveRecord::Migration[5.2] + def change + create_table :competition_awards do |t| + t.references :competition, index: true + t.string :name + t.integer :num, default: 0 + t.integer :award_type, default: 0 + + t.timestamps + end + end +end diff --git a/db/migrate/20191018030029_add_rate_to_competition_stage.rb b/db/migrate/20191018030029_add_rate_to_competition_stage.rb new file mode 100644 index 000000000..6e819cc17 --- /dev/null +++ b/db/migrate/20191018030029_add_rate_to_competition_stage.rb @@ -0,0 +1,5 @@ +class AddRateToCompetitionStage < ActiveRecord::Migration[5.2] + def change + add_column :competition_stages, :rate, :float, default: 1.0 + end +end diff --git a/db/migrate/20191019075105_create_module_settings.rb b/db/migrate/20191019075105_create_module_settings.rb new file mode 100644 index 000000000..85e3ddb9f --- /dev/null +++ b/db/migrate/20191019075105_create_module_settings.rb @@ -0,0 +1,12 @@ +class CreateModuleSettings < ActiveRecord::Migration[5.2] + def change + create_table :module_settings do |t| + t.string :module_type + t.string :property + + t.timestamps + end + + add_index :module_settings, :module_type + end +end diff --git a/db/migrate/20191019082903_change_column_competition_stage.rb b/db/migrate/20191019082903_change_column_competition_stage.rb new file mode 100644 index 000000000..a9942ec08 --- /dev/null +++ b/db/migrate/20191019082903_change_column_competition_stage.rb @@ -0,0 +1,5 @@ +class ChangeColumnCompetitionStage < ActiveRecord::Migration[5.2] + def change + rename_column :competition_stages, :rate, :score_rate + end +end diff --git a/db/migrate/20191021141738_migrate_exercise_challenge_score.rb b/db/migrate/20191021141738_migrate_exercise_challenge_score.rb new file mode 100644 index 000000000..6d67398db --- /dev/null +++ b/db/migrate/20191021141738_migrate_exercise_challenge_score.rb @@ -0,0 +1,25 @@ +class MigrateExerciseChallengeScore < ActiveRecord::Migration[5.2] + include ExercisesHelper + def change + exercise = Exercise.find_by(id: 2734) + if exercise + exercise.exercise_users.where("start_at is not null").each do |exercise_user| + if exercise_user.end_at.nil? + calculate_score = calculate_student_score(exercise, exercise_user.user)[:total_score] + subjective_score = exercise_user.subjective_score + total_score_subjective_score = subjective_score < 0.0 ? 0.0 : subjective_score + total_score = calculate_score + total_score_subjective_score + exercise_user.update_attributes!(score:total_score,objective_score:calculate_score,end_at:exercise.end_time,commit_status:1,status:1,commit_method:3) + puts exercise_user.id + else + calculate_score = ExerciseShixunAnswer.where(user_id: exercise_user.user_id, exercise_question_id: exercise.exercise_questions.pluck(:id)).pluck(:score).sum + subjective_score = exercise_user.subjective_score + total_score_subjective_score = subjective_score < 0.0 ? 0.0 : subjective_score + total_score = calculate_score + total_score_subjective_score + exercise_user.update_attributes!(score:total_score,objective_score:calculate_score) if total_score > exercise_user.score + puts exercise_user.id + end + end + end + end +end diff --git a/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json b/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json index 34d5f9d73..81a6274f1 100644 --- a/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json +++ b/public/assets/.sprockets-manifest-4627fa5586ef7fed55ca286af7c028e9.json @@ -1 +1 @@ -{"files":{"admin-cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121.js":{"logical_path":"admin.js","mtime":"2019-09-11T16:20:07+08:00","size":4350881,"digest":"cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121","integrity":"sha256-zZyousyXPOLbrOMMl/bEC8COLC7kSXL2aOc44ZAsASE="},"admin-a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa.css":{"logical_path":"admin.css","mtime":"2019-09-11T16:20:07+08:00","size":773445,"digest":"a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa","integrity":"sha256-obM1bv5Q/0cXzyJHVjm1MzxTVLoD/RB8m3qNSudvR6o="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2019-08-14T17:22:43+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2019-08-14T17:22:43+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2019-08-14T17:22:43+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2019-08-14T17:22:43+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2019-08-14T17:22:43+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js":{"logical_path":"college.js","mtime":"2019-10-17T09:44:58+08:00","size":3352744,"digest":"18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287","integrity":"sha256-GPXoQAMxY06JijWswhh4FcCWwl4Kt0q6NBrpFhZs0oc="},"college-944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9.css":{"logical_path":"college.css","mtime":"2019-09-11T16:20:07+08:00","size":546841,"digest":"944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9","integrity":"sha256-lE1Cc/YsdTg2i5AX/dM4e1476jGoeHN3DrIxMkVG1Nk="},"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png":{"logical_path":"logo.png","mtime":"2019-09-03T08:55:53+08:00","size":2816,"digest":"7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423","integrity":"sha256-f/ESVocJv5f5iY/ockm3qPIA/x9I1TfYWvhyFfGHBCM="},"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js":{"logical_path":"application.js","mtime":"2019-10-17T09:44:58+08:00","size":600706,"digest":"9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb","integrity":"sha256-nPvD15JZmh0N5ce4QgnhwrLmAzbw8B4Z8FgWY5GHCPs="},"application-5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2.css":{"logical_path":"application.css","mtime":"2019-09-11T16:20:07+08:00","size":1844002,"digest":"5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2","integrity":"sha256-Xrh8bhNnbQGDMX3rzhf63ifmjErO4oxBlDjaFdU8lPI="},"admin-c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4.js":{"logical_path":"admin.js","mtime":"2019-09-21T15:28:08+08:00","size":4382031,"digest":"c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4","integrity":"sha256-yeXr5hkVSFUOJ1FBluoSXPu0AoIOwSWgyaz5nS03j+Q="},"admin-59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38.css":{"logical_path":"admin.css","mtime":"2019-09-21T14:49:04+08:00","size":840093,"digest":"59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38","integrity":"sha256-WcWfjK6L70qDWShsmFRYEQydA+oSFRZZXJiJQ/RxfDg="},"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css":{"logical_path":"college.css","mtime":"2019-09-16T13:56:09+08:00","size":579109,"digest":"38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437","integrity":"sha256-OPlT1rpbhdP6tjyzwrvw0FfMxkVNB8+q+sOwbaN7hDc="},"application-646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3.css":{"logical_path":"application.css","mtime":"2019-09-21T14:49:04+08:00","size":1988767,"digest":"646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3","integrity":"sha256-ZGsRWKTowfE+aE1v6QJavHX407pSVuRAgCwDmCIzdPM="},"admin-a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:33:05+08:00","size":4383107,"digest":"a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751","integrity":"sha256-pH43wOx89fIjgCSXdtHoLWW2tqonLtc4kYWqIA+kB1E="},"admin-432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:35:20+08:00","size":4383103,"digest":"432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9","integrity":"sha256-QyxOrAmwNsV/8eiNkCuKp9+BFk5LQZusVXzxNmwdOtk="},"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js":{"logical_path":"admin.js","mtime":"2019-09-30T14:43:41+08:00","size":4387200,"digest":"978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a","integrity":"sha256-l45c5gf3fCaBShdPSA2nmsJGwiAYaO+EZUqgO7Zye1o="},"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css":{"logical_path":"admin.css","mtime":"2019-09-30T14:43:41+08:00","size":842269,"digest":"896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7","integrity":"sha256-iWKB9HMXIrDAhNuxryHQ80pbwULViv9Xs5GGSrcd3Kc="},"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css":{"logical_path":"application.css","mtime":"2019-09-30T14:43:41+08:00","size":1993118,"digest":"97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd","integrity":"sha256-l/MT6bt9JUdmSffXIVlZz0IUgP0KN4XRlWlTv5Sh6L0="},"admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js":{"logical_path":"admin.js","mtime":"2019-10-11T14:38:33+08:00","size":4394616,"digest":"2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888","integrity":"sha256-LNsjRC+nNQJThbiPKQDfBP7zi2FTAEGm2+N17w8K6Ig="},"admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css":{"logical_path":"admin.css","mtime":"2019-10-10T17:12:05+08:00","size":846514,"digest":"2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc","integrity":"sha256-LChUuaAhWN7VqAmq9xRKhjCxA1SrTlb+zE3/zHE3lsw="},"application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css":{"logical_path":"application.css","mtime":"2019-10-10T17:12:05+08:00","size":2001607,"digest":"50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af","integrity":"sha256-UAWa6SmGYEO0cBUShwL8+6U9MqLfFI5k4dlhwQZRxq8="},"admin-992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2.js":{"logical_path":"admin.js","mtime":"2019-10-16T16:11:32+08:00","size":4394790,"digest":"992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2","integrity":"sha256-mSzeCbbRfwCklXauLZ8c7RJyRLpAHvW31nfKuXQWiNI="},"admin-84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257.css":{"logical_path":"admin.css","mtime":"2019-10-16T19:25:40+08:00","size":846676,"digest":"84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257","integrity":"sha256-hPKneR4nXW+CBRQ3Cz+WgXa5lLnde4w7qL9IM2sD8lc="},"application-ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997.css":{"logical_path":"application.css","mtime":"2019-10-16T19:25:40+08:00","size":2001931,"digest":"ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997","integrity":"sha256-72urhIUrqvaakf5q+HW24bEYxVtMfRZWZcSI+sgMSZc="},"admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js":{"logical_path":"admin.js","mtime":"2019-10-17T09:44:58+08:00","size":4394897,"digest":"c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a","integrity":"sha256-yZAw0wVmL3QKqEtskloa27qtqgf9dOJlXmTUS0uX/Eo="},"admin-534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d.css":{"logical_path":"admin.css","mtime":"2019-10-17T10:22:41+08:00","size":846699,"digest":"534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d","integrity":"sha256-U0vehx1n9Nb8jaYRkX14vkBm/HWTulPukqoXBooZnW0="},"cooperative-04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T10:17:56+08:00","size":4330072,"digest":"04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7","integrity":"sha256-BM1qYNQSINOO5FzkCx0ATh0LzYfBMvsae6thRMHeuNc="},"cooperative-a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T10:21:41+08:00","size":830628,"digest":"a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3","integrity":"sha256-o0W7/Y44twyShezBdHAS/83kKRh5g+KupWV6u1a5tPM="},"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css":{"logical_path":"application.css","mtime":"2019-09-03T08:55:53+08:00","size":442932,"digest":"0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8","integrity":"sha256-DkF0eNVvQkZ+hXzRhrKcu8DWx8boXIpvQvOaxhiUPeg="},"cooperative-149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T14:03:03+08:00","size":4338033,"digest":"149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b","integrity":"sha256-FJ9HuGddYKgBTM/1DwD5Mv9p4r4ob/t0NDvEo+/7E1s="},"cooperative-6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T11:13:07+08:00","size":832914,"digest":"6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8","integrity":"sha256-YnO3ZtbvEd1WF02Gi6tV5/F68XVGyIjSug3Qprzadsg="}},"assets":{"admin.js":"admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js","admin.css":"admin-534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js","college.css":"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css","cooperative.js":"cooperative-149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b.js","cooperative.css":"cooperative-6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8.css"}} \ No newline at end of file +{"files":{"admin-cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121.js":{"logical_path":"admin.js","mtime":"2019-09-11T16:20:07+08:00","size":4350881,"digest":"cd9ca8bacc973ce2dbace30c97f6c40bc08e2c2ee44972f668e738e1902c0121","integrity":"sha256-zZyousyXPOLbrOMMl/bEC8COLC7kSXL2aOc44ZAsASE="},"admin-a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa.css":{"logical_path":"admin.css","mtime":"2019-09-11T16:20:07+08:00","size":773445,"digest":"a1b3356efe50ff4717cf22475639b5333c5354ba03fd107c9b7a8d4ae76f47aa","integrity":"sha256-obM1bv5Q/0cXzyJHVjm1MzxTVLoD/RB8m3qNSudvR6o="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2019-08-14T17:22:43+08:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2019-08-14T17:22:43+08:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2019-08-14T17:22:43+08:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2019-08-14T17:22:43+08:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2019-08-14T17:22:43+08:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js":{"logical_path":"college.js","mtime":"2019-10-17T09:44:58+08:00","size":3352744,"digest":"18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287","integrity":"sha256-GPXoQAMxY06JijWswhh4FcCWwl4Kt0q6NBrpFhZs0oc="},"college-944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9.css":{"logical_path":"college.css","mtime":"2019-09-11T16:20:07+08:00","size":546841,"digest":"944d4273f62c7538368b9017fdd3387b5e3bea31a87873770eb231324546d4d9","integrity":"sha256-lE1Cc/YsdTg2i5AX/dM4e1476jGoeHN3DrIxMkVG1Nk="},"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png":{"logical_path":"logo.png","mtime":"2019-09-03T08:55:53+08:00","size":2816,"digest":"7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423","integrity":"sha256-f/ESVocJv5f5iY/ockm3qPIA/x9I1TfYWvhyFfGHBCM="},"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js":{"logical_path":"application.js","mtime":"2019-10-17T09:44:58+08:00","size":600706,"digest":"9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb","integrity":"sha256-nPvD15JZmh0N5ce4QgnhwrLmAzbw8B4Z8FgWY5GHCPs="},"application-5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2.css":{"logical_path":"application.css","mtime":"2019-09-11T16:20:07+08:00","size":1844002,"digest":"5eb87c6e13676d0183317debce17fade27e68c4acee28c419438da15d53c94f2","integrity":"sha256-Xrh8bhNnbQGDMX3rzhf63ifmjErO4oxBlDjaFdU8lPI="},"admin-c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4.js":{"logical_path":"admin.js","mtime":"2019-09-21T15:28:08+08:00","size":4382031,"digest":"c9e5ebe6191548550e27514196ea125cfbb402820ec125a0c9acf99d2d378fe4","integrity":"sha256-yeXr5hkVSFUOJ1FBluoSXPu0AoIOwSWgyaz5nS03j+Q="},"admin-59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38.css":{"logical_path":"admin.css","mtime":"2019-09-21T14:49:04+08:00","size":840093,"digest":"59c59f8cae8bef4a8359286c985458110c9d03ea121516595c988943f4717c38","integrity":"sha256-WcWfjK6L70qDWShsmFRYEQydA+oSFRZZXJiJQ/RxfDg="},"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css":{"logical_path":"college.css","mtime":"2019-09-16T13:56:09+08:00","size":579109,"digest":"38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437","integrity":"sha256-OPlT1rpbhdP6tjyzwrvw0FfMxkVNB8+q+sOwbaN7hDc="},"application-646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3.css":{"logical_path":"application.css","mtime":"2019-09-21T14:49:04+08:00","size":1988767,"digest":"646b1158a4e8c1f13e684d6fe9025abc75f8d3ba5256e440802c0398223374f3","integrity":"sha256-ZGsRWKTowfE+aE1v6QJavHX407pSVuRAgCwDmCIzdPM="},"admin-a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:33:05+08:00","size":4383107,"digest":"a47e37c0ec7cf5f22380249776d1e82d65b6b6aa272ed7389185aa200fa40751","integrity":"sha256-pH43wOx89fIjgCSXdtHoLWW2tqonLtc4kYWqIA+kB1E="},"admin-432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9.js":{"logical_path":"admin.js","mtime":"2019-09-25T15:35:20+08:00","size":4383103,"digest":"432c4eac09b036c57ff1e88d902b8aa7df81164e4b419bac557cf1366c1d3ad9","integrity":"sha256-QyxOrAmwNsV/8eiNkCuKp9+BFk5LQZusVXzxNmwdOtk="},"admin-978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a.js":{"logical_path":"admin.js","mtime":"2019-09-30T14:43:41+08:00","size":4387200,"digest":"978e5ce607f77c26814a174f480da79ac246c2201868ef84654aa03bb6727b5a","integrity":"sha256-l45c5gf3fCaBShdPSA2nmsJGwiAYaO+EZUqgO7Zye1o="},"admin-896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7.css":{"logical_path":"admin.css","mtime":"2019-09-30T14:43:41+08:00","size":842269,"digest":"896281f4731722b0c084dbb1af21d0f34a5bc142d58aff57b391864ab71ddca7","integrity":"sha256-iWKB9HMXIrDAhNuxryHQ80pbwULViv9Xs5GGSrcd3Kc="},"application-97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd.css":{"logical_path":"application.css","mtime":"2019-09-30T14:43:41+08:00","size":1993118,"digest":"97f313e9bb7d25476649f7d7215959cf421480fd0a3785d1956953bf94a1e8bd","integrity":"sha256-l/MT6bt9JUdmSffXIVlZz0IUgP0KN4XRlWlTv5Sh6L0="},"admin-2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888.js":{"logical_path":"admin.js","mtime":"2019-10-11T14:38:33+08:00","size":4394616,"digest":"2cdb23442fa735025385b88f2900df04fef38b61530041a6dbe375ef0f0ae888","integrity":"sha256-LNsjRC+nNQJThbiPKQDfBP7zi2FTAEGm2+N17w8K6Ig="},"admin-2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc.css":{"logical_path":"admin.css","mtime":"2019-10-10T17:12:05+08:00","size":846514,"digest":"2c2854b9a02158ded5a809aaf7144a8630b10354ab4e56fecc4dffcc713796cc","integrity":"sha256-LChUuaAhWN7VqAmq9xRKhjCxA1SrTlb+zE3/zHE3lsw="},"application-50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af.css":{"logical_path":"application.css","mtime":"2019-10-10T17:12:05+08:00","size":2001607,"digest":"50059ae929866043b47015128702fcfba53d32a2df148e64e1d961c10651c6af","integrity":"sha256-UAWa6SmGYEO0cBUShwL8+6U9MqLfFI5k4dlhwQZRxq8="},"admin-992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2.js":{"logical_path":"admin.js","mtime":"2019-10-16T16:11:32+08:00","size":4394790,"digest":"992cde09b6d17f00a49576ae2d9f1ced127244ba401ef5b7d677cab9741688d2","integrity":"sha256-mSzeCbbRfwCklXauLZ8c7RJyRLpAHvW31nfKuXQWiNI="},"admin-84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257.css":{"logical_path":"admin.css","mtime":"2019-10-16T19:25:40+08:00","size":846676,"digest":"84f2a7791e275d6f820514370b3f968176b994b9dd7b8c3ba8bf48336b03f257","integrity":"sha256-hPKneR4nXW+CBRQ3Cz+WgXa5lLnde4w7qL9IM2sD8lc="},"application-ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997.css":{"logical_path":"application.css","mtime":"2019-10-16T19:25:40+08:00","size":2001931,"digest":"ef6bab84852baaf69a91fe6af875b6e1b118c55b4c7d165665c488fac80c4997","integrity":"sha256-72urhIUrqvaakf5q+HW24bEYxVtMfRZWZcSI+sgMSZc="},"admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js":{"logical_path":"admin.js","mtime":"2019-10-17T09:44:58+08:00","size":4394897,"digest":"c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a","integrity":"sha256-yZAw0wVmL3QKqEtskloa27qtqgf9dOJlXmTUS0uX/Eo="},"admin-534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d.css":{"logical_path":"admin.css","mtime":"2019-10-17T10:22:41+08:00","size":846699,"digest":"534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d","integrity":"sha256-U0vehx1n9Nb8jaYRkX14vkBm/HWTulPukqoXBooZnW0="},"cooperative-04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T10:17:56+08:00","size":4330072,"digest":"04cd6a60d41220d38ee45ce40b1d004e1d0bcd87c132fb1a7bab6144c1deb8d7","integrity":"sha256-BM1qYNQSINOO5FzkCx0ATh0LzYfBMvsae6thRMHeuNc="},"cooperative-a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T10:21:41+08:00","size":830628,"digest":"a345bbfd8e38b70c9285ecc1747012ffcde429187983e2aea5657abb56b9b4f3","integrity":"sha256-o0W7/Y44twyShezBdHAS/83kKRh5g+KupWV6u1a5tPM="},"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css":{"logical_path":"application.css","mtime":"2019-09-03T08:55:53+08:00","size":442932,"digest":"0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8","integrity":"sha256-DkF0eNVvQkZ+hXzRhrKcu8DWx8boXIpvQvOaxhiUPeg="},"cooperative-149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b.js":{"logical_path":"cooperative.js","mtime":"2019-10-17T14:03:03+08:00","size":4338033,"digest":"149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b","integrity":"sha256-FJ9HuGddYKgBTM/1DwD5Mv9p4r4ob/t0NDvEo+/7E1s="},"cooperative-6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8.css":{"logical_path":"cooperative.css","mtime":"2019-10-17T11:13:07+08:00","size":832914,"digest":"6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8","integrity":"sha256-YnO3ZtbvEd1WF02Gi6tV5/F68XVGyIjSug3Qprzadsg="},"admin-82f66cc80b5649c6530a562567f28fe8d05f7bc3b8221e0695b2216255c52ba6.js":{"logical_path":"admin.js","mtime":"2019-10-21T13:51:43+08:00","size":4397012,"digest":"82f66cc80b5649c6530a562567f28fe8d05f7bc3b8221e0695b2216255c52ba6","integrity":"sha256-gvZsyAtWScZTClYlZ/KP6NBfe8O4Ih4GlbIhYlXFK6Y="},"admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js":{"logical_path":"admin.js","mtime":"2019-10-21T16:41:06+08:00","size":4397437,"digest":"1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec","integrity":"sha256-G1co2U9rzPvO9FKnYNlMO28xlmvGXX+Jvgd/wupRK+w="}},"assets":{"admin.js":"admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js","admin.css":"admin-534bde871d67f4d6fc8da611917d78be4066fc7593ba53ee92aa17068a199d6d.css","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","college.js":"college-18f5e8400331634e898a35acc2187815c096c25e0ab74aba341ae916166cd287.js","college.css":"college-38f953d6ba5b85d3fab63cb3c2bbf0d057ccc6454d07cfaafac3b06da37b8437.css","logo.png":"logo-7ff112568709bf97f9898fe87249b7a8f200ff1f48d537d85af87215f1870423.png","application.js":"application-9cfbc3d792599a1d0de5c7b84209e1c2b2e60336f0f01e19f0581663918708fb.js","application.css":"application-0e417478d56f42467e857cd186b29cbbc0d6c7c6e85c8a6f42f39ac618943de8.css","cooperative.js":"cooperative-149f47b8675d60a8014ccff50f00f932ff69e2be286ffb74343bc4a3effb135b.js","cooperative.css":"cooperative-6273b766d6ef11dd56174d868bab55e7f17af17546c888d2ba0dd0a6bcda76c8.css"}} \ No newline at end of file diff --git a/public/assets/admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js b/public/assets/admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js similarity index 99% rename from public/assets/admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js rename to public/assets/admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js index 72a1ca344..1c9d07222 100644 --- a/public/assets/admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js +++ b/public/assets/admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js @@ -134049,6 +134049,17 @@ $(document).on('turbolinks:load', function() { }); } }); +$(document).on('turbolinks:load', function() { + if ($('body.admins-competitions-index-page').length > 0) { + $('.modal.admin-upload-file-modal').on('upload:success', function(e, data){ + var $imageElement = $('.competition-image-' + data.source_id); + $imageElement.attr('src', data.url); + $imageElement.show(); + $imageElement.next().html('重新上传'); + }) + } +}); + $(document).on('turbolinks:load', function() { if ($('body.admins-cooperatives-index-page').length > 0) { // ------------ 保存链接 ----------- @@ -135645,6 +135656,79 @@ $(document).on('turbolinks:load', function() { } }) ; +$(document).on('turbolinks:load', function() { + if ($('body.admins-user-statistics-index-page').length > 0) { + var $form = $('.user-statistic-list-form'); + + // ************** 学校选择 ************* + 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) { + $form.find('.school-select').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) { + $form.find('#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); + } + }); + + // 清空 + $form.on('click', '.clear-btn', function(){ + $form.find('select[name="date"]').val(''); + $form.find('.school-select').val('').trigger('change'); + $form.find('input[type="submit"]').trigger('click'); + }) + + + // 导出 + $('.export-action').on('click', function(){ + var form = $(".user-statistic-list-form .search-form") + var exportLink = $(this); + var date = form.find("select[name='date']").val(); + var schoolId = form.find('input[name="school_id"]').val(); + + var url = exportLink.data("url").split('?')[0] + "?date=" + date + "&school_id=" + schoolId; + window.open(url); + }); + } +}); $(document).on('turbolinks:load', function() { if ($('body.admins-users-edit-page, body.admins-users-update-page').length > 0) { var initDepartmentSelect = true; diff --git a/public/assets/admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js.gz b/public/assets/admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js.gz similarity index 98% rename from public/assets/admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js.gz rename to public/assets/admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js.gz index f24a42fc6..519d4b32f 100644 Binary files a/public/assets/admin-c99030d305662f740aa84b6c925a1adbbaadaa07fd74e2655e64d44b4b97fc4a.js.gz and b/public/assets/admin-1b5728d94f6bccfbcef452a760d94c3b6f31966bc65d7f89be077fc2ea512bec.js.gz differ diff --git a/public/react/src/AppConfig.js b/public/react/src/AppConfig.js index 9cf68503d..793542f4b 100644 --- a/public/react/src/AppConfig.js +++ b/public/react/src/AppConfig.js @@ -47,7 +47,8 @@ export function initAxiosInterceptors(props) { proxy="https://pre-newweb.educoder.net" proxy="https://test-newweb.educoder.net" - // 在这里使用requestMap控制,避免用户通过双击等操作发出重复的请求; + + // 在这里使用requestMap控制,避免用户通过双击等操作发出重复的请求; // 如果需要支持重复的请求,考虑config里面自定义一个allowRepeat参考来控制 const requestMap = {}; diff --git a/public/react/src/context/TPIContextProvider.js b/public/react/src/context/TPIContextProvider.js index ee01c3c72..bcd470134 100644 --- a/public/react/src/context/TPIContextProvider.js +++ b/public/react/src/context/TPIContextProvider.js @@ -435,6 +435,9 @@ pop_box_new(htmlvalue, 480, 182); // TODO 测试 // resData.power = 0; + resData.shixun.vnc = !!resData.vnc_url + resData.shixun.vnc_evaluate = resData.vnc_evaluate + this.setState({ ...resData, diff --git a/public/react/src/modules/courses/poll/PollDetailTabSecond.js b/public/react/src/modules/courses/poll/PollDetailTabSecond.js index cebae4153..bf8d8b5f8 100644 --- a/public/react/src/modules/courses/poll/PollDetailTabSecond.js +++ b/public/react/src/modules/courses/poll/PollDetailTabSecond.js @@ -25,6 +25,7 @@ class PollDetailTabSecond extends Component{ axios.get(url).then((result)=>{ if(result){ this.setState({ + page: page, questions:result.data.questions, questionsInfo:result.data.question_types }) @@ -55,7 +56,7 @@ class PollDetailTabSecond extends Component{ } render(){ - let{page,limit,questions,question_types}=this.state; + let {page, limit, questions, questionsInfo} = this.state; return(
    { @@ -160,9 +161,10 @@ class PollDetailTabSecond extends Component{ }): } { - question_types && question_types.q_counts>limit && + questionsInfo && questionsInfo.q_counts > limit &&
    - +
    } diff --git a/public/react/src/modules/page/VNCContainer.js b/public/react/src/modules/page/VNCContainer.js index 6ef10888a..4a591f4ac 100644 --- a/public/react/src/modules/page/VNCContainer.js +++ b/public/react/src/modules/page/VNCContainer.js @@ -24,7 +24,7 @@ class VNCContainer extends Component { repositoryCode: '', displayKey: 1, vnc_reseting: false, - + saving: false, } } componentDidMount() { @@ -40,27 +40,61 @@ class VNCContainer extends Component { getSecondDrawerWidth = () => { return $('#game_right_contents').width() - firstDrawerWidth } + onEditBlur = () => { + console.log('blurblur') + this.doFileUpdateRequestOnCodeMirrorBlur() + } + doFileUpdateRequestOnCodeMirrorBlur = () => { + if (!this.currentPath) { + console.error('未找到文件path') + return; + } + const { myshixun, game } = this.props + var url = `/myshixuns/${myshixun.identifier}/update_file.json` + const codeContent = window.editor_monaco.getValue() + + this.setState({saving: true}) + axios.post(url, { + content: codeContent, + // 评测的时候传1,其它情况不用传,主要是为了区分是用户自己提交还是自动提交 + // evaluate: 0, + game_id : game.id, + path: this.currentPath + } + ).then(res => { + this.setState({saving: false}) + }).catch(e => { + this.setState({saving: false}) + console.error('update_file error') + }) + } renderSecondDrawerChildren = () => { - const { readingCodeLoading, repositoryCode } = this.state; + const { readingCodeLoading, repositoryCode, saving } = this.state; + const { shixun } = this.props const height = $(window).height() - 130 const isEditablePath = false; return ( - +
    + {/* (isEditablePath ? 'none' : 'block') */}
    + style={{ backgroundImage: `url('${notEditablePathImg}')`, display: (shixun.code_edit_permission ? 'none' : 'block') }}>
    ); } fetchReadRepositoryCode = (path) => { + this.currentPath = path; const status = 1 const fetchRepoCodeUrl = `/tasks/${this.props.game.identifier}/rep_content.json?path=${path}&status=${status}` this.setState({ readingCodeLoading: true }); diff --git a/public/react/src/modules/page/component/monaco/TPIMonaco.js b/public/react/src/modules/page/component/monaco/TPIMonaco.js index 0d5eb2d34..b814ecb44 100644 --- a/public/react/src/modules/page/component/monaco/TPIMonaco.js +++ b/public/react/src/modules/page/component/monaco/TPIMonaco.js @@ -347,6 +347,9 @@ class TPIMonaco extends Component { this.props.doFileUpdateRequestOnCodeMirrorBlur(); return false; }); + window.editor_monaco.onDidBlurEditorWidget(() => { + this.props.onEditBlur && this.props.onEditBlur(); + }) }) // window.document.onkeydown = (e) => { diff --git a/public/react/src/modules/tpm/challengesnew/TPMquestion.js b/public/react/src/modules/tpm/challengesnew/TPMquestion.js index 8fc2b1133..b76e17b17 100644 --- a/public/react/src/modules/tpm/challengesnew/TPMquestion.js +++ b/public/react/src/modules/tpm/challengesnew/TPMquestion.js @@ -698,7 +698,7 @@ export default class TPMquestion extends Component { let newnewanswerMDvalue = this.editanswersRef.current.getValue().trim(); console.log(newnewanswerMDvalue) if(newnewanswerMDvalue===""||newnewanswerMDvalue===" "){ - newnewanswerMDvalue=newlist + newnewanswerMDvalue=undefined } url="/shixuns/" + id + "/challenges/" + challenge_id + "/update_choose_question.json?choose_id="+challenge_choose_id; @@ -747,7 +747,7 @@ export default class TPMquestion extends Component { let newnewanswerMDvalue = this.newquestioMDMdCont.current.getValue().trim(); if(newnewanswerMDvalue===""||newnewanswerMDvalue===" "){ - newnewanswerMDvalue=newlist + newnewanswerMDvalue=undefined } url="/shixuns/" + id + "/challenges/" + challenge_id + "/create_choose_question.json"; axios.post(url, { diff --git a/spec/models/chart_rule_spec.rb b/spec/models/chart_rule_spec.rb new file mode 100644 index 000000000..b3059c94d --- /dev/null +++ b/spec/models/chart_rule_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ChartRule, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/competition_award_spec.rb b/spec/models/competition_award_spec.rb new file mode 100644 index 000000000..dac3bddcf --- /dev/null +++ b/spec/models/competition_award_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe CompetitionAward, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/competition_score_spec.rb b/spec/models/competition_score_spec.rb new file mode 100644 index 000000000..d0f1372b1 --- /dev/null +++ b/spec/models/competition_score_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe CompetitionScore, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/module_setting_spec.rb b/spec/models/module_setting_spec.rb new file mode 100644 index 000000000..ede893338 --- /dev/null +++ b/spec/models/module_setting_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ModuleSetting, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end