mirror of
https://gitlink.org.cn/Gitlink/forgeplus.git
synced 2026-05-02 19:30:48 +08:00
init project
This commit is contained in:
19
app/models/application_record.rb
Normal file
19
app/models/application_record.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
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
|
||||
|
||||
def allow_sync_to_trustie?
|
||||
Rails.env.production? && EduSetting.get('host_name') == 'https://www.educoder.net'
|
||||
end
|
||||
end
|
||||
5
app/models/applied_message.rb
Normal file
5
app/models/applied_message.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AppliedMessage < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :applied, polymorphic: true
|
||||
|
||||
end
|
||||
9
app/models/applied_project.rb
Normal file
9
app/models/applied_project.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class AppliedProject < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
|
||||
has_many :applied_messages, as: :applied, dependent: :destroy
|
||||
has_many :forge_activities, as: :forge_act, dependent: :destroy
|
||||
|
||||
scope :pending, -> { where(status: 0) }
|
||||
end
|
||||
30
app/models/apply_action.rb
Normal file
30
app/models/apply_action.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# 申请消息
|
||||
class ApplyAction < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
has_many :tidings, :as => :container, :dependent => :destroy
|
||||
after_create :send_tiding
|
||||
|
||||
def status_text
|
||||
I18n.t!("apply_action.status.#{status}")
|
||||
rescue I18n::MissingTranslationData
|
||||
nil
|
||||
end
|
||||
|
||||
def send_tiding
|
||||
if container_type == 'TrialAuthorization' && status == 1
|
||||
tidings.create(user_id: user_id, trigger_user_id: 0, status: 1, viewed: 0, tiding_type: 'System',
|
||||
parent_container_id: container_id, parent_container_type: container_type,
|
||||
belong_container_id: container_id, belong_container_type: 'User')
|
||||
elsif %w(ApplyShixun ApplySubject TrialAuthorization).include?(container_type)
|
||||
belong_container_type = if container_type == 'TrialAuthorization'
|
||||
'User'
|
||||
else
|
||||
container_type == 'ApplyShixun' ? 'Shixun' : 'Subject'
|
||||
end
|
||||
tidings.create(user_id: '1', trigger_user_id: user_id, status: 0, viewed: 0, tiding_type: 'Apply',
|
||||
parent_container_id: container_id, parent_container_type: container_type,
|
||||
belong_container_id: container_id, belong_container_type: belong_container_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
15
app/models/apply_add_department.rb
Normal file
15
app/models/apply_add_department.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
class ApplyAddDepartment < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :school
|
||||
belongs_to :department
|
||||
|
||||
has_many :applied_messages, as: :applied
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
|
||||
after_create :send_notify
|
||||
|
||||
private
|
||||
def send_notify
|
||||
tidings.create!(user_id: 1, trigger_user_id: user_id, belong_container: school, tiding_type: 'Apply', status: 0)
|
||||
end
|
||||
end
|
||||
22
app/models/apply_add_school.rb
Normal file
22
app/models/apply_add_school.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
class ApplyAddSchool < ApplicationRecord
|
||||
belongs_to :school
|
||||
belongs_to :user
|
||||
|
||||
has_many :applied_messages, as: :applied
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
|
||||
after_create :send_notify
|
||||
# after_destroy :after_delete_apply
|
||||
|
||||
private
|
||||
|
||||
def send_notify
|
||||
Tiding.create!(user_id: 1, status: 0, container_id: id, container_type: 'ApplyAddSchools',
|
||||
trigger_user_id: user_id, belong_container: school, tiding_type: 'Apply')
|
||||
end
|
||||
|
||||
# def after_delete_apply
|
||||
#
|
||||
# end
|
||||
|
||||
end
|
||||
31
app/models/apply_user_authentication.rb
Normal file
31
app/models/apply_user_authentication.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
# status:0 审核中 1 同意 2 拒绝 3 撤销
|
||||
# auth_type:1 实名认证, 2 职业认证
|
||||
class ApplyUserAuthentication < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
has_many :tidings, :as => :container, :dependent => :destroy
|
||||
has_one :attachment, as: :container, dependent: :destroy
|
||||
|
||||
scope :real_name_auth, -> { where(auth_type: 1) }
|
||||
scope :professional_auth, -> { where(auth_type: 2) }
|
||||
scope :processing, -> { where(status: 0) }
|
||||
scope :passed, -> { where(status: 1) }
|
||||
|
||||
after_create :send_tiding
|
||||
|
||||
def status_text
|
||||
I18n.t!("apply_user_authentication.status.#{status}")
|
||||
rescue I18n::MissingTranslationData
|
||||
nil
|
||||
end
|
||||
|
||||
def revoke!
|
||||
update!(status: 3)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_tiding
|
||||
self.tidings << Tiding.new(:user_id => '1', :status=> 0, :trigger_user_id => user_id, :belong_container_id => 1, :belong_container_type =>'User', :tiding_type => "Apply")
|
||||
end
|
||||
end
|
||||
145
app/models/attachment.rb
Normal file
145
app/models/attachment.rb
Normal file
@@ -0,0 +1,145 @@
|
||||
class Attachment < ApplicationRecord
|
||||
include BaseModel
|
||||
include Publicable
|
||||
include Publishable
|
||||
include Lockable
|
||||
|
||||
belongs_to :container, polymorphic: true, optional: true
|
||||
belongs_to :author, class_name: "User", foreign_key: :author_id
|
||||
belongs_to :course, foreign_key: :container_id, optional: true
|
||||
has_many :attachment_group_settings, :dependent => :destroy
|
||||
has_many :attachment_histories, -> { order(version: :desc) }, :dependent => :destroy
|
||||
# 二级目录
|
||||
belongs_to :course_second_category, optional: true
|
||||
|
||||
scope :by_filename_or_user_name, -> (keywords) { joins(:author).where("filename like :search or LOWER(concat(users.lastname, users.firstname)) LIKE :search",
|
||||
:search => "%#{keywords.split(" ").join('|')}%") unless keywords.blank? }
|
||||
scope :by_keywords, -> (keywords) { where("filename LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank? }
|
||||
scope :ordered, -> (opts = {}) { order("#{opts[:sort_type]} #{opts[:sort] == 1 ? 'asc': 'desc'}") }
|
||||
scope :by_course_second_category_id, -> (course_second_category_id = 0) { where(course_second_category_id: course_second_category_id) }
|
||||
scope :contains_only_course, -> { where(container_type: 'Course') }
|
||||
scope :contains_only_project, -> { where(container_type: 'Project') }
|
||||
scope :contains_course_and_project, -> { contains_only_course.or(contains_only_project) }
|
||||
scope :mine, -> (author_id) { where(author_id: author_id) }
|
||||
scope :simple_columns, -> { select(:id, :filename, :filesize, :created_on, :cloud_url, :author_id, :content_type, :container_type, :container_id) }
|
||||
scope :search_by_container, -> (ids) {where(container_id: ids)}
|
||||
scope :unified_setting, -> {where("unified_setting = ? ", 1)}
|
||||
|
||||
validates_length_of :description, maximum: 100, message: "不能超过100个字符"
|
||||
|
||||
DCODES = %W(2 3 4 5 6 7 8 9 a b c f e f g h i j k l m n o p q r s t u v w x y z)
|
||||
|
||||
def diskfile
|
||||
File.join(File.join(Rails.root, "files"), disk_directory.to_s, disk_filename.to_s)
|
||||
end
|
||||
|
||||
def relative_path_filename
|
||||
File.join(disk_directory.to_s, disk_filename.to_s)
|
||||
end
|
||||
|
||||
def title
|
||||
title = filename
|
||||
if container && container.is_a?(StudentWork) && author_id != User.current.id
|
||||
course = container&.homework_common&.course
|
||||
unless User.current.teacher_of_course?(course)
|
||||
title = "#{Time.now.strftime('%Y%m%d%H%M%S')}_#{DCODES.sample(8).join}" + File.extname(filename)
|
||||
end
|
||||
end
|
||||
title
|
||||
end
|
||||
|
||||
def downloads_count
|
||||
downloads
|
||||
end
|
||||
|
||||
def quotes_count
|
||||
quotes.nil? ? 0 : quotes
|
||||
end
|
||||
|
||||
def self.associate_container(ids, container_id, container_type, attachtype=1)
|
||||
return false if ids.blank? || !ids.is_a?(Array)
|
||||
ids.each do |id|
|
||||
attachment = Attachment.find id
|
||||
attachment.update_attributes(container_id: container_id, container_type: container_type, attachtype: attachtype)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an unsaved copy of the attachment
|
||||
def copy(attributes=nil)
|
||||
copy = self.class.new
|
||||
copy.attributes = self.attributes.dup.except("id", "downloads", "quotes")
|
||||
copy.attributes = attributes if attributes
|
||||
copy
|
||||
end
|
||||
|
||||
def set_publish_time(publish_time)
|
||||
self.unified_setting = 1
|
||||
if publish_time.blank?
|
||||
self.publish_time = Time.now
|
||||
self.is_publish = 1
|
||||
else
|
||||
self.is_publish = publish_time.to_s > (format_time Time.now).to_s ? 0 : 1
|
||||
self.publish_time = publish_time.to_s > (format_time Time.now).to_s ? publish_time : Time.now
|
||||
end
|
||||
end
|
||||
|
||||
def set_course_group_publish_time(course, course_group_publish_times)
|
||||
self.unified_setting = 0
|
||||
min_publish_time = ""
|
||||
course_group_publish_times.each do |obj|
|
||||
if obj && obj[:course_group_id]
|
||||
publish_time = obj[:publish_time]
|
||||
if !publish_time.blank? && publish_time < min_publish_time
|
||||
min_publish_time = publish_time
|
||||
elsif publish_time.blank?
|
||||
publish_time = Time.now
|
||||
end
|
||||
attachment_group_setting = self.attachment_group_settings.where(course_group_id: obj[:course_group_id], course_id: course.id).first
|
||||
if attachment_group_setting.present?
|
||||
attachment_group_setting.update_columns(publish_time: publish_time)
|
||||
else
|
||||
self.attachment_group_settings.create(
|
||||
:course_group_id => obj[:course_group_id],
|
||||
:course_id => course.id,
|
||||
:publish_time => publish_time
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.is_publish = min_publish_time > (format_time Time.now).to_s ? 0 : 1
|
||||
self.publish_time = min_publish_time > (format_time Time.now).to_s ? min_publish_time : self.created_on
|
||||
end
|
||||
|
||||
def become_history
|
||||
history = self.attachment_histories.first
|
||||
new_attachment_history = AttachmentHistory.new(self.attributes.except("id", "resource_bank_id", "unified_setting", "course_second_category_id", "delay_publish").merge(
|
||||
attachment_id: self.id,
|
||||
version: history.nil? ? 1 : history.version + 1,
|
||||
))
|
||||
new_attachment_history
|
||||
end
|
||||
|
||||
def copy_attributes_from_new_attachment(new_attachment)
|
||||
self.attributes = new_attachment.attributes.dup.except("id","container_id","container_type","is_public","downloads", "quotes",'is_publish','publish_time', "delay_publish")
|
||||
end
|
||||
|
||||
def set_public(is_public)
|
||||
if is_public == true
|
||||
is_public = 1
|
||||
elsif is_public == false
|
||||
is_public = 0
|
||||
end
|
||||
end
|
||||
|
||||
#判断是否为pdf文件
|
||||
def is_pdf?
|
||||
is_pdf = false
|
||||
file_content_type = content_type
|
||||
file_ext_type = File.extname(filename).strip.downcase[1..-1]
|
||||
if (file_content_type.present? && file_content_type.downcase.include?("pdf")) || (file_ext_type.present? && file_ext_type.include?("pdf"))
|
||||
is_pdf = true
|
||||
end
|
||||
is_pdf
|
||||
end
|
||||
|
||||
end
|
||||
8
app/models/attachment_group_setting.rb
Normal file
8
app/models/attachment_group_setting.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class AttachmentGroupSetting < ActiveRecord::Base
|
||||
belongs_to :attachment
|
||||
belongs_to :course_group
|
||||
belongs_to :course
|
||||
|
||||
scope :none_published, -> {where("attachment_group_settings.publish_time IS NULL OR attachment_group_settings.publish_time > ?", Time.now)}
|
||||
|
||||
end
|
||||
33
app/models/attachment_history.rb
Normal file
33
app/models/attachment_history.rb
Normal file
@@ -0,0 +1,33 @@
|
||||
class AttachmentHistory < ApplicationRecord
|
||||
include Publishable
|
||||
include Publicable
|
||||
|
||||
belongs_to :attachment, foreign_key: 'attachment_id'
|
||||
|
||||
def title
|
||||
filename
|
||||
end
|
||||
|
||||
def downloads_count
|
||||
downloads
|
||||
end
|
||||
|
||||
def quotes_count
|
||||
quotes.nil? ? 0 : quotes
|
||||
end
|
||||
|
||||
def public?
|
||||
is_public == 1
|
||||
end
|
||||
|
||||
def is_history_pdf?
|
||||
is_pdf = false
|
||||
file_content_type = content_type
|
||||
file_ext_type = File.extname(filename).strip.downcase[1..-1]
|
||||
if (file_content_type.present? && file_content_type.downcase.include?("pdf")) || (file_ext_type.present? && file_ext_type.include?("pdf"))
|
||||
is_pdf = true
|
||||
end
|
||||
is_pdf
|
||||
end
|
||||
|
||||
end
|
||||
16
app/models/attendance.rb
Normal file
16
app/models/attendance.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class Attendance < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
default_scope { order(created_at: :desc) }
|
||||
|
||||
def next_gold
|
||||
# 超过1天即没有连续的签到则又从10个金币开始累加
|
||||
return 50 if Util.days_between(Time.zone.now, created_at) > 1
|
||||
|
||||
[[score.to_i, 50].max + 10, 100].min
|
||||
end
|
||||
|
||||
def today?
|
||||
Util.days_between(Time.current, created_at).zero?
|
||||
end
|
||||
end
|
||||
24
app/models/bidding_user.rb
Normal file
24
app/models/bidding_user.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
class BiddingUser < ApplicationRecord
|
||||
include AASM
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :project_package, counter_cache: true
|
||||
|
||||
aasm(:status) do
|
||||
state :pending, initial: true
|
||||
state :bidding_won
|
||||
state :bidding_lost
|
||||
|
||||
event :win do
|
||||
transitions from: [:pending], to: :bid_won
|
||||
end
|
||||
|
||||
event :lose do
|
||||
transitions from: [:pending], to: :bid_lost
|
||||
end
|
||||
end
|
||||
|
||||
def status_text
|
||||
I18n.t("bidding_user.status.#{status}")
|
||||
end
|
||||
end
|
||||
2
app/models/career.rb
Normal file
2
app/models/career.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
class Career < ApplicationRecord
|
||||
end
|
||||
150
app/models/challenge.rb
Normal file
150
app/models/challenge.rb
Normal file
@@ -0,0 +1,150 @@
|
||||
class Challenge < ApplicationRecord
|
||||
# difficulty: 关卡难度: 1.简单 2.中等 3.困难
|
||||
# show_type: 效果展示:-1.无效果 1.图片 2.apk/exe 3.txt 4.html 5.mp3 6.mp4
|
||||
|
||||
belongs_to :shixun, :touch => true, counter_cache: true
|
||||
belongs_to :user
|
||||
has_many :challenge_samples, :dependent => :destroy
|
||||
has_many :test_sets, :dependent => :destroy
|
||||
has_many :challenge_tags, :dependent => :destroy
|
||||
has_many :games, :dependent => :destroy
|
||||
has_many :challenge_chooses, :dependent => :destroy
|
||||
has_many :homework_challenge_settings, :dependent => :destroy
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
has_one :praise_tread_cache, as: :object, dependent: :destroy
|
||||
has_many :tidings
|
||||
# 参考答案
|
||||
has_many :challenge_answers, :dependent => :destroy
|
||||
has_many :exercise_bank_shixun_challenges, :dependent => :destroy
|
||||
# 回复
|
||||
has_many :discusses, :dependent => :destroy
|
||||
|
||||
# acts_as_attachable
|
||||
|
||||
scope :base_attrs, -> { select([:id, :subject, :position, :shixun_id, :st, :score, :path, :task_pass, :modify_time,
|
||||
:web_route, :answer, :exec_time, :praises_count]) }
|
||||
scope :choose_type, -> { where(st: 1) }
|
||||
scope :practice_type, -> { where(st: 0) }
|
||||
|
||||
scope :fields_for_list, -> { select([:id, :subject, :st, :score, :position, :shixun_id]) }
|
||||
|
||||
validates :task_pass, length: { maximum: 35000, too_long: "不能超过35000个字符" }
|
||||
|
||||
|
||||
after_commit :create_diff_record
|
||||
|
||||
def next_challenge
|
||||
position = self.position + 1
|
||||
Challenge.where(:position => position, :shixun_id => self.shixun).first
|
||||
end
|
||||
|
||||
# 用户关卡是否通关
|
||||
def has_passed?(user_id)
|
||||
self.games.present? && self.games.select{|game| game.user_id == user_id && game.status == 2}.length > 0
|
||||
end
|
||||
|
||||
## 选择题总分
|
||||
def choose_score
|
||||
self.score
|
||||
#self.challenge_chooses.pluck(:score).sum
|
||||
end
|
||||
|
||||
def challenge_difficulty
|
||||
case difficulty
|
||||
when 1 then "简单"
|
||||
when 2 then "中等"
|
||||
when 3 then "困难"
|
||||
else ''
|
||||
end
|
||||
end
|
||||
|
||||
# 关卡总分
|
||||
def all_score
|
||||
self.score
|
||||
# if self.st == 1
|
||||
# self.choose_score
|
||||
# else
|
||||
# self.score
|
||||
# end
|
||||
end
|
||||
|
||||
# 开启挑战
|
||||
def open_game user_id, shixun
|
||||
game = self.games.where(user_id: user_id).first
|
||||
if game.present?
|
||||
shixun.task_pass || game.status != 3 ? "/tasks/#{game.identifier}" : ""
|
||||
else
|
||||
"/api/shixuns/#{shixun.identifier}/shixun_exec"
|
||||
end
|
||||
end
|
||||
|
||||
# # 开启挑战
|
||||
# def open_game(user_id, shixun)
|
||||
#
|
||||
#
|
||||
# game = self.games.select([:status, :identifier]).where(user_id: user_id).first
|
||||
# game = self.games.select{|game| game.user_id == user_id}
|
||||
# if game.present?
|
||||
# shixun.task_pass || game.status != 3 ? "/tasks/#{game.identifier}" : ""
|
||||
# else
|
||||
# "/api/shixuns/#{shixun.identifier}/shixun_exec"
|
||||
# end
|
||||
# end
|
||||
|
||||
## 用户关卡状态 0: 不能开启实训; 1:直接开启; 2表示已完成
|
||||
def user_tpi_status user_id
|
||||
# todo: 以前没加索引导致相同关卡,同一用户有多个games
|
||||
# 允许跳关则直接开启
|
||||
game = games.where(user_id: user_id).take
|
||||
if game.blank?
|
||||
position == 1 ? 1 : 0
|
||||
else
|
||||
if game.status == 3
|
||||
shixun.task_pass ? 1 : 0
|
||||
elsif game.status == 2
|
||||
2
|
||||
else
|
||||
1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def tags_show
|
||||
if self.challenge_tags.nil?
|
||||
"--"
|
||||
else
|
||||
self.try(:challenge_tags).map(&:name).join(";")
|
||||
end
|
||||
end
|
||||
|
||||
## 选择题答案
|
||||
def choose_answer
|
||||
result = []
|
||||
self.challenge_chooses.each do |choose|
|
||||
result << {:position => choose.position, :answer => (choose.answer.blank? ? choose.standard_answer : choose.answer)}
|
||||
end
|
||||
end
|
||||
|
||||
# 关卡用户通关数
|
||||
def user_passed_count
|
||||
games.where(status: 2).count
|
||||
end
|
||||
|
||||
# 关卡用户正在挑战的人数
|
||||
def playing_count
|
||||
games.where(status: [0, 1]).count
|
||||
end
|
||||
|
||||
def last_challenge
|
||||
Challenge.find_by(position: position - 1, shixun_id: shixun_id)
|
||||
end
|
||||
|
||||
# 关卡评测文件
|
||||
|
||||
private
|
||||
|
||||
def create_diff_record
|
||||
return unless task_pass_previously_changed?
|
||||
CreateDiffRecordJob.perform_later(User.current.id, id, 'Challenge', 'task_pass', task_pass_before_last_save, task_pass)
|
||||
end
|
||||
end
|
||||
11
app/models/challenge_answer.rb
Normal file
11
app/models/challenge_answer.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class ChallengeAnswer < ApplicationRecord
|
||||
default_scope { order("challenge_answers.level asc") }
|
||||
belongs_to :challenge
|
||||
has_many :game_answers, :dependent => :destroy
|
||||
|
||||
validates :contents, length: { maximum: 25000 , too_long: "不能超过25000个字符"}
|
||||
|
||||
def view_answer_time(user_id)
|
||||
game_answers.where(user_id: user_id).last&.view_time
|
||||
end
|
||||
end
|
||||
9
app/models/challenge_choose.rb
Normal file
9
app/models/challenge_choose.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class ChallengeChoose < ApplicationRecord
|
||||
default_scope {order("challenge_chooses.position asc")}
|
||||
belongs_to :challenge, optional: true
|
||||
has_many :challenge_tags, :dependent => :destroy
|
||||
has_many :challenge_questions, dependent: :destroy
|
||||
|
||||
validates :subject, length: { maximum: 25000, too_long: "不能超过25000个字符" }
|
||||
|
||||
end
|
||||
6
app/models/challenge_question.rb
Normal file
6
app/models/challenge_question.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class ChallengeQuestion < ApplicationRecord
|
||||
belongs_to :challenge_choose
|
||||
|
||||
validates :option_name, length: { maximum: 500, too_long: "不能超过500个字符" }
|
||||
|
||||
end
|
||||
4
app/models/challenge_sample.rb
Normal file
4
app/models/challenge_sample.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class ChallengeSample < ApplicationRecord
|
||||
belongs_to :challenge
|
||||
end
|
||||
|
||||
6
app/models/challenge_tag.rb
Normal file
6
app/models/challenge_tag.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class ChallengeTag < ApplicationRecord
|
||||
include Searchable::Dependents::ChallengeTag
|
||||
|
||||
belongs_to :challenge, counter_cache: true
|
||||
belongs_to :challenge_choose, optional: true
|
||||
end
|
||||
15
app/models/challenge_work_score.rb
Normal file
15
app/models/challenge_work_score.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
class ChallengeWorkScore < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :student_work
|
||||
belongs_to :challenge
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
|
||||
validates :comment, length: { maximum: 500, too_long: "不能超过500个字符" }
|
||||
|
||||
def create_tiding trigger_user_id
|
||||
tidings << Tiding.new(user_id: student_work.user_id, trigger_user_id: trigger_user_id, container_id: id,
|
||||
container_type: "ChallengeWorkScore", parent_container_id: student_work_id,
|
||||
parent_container_type: "StudentWork", belong_container_id: student_work&.homework_common&.course_id,
|
||||
belong_container_type: "Course", viewed: 0, tiding_type: "HomeworkCommon")
|
||||
end
|
||||
end
|
||||
4
app/models/chart_rule.rb
Normal file
4
app/models/chart_rule.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class ChartRule < ApplicationRecord
|
||||
|
||||
validates :content, length: { maximum: 1000, too_long: "不能超过1000个字符" }
|
||||
end
|
||||
4
app/models/commit.rb
Normal file
4
app/models/commit.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class Commit < ApplicationRecord
|
||||
belongs_to :project, foreign_key: :project_id
|
||||
|
||||
end
|
||||
4
app/models/commit_issue.rb
Normal file
4
app/models/commit_issue.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class CommitIssue < ApplicationRecord
|
||||
belongs_to :issue, foreign_key: :issue_id
|
||||
|
||||
end
|
||||
12
app/models/compose.rb
Normal file
12
app/models/compose.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class Compose < ApplicationRecord
|
||||
#组织
|
||||
belongs_to :user
|
||||
has_many :compose_projects
|
||||
has_many :compose_users
|
||||
|
||||
validates :title, presence: {message: "组织名称不能为空"}, uniqueness: {message: "组织名称已存在"}
|
||||
|
||||
scope :compose_includes, ->{includes(:compose_projects, :compose_users, :user)}
|
||||
|
||||
end
|
||||
|
||||
4
app/models/compose_project.rb
Normal file
4
app/models/compose_project.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class ComposeProject < ApplicationRecord
|
||||
#组织的项目记录表
|
||||
belongs_to :compose
|
||||
end
|
||||
4
app/models/compose_user.rb
Normal file
4
app/models/compose_user.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class ComposeUser < ApplicationRecord
|
||||
belongs_to :compose
|
||||
belongs_to :user
|
||||
end
|
||||
0
app/models/concerns/.keep
Normal file
0
app/models/concerns/.keep
Normal file
34
app/models/concerns/base_model.rb
Executable file
34
app/models/concerns/base_model.rb
Executable file
@@ -0,0 +1,34 @@
|
||||
module BaseModel
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :recent, -> { order(id: :desc) }
|
||||
scope :exclude_ids, -> (ids) { where.not(id: ids.map(&:to_i)) }
|
||||
scope :by_ids, -> (ids) { where(id: ids) unless ids.blank? }
|
||||
scope :by_week, -> { where("created_at > ?", 7.days.ago.utc) }
|
||||
|
||||
delegate :url_helpers, to: 'Rails.application.routes'
|
||||
end
|
||||
|
||||
# FIXME: 需要原子化操作
|
||||
def push(hash)
|
||||
hash.each_key do |key|
|
||||
self.send("#{key}_will_change!")
|
||||
old_val = self[key] || []
|
||||
old_val << hash[key].to_i
|
||||
old_val.uniq!
|
||||
update_attributes(key => old_val)
|
||||
end
|
||||
end
|
||||
|
||||
# FIXME: 需要原子化操作
|
||||
def pull(hash)
|
||||
hash.each_key do |key|
|
||||
self.send("#{key}_will_change!")
|
||||
old_val = self[key]
|
||||
return true if old_val.blank?
|
||||
old_val.delete(hash[key].to_i)
|
||||
update_attributes(key => old_val)
|
||||
end
|
||||
end
|
||||
end
|
||||
21
app/models/concerns/likeable.rb
Normal file
21
app/models/concerns/likeable.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
module Likeable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
end
|
||||
|
||||
def liked?(praiseable)
|
||||
praiseable.praise_treads.exists?(user_id: self.id)
|
||||
end
|
||||
|
||||
def like!(praiseable)
|
||||
praiseable.praise_treads.create!(user_id: self.id)
|
||||
end
|
||||
|
||||
def unlike!(praiseable)
|
||||
obj = praiseable.praise_treads.find_by(user_id: self.id)
|
||||
obj.destroy! if obj.present?
|
||||
end
|
||||
|
||||
end
|
||||
10
app/models/concerns/lockable.rb
Normal file
10
app/models/concerns/lockable.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
module Lockable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
end
|
||||
|
||||
def locked?(is_member)
|
||||
is_member == true ? false : !publiced?
|
||||
end
|
||||
end
|
||||
13
app/models/concerns/matchable.rb
Normal file
13
app/models/concerns/matchable.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
module Matchable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :like, lambda { |keywords|
|
||||
where("name LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
|
||||
}
|
||||
scope :with_project_category, ->(category_id) { where(project_category_id: category_id) unless category_id.blank? }
|
||||
scope :with_project_language, ->(language_id) { where(project_language_id: language_id) unless language_id.blank? }
|
||||
scope :with_project_type, ->(project_type) { where(project_type: project_type) if Project.project_types.include?(project_type) }
|
||||
end
|
||||
|
||||
end
|
||||
13
app/models/concerns/number_display_helper.rb
Normal file
13
app/models/concerns/number_display_helper.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
module NumberDisplayHelper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def number_display_methods(*columns, **opts)
|
||||
columns.each do |column|
|
||||
define_method "display_#{column}" do
|
||||
number_to_currency(column.to_f, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
43
app/models/concerns/project_operable.rb
Normal file
43
app/models/concerns/project_operable.rb
Normal file
@@ -0,0 +1,43 @@
|
||||
module ProjectOperable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :members
|
||||
# has_many :except_owner_members, -> { members.where("members.use_id != ? ", self.owner.id ) }
|
||||
has_many :manager_members, -> { joins(:roles).where(roles: { name: 'Manager' }) }, class_name: 'Member'
|
||||
end
|
||||
|
||||
def add_member!(user_id, role_name='Developer')
|
||||
member = members.create!(user_id: user_id)
|
||||
set_developer_role(member)
|
||||
end
|
||||
|
||||
def remove_member!(user_id)
|
||||
member = members.find_by(user_id: user_id)
|
||||
member.destroy! if member && self.user_id != user_id
|
||||
end
|
||||
|
||||
def member?(user_id)
|
||||
members.exists?(user_id: user_id)
|
||||
end
|
||||
|
||||
# 除了项目创建者本身
|
||||
def member(user_id)
|
||||
members.where.not("members.user_id = ? ", owner.id).find_by(user_id: user_id)
|
||||
end
|
||||
|
||||
def change_member_role!(user_id, role)
|
||||
member = self.member(user_id)
|
||||
member.member_roles.last.update_attributes!(role: role)
|
||||
end
|
||||
|
||||
def owner?(user)
|
||||
self.owner == user
|
||||
end
|
||||
|
||||
def set_developer_role(member)
|
||||
role = Role.find_by_name 'Developer'
|
||||
member.member_roles.create!(role: role)
|
||||
end
|
||||
|
||||
end
|
||||
15
app/models/concerns/projectable.rb
Normal file
15
app/models/concerns/projectable.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
module Projectable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :projects, -> { order(position: :asc) }
|
||||
|
||||
scope :without_content, -> { select(column_names - ['content'])}
|
||||
scope :search, lambda { |keywords|
|
||||
where("name LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
|
||||
}
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
end
|
||||
end
|
||||
7
app/models/concerns/publicable.rb
Normal file
7
app/models/concerns/publicable.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module Publicable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
scope :visible, -> { where(is_public: true) }
|
||||
end
|
||||
end
|
||||
9
app/models/concerns/publishable.rb
Normal file
9
app/models/concerns/publishable.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module Publishable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
alias_attribute :publish, :is_publish
|
||||
enum publish: { published: 1, unpublish: 0 }
|
||||
end
|
||||
|
||||
end
|
||||
26
app/models/concerns/watchable.rb
Normal file
26
app/models/concerns/watchable.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
module Watchable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
has_many :watchers, as: :watchable, dependent: :destroy
|
||||
has_many :watcher_users, through: :watchers, source: :user, validate: false
|
||||
|
||||
scope :watched_by, -> (user_id) { includes(:watchers).where(watchers: { user_id: user_id }) }
|
||||
end
|
||||
|
||||
def watched?(watchable)
|
||||
watchable.watchers.exists?(user: self)
|
||||
end
|
||||
|
||||
def watch!(watchable)
|
||||
watchable.watchers.create!(user: self)
|
||||
end
|
||||
|
||||
def unwatch!(watchable)
|
||||
obj = watchable.watchers.find_by(user: self)
|
||||
obj.destroy! if obj.present?
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
end
|
||||
end
|
||||
5
app/models/coo_img.rb
Normal file
5
app/models/coo_img.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class CooImg < ApplicationRecord
|
||||
extend Enumerize
|
||||
|
||||
enumerize :img_type, in: %i[com_coop edu_coop alliance_coop]
|
||||
end
|
||||
9
app/models/cooperation.rb
Normal file
9
app/models/cooperation.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class Cooperation < ApplicationRecord
|
||||
def user_type_text
|
||||
case user_type.to_i
|
||||
when 1 then '高校合作'
|
||||
when 2 then '企业合作'
|
||||
when 3 then '实训投稿'
|
||||
end
|
||||
end
|
||||
end
|
||||
455
app/models/course.rb
Normal file
455
app/models/course.rb
Normal file
@@ -0,0 +1,455 @@
|
||||
class Course < ApplicationRecord
|
||||
include Searchable::Course
|
||||
|
||||
has_many :boards, dependent: :destroy
|
||||
|
||||
belongs_to :teacher, class_name: 'User', foreign_key: :tea_id # 定义一个方法teacher,该方法通过tea_id来调用User表
|
||||
belongs_to :school, class_name: 'School', foreign_key: :school_id #定义一个方法school,该方法通过school_id来调用School表
|
||||
belongs_to :course_list, optional: true
|
||||
|
||||
belongs_to :laboratory, optional: true
|
||||
|
||||
# 所属实践课程
|
||||
belongs_to :subject, optional: true
|
||||
has_many :informs, as: :container, dependent: :destroy
|
||||
|
||||
has_many :course_infos, dependent: :destroy
|
||||
# 课堂左侧导航栏的模块
|
||||
has_many :course_modules, dependent: :destroy
|
||||
has_many :none_hidden_course_modules, -> { not_hidden }, class_name: "CourseModule"
|
||||
has_many :board_course_modules, -> { board_module }, class_name: "CourseModule"
|
||||
has_many :attachment_course_modules, -> { attachment_module }, class_name: "CourseModule"
|
||||
has_many :common_course_modules, -> { common_homework_module }, class_name: "CourseModule"
|
||||
has_many :group_course_modules, -> { group_homework_module }, class_name: "CourseModule"
|
||||
has_many :shixun_course_modules, -> { shixun_homework_module }, class_name: "CourseModule"
|
||||
|
||||
# 课堂模块的二级目录
|
||||
has_many :course_second_categories, dependent: :destroy
|
||||
# 课堂分班
|
||||
has_many :course_groups, dependent: :destroy
|
||||
# 答辩组
|
||||
has_many :graduation_groups, dependent: :destroy
|
||||
|
||||
has_many :course_members, dependent: :destroy
|
||||
has_many :students, -> { course_students }, class_name: 'CourseMember'
|
||||
has_many :teacher_course_members, -> { teachers_and_admin }, class_name: 'CourseMember'
|
||||
has_many :teacher_users, through: :teacher_course_members, source: :user
|
||||
has_many :course_messages, dependent: :destroy
|
||||
|
||||
has_many :homework_commons, dependent: :destroy
|
||||
has_many :normal_homeworks, -> { normals }, class_name: 'HomeworkCommon'
|
||||
has_many :group_homeworks, -> { groups }, class_name: 'HomeworkCommon'
|
||||
has_many :practice_homeworks, -> { practices }, class_name: 'HomeworkCommon'
|
||||
|
||||
has_many :homework_group_settings
|
||||
has_many :graduation_works, dependent: :destroy
|
||||
|
||||
# 实训作业的二级目录(已弃用)
|
||||
has_many :course_homework_categories, dependent: :destroy
|
||||
has_many :exercises, dependent: :destroy
|
||||
|
||||
#课堂的试卷
|
||||
has_many :exercise_group_settings, :dependent => :destroy
|
||||
|
||||
# 课堂的问卷
|
||||
has_many :polls, dependent: :destroy
|
||||
has_many :poll_group_settings, :dependent => :destroy
|
||||
|
||||
# 毕业设计
|
||||
has_many :graduation_topics, dependent: :destroy
|
||||
has_many :graduation_tasks, dependent: :destroy
|
||||
has_many :student_graduation_topics, :dependent => :destroy
|
||||
has_many :teacher_course_groups, :dependent => :destroy
|
||||
|
||||
# 资源
|
||||
has_many :attachments, as: :container, dependent: :destroy
|
||||
has_many :attachment_group_settings, :dependent => :destroy
|
||||
|
||||
# 课堂学生,弃用
|
||||
has_many :student, :class_name => 'StudentsForCourse', :source => :user
|
||||
|
||||
|
||||
# 课堂动态
|
||||
has_one :course_act, class_name: 'CourseActivity', as: :course_act, dependent: :destroy
|
||||
has_many :course_activities
|
||||
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
|
||||
# 开放课堂
|
||||
has_many :course_stages, -> { order("course_stages.position ASC") }, dependent: :destroy
|
||||
has_many :course_stage_shixuns, dependent: :destroy
|
||||
has_many :shixuns, through: :course_stage_shixuns
|
||||
|
||||
# 老版的members弃用 现用course_members
|
||||
has_many :members
|
||||
|
||||
# 视频
|
||||
has_many :course_videos, dependent: :destroy
|
||||
has_many :videos, through: :course_videos
|
||||
|
||||
# 直播
|
||||
has_many :live_links, dependent: :destroy
|
||||
|
||||
validate :validate_sensitive_string
|
||||
|
||||
scope :hidden, ->(is_hidden = true) { where(is_hidden: is_hidden) }
|
||||
scope :ended, ->(is_end = true) { where(is_end: is_end) }
|
||||
scope :processing, -> { where(is_end: false) }
|
||||
scope :not_deleted, -> { where(is_delete: 0) }
|
||||
scope :not_excellent, -> { where(excellent: 0) }
|
||||
scope :deleted, ->(is_delete = 1) { where(is_delete: is_delete) }
|
||||
scope :by_user, ->(user) { joins(:course_members).where('course_members.user_id = ?', user.id).order(updated_at: :desc) }
|
||||
scope :by_keywords, lambda { |keywords|
|
||||
where("name LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
|
||||
}
|
||||
scope :started, -> { where("start_date is null or start_date <= '#{Date.today}'") }
|
||||
|
||||
# acts_as_taggable
|
||||
|
||||
|
||||
# 课程权限判断
|
||||
ADMIN = 0 # 超级管理员
|
||||
BUSINESS = 1 # 运营人员
|
||||
CREATOR = 2 # 课程创建者
|
||||
PROFESSOR = 3 # 课程老师
|
||||
ASSISTANT_PROFESSOR = 4 # 课程助教
|
||||
STUDENT = 5 # 学生
|
||||
NORMAL = 6 # 普通用户
|
||||
Anonymous = 7 # 普未登录
|
||||
|
||||
validates :name, presence: true, length: { maximum: 60, too_long: "不能超过60个字符" }
|
||||
|
||||
before_save :set_laboratory
|
||||
after_create :create_board_sync, :act_as_course_activity, :send_tiding
|
||||
|
||||
def course_member? user_id, role
|
||||
course_members.where(user_id: user_id, role: role).exists?
|
||||
end
|
||||
|
||||
def course_group_module?
|
||||
course_modules.exists?(module_type: "course_group", hidden: 0)
|
||||
end
|
||||
|
||||
# 作业对应的子目录/父目录名称
|
||||
def category_info type
|
||||
course_module = course_modules.find_by(module_type: type)
|
||||
{ category_id: course_module&.id, category_name: course_module&.module_name }
|
||||
end
|
||||
|
||||
# 未分班的学生数
|
||||
def none_group_count
|
||||
course_members.where(role: 4, course_group_id: 0).size
|
||||
end
|
||||
|
||||
def course_member(user_id)
|
||||
course_members.find_by(user_id: user_id, is_active: 1)
|
||||
end
|
||||
|
||||
def course_student(user_id)
|
||||
course_members.find_by(user_id: user_id, role: %i(STUDENT))
|
||||
end
|
||||
|
||||
def user_group_name(user_id)
|
||||
students.find_by(user_id: user_id)&.course_group_name
|
||||
end
|
||||
|
||||
|
||||
def teacher_group(user_id)
|
||||
data =
|
||||
if teacher_course_groups.exists?(user_id: user_id)
|
||||
teacher_course_groups.joins(:course_group).where(user_id: user_id)
|
||||
.pluck('course_groups.id', 'course_groups.name')
|
||||
else
|
||||
course_groups.pluck(:id, :name)
|
||||
end
|
||||
|
||||
data.map { |arr| { group_id: arr.first, group_name: arr.last } }
|
||||
end
|
||||
|
||||
#当前老师的班级id
|
||||
def teacher_course_ids(user_id)
|
||||
course_teacher_member = teacher_course_groups.get_user_groups(user_id).select(:course_group_id) #获取当前老师的分班
|
||||
if course_teacher_member.blank?
|
||||
if none_group_count > 0 #有未分班的,则发布到未发布分班
|
||||
un_group_ids = [0]
|
||||
else
|
||||
un_group_ids = []
|
||||
end
|
||||
course_groups.pluck(:id) + un_group_ids #所有分班和未分班
|
||||
else
|
||||
course_teacher_member.pluck(:course_group_id).reject(&:blank?).uniq #当前用户所在的班级,老师可能有多个班级
|
||||
end
|
||||
end
|
||||
|
||||
# 查询老师分班的所有学生
|
||||
def teacher_group_user_ids user_id
|
||||
teachers = teacher_course_groups.where(user_id: user_id)
|
||||
if teachers.exists?
|
||||
students.where(course_group_id: teachers.pluck(:course_group_id)).pluck(:user_id)
|
||||
else
|
||||
students.pluck(:user_id)
|
||||
end
|
||||
end
|
||||
|
||||
# 创建课程模块
|
||||
def create_course_modules(course_module_types)
|
||||
course_modules.destroy_all if course_modules.present?
|
||||
|
||||
all_course_module_types.each do |type|
|
||||
name = get_name_by_type(type)
|
||||
position = get_position_by_type(type)
|
||||
|
||||
hidden = course_module_types.include?(type) ? 0 : 1
|
||||
CourseModule.create(course_id: id, module_type: type, position: position, hidden: hidden, module_name: name)
|
||||
end
|
||||
end
|
||||
|
||||
# 更新课程模块
|
||||
def update_course_modules(course_module_types)
|
||||
all_course_module_types.each do |type|
|
||||
hidden_value = course_module_types.include?(type) ? 0 : 1
|
||||
|
||||
course_module = course_modules.where(module_type: type).first
|
||||
course_module.update_attribute(:hidden, hidden_value) if course_module.present?
|
||||
end
|
||||
end
|
||||
|
||||
def all_course_module_types
|
||||
%w[activity announcement online_learning shixun_homework common_homework group_homework exercise attachment course_group graduation poll board statistics video]
|
||||
end
|
||||
|
||||
def get_course_module_by_type(type)
|
||||
#CourseModule.where(course_id: course_id, module_type: type).first
|
||||
self.course_modules.where(module_type: type).first
|
||||
end
|
||||
|
||||
# 创建课程讨论区
|
||||
def create_board_sync
|
||||
boards.create(name: '讨论区', description: name, project_id: -1)
|
||||
end
|
||||
|
||||
def delete!
|
||||
update_attribute(:is_delete, true)
|
||||
end
|
||||
|
||||
def attachment_count
|
||||
Attachment.where(container: self).count
|
||||
end
|
||||
|
||||
# 课堂某角色的成员数量:[1, 2, 3] 是教师身份、4 学生身份
|
||||
def course_member_count(roles)
|
||||
course_members.where(role: roles).size
|
||||
end
|
||||
|
||||
# 课堂老师
|
||||
def teachers
|
||||
course_members.where(role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR])
|
||||
end
|
||||
|
||||
def teachers_without_assistant_professor
|
||||
course_members.where(role: %i[CREATOR PROFESSOR])
|
||||
end
|
||||
|
||||
# 更新课程的访问人数
|
||||
def update_visits(new_visits)
|
||||
update_attributes(visits: new_visits)
|
||||
end
|
||||
|
||||
# 老师负责的分班id
|
||||
def charge_group_ids user
|
||||
member = course_member(user.id)
|
||||
group_ids = if member.present?
|
||||
member.teacher_course_groups.size > 0 ? member.teacher_course_groups.pluck(:course_group_id) : course_groups.pluck(:id)
|
||||
elsif user.admin_or_business?
|
||||
course_groups.pluck(:id)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
# 生成邀请码
|
||||
CODES = %W(2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z)
|
||||
def generate_invite_code
|
||||
return invite_code if invite_code.present? && invite_code.size >= 5
|
||||
|
||||
code = CODES.sample(5).join
|
||||
while Course.exists?(invite_code: code) do
|
||||
code = CODES.sample(5).join
|
||||
end
|
||||
update_attribute(:invite_code, code)
|
||||
|
||||
code
|
||||
end
|
||||
|
||||
# 课堂主讨论区
|
||||
def course_board
|
||||
board = boards.find_by(parent_id: 0)
|
||||
return board if board.present?
|
||||
|
||||
create_board_sync
|
||||
Board.find_by(parent_id: 0, course_id: id)
|
||||
end
|
||||
|
||||
# 是否是课堂的成员(未实现,暂时返回true)
|
||||
def member?(user)
|
||||
true
|
||||
end
|
||||
|
||||
# 是否具有分班权限,返回分班的id
|
||||
def group_course_power(user_id)
|
||||
teacher_course_groups.where(user_id: user_id).pluck(:course_group_id)
|
||||
end
|
||||
|
||||
#课程动态公共表记录
|
||||
def act_as_course_activity
|
||||
self.course_act << CourseActivity.new(user_id: tea_id, course_id: id)
|
||||
end
|
||||
|
||||
# 当前老师分班下的所有学生
|
||||
def user_group_students(user_id)
|
||||
group_ids = teacher_course_groups.where(user_id: user_id).pluck(:course_group_id)
|
||||
course_members.where(course_group_id: group_ids)
|
||||
end
|
||||
|
||||
def self_duplicate
|
||||
DuplicateCourseService.call(self, User.current)
|
||||
end
|
||||
|
||||
def update_quotes attachment
|
||||
if attachment.copy_from
|
||||
attachments = Attachment.find_by_sql("select * from attachments where copy_from = #{attachment.copy_from} or id = #{attachment.copy_from}")
|
||||
else
|
||||
attachments = Attachment.find_by_sql("select * from attachments where copy_from = #{attachment.id} or id = #{attachment.copy_from}")
|
||||
end
|
||||
attachment.quotes = get_qute_number attachment
|
||||
attachment.save
|
||||
attachments.each do |att|
|
||||
att.quotes = attachment.quotes
|
||||
att.save
|
||||
end
|
||||
end
|
||||
|
||||
def get_qute_number attachment
|
||||
if attachment.copy_from
|
||||
result = Attachment.find_by_sql("select count(*) as number from attachments where copy_from = #{attachment.copy_from}")
|
||||
else
|
||||
result = Attachment.find_by_sql("select count(*) as number from attachments where copy_from = #{attachment.id}")
|
||||
end
|
||||
if result.nil? || result.count <= 0
|
||||
0
|
||||
else
|
||||
result[0].number
|
||||
end
|
||||
end
|
||||
|
||||
#获取试卷/问卷已发布的班级id,名称和人数。当为统一设置时,显示全部,否则只显示当前已发布的班级信息
|
||||
def get_ex_published_course(common_ids)
|
||||
teacher_power_courses = []
|
||||
publish_groups = course_groups.where(id: common_ids)
|
||||
if common_ids.include?(0)
|
||||
teacher_power_courses << {course_name:"未分班", course_id: 0, student_count: none_group_count}
|
||||
end
|
||||
if publish_groups.present?
|
||||
publish_groups.each do |group|
|
||||
teacher_power_courses << {course_name: group&.name,course_id: group&.id, student_count: group&.course_members_count}
|
||||
end
|
||||
end
|
||||
teacher_power_courses
|
||||
end
|
||||
|
||||
def create_stages subject
|
||||
if subject
|
||||
subject.stages.each do |stage|
|
||||
new_stage = CourseStage.create!(course_id: id, name: stage.name, description: stage.description, position: stage.position)
|
||||
stage.stage_shixuns.each do |stage_shixun|
|
||||
CourseStageShixun.create!(course_id: id, course_stage_id: new_stage.id, shixun_id: stage_shixun.shixun_id, position: stage_shixun.position)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def learning? user_id
|
||||
Myshixun.where(user_id: user_id, shixun_id: shixuns).exists?
|
||||
end
|
||||
|
||||
def my_subject_progress myshixuns
|
||||
my_challenge_count = Game.where(myshixun_id: myshixuns.pluck(:id), status: 2).pluck(:challenge_id).uniq.size
|
||||
course_challeng_count = shixuns.pluck(:challenges_count).sum
|
||||
count = course_challeng_count == 0 ? 0 : ((my_challenge_count.to_f / course_challeng_count).round(2) * 100).to_i
|
||||
end
|
||||
|
||||
# 课堂实训作业的评测次数
|
||||
def evaluate_count
|
||||
course_user_ids = students.pluck(:user_id)
|
||||
shixun_ids = homework_commons.joins(:homework_commons_shixun).where(homework_type: 4).pluck(:shixun_id)
|
||||
return 0 if shixun_ids.blank?
|
||||
Game.joins(:challenge).where(challenges: {shixun_id: shixun_ids}, games: {user_id: course_user_ids}).sum(:evaluate_count)
|
||||
end
|
||||
|
||||
def max_activity_time
|
||||
course_acts.pluck(:updated_at).max
|
||||
end
|
||||
|
||||
# 课堂作业数
|
||||
def course_homework_count type
|
||||
homework_commons.select{|homework| homework.homework_type == type}.size
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#创建课程后,给该用户发送消息
|
||||
def send_tiding
|
||||
self.tidings << Tiding.new(user_id: tea_id, trigger_user_id: 0, belong_container_id: id,
|
||||
belong_container_type: 'Course', tiding_type: 'System')
|
||||
end
|
||||
|
||||
def get_name_by_type(type)
|
||||
case type
|
||||
when 'activity' then '动态'
|
||||
when 'announcement' then '公告栏'
|
||||
when 'online_learning' then '课程学习'
|
||||
when 'shixun_homework' then '实训作业'
|
||||
when 'common_homework' then '普通作业'
|
||||
when 'group_homework' then '分组作业'
|
||||
when 'graduation' then '毕业设计'
|
||||
when 'exercise' then '试卷'
|
||||
when 'poll' then '问卷'
|
||||
when 'attachment' then '资源'
|
||||
when 'video' then '视频直播'
|
||||
when 'board' then '讨论'
|
||||
when 'course_group' then '分班'
|
||||
when 'statistics' then '统计'
|
||||
else ''
|
||||
end
|
||||
end
|
||||
|
||||
def get_position_by_type(type)
|
||||
case type
|
||||
when 'activity' then 1
|
||||
when 'announcement' then 2
|
||||
when 'online_learning' then 3
|
||||
when 'shixun_homework' then 4
|
||||
when 'common_homework' then 5
|
||||
when 'group_homework' then 6
|
||||
when 'graduation' then 7
|
||||
when 'exercise' then 8
|
||||
when 'poll' then 9
|
||||
when 'attachment' then 10
|
||||
when 'video' then 11
|
||||
when 'board' then 12
|
||||
when 'course_group' then 13
|
||||
when 'statistics' then 14
|
||||
else 100
|
||||
end
|
||||
end
|
||||
|
||||
def set_laboratory
|
||||
return unless new_record? # 新记录才需要标记
|
||||
|
||||
self.laboratory = Laboratory.current if laboratory_id.blank?
|
||||
end
|
||||
|
||||
def validate_sensitive_string
|
||||
raise("课堂名称包含敏感词汇,请重新输入") unless HarmoniousDictionary.clean?(name)
|
||||
end
|
||||
end
|
||||
54
app/models/course_activity.rb
Normal file
54
app/models/course_activity.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
class CourseActivity < ApplicationRecord
|
||||
belongs_to :course_act, polymorphic: true
|
||||
belongs_to :course
|
||||
belongs_to :user
|
||||
belongs_to :exercise
|
||||
belongs_to :poll
|
||||
belongs_to :course_message
|
||||
belongs_to :homework_common
|
||||
|
||||
# after_create :add_course_lead
|
||||
|
||||
def container_name
|
||||
case course_act_type
|
||||
when "HomeworkCommon"
|
||||
course_act&.name
|
||||
when "Exercise"
|
||||
course_act&.exercise_name
|
||||
when "Poll"
|
||||
course_act&.poll_name
|
||||
when "Message"
|
||||
course_act&.subject
|
||||
else
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
# 发布新课导语
|
||||
# 导语要放置在课程创建信息之后
|
||||
def add_course_lead
|
||||
# 避免空数据迁移报错问题
|
||||
if self.course_act_type == "Course"
|
||||
sample = PlatformSample.where(:samples_type => "courseGuide").first
|
||||
if sample.present? && sample.contents.present?
|
||||
content = sample.contents
|
||||
elsif Message.find(12440)
|
||||
lead_message = Message.find(12440)
|
||||
content = lead_message.content
|
||||
end
|
||||
if content
|
||||
# message的status状态为0为正常,为1表示创建课程时发送的message
|
||||
# author_id 默认为课程使者创建
|
||||
message = Message.create(subject: "新课导语",
|
||||
board_id: course.course_board.try(:id),
|
||||
author_id: 1,
|
||||
sticky: true,
|
||||
status: true,
|
||||
message_detail_attributes: {content: content}
|
||||
)
|
||||
# 更新的目的是为了排序,因为该条动态的时间可能与课程创建的动态创建时间一致
|
||||
message.course_acts.first.update_attribute(:updated_at, message.course_acts.first.updated_at + 1) if message.course_acts.first
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
34
app/models/course_group.rb
Normal file
34
app/models/course_group.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
class CourseGroup < ApplicationRecord
|
||||
default_scope { order("course_groups.position ASC") }
|
||||
belongs_to :course, counter_cache: true
|
||||
has_many :course_members
|
||||
has_many :exercise_group_settings,:dependent => :destroy
|
||||
has_many :attachment_group_settings, :dependent => :destroy
|
||||
has_many :homework_group_reviews, :dependent => :destroy
|
||||
has_many :teacher_course_groups, :dependent => :destroy
|
||||
has_many :homework_group_settings, :dependent => :destroy
|
||||
scope :by_group_ids, lambda { |ids| where(id: ids)}
|
||||
|
||||
validates :name, length: { maximum: 60, too_long: "不能超过60个字符" }
|
||||
validates_uniqueness_of :name, scope: :course_id, message: "不能创建相同名称的分班"
|
||||
|
||||
after_create :generate_invite_code
|
||||
|
||||
# 延迟生成邀请码
|
||||
def invite_code
|
||||
return generate_invite_code
|
||||
end
|
||||
|
||||
# 生成邀请码
|
||||
CODES = %W(2 3 4 5 6 7 8 9 A B C D E F G H J K L N M O P Q R S T U V W X Y Z)
|
||||
def generate_invite_code
|
||||
code = read_attribute(:invite_code)
|
||||
if !code || code.size < 6
|
||||
code = CODES.sample(6).join
|
||||
return generate_invite_code if CourseGroup.where(invite_code: code).present?
|
||||
update_attribute(:invite_code, code)
|
||||
end
|
||||
code
|
||||
end
|
||||
|
||||
end
|
||||
3
app/models/course_homework_category.rb
Normal file
3
app/models/course_homework_category.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class CourseHomeworkCategory < ApplicationRecord
|
||||
belongs_to :course
|
||||
end
|
||||
3
app/models/course_info.rb
Normal file
3
app/models/course_info.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class CourseInfo < ApplicationRecord
|
||||
belongs_to :course
|
||||
end
|
||||
15
app/models/course_list.rb
Normal file
15
app/models/course_list.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
class CourseList < ApplicationRecord
|
||||
has_many :courses
|
||||
has_many :question_banks
|
||||
has_many :homework_banks
|
||||
has_many :exercise_banks
|
||||
has_many :gtask_banks
|
||||
has_many :gtopic_banks
|
||||
belongs_to :user
|
||||
|
||||
validate :validate_sensitive_string
|
||||
|
||||
def validate_sensitive_string
|
||||
raise("课程名称包含敏感词汇,请重新输入") unless HarmoniousDictionary.clean?(name)
|
||||
end
|
||||
end
|
||||
159
app/models/course_member.rb
Normal file
159
app/models/course_member.rb
Normal file
@@ -0,0 +1,159 @@
|
||||
class CourseMember < ApplicationRecord
|
||||
# role 1:创建者 2:老师 3:助教 4:学生
|
||||
enum role: { CREATOR: 1, PROFESSOR: 2, ASSISTANT_PROFESSOR: 3, STUDENT: 4 }
|
||||
# is_active: true:当前活跃身份(多重身份的用户)
|
||||
|
||||
belongs_to :course, counter_cache: true
|
||||
belongs_to :user
|
||||
belongs_to :course_group, counter_cache: true, optional: true
|
||||
belongs_to :graduation_group, optional: true
|
||||
has_many :teacher_course_groups, dependent: :destroy
|
||||
|
||||
scope :teachers_and_admin, -> { where(role: %i[CREATOR PROFESSOR ASSISTANT_PROFESSOR]) }
|
||||
scope :students, ->(course) { where(course_id: course.id, role: %i[STUDENT])}
|
||||
scope :course_find_by_ids, lambda { |k,ids| where("#{k}": ids)}
|
||||
scope :course_students, -> {where(role: %i[STUDENT])}
|
||||
|
||||
#用户的身份查询
|
||||
scope :course_user_role, lambda { |k| where(role: k)}
|
||||
|
||||
# 未分班
|
||||
scope :ungroup_students, -> { where(course_group_id: 0, role: 4) }
|
||||
|
||||
# after_destroy :delete_works
|
||||
# after_create :work_operation
|
||||
def delete_works
|
||||
if self.role == "STUDENT"
|
||||
course = self.course
|
||||
student_works = StudentWork.joins(:homework_common).where(user_id: self.user_id, homework_commons: {course_id: course.id})
|
||||
student_works.update_all(is_delete: 1)
|
||||
|
||||
exercise_users = ExerciseUser.joins(:exercise).where(user_id: self.user_id, exercises: {course_id: course.id})
|
||||
exercise_users.update_all(is_delete: 1)
|
||||
|
||||
poll_users = PollUser.joins(:poll).where(user_id: self.user_id, polls: {course_id: course.id})
|
||||
poll_users.update_all(is_delete: 1)
|
||||
|
||||
course.graduation_works.where(user_id: self.user_id).update_all(is_delete: 1)
|
||||
end
|
||||
end
|
||||
|
||||
def work_operation
|
||||
if self.role == "STUDENT"
|
||||
recover_works
|
||||
create_exercise_users
|
||||
create_graduation_works
|
||||
create_poll_users
|
||||
create_student_works
|
||||
end
|
||||
end
|
||||
|
||||
# 加入班级时还原作品(如果有已删除作品的话)
|
||||
def recover_works
|
||||
course = self.course
|
||||
|
||||
student_works = StudentWork.joins(:homework_common).where(user_id: self.user_id, homework_commons: {course_id: course.id})
|
||||
student_works.update_all(is_delete: 0)
|
||||
|
||||
exercise_users = ExerciseUser.joins(:exercise).where(user_id: self.user_id, exercises: {course_id: course.id})
|
||||
exercise_users.update_all(is_delete: 0)
|
||||
|
||||
poll_users = PollUser.joins(:poll).where(user_id: self.user_id, polls: {course_id: course.id})
|
||||
poll_users.update_all(is_delete: 0)
|
||||
|
||||
graduation_works = course.graduation_works.where(user_id: self.user_id)
|
||||
graduation_works.update_all(is_delete: 0)
|
||||
end
|
||||
|
||||
# 加入班级时创建作业的作品(如果没有作品才创建)
|
||||
def create_student_works
|
||||
course = self.course
|
||||
|
||||
homework_commons = course.homework_commons.where(homework_type: %i[normal group practice])
|
||||
str = ""
|
||||
homework_commons.each do |homework|
|
||||
next if homework.student_works.where(user_id: self.user_id).any?
|
||||
str += "," if str != ""
|
||||
str += "(#{homework.id}, #{user_id}, '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')"
|
||||
end
|
||||
|
||||
if str != ""
|
||||
sql = "insert into student_works (homework_common_id, user_id, created_at, updated_at) values" + str
|
||||
ActiveRecord::Base.connection.execute sql
|
||||
end
|
||||
end
|
||||
|
||||
# 加入班级时创建已发布试卷的作品(如果没有作品才创建)
|
||||
def create_exercise_users
|
||||
course = self.course
|
||||
exercises = course.exercises
|
||||
|
||||
str = ""
|
||||
exercises.each do |exercise|
|
||||
next if exercise.exercise_users.where(user_id: self.user_id).any?
|
||||
str += "," if str != ""
|
||||
str += "(#{user_id}, #{exercise.id}, 0, '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')"
|
||||
end
|
||||
|
||||
if str != ""
|
||||
sql = "insert into exercise_users (user_id, exercise_id, commit_status, created_at, updated_at) values" + str
|
||||
ActiveRecord::Base.connection.execute sql
|
||||
end
|
||||
end
|
||||
|
||||
# 加入班级时创建已发布问卷的作品(如果没有作品才创建)
|
||||
def create_poll_users
|
||||
course = self.course
|
||||
polls = course.polls
|
||||
|
||||
str = ""
|
||||
polls.each do |poll|
|
||||
next if poll.poll_users.where(user_id: self.user_id).any?
|
||||
str += "," if str != ""
|
||||
str += "(#{user_id}, #{poll.id}, 0, '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')"
|
||||
end
|
||||
|
||||
if str != ""
|
||||
sql = "insert into poll_users (user_id, poll_id, commit_status, created_at, updated_at) values" + str
|
||||
ActiveRecord::Base.connection.execute sql
|
||||
end
|
||||
end
|
||||
|
||||
# 创建毕设任务作品(如果没有作品才创建)
|
||||
def create_graduation_works
|
||||
course = self.course
|
||||
tasks = course.graduation_tasks
|
||||
|
||||
str = ""
|
||||
tasks.each do |task|
|
||||
next if task.graduation_works.where(user_id: self.user_id).any?
|
||||
str += "," if str != ""
|
||||
str += "(#{task.id}, #{user_id}, #{course_id}, '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}', '#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}')"
|
||||
end
|
||||
|
||||
if str != ""
|
||||
sql = "insert into graduation_works (graduation_task_id, user_id, course_id, created_at, updated_at) values" + str
|
||||
ActiveRecord::Base.connection.execute sql
|
||||
end
|
||||
end
|
||||
|
||||
# 分班名称
|
||||
def course_group_name
|
||||
self.course_group_id == 0 ? "未分班" : course_group.try(:name)
|
||||
end
|
||||
|
||||
# 学生的分班老师
|
||||
def member_teachers
|
||||
teacher_groups = course.teacher_course_groups
|
||||
if teacher_groups.count > 0
|
||||
member_ids = teacher_groups.where(course_group_id: self.try(:course_group_id)).pluck(:course_member_id).compact
|
||||
|
||||
none_group_teachers = teacher_groups.pluck(:course_member_id).compact.size > 0 ? teacher_groups.pluck(:course_member_id).compact.join(',') : -1
|
||||
teachers = course.teachers.where("course_members.id not in (#{none_group_teachers}) or
|
||||
course_members.id in (#{member_ids.size > 0 ? member_ids.join(',') : -1})")
|
||||
else
|
||||
teachers = course.teachers
|
||||
end
|
||||
teachers
|
||||
end
|
||||
end
|
||||
45
app/models/course_message.rb
Normal file
45
app/models/course_message.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
class CourseMessage < ApplicationRecord
|
||||
enum status: { UNHANDLED: 0, PASSED: 1, REJECTED: 2 }
|
||||
belongs_to :course
|
||||
belongs_to :user
|
||||
has_one :course_act, class_name: 'CourseActivity', as: :course_act, dependent: :destroy
|
||||
|
||||
scope :find_by_course, ->(course) { where(course_id: course.id) }
|
||||
scope :join_course_requests, -> { where(course_message_type: "JoinCourseRequest") }
|
||||
scope :unhandled, -> { where(status: :UNHANDLED) }
|
||||
|
||||
scope :unhandled_join_course_requests_by_course, ->(course) { find_by_course(course).join_course_requests.unhandled }
|
||||
|
||||
after_create :act_as_course_activity
|
||||
|
||||
def pass!
|
||||
update!(status: :PASSED)
|
||||
send_deal_tiding(1)
|
||||
end
|
||||
|
||||
def application_user
|
||||
User.find_by(id: course_message_id)
|
||||
end
|
||||
|
||||
def reject!
|
||||
update!(status: :REJECTED)
|
||||
send_deal_tiding(2)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#课程动态公共表记录
|
||||
def act_as_course_activity
|
||||
self.course_act << CourseActivity.new(user_id: course_message_id, course_id: course_id)
|
||||
end
|
||||
|
||||
def send_deal_tiding deal_status
|
||||
# 发送申请处理结果消息
|
||||
Tiding.create!(
|
||||
user_id: course_message_id, trigger_user_id: 0, container_id: course_id, container_type: 'DealCourse',
|
||||
belong_container: course, extra: content.to_i == 2 ? '9' : '7', tiding_type: 'System', status: deal_status
|
||||
)
|
||||
# 将申请消息置为已处理
|
||||
Tiding.where(trigger_user_id: course_message_id, container_id: course_id, container_type: 'JoinCourse', status: 0).update_all(status: 1)
|
||||
end
|
||||
end
|
||||
28
app/models/course_module.rb
Normal file
28
app/models/course_module.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
class CourseModule < ApplicationRecord
|
||||
default_scope { order("course_modules.position ASC") }
|
||||
belongs_to :course
|
||||
|
||||
# 二级目录
|
||||
has_many :course_second_categories
|
||||
|
||||
validates :module_name, length: { maximum: 20, too_long: "不能超过20个字符" }
|
||||
|
||||
scope :not_hidden, -> { where(hidden: 0) }
|
||||
scope :graduation_module, -> { where(module_type: "graduation") }
|
||||
scope :graduation_module_not_hidden, -> { graduation_module.where(hidden: 0) }
|
||||
scope :board_module, -> { where(module_type: 'board') }
|
||||
scope :attachment_module, -> { includes(:course_second_categories).where(module_type: 'attachment') }
|
||||
scope :common_homework_module, -> { where(module_type: 'common_homework') }
|
||||
scope :group_homework_module, -> { where(module_type: 'group_homework') }
|
||||
scope :shixun_homework_module, -> { where(module_type: 'shixun_homework') }
|
||||
scope :search_by_module_type, -> (type) {where(module_type:type)}
|
||||
|
||||
# 课堂模块的子目录
|
||||
def course_second_categories
|
||||
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
|
||||
15
app/models/course_second_category.rb
Normal file
15
app/models/course_second_category.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
class CourseSecondCategory < ApplicationRecord
|
||||
default_scope { order("course_second_categories.position ASC") }
|
||||
|
||||
belongs_to :course
|
||||
belongs_to :course_module
|
||||
has_many :homework_commons
|
||||
|
||||
validates :name, length: { maximum: 60, too_long: "不能超过60个字符" }
|
||||
|
||||
def category_type_str
|
||||
category_type == "graduation" && name == "毕设选题" ? "graduation_topics" : (
|
||||
category_type == "graduation" && name == "毕设任务" ? "graduation_tasks" : category_type
|
||||
)
|
||||
end
|
||||
end
|
||||
9
app/models/course_stage.rb
Normal file
9
app/models/course_stage.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class CourseStage < ApplicationRecord
|
||||
belongs_to :course
|
||||
|
||||
has_many :course_stage_shixuns, -> { order("course_stage_shixuns.position ASC") }, dependent: :destroy
|
||||
has_many :shixuns, :through => :course_stage_shixuns
|
||||
|
||||
validates :name, length: { maximum: 60 , too_long: "不能超过60个字符"}
|
||||
validates :description, length: { maximum: 1000, too_long: "不能超过1000个字符" }
|
||||
end
|
||||
5
app/models/course_stage_shixun.rb
Normal file
5
app/models/course_stage_shixun.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class CourseStageShixun < ApplicationRecord
|
||||
belongs_to :course
|
||||
belongs_to :course_stage, counter_cache: :shixuns_count
|
||||
belongs_to :shixun
|
||||
end
|
||||
4
app/models/course_video.rb
Normal file
4
app/models/course_video.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class CourseVideo < ApplicationRecord
|
||||
belongs_to :course
|
||||
belongs_to :video
|
||||
end
|
||||
11
app/models/customer.rb
Normal file
11
app/models/customer.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class Customer < ApplicationRecord
|
||||
default_scope { order(created_at: :desc) }
|
||||
|
||||
belongs_to :school
|
||||
belongs_to :partner_manager_group, optional: true
|
||||
|
||||
has_many :partner_customers, dependent: :destroy
|
||||
has_many :partners, through: :partner_customers
|
||||
|
||||
has_many :users
|
||||
end
|
||||
19
app/models/department.rb
Normal file
19
app/models/department.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
class Department < ApplicationRecord
|
||||
belongs_to :school
|
||||
|
||||
has_many :department_members, dependent: :destroy
|
||||
has_many :member_users, through: :department_members, source: :user
|
||||
|
||||
has_many :user_extensions, dependent: :nullify
|
||||
has_many :apply_add_departments, dependent: :destroy
|
||||
|
||||
scope :without_deleted, -> { where(is_delete: false) }
|
||||
|
||||
def member?(user)
|
||||
department_members.exists?(user_id: user.id)
|
||||
end
|
||||
|
||||
def soft_delete!
|
||||
update!(is_delete: true)
|
||||
end
|
||||
end
|
||||
4
app/models/department_member.rb
Normal file
4
app/models/department_member.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class DepartmentMember < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :department
|
||||
end
|
||||
8
app/models/diff_record.rb
Normal file
8
app/models/diff_record.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
class DiffRecord < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :container, polymorphic: true
|
||||
|
||||
has_one :diff_record_content, dependent: :destroy
|
||||
|
||||
delegate :content, to: :diff_record_content
|
||||
end
|
||||
3
app/models/diff_record_content.rb
Normal file
3
app/models/diff_record_content.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class DiffRecordContent < ApplicationRecord
|
||||
belongs_to :diff_record
|
||||
end
|
||||
12
app/models/discipline.rb
Normal file
12
app/models/discipline.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class Discipline < ApplicationRecord
|
||||
default_scope { order(position: :asc) }
|
||||
|
||||
has_many :sub_disciplines, -> { order("sub_disciplines.position ASC") }, dependent: :destroy
|
||||
|
||||
has_many :shixun_sub_disciplines, -> { where("shixun = 1") }, class_name: "SubDiscipline"
|
||||
has_many :subject_sub_disciplines, -> { where("subject = 1") }, class_name: "SubDiscipline"
|
||||
has_many :question_sub_disciplines, -> { where("question = 1") }, class_name: "SubDiscipline"
|
||||
|
||||
validates_presence_of :name
|
||||
|
||||
end
|
||||
80
app/models/discuss.rb
Normal file
80
app/models/discuss.rb
Normal file
@@ -0,0 +1,80 @@
|
||||
class Discuss < ApplicationRecord
|
||||
default_scope { order(created_at: :desc) }
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :parent, class_name: 'Discuss', foreign_key: :parent_id, optional: true
|
||||
|
||||
has_many :children, -> { reorder(created_at: :asc) }, class_name: 'Discuss', foreign_key: :parent_id
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
has_one :praise_tread_cache, as: :object, dependent: :destroy
|
||||
belongs_to :dis, polymorphic: true
|
||||
|
||||
belongs_to :challenge, optional: true
|
||||
validate :validate_sensitive_string
|
||||
|
||||
validates :content, length: { maximum: 2000, too_long: "不能超过2000个字符" }
|
||||
|
||||
after_create :send_tiding
|
||||
|
||||
scope :children, -> (discuss_id){ where(parent_id: discuss_id).includes(:user).reorder(created_at: :asc) }
|
||||
|
||||
def has_parent?
|
||||
parent_id.present?
|
||||
end
|
||||
|
||||
def username
|
||||
user.full_name
|
||||
end
|
||||
|
||||
def can_deleted?(user)
|
||||
user.admin? || user.id == user_id
|
||||
end
|
||||
|
||||
def game_url(shixun, user)
|
||||
return '' unless shixun.has_manager?(user)
|
||||
|
||||
game = Game.joins(:challenge).where(challenges: { shixun_id: shixun.id, position: position || 1 })
|
||||
.select(:identifier).find_by(user_id: user_id)
|
||||
"/tasks/#{game&.identifier}"
|
||||
end
|
||||
|
||||
# def contents(shixun, user)
|
||||
# return content unless hidden?
|
||||
#
|
||||
# shixun.has_manager?(user) ? content : ''
|
||||
# end
|
||||
|
||||
def child_discuss(user)
|
||||
Discuss.where(parent_id: self.id).includes(:user).reorder(created_at: :asc)
|
||||
end
|
||||
|
||||
def child_discuss_count
|
||||
Discuss.where(root_id: id).count
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_tiding
|
||||
if dis_type == 'Shixun'
|
||||
send_user_id = has_parent? ? parent.user_id : Challenge.find(challenge_id).user_id
|
||||
parent_container_type = 'Challenge'
|
||||
challenge_id = challenge_id
|
||||
extra = ''
|
||||
elsif dis_type == 'Hack'
|
||||
send_user_id = has_parent? ? parent.user_id : Hack.find(dis_id).user_id
|
||||
parent_container_type = 'Hack'
|
||||
challenge_id = dis_id
|
||||
extra = HackUserLastestCode.where(user_id: user_id, hack_id: dis_id).first&.identifier
|
||||
end
|
||||
base_attrs = {
|
||||
trigger_user_id: user_id, parent_container_id: challenge_id, parent_container_type: parent_container_type,
|
||||
belong_container_id: dis_id, belong_container_type: dis_type, viewed: 0, tiding_type: 'Comment', extra: extra
|
||||
}
|
||||
tidings.create!(base_attrs.merge(user_id: send_user_id))
|
||||
end
|
||||
|
||||
def validate_sensitive_string
|
||||
raise("内容包含敏感词汇,请重新输入") unless HarmoniousDictionary.clean?(content)
|
||||
end
|
||||
end
|
||||
25
app/models/edu_setting.rb
Normal file
25
app/models/edu_setting.rb
Normal file
@@ -0,0 +1,25 @@
|
||||
class EduSetting < ApplicationRecord
|
||||
after_commit :expire_value_cache
|
||||
|
||||
def value_cache_key
|
||||
self.class.value_cache_key(name)
|
||||
end
|
||||
|
||||
def self.get(key)
|
||||
Rails.cache.fetch(value_cache_key(key), expires_in: 1.days) do
|
||||
find_by_name(key.to_s)&.value
|
||||
end
|
||||
end
|
||||
|
||||
def self.value_cache_key(name)
|
||||
raise ArgumentError if name.blank?
|
||||
|
||||
"educoder/edu-settings/#{name.to_s}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expire_value_cache
|
||||
Rails.cache.write(value_cache_key, value)
|
||||
end
|
||||
end
|
||||
3
app/models/experience.rb
Normal file
3
app/models/experience.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class Experience < ApplicationRecord
|
||||
belongs_to :user
|
||||
end
|
||||
5
app/models/forge_activity.rb
Normal file
5
app/models/forge_activity.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class ForgeActivity < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :project
|
||||
belongs_to :forge_act, polymorphic: true
|
||||
end
|
||||
3
app/models/forum.rb
Normal file
3
app/models/forum.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class Forum < ApplicationRecord
|
||||
has_many :memos, dependent: :destroy
|
||||
end
|
||||
3
app/models/help.rb
Normal file
3
app/models/help.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class Help < ApplicationRecord
|
||||
|
||||
end
|
||||
3
app/models/ignore.rb
Normal file
3
app/models/ignore.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class Ignore < ApplicationRecord
|
||||
include Projectable
|
||||
end
|
||||
104
app/models/issue.rb
Normal file
104
app/models/issue.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
class Issue < ApplicationRecord
|
||||
#issue_type 1为普通,2为悬赏
|
||||
belongs_to :project, :counter_cache => true
|
||||
belongs_to :tracker,optional: true
|
||||
has_many :project_trends, as: :trend, dependent: :destroy
|
||||
has_one :pull_request
|
||||
# belongs_to :issue_tag,optional: true
|
||||
belongs_to :priority, :class_name => 'IssuePriority', foreign_key: :priority_id,optional: true
|
||||
belongs_to :version, foreign_key: :fixed_version_id,optional: true, counter_cache: true
|
||||
belongs_to :user,optional: true, foreign_key: :author_id
|
||||
belongs_to :issue_status, foreign_key: :status_id,optional: true
|
||||
has_many :commit_issues
|
||||
has_many :attachments, as: :container, dependent: :destroy
|
||||
has_many :memos
|
||||
has_many :journals, :as => :journalized, :dependent => :destroy
|
||||
has_many :journal_details, through: :journals
|
||||
has_many :issue_tags_relates, dependent: :destroy
|
||||
has_many :issue_tags, through: :issue_tags_relates
|
||||
has_many :issue_times, dependent: :destroy
|
||||
has_many :issue_depends, dependent: :destroy
|
||||
scope :issue_includes, ->{includes(:user)}
|
||||
scope :issue_many_includes, ->{includes(journals: :user)}
|
||||
scope :issue_issue, ->{where(issue_classify: [nil,"issue"])}
|
||||
scope :issue_pull_request, ->{where(issue_classify: "pull_request")}
|
||||
|
||||
after_update :change_versions_count
|
||||
|
||||
|
||||
def get_assign_user
|
||||
User.select(:login, :lastname,:firstname, :nickname)&.find_by_id(self.assigned_to_id)
|
||||
end
|
||||
|
||||
def create_journal_detail(change_files, issue_files, issue_file_ids)
|
||||
journal_params = {
|
||||
journalized_id: self.id, journalized_type: "Issue", user_id: self.author_id
|
||||
}
|
||||
journal = Journal.new journal_params
|
||||
|
||||
if journal.save
|
||||
if change_files
|
||||
old_attachment_names = self.attachments.select(:filename,:id).where(id: issue_file_ids).pluck(:filename).join(",")
|
||||
new_attachment_name = self.attachments.select(:filename,:id).where(id: issue_files).pluck(:filename).join(",")
|
||||
journal.journal_details.create(property: "attachment", prop_key: "#{issue_files.size}", old_value: old_attachment_names, value: new_attachment_name)
|
||||
end
|
||||
change_values = %w(subject description is_private assigned_to_id tracker_id status_id priority_id fixed_version_id start_date due_date estimated_hours done_ratio issue_tags_value issue_type token)
|
||||
change_values.each do |at|
|
||||
if self.send("saved_change_to_#{at}?")
|
||||
journal.journal_details.create(property: "attr", prop_key: "#{at}", old_value: self.send("#{at}_before_last_save"), value: self.send(at))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def custom_journal_detail(prop_key, old_value, value)
|
||||
journal_params = {
|
||||
journalized_id: self.id, journalized_type: "Issue", user_id: self.author_id
|
||||
}
|
||||
journal = Journal.new journal_params
|
||||
if journal.save
|
||||
journal.journal_details.create(property: "attr", prop_key: prop_key, old_value: old_value, value: value)
|
||||
end
|
||||
end
|
||||
|
||||
def get_journals_size
|
||||
journals.size
|
||||
end
|
||||
|
||||
def self.issues_count(tracker_id)
|
||||
includes(:trakcer).where(tracker_id: tracker_id).size
|
||||
end
|
||||
|
||||
def get_issue_tags
|
||||
if issue_tags.present?
|
||||
issue_tags.select(:id,:name,:color).uniq.as_json
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_issue_tags_name
|
||||
if issue_tags.present?
|
||||
issue_tags.select(:name).uniq.pluck(:name).join(",")
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def only_reply_journals
|
||||
journals.where.not(notes: [nil, ""]).journal_includes.limit(2)
|
||||
end
|
||||
|
||||
def change_versions_count
|
||||
if self.version.present?
|
||||
if self.status_id == 5
|
||||
percent = self.version.issues_count == 0 ? 0.0 : ((self.version.closed_issues_count + 1).to_f / self.version.issues_count)
|
||||
self.version.update_attributes(closed_issues_count: (self.version.closed_issues_count + 1), percent: percent)
|
||||
else
|
||||
percent = self.version.issues_count == 0 ? 0.0 : ((self.version.closed_issues_count - 1).to_f / self.version.issues_count)
|
||||
self.version.update_attributes(closed_issues_count: (self.version.closed_issues_count - 1), percent: percent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
3
app/models/issue_depend.rb
Normal file
3
app/models/issue_depend.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class IssueDepend < ApplicationRecord
|
||||
belongs_to :issue
|
||||
end
|
||||
3
app/models/issue_priority.rb
Normal file
3
app/models/issue_priority.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class IssuePriority < ApplicationRecord
|
||||
has_many :issues
|
||||
end
|
||||
4
app/models/issue_status.rb
Normal file
4
app/models/issue_status.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class IssueStatus < ApplicationRecord
|
||||
has_many :issues
|
||||
belongs_to :project, optional: true
|
||||
end
|
||||
7
app/models/issue_tag.rb
Normal file
7
app/models/issue_tag.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class IssueTag < ApplicationRecord
|
||||
|
||||
has_many :issue_tags_relates, dependent: :destroy
|
||||
has_many :issues, through: :issue_tags_relates
|
||||
belongs_to :project, optional: true
|
||||
|
||||
end
|
||||
4
app/models/issue_tags_relate.rb
Normal file
4
app/models/issue_tags_relate.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class IssueTagsRelate < ApplicationRecord
|
||||
belongs_to :issue
|
||||
belongs_to :issue_tag, counter_cache: :issues_count
|
||||
end
|
||||
4
app/models/issue_time.rb
Normal file
4
app/models/issue_time.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class IssueTime < ApplicationRecord
|
||||
belongs_to :issue
|
||||
belongs_to :user
|
||||
end
|
||||
4
app/models/item_analysis.rb
Normal file
4
app/models/item_analysis.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class ItemAnalysis < ApplicationRecord
|
||||
belongs_to :item_bank, touch: true
|
||||
validates :analysis, length: { maximum: 5000, too_long: "不能超过5000个字符" }
|
||||
end
|
||||
45
app/models/item_bank.rb
Normal file
45
app/models/item_bank.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
class ItemBank < ApplicationRecord
|
||||
# difficulty: 1 简单 2 适中 3 困难
|
||||
enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 }
|
||||
# item_type: 0 单选 1 多选 2 判断 3 填空 4 简答 5 实训 6 编程
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :sub_discipline, optional: true
|
||||
|
||||
has_one :item_analysis, dependent: :destroy
|
||||
has_many :item_choices, dependent: :destroy
|
||||
has_many :item_baskets, dependent: :destroy
|
||||
has_many :tag_discipline_containers, as: :container, dependent: :destroy
|
||||
has_many :tag_disciplines, through: :tag_discipline_containers
|
||||
|
||||
belongs_to :container, polymorphic: true, optional: true
|
||||
validates :name, presence: true, length: { maximum: 1000, too_long: "不能超过1000个字符" }
|
||||
|
||||
def analysis
|
||||
item_analysis&.analysis
|
||||
end
|
||||
|
||||
def apply?
|
||||
!public && ApplyAction.exists?(container_type: "ItemBank", container_id: id, status: 0)
|
||||
end
|
||||
|
||||
def type_string
|
||||
case item_type
|
||||
when "SINGLE" then "单选题"
|
||||
when "MULTIPLE" then "多选题"
|
||||
when "JUDGMENT" then "判断题"
|
||||
when "COMPLETION" then "填空题"
|
||||
when "SUBJECTIVE" then "简答题"
|
||||
when "PRACTICAL" then "实训题"
|
||||
when "PROGRAM" then "编程题"
|
||||
end
|
||||
end
|
||||
|
||||
def difficulty_string
|
||||
case difficulty
|
||||
when 1 then "简单"
|
||||
when 2 then "适中"
|
||||
when 3 then "困难"
|
||||
end
|
||||
end
|
||||
end
|
||||
7
app/models/item_basket.rb
Normal file
7
app/models/item_basket.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
class ItemBasket < ApplicationRecord
|
||||
enum item_type: { SINGLE: 0, MULTIPLE: 1, JUDGMENT: 2, COMPLETION: 3, SUBJECTIVE: 4, PRACTICAL: 5, PROGRAM: 6 }
|
||||
|
||||
belongs_to :item_bank
|
||||
belongs_to :user, optional: true
|
||||
belongs_to :examination_intelligent_setting, optional: true
|
||||
end
|
||||
5
app/models/item_choice.rb
Normal file
5
app/models/item_choice.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class ItemChoice < ApplicationRecord
|
||||
belongs_to :item_bank, touch: true
|
||||
validates :choice_text, presence: true, length: { maximum: 500, too_long: "不能超过500个字符" }
|
||||
|
||||
end
|
||||
150
app/models/journal.rb
Normal file
150
app/models/journal.rb
Normal file
@@ -0,0 +1,150 @@
|
||||
class Journal < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :issue, foreign_key: :journalized_id, :touch => true
|
||||
has_many :journal_details, :dependent => :delete_all
|
||||
has_many :attachments, as: :container, dependent: :destroy
|
||||
|
||||
scope :journal_includes, ->{includes(:user, :journal_details, :attachments)}
|
||||
scope :parent_journals, ->{where(parent_id: nil)}
|
||||
scope :children_journals, lambda{|journal_id| where(parent_id: journal_id)}
|
||||
|
||||
|
||||
def is_journal_detail?
|
||||
self.notes.blank? && self.journal_details.present?
|
||||
end
|
||||
|
||||
def journal_content
|
||||
send_details = []
|
||||
if self.is_journal_detail?
|
||||
details = self.journal_details.select(:property, :prop_key, :old_value, :value).pluck(:property, :prop_key, :old_value, :value)
|
||||
if details.size > 0
|
||||
details.each do |de|
|
||||
if de[0] == "attr"
|
||||
content = ""
|
||||
else
|
||||
content = "附件"
|
||||
end
|
||||
old_value = de[2]
|
||||
value = de[3]
|
||||
if de[1].to_i > 0
|
||||
prop_name = ""
|
||||
else
|
||||
prop_name = I18n.t("journal_detail.#{de[1]}")
|
||||
case de[1]
|
||||
when "is_private"
|
||||
old_value = I18n.t("journal_detail.#{de[2]}")
|
||||
value = I18n.t("journal_detail.#{de[3]}")
|
||||
when "assigned_to_id"
|
||||
u = User.select(:id, :login, :lastname, :firstname, :nickname)
|
||||
old_value = de[2].to_i > 0 ? u.find_by_id(de[2]).try(:show_real_name) : ""
|
||||
assign_user = de[3].to_i > 0 ? u.find_by_id(de[3]) : ""
|
||||
if assign_user.present?
|
||||
value = assign_user.try(:show_real_name)
|
||||
else
|
||||
value = ""
|
||||
end
|
||||
|
||||
when "tracker_id"
|
||||
t = Tracker.select(:id, :name)
|
||||
old_value = de[2].to_i > 0 ? t.find_by_id(de[2]).try(:name) : ""
|
||||
tracker_name = de[3].to_i > 0 ? t.find_by_id(de[3]) : ""
|
||||
if tracker_name
|
||||
value = tracker_name.try(:name)
|
||||
else
|
||||
value = ""
|
||||
end
|
||||
|
||||
when "status_id"
|
||||
t = IssueStatus.select(:id, :name)
|
||||
old_value = de[2].to_i > 0 ? t.find_by_id(de[2]).try(:name) : ""
|
||||
type_name = de[3].to_i > 0 ? t.find_by_id(de[3]) : ""
|
||||
if type_name
|
||||
value = type_name.try(:name)
|
||||
else
|
||||
value = ""
|
||||
end
|
||||
when "priority_id"
|
||||
t = IssuePriority.select(:id, :name)
|
||||
old_value = de[2].to_i > 0 ? t.find_by_id(de[2]).try(:name): ""
|
||||
type_name = de[3].to_i > 0 ? t.find_by_id(de[3]) : ""
|
||||
if type_name
|
||||
value = type_name.try(:name)
|
||||
else
|
||||
value = ""
|
||||
end
|
||||
when "issue_tags_value"
|
||||
t = IssueTag.select(:id, :name)
|
||||
old_value = de[2].to_i > 0 ? t.where(id: de[2].split(",")).select(:id,:name,:color).as_json : ""
|
||||
if de[3].present?
|
||||
value = t.where(id: de[3].split(",")).select(:id,:name,:color).as_json
|
||||
else
|
||||
value = ""
|
||||
end
|
||||
when "fixed_version_id"
|
||||
t = Version.select(:id, :name)
|
||||
old_value = de[2].to_i > 0 ? t.find_by_id(de[2]).try(:name) : ""
|
||||
type_name = de[3].to_i > 0 ? t.find_by_id(de[3]) : ""
|
||||
if type_name
|
||||
value = type_name.try(:name)
|
||||
else
|
||||
value = ""
|
||||
end
|
||||
when "end_time"
|
||||
t = IssueTime.select(:id, :start_time, :end_time)
|
||||
type_name = de[2].to_i > 0 ? t.find_by_id(de[2]) : ""
|
||||
if type_name.present?
|
||||
old_value = "停止工作"
|
||||
d_value = type_name.end_time.to_i - type_name.start_time.to_i
|
||||
value = "#{Time.at(d_value).utc.strftime('%H h %M min %S s')}"
|
||||
else
|
||||
old_value = "停止工作"
|
||||
value = ""
|
||||
end
|
||||
when "issue_depend"
|
||||
t = Issue.select(:id,:subject )
|
||||
type_name = de[3].present? ? t&.find_by_id(de[3]) : ""
|
||||
if type_name.present?
|
||||
old_value = "增加依赖"
|
||||
value = {
|
||||
id: de[3],
|
||||
name: type_name.try(:subject)
|
||||
}
|
||||
else
|
||||
old_value = "增加依赖"
|
||||
value = ""
|
||||
end
|
||||
when "destroy_issue_depend"
|
||||
t = Issue.select(:id,:subject )
|
||||
type_name = de[3].present? ? t&.find_by_id(de[3]) : ""
|
||||
if type_name.present?
|
||||
old_value = "删除依赖"
|
||||
value = {
|
||||
id: de[3],
|
||||
name: type_name.try(:subject)
|
||||
}
|
||||
else
|
||||
old_value = "删除依赖"
|
||||
value = ""
|
||||
end
|
||||
when "done_ratio"
|
||||
old_value = "#{de[2]}%"
|
||||
value = "#{de[3]}%"
|
||||
else
|
||||
old_value = de[2]
|
||||
value = de[3]
|
||||
end
|
||||
end
|
||||
prop_hash = {
|
||||
detail: (content + prop_name),
|
||||
old_value: old_value,
|
||||
value: value
|
||||
}
|
||||
send_details.push(prop_hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
send_details
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
3
app/models/journal_detail.rb
Normal file
3
app/models/journal_detail.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class JournalDetail < ApplicationRecord
|
||||
belongs_to :journal
|
||||
end
|
||||
80
app/models/journals_for_message.rb
Normal file
80
app/models/journals_for_message.rb
Normal file
@@ -0,0 +1,80 @@
|
||||
class JournalsForMessage < ApplicationRecord
|
||||
belongs_to :jour, :polymorphic => true
|
||||
belongs_to :user
|
||||
belongs_to :parent, class_name: "JournalsForMessage", foreign_key: "m_parent_id",
|
||||
counter_cache: :m_reply_count, optional: true
|
||||
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
|
||||
#scope :children, -> {where(m_parent_id: self.id).includes(:user).reorder("created_on asc")}
|
||||
#scope :children, -> (discuss_id){ where(parent_id: discuss_id).includes(:user).reorder("created_at asc") }
|
||||
|
||||
scope :parent_comment, -> { where(m_parent_id: nil)}
|
||||
scope :search_by_jour_type, lambda{|type,ids| where(jour_type:type,jour_id: ids)}
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
|
||||
# "jour_type", # 留言所属类型
|
||||
# "jour_id", # 留言所属类型的id
|
||||
# "notes", # 留言内容
|
||||
# "reply_id", # 留言被回复留言者的用户id(用户a回复了用户b,这是b的id,用以查询谁给b留言了)
|
||||
# "status", # 留言是否被查看(弃用)
|
||||
# "user_id", # 留言者的id
|
||||
# "m_parent_id", # 留言信息的父留言id
|
||||
# "is_readed", # 留言是否已读
|
||||
# "m_reply_count", # 留言的回复数量
|
||||
# "m_reply_id" , # 回复某留言的留言id(a留言回复了b留言,这是b留言的id)
|
||||
# "is_comprehensive_evaluation", # 1 教师评论、2 匿评、3 留言
|
||||
# "hidden", 隐藏、
|
||||
|
||||
validates :notes, length: { maximum: 2000, too_long: "不能超过2000个字符" }
|
||||
|
||||
after_create :send_tiding
|
||||
|
||||
|
||||
# course_identity 课堂用户身份
|
||||
def contents_show course_identity
|
||||
if self.hidden && course_identity >= Course::STUDENT
|
||||
nil
|
||||
else
|
||||
self.notes
|
||||
end
|
||||
end
|
||||
|
||||
def can_delete course_identity
|
||||
course_identity < Course::STUDENT
|
||||
end
|
||||
|
||||
def created_at
|
||||
self.created_on
|
||||
end
|
||||
|
||||
def children page, limit
|
||||
JournalsForMessage.includes(:user).where(m_parent_id: self.id).page(page).per(limit).reorder("created_on asc")
|
||||
end
|
||||
|
||||
|
||||
def send_tiding
|
||||
# 回复和@同一个人时:只发@的消息(因@的消息先创建)
|
||||
case self.jour_type
|
||||
# 用户留言当做私信处理 不发消息
|
||||
when "Principal"
|
||||
=begin
|
||||
user_id = self.m_parent_id.present? ? JournalsForMessage.find(self.m_parent_id).user_id : self.jour_id
|
||||
if user_id != self.user_id && !self.tidings.where(:user_id => user_id, :trigger_user_id => self.user_id, :tiding_type => "Mentioned").first.present?
|
||||
self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => user_id, :parent_container_id => self.jour_id, :parent_container_type => self.jour_type, :belong_container_id => self.jour_id, :belong_container_type => "User", :viewed => 0, :tiding_type => self.m_parent_id.present? ? "Comment" : "Journal")
|
||||
end
|
||||
=end
|
||||
when "HomeworkCommon", "GraduationTopic"
|
||||
user_id = self.m_parent_id.present? ? JournalsForMessage.find(self.m_parent_id).user_id : (self.jour_type == "HomeworkCommon" ? self.jour.user_id : self.jour.tea_id)
|
||||
if user_id != self.user_id && !self.tidings.where(:user_id => user_id, :trigger_user_id => self.user_id, :tiding_type => "Mentioned").first.present?
|
||||
self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => user_id, :parent_container_id => self.jour_id, :parent_container_type => self.jour_type, :belong_container_id => self.jour.course_id, :belong_container_type => "Course", :viewed => 0, :tiding_type => "Comment")
|
||||
end
|
||||
when "StudentWorksScore"
|
||||
course_id = self.jour.try(:student_work).try(:homework_common).try(:course_id)
|
||||
user_id = self.m_parent_id.present? ? JournalsForMessage.find(self.m_parent_id).user_id : self.jour.user_id
|
||||
if user_id != self.user_id && !self.tidings.where(:user_id => user_id, :trigger_user_id => self.user_id, :tiding_type => "Mentioned").first.present?
|
||||
self.tidings << Tiding.new(:trigger_user_id => self.user_id, :user_id => user_id, :parent_container_id => self.jour_id, :parent_container_type => self.jour_type, :belong_container_id => course_id, :belong_container_type => "Course", :viewed => 0, :tiding_type => "Comment")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
129
app/models/laboratory.rb
Normal file
129
app/models/laboratory.rb
Normal file
@@ -0,0 +1,129 @@
|
||||
class Laboratory < ApplicationRecord
|
||||
belongs_to :school, optional: true
|
||||
|
||||
has_many :laboratory_users, dependent: :destroy
|
||||
has_many :users, through: :laboratory_users, source: :user
|
||||
|
||||
has_one :laboratory_setting, dependent: :destroy
|
||||
|
||||
has_many :portal_images, dependent: :destroy
|
||||
|
||||
has_many :laboratory_shixuns, dependent: :destroy
|
||||
# has_many :shixuns, through: :laboratory_shixuns, source: :shixun
|
||||
|
||||
has_many :laboratory_subjects, dependent: :destroy
|
||||
# has_many :subjects, through: :laboratory_subjects, source: :subject
|
||||
|
||||
has_many :courses, dependent: :destroy
|
||||
has_many :competitions, dependent: :destroy
|
||||
has_many :libraries, dependent: :destroy
|
||||
|
||||
validates :identifier, uniqueness: { case_sensitive: false }, allow_nil: true
|
||||
|
||||
delegate :name, :navbar, :footer, :login_logo_url, :nav_logo_url, :tab_logo_url, :default_navbar, to: :laboratory_setting
|
||||
|
||||
def site
|
||||
rails_env = EduSetting.get('rails_env')
|
||||
suffix = rails_env && rails_env != 'production' ? ".#{rails_env}.educoder.net" : '.educoder.net'
|
||||
|
||||
identifier ? "#{identifier}#{suffix}" : ''
|
||||
end
|
||||
|
||||
def self.find_by_subdomain(subdomain)
|
||||
return if subdomain.blank?
|
||||
|
||||
rails_env = EduSetting.get('rails_env')
|
||||
subdomain = subdomain.slice(0, subdomain.size - rails_env.size - 1) if rails_env && subdomain.end_with?(rails_env) # winse.dev => winse
|
||||
|
||||
find_by_identifier(subdomain)
|
||||
end
|
||||
|
||||
# def self.current=(laboratory)
|
||||
# Thread.current[:current_laboratory] = laboratory
|
||||
# end
|
||||
#
|
||||
# def self.current
|
||||
# Thread.current[:current_laboratory] ||= Laboratory.find(1)
|
||||
# end
|
||||
|
||||
def self.current=(user)
|
||||
RequestStore.store[:current_laboratory] = user
|
||||
end
|
||||
|
||||
def self.current
|
||||
RequestStore.store[:current_laboratory] ||= User.anonymous
|
||||
end
|
||||
|
||||
def shixuns
|
||||
if main_site?
|
||||
not_shixun_ids = Shixun.joins(:laboratory_shixuns).where("laboratory_shixuns.laboratory_id != #{Laboratory.current.id}")
|
||||
Shixun.where.not(id: not_shixun_ids.pluck(:shixun_id))
|
||||
elsif sync_shixun
|
||||
laboratory_shixun_ids = laboratory_shixuns.pluck(:shixun_id)
|
||||
school_shixun_ids = Shixun.joins("join user_extensions on shixuns.user_id=user_extensions.user_id").where(user_extensions: { school_id: school_id }).pluck(:id)
|
||||
shixun_ids = laboratory_shixun_ids + school_shixun_ids
|
||||
Shixun.where(id: shixun_ids.uniq)
|
||||
else
|
||||
Shixun.joins(:laboratory_shixuns).where(laboratory_shixuns: { laboratory_id: id })
|
||||
end
|
||||
end
|
||||
|
||||
def subjects
|
||||
if main_site?
|
||||
not_subject_ids = Subject.joins(:laboratory_subjects).where("laboratory_subjects.laboratory_id != #{Laboratory.current.id}")
|
||||
Subject.where.not(id: not_subject_ids.pluck(:subject_id))
|
||||
elsif sync_subject
|
||||
laboratory_subject_ids = laboratory_subjects.pluck(:subject_id)
|
||||
school_subject_ids = Subject.joins("join user_extensions on subjects.user_id=user_extensions.user_id").where(user_extensions: { school_id: school_id }).pluck(:id)
|
||||
subject_ids = laboratory_subject_ids + school_subject_ids
|
||||
Subject.where(id: subject_ids.uniq)
|
||||
else
|
||||
Subject.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
|
||||
end
|
||||
end
|
||||
|
||||
def all_courses
|
||||
main_site? || !sync_course ? courses : courses.or(Course.where(school_id: school_id))
|
||||
end
|
||||
|
||||
def shixun_repertoires
|
||||
where_sql = ShixunTagRepertoire.where("shixun_tag_repertoires.tag_repertoire_id = tag_repertoires.id")
|
||||
|
||||
# 云上实验室过滤
|
||||
unless main_site?
|
||||
where_sql = where_sql.joins("JOIN laboratory_shixuns ls ON ls.shixun_id = shixun_tag_repertoires.shixun_id "\
|
||||
"AND ls.laboratory_id = #{id}")
|
||||
end
|
||||
where_sql = where_sql.select('1').to_sql
|
||||
tags = TagRepertoire.where("EXISTS(#{where_sql})").distinct.includes(sub_repertoire: :repertoire)
|
||||
|
||||
tags_map = tags.group_by(&:sub_repertoire)
|
||||
sub_reps_map = tags_map.keys.group_by(&:repertoire)
|
||||
|
||||
sub_reps_map.keys.sort_by(&:updated_at).reverse.map do |repertoire|
|
||||
repertoire_hash = repertoire.as_json(only: %i[id name])
|
||||
repertoire_hash[:sub_repertoires] =
|
||||
sub_reps_map[repertoire].sort_by(&:updated_at).reverse.map do |sub_repertoire|
|
||||
sub_repertoire_hash = sub_repertoire.as_json(only: %i[id name])
|
||||
sub_repertoire_hash[:tags] = tags_map[sub_repertoire].sort_by(&:updated_at).reverse.map { |tag| tag.as_json(only: %i[id name]) }
|
||||
sub_repertoire_hash
|
||||
end
|
||||
repertoire_hash
|
||||
end
|
||||
end
|
||||
|
||||
def subject_repertoires
|
||||
exist_sql = Subject.where('subjects.repertoire_id = repertoires.id')
|
||||
|
||||
unless main_site?
|
||||
exist_sql = exist_sql.joins(:laboratory_subjects).where(laboratory_subjects: { laboratory_id: id })
|
||||
end
|
||||
|
||||
Repertoire.where("EXISTS(#{exist_sql.select('1').to_sql})").order(updated_at: :desc).distinct
|
||||
end
|
||||
|
||||
# 是否为主站
|
||||
def main_site?
|
||||
id == 1
|
||||
end
|
||||
end
|
||||
75
app/models/laboratory_setting.rb
Normal file
75
app/models/laboratory_setting.rb
Normal file
@@ -0,0 +1,75 @@
|
||||
class LaboratorySetting < ApplicationRecord
|
||||
belongs_to :laboratory
|
||||
|
||||
serialize :config, JSON
|
||||
|
||||
%i[name navbar footer].each do |method_name|
|
||||
define_method method_name do
|
||||
config&.[](method_name.to_s)
|
||||
end
|
||||
|
||||
define_method "#{method_name}=" do |value|
|
||||
self.config ||= {}
|
||||
config.[]=(method_name.to_s, value)
|
||||
end
|
||||
end
|
||||
|
||||
def login_logo_url
|
||||
image_url('login')
|
||||
end
|
||||
|
||||
def nav_logo_url
|
||||
image_url('nav')
|
||||
end
|
||||
|
||||
def tab_logo_url
|
||||
image_url('tab')
|
||||
end
|
||||
|
||||
def subject_banner_url
|
||||
image_url('_subject_banner')
|
||||
end
|
||||
|
||||
def course_banner_url
|
||||
image_url('_course_banner')
|
||||
end
|
||||
|
||||
def competition_banner_url
|
||||
image_url('_competition_banner')
|
||||
end
|
||||
|
||||
def moop_cases_banner_url
|
||||
image_url('_moop_cases_banner')
|
||||
end
|
||||
|
||||
def oj_banner_url
|
||||
image_url('_oj_banner')
|
||||
end
|
||||
|
||||
def default_navbar
|
||||
self.class.default_config[:navbar]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def image_url(type)
|
||||
return nil unless Util::FileManage.exists?(self, type)
|
||||
Util::FileManage.source_disk_file_url(self, type)
|
||||
end
|
||||
|
||||
def self.default_config
|
||||
{
|
||||
name: nil,
|
||||
navbar: [
|
||||
{ 'name' => '实践课程', 'link' => '/paths', 'hidden' => false },
|
||||
{ 'name' => '翻转课堂', 'link' => '/courses', 'hidden' => false },
|
||||
{ 'name' => '实训项目', 'link' => '/shixuns', 'hidden' => false },
|
||||
{ 'name' => '在线竞赛', 'link' => '/competitions', 'hidden' => false },
|
||||
{ 'name' => '教学案例', 'link' => '/moop_cases', 'hidden' => false },
|
||||
{ 'name' => '交流问答', 'link' => '/forums', 'hidden' => false },
|
||||
{ 'name' => '开发者社区', 'link' => '/problems', 'hidden' => false },
|
||||
],
|
||||
footer: nil
|
||||
}
|
||||
end
|
||||
end
|
||||
4
app/models/laboratory_user.rb
Normal file
4
app/models/laboratory_user.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class LaboratoryUser < ApplicationRecord
|
||||
belongs_to :laboratory
|
||||
belongs_to :user
|
||||
end
|
||||
3
app/models/license.rb
Normal file
3
app/models/license.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class License < ApplicationRecord
|
||||
include Projectable
|
||||
end
|
||||
21
app/models/live_link.rb
Normal file
21
app/models/live_link.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
class LiveLink < ApplicationRecord
|
||||
belongs_to :course
|
||||
belongs_to :user
|
||||
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
|
||||
# validates :url, format: { with: CustomRegexp::URL, message: "必须为网址超链接" }
|
||||
validates :description, length: { maximum: 100, too_long: "不能超过100个字符" }
|
||||
validates :course_name, presence: true
|
||||
validates :platform, presence: true
|
||||
# validates :live_time, presence: true
|
||||
validates :duration, numericality: { only_integer: true, greater_than: 0}, allow_blank: true
|
||||
|
||||
def op_auth?
|
||||
user == User.current || User.current.admin_or_business?
|
||||
end
|
||||
|
||||
def delete_auth?
|
||||
user == User.current || User.current.admin?
|
||||
end
|
||||
end
|
||||
11
app/models/member.rb
Normal file
11
app/models/member.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class Member < ApplicationRecord
|
||||
belongs_to :user
|
||||
belongs_to :course, optional: true
|
||||
belongs_to :project, optional: true
|
||||
|
||||
has_many :member_roles, dependent: :destroy
|
||||
has_many :roles, through: :member_roles
|
||||
|
||||
validates :user_id, :project_id, presence: true
|
||||
|
||||
end
|
||||
6
app/models/member_role.rb
Normal file
6
app/models/member_role.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
class MemberRole < ApplicationRecord
|
||||
belongs_to :role
|
||||
belongs_to :member
|
||||
|
||||
validates :member_id, :role_id, presence: true
|
||||
end
|
||||
72
app/models/memo.rb
Normal file
72
app/models/memo.rb
Normal file
@@ -0,0 +1,72 @@
|
||||
class Memo < ApplicationRecord
|
||||
include Searchable::Memo
|
||||
|
||||
belongs_to :forum, touch: true
|
||||
|
||||
has_many :memo_tag_repertoires, dependent: :destroy
|
||||
has_many :tag_repertoires, :through => :memo_tag_repertoires
|
||||
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
has_one :praise_tread_cache, as: :object, dependent: :destroy
|
||||
|
||||
belongs_to :author, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :parent, class_name: 'Memo', foreign_key: 'parent_id'
|
||||
|
||||
has_many :descendants, foreign_key: :root_id, class_name: 'Memo'
|
||||
has_many :children, foreign_key: :parent_id, class_name: 'Memo'
|
||||
has_many :attachments, as: :container, dependent: :destroy
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
validate :validate_sensitive_string
|
||||
|
||||
scope :field_for_list, lambda{
|
||||
select([:id, :subject, :author_id, :sticky, :updated_at, :language, :reward, :all_replies_count, :viewed_count, :forum_id])
|
||||
}
|
||||
scope :user_posts, -> (user_id){ where(root_id: nil, author_id: user_id, forum_id: [3, 5]) }
|
||||
scope :field_for_recommend, -> { select([:id, :subject, :language, :forum_id, :all_replies_count]) }
|
||||
scope :memo_replies, -> (id) { where(root_id: id) }
|
||||
scope :hot, -> { order("all_replies_count desc, updated_at desc") }
|
||||
scope :posts, -> { where(root_id: nil, forum_id: [3, 5]) }
|
||||
|
||||
validates :content, length: { maximum: 10000, too_long: "不能超过10000个字符" }
|
||||
|
||||
after_create :send_tiding
|
||||
|
||||
# 帖子的回复
|
||||
def reply_for_memo
|
||||
Memo.where(parent_id: id)
|
||||
end
|
||||
|
||||
# 子回复
|
||||
def children_of_reply
|
||||
Memo.where(parent_id: id).includes(:author).reorder("created_at asc")
|
||||
end
|
||||
|
||||
# 主贴的名称
|
||||
def main_subject
|
||||
memo = Memo.find_by(root_id: id)
|
||||
Rails.logger.info("###############memo: #{memo&.subject}")
|
||||
memo ? memo.subject : subject
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def send_tiding
|
||||
tiding_attr = {
|
||||
trigger_user_id: author_id, viewed: 0, tiding_type: 'Comment',
|
||||
parent_container_type: 'Memo', belong_container_id: forum_id, belong_container_type: 'Forum'
|
||||
}
|
||||
if parent_id.present?
|
||||
tiding_attr.merge!(user_id: parent.author_id, parent_container_id: root_id)
|
||||
else
|
||||
# 新帖子给超级管理员发消息
|
||||
tiding_attr.merge!(user_id: 1, parent_container_id: id)
|
||||
end
|
||||
|
||||
self.tidings << Tiding.new(tiding_attr)
|
||||
end
|
||||
|
||||
def validate_sensitive_string
|
||||
raise("标题包含敏感词汇,请重新输入") unless HarmoniousDictionary.clean?(subject)
|
||||
raise("内容包含敏感词汇,请重新输入") unless HarmoniousDictionary.clean?(content)
|
||||
end
|
||||
end
|
||||
4
app/models/memo_tag_repertoire.rb
Normal file
4
app/models/memo_tag_repertoire.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class MemoTagRepertoire < ApplicationRecord
|
||||
belongs_to :memo
|
||||
belongs_to :tag_repertoire
|
||||
end
|
||||
102
app/models/message.rb
Normal file
102
app/models/message.rb
Normal file
@@ -0,0 +1,102 @@
|
||||
class Message < ApplicationRecord
|
||||
attr_accessor :total_replies_count
|
||||
|
||||
belongs_to :board, counter_cache: true
|
||||
belongs_to :author, class_name: "User", foreign_key: 'author_id'
|
||||
belongs_to :parent, class_name: "Message", foreign_key: "parent_id", counter_cache: :replies_count, optional: true
|
||||
belongs_to :root, class_name: 'Message', foreign_key: :root_id, counter_cache: :descendants_count, optional: true
|
||||
|
||||
has_one :message_detail, dependent: :destroy
|
||||
accepts_nested_attributes_for :message_detail, update_only: true
|
||||
|
||||
has_many :children, -> { order(updated_on: :desc ) }, class_name: "Message", foreign_key: "parent_id", dependent: :destroy
|
||||
has_many :praise_treads, as: :praise_tread_object, dependent: :destroy
|
||||
has_many :tidings, as: :container, dependent: :destroy
|
||||
has_many :attachments, as: :container, dependent: :destroy
|
||||
has_many :course_acts, :class_name => 'CourseActivity',:as =>:course_act ,:dependent => :destroy # 课程动态
|
||||
has_many :descendants, class_name: 'Message', foreign_key: :root_id, dependent: :destroy
|
||||
|
||||
scope :root_nodes, -> { where("parent_id IS NULL") } #判断该信息是帖子还是回复。null为发布的帖子
|
||||
scope :reply_nodes, -> { where("parent_id IS NOT NULL") }
|
||||
scope :visible, -> { where(is_hidden: false) }
|
||||
scope :by_user, ->(user) { visible if user.nil? || !user.admin? }
|
||||
scope :preload_messages, -> { includes(:author, :message_detail) }
|
||||
scope :short, -> { select(:id, :subject, :created_on, :replies_count, :visits, :sticky, :praises_count) }
|
||||
scope :ordered, -> (opts={}) { reorder("created_on #{opts[:sort] == 1 ? 'asc': 'desc'}") }
|
||||
scope :by_ids, lambda { |ids| where(id: ids) unless ids.blank? }
|
||||
scope :find_by_boards, ->(ids) {where(board_id: ids)}
|
||||
scope :by_keywords, lambda { |keywords|
|
||||
where("subject LIKE ?", "%#{keywords.split(" ").join('|')}%") unless keywords.blank?
|
||||
}
|
||||
|
||||
|
||||
#转发表
|
||||
# has_many :forwards, as: :from, dependent: :destroy
|
||||
|
||||
validates :subject, length: { maximum: 255, too_long: "不能超过255个字符" }
|
||||
|
||||
def update_content(content)
|
||||
message_detail.update_attributes(content: content)
|
||||
end
|
||||
|
||||
# 主贴的名称
|
||||
def main_subject
|
||||
Rails.logger.info("##########parent: #{parent&.subject}")
|
||||
parent.present? ? parent.subject : subject
|
||||
end
|
||||
|
||||
def copy_attachments_to_new_message(new_message, user)
|
||||
attachments.each do |attach|
|
||||
new_message.attachments << Attachment.new(attach.attributes.except("id").merge(
|
||||
quotes: 0,
|
||||
downloads: 0,
|
||||
author_id: user.id,
|
||||
created_on: Time.now
|
||||
))
|
||||
end
|
||||
end
|
||||
|
||||
def self.bulk_move_to_other_board(message_ids, to_board_id)
|
||||
to_board = Board.find(to_board_id)
|
||||
|
||||
messages = Message.where(id: message_ids, parent_id: nil).select(:id, :board_id).to_a
|
||||
return if messages.blank?
|
||||
|
||||
from_board = Board.find(messages.first.board_id)
|
||||
|
||||
root_ids = messages.map(&:id)
|
||||
children_ids = Message.where(parent_id: root_ids).pluck(:id)
|
||||
second_children_ids = Message.where(parent_id: children_ids).pluck(:id)
|
||||
|
||||
ids = root_ids.concat(children_ids).concat(second_children_ids).uniq
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
Message.where(id: ids, board_id: from_board.id).update_all(board_id: to_board.id)
|
||||
to_board.increment!(:messages_count, ids.size)
|
||||
from_board.increment!(:messages_count, - ids.size)
|
||||
end
|
||||
end
|
||||
|
||||
# 包含二级回复的总点赞数
|
||||
def total_praises_count
|
||||
praises_count + descendants.sum(:praises_count)
|
||||
end
|
||||
|
||||
# 包含二级回复数的总回复数
|
||||
def total_replies_count
|
||||
descendants_count
|
||||
end
|
||||
|
||||
def has_replies
|
||||
children.exists?
|
||||
end
|
||||
|
||||
#
|
||||
def by_user_with_visible(user)
|
||||
user.nil? || !user.admin? ? children.visible.limit(5) : children.limit(5)
|
||||
end
|
||||
|
||||
def update_visits
|
||||
update_attributes(:visits => visits + 1)
|
||||
end
|
||||
end
|
||||
5
app/models/message_detail.rb
Normal file
5
app/models/message_detail.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class MessageDetail < ApplicationRecord
|
||||
belongs_to :message, :touch => true
|
||||
validates :content, length: { maximum: 10000, too_long: "内容不能超过10000个字符" }
|
||||
|
||||
end
|
||||
7
app/models/mirror_operation_record.rb
Normal file
7
app/models/mirror_operation_record.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
# status: 0 创建镜像; 1 修改镜像ID; 2 修改镜像name 3 删除镜像 4.从主节点同步镜像到子节点(子节点发生异常), 5. 修改镜像别名, 6. 修改镜像的状态
|
||||
# user_id: -1时,证明是非人为因素造成,中间层异常导致
|
||||
class MirrorOperationRecord < ActiveRecord::Base
|
||||
default_scope { order(created_at: :desc) }
|
||||
|
||||
belongs_to :mirror_repository
|
||||
end
|
||||
16
app/models/mirror_repository.rb
Normal file
16
app/models/mirror_repository.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class MirrorRepository < ApplicationRecord
|
||||
has_many :shixun_mirror_repositories, :dependent => :destroy
|
||||
has_many :shixun, :through => :shixun_mirror_repositories
|
||||
has_many :mirror_scripts, :dependent => :destroy
|
||||
|
||||
|
||||
|
||||
scope :published_mirror, -> { where(status: [1,2,3,5]) }
|
||||
scope :published_main_mirror, -> { published_mirror.where(main_type: 1) }
|
||||
scope :published_small_mirror, -> { published_mirror.where(main_type: 0) }
|
||||
scope :small_mirror, -> { where(main_type: 0) }
|
||||
|
||||
def deletable?
|
||||
status != 1 && !shixun_mirror_repositories.exists?
|
||||
end
|
||||
end
|
||||
4
app/models/mirror_script.rb
Normal file
4
app/models/mirror_script.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class MirrorScript < ApplicationRecord
|
||||
belongs_to :mirror_repository
|
||||
|
||||
end
|
||||
2
app/models/module_setting.rb
Normal file
2
app/models/module_setting.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
class ModuleSetting < ApplicationRecord
|
||||
end
|
||||
118
app/models/myshixun.rb
Normal file
118
app/models/myshixun.rb
Normal file
@@ -0,0 +1,118 @@
|
||||
class Myshixun < ApplicationRecord
|
||||
include ApplicationHelper
|
||||
has_many :games, :dependent => :destroy
|
||||
has_many :student_works
|
||||
has_one :shixun_modify, :dependent => :destroy
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :user_extension, foreign_key: :user_id
|
||||
belongs_to :shixun, counter_cache: true
|
||||
|
||||
has_one :last_executable_task, -> { where(status: [0, 1]).reorder(created_at: :asc) }, class_name: 'Game'
|
||||
has_one :last_task, -> { all }, class_name: 'Game'
|
||||
|
||||
validates_uniqueness_of :shixun_id, :scope => :user_id, :message => "shixun_id and user_id unique error"
|
||||
scope :finished, lambda { where(status: 1) }
|
||||
scope :search_myshixun_user, ->(user_id){where(user_id:user_id)}
|
||||
|
||||
|
||||
def owner
|
||||
self.user
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
end
|
||||
|
||||
def output_times
|
||||
games.map(&:evaluate_count).sum.to_i
|
||||
end
|
||||
|
||||
def repo_path
|
||||
"#{self.repo_name}.git"
|
||||
end
|
||||
|
||||
|
||||
def repo_save_path
|
||||
self.repo_name.split('/').last
|
||||
end
|
||||
|
||||
def is_complete?
|
||||
self.status == 1
|
||||
end
|
||||
|
||||
# 判断TPM的代码是否被修改了
|
||||
# 判断依据是看tpm的最新提交记录和tpi数据库中存储的commit_id是否一致
|
||||
def repository_is_modified shixun_repo_path
|
||||
myshixun_commit_id = self.commit_id
|
||||
if myshixun_commit_id.blank?
|
||||
myshixun_commit_id = GitService.commits(repo_path: self.repo_path).last["id"]
|
||||
self.update_column(:commit_id, myshixun_commit_id)
|
||||
end
|
||||
shixun_commit_id = GitService.commits(repo_path: shixun_repo_path).first["id"]
|
||||
Rails.logger.warn("###############shixun_commit_id is #{shixun_commit_id}")
|
||||
Rails.logger.warn("###############myshixun_commit_id is #{self.commit_id}")
|
||||
result = myshixun_commit_id != shixun_commit_id ? true :false
|
||||
return result
|
||||
end
|
||||
|
||||
def mirror_name
|
||||
self.shixun.mirror_repositories.map(&:type_name).blank? ? "" : self.shixun.mirror_repositories.map(&:type_name)
|
||||
end
|
||||
|
||||
def main_mirror
|
||||
self.shixun.mirror_repositories.published_main_mirror.try(:first)
|
||||
end
|
||||
|
||||
# 当前任务:一个实训中只可能一个未完成任务(status 0或1只会存在一条记录)
|
||||
# status:0 可以测评的; 1 正在测评的; 2评测通过的; 3未开启的
|
||||
# 如果都完成,则当前任务为最后一个任务
|
||||
def current_task games
|
||||
current_game = games.select{|game| game.status == 1 || game.status == 0}.last
|
||||
if current_game.blank?
|
||||
current_game = games.last
|
||||
end
|
||||
current_game
|
||||
end
|
||||
|
||||
|
||||
# 挑战至第几关(已完成关卡数+1)
|
||||
def exec_count
|
||||
gcount = self.games.select{|game| game.status == 2}.size
|
||||
gcount = gcount < self.games.size ? (gcount + 1) : gcount
|
||||
end
|
||||
|
||||
# 个人实训得分
|
||||
def total_score
|
||||
self.games.select{|game| game.status == 2 && game.final_score > 0}.pluck(:final_score).sum.to_i
|
||||
end
|
||||
|
||||
# 个人通关数
|
||||
def passed_count
|
||||
self.games.select{|game| game.status == 2}.size
|
||||
end
|
||||
|
||||
# 指定时间前完成的关卡数
|
||||
def time_passed_count time
|
||||
time.present? ? self.games.select{|game| game.status == 2 && game.end_time < time}.size : 0
|
||||
end
|
||||
|
||||
# 查看答案的关卡数,只统计通关前看的关卡
|
||||
def view_answer_count
|
||||
answer_ids = user.grades.joins("join games on grades.container_id = games.id").where("container_type = 'Answer' and games.status=2 and games.end_time > grades.created_at").pluck(:container_id)
|
||||
self.games.select{|game| game.status == 2 && game.answer_open != 0 && answer_ids.include?(game.id)}.size
|
||||
end
|
||||
|
||||
# 通关时间
|
||||
def passed_time
|
||||
self.status == 1 ? self.games.select{|game| game.status == 2}.map(&:end_time).max : "--"
|
||||
end
|
||||
|
||||
# 耗时
|
||||
def total_spend_time
|
||||
game_spend_time total_cost_time
|
||||
end
|
||||
|
||||
# 通关总耗时
|
||||
def total_cost_time
|
||||
self.games.select{|game| game.status == 2}.map(&:cost_time).sum.to_i
|
||||
end
|
||||
|
||||
end
|
||||
2
app/models/old_message_detail.rb
Normal file
2
app/models/old_message_detail.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
class OldMessageDetail < ApplicationRecord
|
||||
end
|
||||
4
app/models/onclick_time.rb
Normal file
4
app/models/onclick_time.rb
Normal file
@@ -0,0 +1,4 @@
|
||||
class OnclickTime < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
end
|
||||
11
app/models/open_user.rb
Normal file
11
app/models/open_user.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class OpenUser < ApplicationRecord
|
||||
belongs_to :user
|
||||
|
||||
validates :uid, presence: true, uniqueness: { scope: :type }
|
||||
|
||||
serialize :extra, JSON
|
||||
|
||||
def can_bind_cache_key
|
||||
"open_user:#{type}:#{uid}:can_bind"
|
||||
end
|
||||
end
|
||||
9
app/models/open_users/cas.rb
Normal file
9
app/models/open_users/cas.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class OpenUsers::Cas < OpenUser
|
||||
def nickname
|
||||
extra&.[]('nickname')
|
||||
end
|
||||
|
||||
def en_type
|
||||
'cas'
|
||||
end
|
||||
end
|
||||
9
app/models/open_users/qq.rb
Normal file
9
app/models/open_users/qq.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class OpenUsers::QQ < OpenUser
|
||||
def nickname
|
||||
extra&.[]('nickname')
|
||||
end
|
||||
|
||||
def en_type
|
||||
'qq'
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user