diff --git a/app/controllers/course_modules_controller.rb b/app/controllers/course_modules_controller.rb index bea248b52..821ee81a5 100644 --- a/app/controllers/course_modules_controller.rb +++ b/app/controllers/course_modules_controller.rb @@ -50,11 +50,16 @@ class CourseModulesController < ApplicationController # 添加二级目录 def add_second_category tip_exception("子目录名称不能为空") if params[:name].blank? - tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(name: params[:name].strip) + parent_id = params[:parent_id].to_i + if parent_id != 0 + parent_node = @course_module.course_second_categories.find_by(id: parent_id) + tip_exception("上级目录不存在") if parent_node.blank? + end + tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(parent_id: parent_id, name: params[:name].strip) ActiveRecord::Base.transaction do begin category = @course_module.course_second_categories.create!(name: params[:name].strip, category_type: @course_module.module_type, - course_id: @course.id, position: @course_module.course_second_categories.count + 1) + course_id: @course.id, position: @course_module.course_second_categories.where(parent_id: parent_id).count + 1, parent_id: parent_id) render :json => {category_id: category.id, status: 0, message: "添加成功"} rescue Exception => e uid_logger_error(e.message) diff --git a/app/controllers/course_second_categories_controller.rb b/app/controllers/course_second_categories_controller.rb index c59ffbdbe..45d3804d3 100644 --- a/app/controllers/course_second_categories_controller.rb +++ b/app/controllers/course_second_categories_controller.rb @@ -7,7 +7,7 @@ class CourseSecondCategoriesController < ApplicationController def rename_category tip_exception("毕设子目录不能重命名") if @category.category_type == "graduation" tip_exception("名称不能为空") if params[:name].blank? - tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(name: params[:name].strip) + tip_exception("已存在同名子目录") if @course_module.course_second_categories.exists?(parent_id: @category.parent_id, name: params[:name].strip) @category.update_attributes!(name: params[:name].strip) normal_status(0, "更新成功") end @@ -17,9 +17,13 @@ class CourseSecondCategoriesController < ApplicationController tip_exception("移动失败") if params[:position].blank? unless params[:position].to_i == @category.position if params[:position].to_i < @category.position - @course_module.course_second_categories.where("position < #{@category.position} and position >= ?", params[:position]).update_all("position = position + 1") + @course_module.course_second_categories + .where("parent_id = #{@category.parent_id} and position < #{@category.position} and position >= ?", params[:position]) + .update_all("position = position + 1") else - @course_module.course_second_categories.where("position > #{@category.position} and position <= ?", params[:position]).update_all("position = position - 1") + @course_module.course_second_categories + .where("parent_id = #{@category.parent_id} and position > #{@category.position} and position <= ?", params[:position]) + .update_all("position = position - 1") end @category.update!(position: params[:position]) normal_status(0, "移动成功") @@ -32,22 +36,30 @@ class CourseSecondCategoriesController < ApplicationController tip_exception("毕设子目录不能删除") if @category.category_type == "graduation" ActiveRecord::Base.transaction do begin - @course_module.course_second_categories.where("position > #{@category.position}").update_all("position = position - 1") + parent_id = @category.parent_id + @course_module.course_second_categories.where("parent_id = #{parent_id} and position > #{@category.position}") + .update_all("position = position - 1") + + # 更新相应对象的子目录id if @course_module.module_type == "shixun_homework" - @category.homework_commons.update_all(course_second_category_id: 0) + @category.homework_commons.update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/shixun_homeworks/#{@course_module.id}" elsif @course_module.module_type == "attachment" - Attachment.where(course_second_category_id: @category.id).update_all(course_second_category_id: 0) - @right_url = "/classrooms/#{@course.id}/files/#{@course_module.id}" + Attachment.where(course_second_category_id: @category.id).update_all(course_second_category_id: parent_id) + if parent_id == 0 + @right_url = "/classrooms/#{@course.id}/files/#{@course_module.id}" + else + @right_url = "/classrooms/#{@course.id}/file/#{parent_id}" + end elsif @course_module.module_type == "video" - @course.course_videos.where(course_second_category_id: @category.id).update_all(course_second_category_id: 0) + @course.course_videos.where(course_second_category_id: @category.id).update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/course_videos" elsif @course_module.module_type == "common_homework" - @category.homework_commons.update_all(course_second_category_id: 0) + @category.homework_commons.update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/common_homeworks/#{@course_module.id}" elsif @course_module.module_type == "group_homework" - @category.homework_commons.update_all(course_second_category_id: 0) + @category.homework_commons.update_all(course_second_category_id: parent_id) @right_url = "/classrooms/#{@course.id}/group_homeworks/#{@course_module.id}" end diff --git a/app/controllers/courses_controller.rb b/app/controllers/courses_controller.rb index 68211d034..124fc1094 100644 --- a/app/controllers/courses_controller.rb +++ b/app/controllers/courses_controller.rb @@ -1297,7 +1297,7 @@ class CoursesController < ApplicationController def left_banner @user = current_user @is_teacher = @user_course_identity < Course::ASSISTANT_PROFESSOR - @course_modules = @course.course_modules.where(hidden: 0) + @course_modules = @course.course_modules.where(hidden: 0).includes(first_categories: :children) @hidden_modules = @course.course_modules.where(hidden: 1) @second_category_type = ["shixun_homework", "graduation", "attachment", "board", "course_group", "video", "common_homework", "group_homework"] end diff --git a/app/controllers/files_controller.rb b/app/controllers/files_controller.rb index 03e6dad24..6a7f7b4b0 100644 --- a/app/controllers/files_controller.rb +++ b/app/controllers/files_controller.rb @@ -18,11 +18,23 @@ class FilesController < ApplicationController sort_type = params[:sort_type] || 'created_on' # created_on:时间排序, downloads:下载次数排序; quotes: 引用次数排序 @course_second_category_id = params[:course_second_category_id] || 0 # 0: 为主目录, 其他为次目录id @user = current_user - @attachments = @course_second_category_id.to_i == 0 ? @course.attachments.includes(:course_second_category) : @course.attachments.by_course_second_category_id(@course_second_category_id) + get_category(@course, @course_second_category_id) + + # 主目录显示所有资源,一级目录显示一级和所属二级目录的资源,二级目录只显示该目录下的资源 + if @course_second_category_id.to_i == 0 + @attachments = @course.attachments.includes(:course_second_category) + else + if @parent_category_id == 0 + category_ids = [@category_id] + @category.children.pluck(:id) + @attachments = @course.attachments.where(course_second_category_id: category_ids) + else + @attachments = @course.attachments.by_course_second_category_id(@course_second_category_id) + end + end + @attachments = @attachments.includes(author: [:user_extension, :course_members]) .ordered(sort: sort.to_i, sort_type: sort_type.strip) - get_category(@course, @course_second_category_id) @total_count = @attachments.size @unlink_count = @attachments.no_link.size @@ -354,9 +366,10 @@ class FilesController < ApplicationController @category_id = category.try(:id) @category_name = category.try(:module_name) else - category = CourseSecondCategory.find category_id + @category = CourseSecondCategory.find category_id @category_id = category.try(:id) @category_name = category.try(:name) + @parent_category_id = category&.parent_id.to_i end end diff --git a/app/models/course_module.rb b/app/models/course_module.rb index 32a6a7794..fec22b697 100644 --- a/app/models/course_module.rb +++ b/app/models/course_module.rb @@ -4,6 +4,7 @@ class CourseModule < ApplicationRecord # 二级目录 has_many :course_second_categories + has_many :first_categories, -> { first_categories }, class_name: "CourseSecondCategory" validates :module_name, length: { maximum: 20, too_long: "不能超过20个字符" } @@ -17,12 +18,14 @@ class CourseModule < ApplicationRecord scope :shixun_homework_module, -> { where(module_type: 'shixun_homework') } scope :search_by_module_type, -> (type) {where(module_type:type)} - # 课堂模块的子目录 - def course_second_categories + after_create :create_graduation_module + + private + + def create_graduation_module if module_type == "graduation" && CourseSecondCategory.where(course_module_id: self.id).count == 0 CourseSecondCategory.create!(course_module_id: self.id, course_id: self.course_id, name: "毕设选题", category_type: "graduation", position: 1) CourseSecondCategory.create!(course_module_id: self.id, course_id: self.course_id, name: "毕设任务", category_type: "graduation", position: 2) end - CourseSecondCategory.where(course_module_id: self.id) end end diff --git a/app/models/course_second_category.rb b/app/models/course_second_category.rb index 84b47e27f..3306cd003 100644 --- a/app/models/course_second_category.rb +++ b/app/models/course_second_category.rb @@ -3,7 +3,12 @@ class CourseSecondCategory < ApplicationRecord belongs_to :course belongs_to :course_module + belongs_to :parent, class_name: "CourseSecondCategory", foreign_key: "parent_id", optional: true + has_many :homework_commons + has_many :children, -> { order(position: :asc ) }, class_name: "CourseSecondCategory", foreign_key: "parent_id", dependent: :destroy + + scope :first_categories, -> { where(parent_id: 0) } validates :name, length: { maximum: 60, too_long: "不能超过60个字符" } diff --git a/app/views/course_modules/show.json.jbuilder b/app/views/course_modules/show.json.jbuilder index 9d70797b8..e5a86c42f 100644 --- a/app/views/course_modules/show.json.jbuilder +++ b/app/views/course_modules/show.json.jbuilder @@ -2,7 +2,10 @@ json.course_module do json.id @course_module.id json.module_name @course_module.module_name json.module_type @course_module.module_type - json.course_second_categories do - json.array! @course_module.course_second_categories, :id, :name + json.course_second_categories @course_module.first_categories do |category| + json.(category, :id, :name) + json.course_third_categories category.children do |child| + json.(child, :id, :name) + end end end \ No newline at end of file diff --git a/app/views/courses/_category_info.json.jbuilder b/app/views/courses/_category_info.json.jbuilder new file mode 100644 index 000000000..3ecc5cdc6 --- /dev/null +++ b/app/views/courses/_category_info.json.jbuilder @@ -0,0 +1,6 @@ +json.category_id category.id +json.category_name category.name +json.position category.position +json.category_count category_task_count(@course, category, @user) +json.category_type category.category_type_str +json.second_category_url category_url(category, @course) \ No newline at end of file diff --git a/app/views/courses/left_banner.json.jbuilder b/app/views/courses/left_banner.json.jbuilder index ecd8fe127..b53dbbb79 100644 --- a/app/views/courses/left_banner.json.jbuilder +++ b/app/views/courses/left_banner.json.jbuilder @@ -25,13 +25,12 @@ json.course_modules @course_modules.each do |mod| end end else - json.second_category mod.course_second_categories.each do |category| - json.category_id category.id - json.category_name category.name - json.position category.position - json.category_count category_task_count(@course, category, @user) - json.category_type category.category_type_str - json.second_category_url category_url(category, @course) + json.second_category mod.first_categories.each do |category| + json.partial! "category_info", category: category + + json.third_category category.children do |child| + json.partial! "category_info", category: child + end end end end diff --git a/app/views/files/index.json.jbuilder b/app/views/files/index.json.jbuilder index e0c21e232..07430cd7b 100644 --- a/app/views/files/index.json.jbuilder +++ b/app/views/files/index.json.jbuilder @@ -7,6 +7,7 @@ json.data do json.unpublish_count @unpublish_count json.unlink_count @unlink_count json.course_is_public @course.is_public? + json.parent_category_id @parent_category_id json.files do json.array! @attachments do |attachment| json.is_history_file attachment.attachment_histories.count > 0 #是否有历史文件 @@ -16,7 +17,7 @@ json.data do end # json.partial! "files/course_groups", attachment_group_settings: attachment.attachment_group_settings json.category_id attachment.course_second_category_id - if @course_second_category_id.to_i == 0 + unless @parent_category_id.present? && @parent_category_id != 0 json.category_name attachment.course_second_category&.name end end diff --git a/app/views/shixuns/_top.json.jbuilder b/app/views/shixuns/_top.json.jbuilder index c90de5e5b..362cfd15b 100644 --- a/app/views/shixuns/_top.json.jbuilder +++ b/app/views/shixuns/_top.json.jbuilder @@ -14,7 +14,7 @@ json.name shixun.name json.stu_num shixun.myshixuns_count json.experience shixun.all_score json.diffcult level_to_s(shixun.trainee) -json.score_info shixun.averge_star # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 +json.score_info shixun.shixun_preference_info # todo: 这块可以改成只显示实训的平均分,不用每次都去取每种星的分数了。 json.is_jupyter shixun.is_jupyter # 用于是否显示导航栏中的'背景知识' json.propaedeutics shixun.propaedeutics.present? diff --git a/db/migrate/20200313030919_add_parent_id_to_second_category.rb b/db/migrate/20200313030919_add_parent_id_to_second_category.rb new file mode 100644 index 000000000..ab45c4025 --- /dev/null +++ b/db/migrate/20200313030919_add_parent_id_to_second_category.rb @@ -0,0 +1,7 @@ +class AddParentIdToSecondCategory < ActiveRecord::Migration[5.2] + def change + add_column :course_second_categories, :parent_id, :integer, default: 0 + + add_index :course_second_categories, :parent_id + end +end diff --git a/public/react/src/modules/courses/Video/video-play/index.jsx b/public/react/src/modules/courses/Video/video-play/index.jsx index 8972c588d..84b465014 100644 --- a/public/react/src/modules/courses/Video/video-play/index.jsx +++ b/public/react/src/modules/courses/Video/video-play/index.jsx @@ -1,5 +1,26 @@ import React, { useRef, useEffect, useCallback } from 'react' +Object.defineProperty(HTMLMediaElement.prototype, 'playing', { + get: function () { + return !!(this.currentTime > 0 && !this.paused && !this.ended && this.readyState > 2) + } +}) + +function compareNumbers(a, b) { + return a - b; +} + +function getTotalEffectTime(pos) { + pos.sort(compareNumbers) + let sum = 0 + for (let i = 0; i < pos.length - 1; i++) { + let v = pos[i + 1] - pos[i] + if (v < 21) { + sum += v + } + } + return sum +} const regex = /(android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini)/i //接口文档 https://www.showdoc.cc/educoder?page_id=4029884447803706 export default ({ src, videoId, logWatchHistory, courseId = null }) => { @@ -12,22 +33,21 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { const device = deviceMatch ? deviceMatch[0] : 'pc' let totalDuration = 0 - let totalTimePlayed = 0 let sumTimePlayed = 0 let lastUpdatedTime = 0 - let lastEffectUpdatedTime = 0 let logId = null let initLog = false let timeTick = 20 //记录频率 默认20s let logCount = 1 let isLoging = false let isSeeking = false + let pos = []//播放时间点集 const log = useCallback((callback) => { let params = {} if (logId) { params['log_id'] = logId - params['watch_duration'] = totalTimePlayed //当前观看视频时长,拖放进度条,重复的视频片段观看时,不会把重复的时长累积进来,最大时长是视频的总时长 + params['watch_duration'] = getTotalEffectTime(pos) //当前观看视频时长,拖放进度条,重复的视频片段观看时,不会把重复的时长累积进来,最大时长是视频的总时长 params['total_duration'] = sumTimePlayed //累计观看视频时长,拖放进度条,重复的视频片段观看时,重复观看时长要累积进来 } else { if (courseId) { @@ -82,6 +102,7 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { useEffect(() => { function onPlay() { + pos.push(el.current.currentTime) if (!initLog) { initLog = true log() @@ -92,13 +113,12 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { log(() => { logId = null logCount = 1 - totalTimePlayed = 0 lastUpdatedTime = 0 sumTimePlayed = 0 initLog = false isLoging = false - lastEffectUpdatedTime = 0 isSeeking = false + pos = [] }) } @@ -106,18 +126,16 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { if (!isSeeking) { let newTime = el.current.currentTime let timeDiff = newTime - lastUpdatedTime - let effectTimeDiff = newTime - lastEffectUpdatedTime - if (effectTimeDiff > 0) { - totalTimePlayed += effectTimeDiff - lastEffectUpdatedTime = newTime - } - sumTimePlayed += Math.abs(timeDiff) - lastUpdatedTime = newTime - - if (!isLoging) { - if (sumTimePlayed - logCount * timeTick >= 0) { - logCount++ - log() + //currenttime update before Seeking & Seeked fired + if (Math.abs(timeDiff) < 0.5) { + sumTimePlayed += Math.abs(timeDiff) + lastUpdatedTime = newTime + if (!isLoging) { + if (sumTimePlayed - logCount * timeTick >= 0) { + logCount++ + pos.push(lastUpdatedTime) + log() + } } } } @@ -126,11 +144,13 @@ export default ({ src, videoId, logWatchHistory, courseId = null }) => { function onSeeking() { isSeeking = true - lastUpdatedTime = el.current.currentTime - lastEffectUpdatedTime = el.current.currentTime } function onSeeked() { + if (el.current.playing) { + pos.push(el.current.currentTime, lastUpdatedTime) + } + lastUpdatedTime = el.current.currentTime isSeeking = false }