From 0c2c7af7fee85ca48d23c241d11b12b2c2bddb88 Mon Sep 17 00:00:00 2001
From: daiao <358551898@qq.com>
Date: Thu, 12 Mar 2020 23:32:19 +0800
Subject: [PATCH 1/4] =?UTF-8?q?=E6=89=93=E6=98=9F=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/views/shixuns/_top.json.jbuilder | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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?

From 9eadb7ffc25b4eb829d9d13405d5f631425c13c0 Mon Sep 17 00:00:00 2001
From: cxt <853663049@qq.com>
Date: Fri, 13 Mar 2020 12:27:36 +0800
Subject: [PATCH 2/4] =?UTF-8?q?=E8=B5=84=E6=BA=90=E4=BA=8C=E7=BA=A7?=
 =?UTF-8?q?=E7=9B=AE=E5=BD=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/controllers/course_modules_controller.rb  |  9 ++++--
 .../course_second_categories_controller.rb    | 32 +++++++++++++------
 app/controllers/courses_controller.rb         |  2 +-
 app/models/course_module.rb                   |  9 ++++--
 app/models/course_second_category.rb          |  5 +++
 app/views/course_modules/show.json.jbuilder   |  7 ++--
 .../courses/_category_info.json.jbuilder      |  6 ++++
 app/views/courses/left_banner.json.jbuilder   | 13 ++++----
 ...030919_add_parent_id_to_second_category.rb |  7 ++++
 9 files changed, 65 insertions(+), 25 deletions(-)
 create mode 100644 app/views/courses/_category_info.json.jbuilder
 create mode 100644 db/migrate/20200313030919_add_parent_id_to_second_category.rb

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/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/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

From 762e340f805311ef3437bf448fd6cb90dde25d94 Mon Sep 17 00:00:00 2001
From: cxt <853663049@qq.com>
Date: Fri, 13 Mar 2020 13:08:47 +0800
Subject: [PATCH 3/4] =?UTF-8?q?=E8=B5=84=E6=BA=90=E8=B0=83=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/controllers/files_controller.rb | 19 ++++++++++++++++---
 app/views/files/index.json.jbuilder |  3 ++-
 2 files changed, 18 insertions(+), 4 deletions(-)

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/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

From a6d09fa89f874f2707653099e565c591bce8f5b6 Mon Sep 17 00:00:00 2001
From: harry <harry@fangmingyi.com>
Date: Fri, 13 Mar 2020 13:42:15 +0800
Subject: [PATCH 4/4] =?UTF-8?q?=E7=B2=BE=E7=A1=AE=E6=9C=89=E6=95=88?=
 =?UTF-8?q?=E8=A7=82=E7=9C=8B=E8=A7=86=E9=A2=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../courses/Video/video-play/index.jsx        | 58 +++++++++++++------
 1 file changed, 39 insertions(+), 19 deletions(-)

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
     }