forked from expertiza/expertiza
-
Notifications
You must be signed in to change notification settings - Fork 0
/
team.rb
316 lines (279 loc) · 11 KB
/
team.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
class Team < ApplicationRecord
has_many :teams_participants, dependent: :destroy
has_many :users, through: :teams_participants
has_many :join_team_requests, dependent: :destroy
has_one :team_node, foreign_key: :node_object_id, dependent: :destroy
has_many :signed_up_teams, dependent: :destroy
has_many :bids, dependent: :destroy
has_paper_trail
scope :find_team_for_assignment_and_user, lambda { |assignment_id, user_id|
joins(:teams_users).where('teams.parent_id = ? AND teams_users.user_id = ?', assignment_id, user_id)
}
# Allowed types of teams -- ASSIGNMENT teams or COURSE teams
def self.allowed_types
# non-interpolated array of single-quoted strings
%w[Assignment Course]
end
# Get the participants of the given team
def participants
users.where(parent_id: parent_id || current_user_id).flat_map(&:participants)
end
alias get_participants participants
# copies content of one object to the another
def self.copy_content(source, destination)
source.each do |each_element|
each_element.copy(destination.id)
end
end
# enum method for team clone operations
def self.team_operation
{ inherit: 'inherit', bequeath: 'bequeath' }.freeze
end
# Get the response review map
def responses
participants.flat_map(&:responses)
end
# Delete the given team
def delete
TeamsUser.where(team_id: id).find_each(&:destroy)
node = TeamNode.find_by(node_object_id: id)
node.destroy if node
destroy
end
# Get the node type of the tree structure
def node_type
'TeamNode'
end
# Get the names of the users
def author_names
names = []
users.each do |user|
names << user.fullname
end
names
end
# Check if the user exist
def user?(user)
users.include? user
end
# Check if the current team is full?
def full?
return false if parent_id.nil? # course team, does not max_team_size
max_team_members = Assignment.find(parent_id).max_team_size
curr_team_size = Team.size(id)
curr_team_size >= max_team_members
end
# Add member to the team, changed to hash by E1776
def add_member(user, _assignment_id = nil)
raise "The user #{user.name} is already a member of the team #{name}" if user?(user)
can_add_member = false
unless full?
can_add_member = true
t_user = TeamsUser.create(user_id: user.id, team_id: id)
parent = TeamNode.find_by(node_object_id: id)
TeamUserNode.create(parent_id: parent.id, node_object_id: t_user.id)
add_participant(parent_id, user)
ExpertizaLogger.info LoggerMessage.new('Model:Team', user.name, "Added member to the team #{id}")
end
can_add_member
end
# Define the size of the team
def self.size(team_id)
#TeamsUser.where(team_id: team_id).count
count = 0
members = TeamsUser.where(team_id: team_id)
members.each do |member|
member_name = member.name
unless member_name.include?(' (Mentor)')
count = count + 1
end
end
count
end
# Copy method to copy this team
def copy_members(new_team)
members = TeamsUser.where(team_id: id)
members.each do |member|
t_user = TeamsUser.create(team_id: new_team.id, user_id: member.user_id)
parent = Object.const_get(parent_model).find(parent_id)
TeamUserNode.create(parent_id: parent.id, node_object_id: t_user.id)
end
end
# Check if the team exists
def self.check_for_existing(parent, name, team_type)
list = Object.const_get(team_type + 'Team').where(parent_id: parent.id, name: name)
raise TeamExistsError, "The team name #{name} is already in use." unless list.empty?
end
# Algorithm
# Start by adding single members to teams that are one member too small.
# Add two-member teams to teams that two members too small. etc.
def self.randomize_all_by_parent(parent, team_type, min_team_size)
participants = Participant.where(parent_id: parent.id, type: parent.class.to_s + 'Participant', can_mentor: [false, nil])
participants = participants.sort { rand(-1..1) }
users = participants.map { |p| User.find(p.user_id) }.to_a
# find teams still need team members and users who are not in any team
teams = Team.where(parent_id: parent.id, type: parent.class.to_s + 'Team').to_a
teams.each do |team|
TeamsUser.where(team_id: team.id).each do |teams_user|
users.delete(User.find(teams_user.user_id))
end
end
teams.reject! { |team| Team.size(team.id) >= min_team_size }
# sort teams that still need members by decreasing team size
teams.sort_by { |team| Team.size(team.id) }.reverse!
# insert users who are not in any team to teams still need team members
assign_single_users_to_teams(min_team_size, parent, teams, users) if !users.empty? && !teams.empty?
# If all the existing teams are fill to the min_team_size and we still have more users, create teams for them.
create_team_from_single_users(min_team_size, parent, team_type, users) unless users.empty?
end
# Creates teams from a list of users based on minimum team size
# Then assigns the created team to the parent object
def self.create_team_from_single_users(min_team_size, parent, team_type, users)
num_of_teams = users.length.fdiv(min_team_size).ceil
next_team_member_index = 0
(1..num_of_teams).to_a.each do |i|
team = Object.const_get(team_type + 'Team').create(name: 'Team_' + i.to_s, parent_id: parent.id)
TeamNode.create(parent_id: parent.id, node_object_id: team.id)
min_team_size.times do
break if next_team_member_index >= users.length
user = users[next_team_member_index]
team.add_member(user, parent.id)
next_team_member_index += 1
end
end
end
# Assigns list of users to list of teams based on minimum team size
def self.assign_single_users_to_teams(min_team_size, parent, teams, users)
teams.each do |team|
curr_team_size = Team.size(team.id)
member_num_difference = min_team_size - curr_team_size
while member_num_difference > 0
team.add_member(users.first, parent.id)
users.delete(users.first)
member_num_difference -= 1
break if users.empty?
end
break if users.empty?
end
end
# Generate the team name
def self.generate_team_name(_team_name_prefix = '')
last_team = Team.where('name LIKE ?', "#{_team_name_prefix} Team_%").order(name: :desc).first
counter = last_team ? last_team.name.scan(/\d+/).first.to_i + 1 : 1
team_name = "#{_team_name_prefix} Team_#{counter}"
team_name
end
# Extract team members from the csv and push to DB, changed to hash by E1776
def import_team_members(row_hash)
row_hash[:teammembers].each_with_index do |teammate, _index|
user = User.find_by(name: teammate.to_s)
if user.nil?
raise ImportError, "The user '#{teammate}' was not found. <a href='/users/new'>Create</a> this user?"
else
add_member(user) if TeamsUser.find_by(team_id: id, user_id: user.id).nil?
end
end
end
# changed to hash by E1776
def self.import(row_hash, id, options, teamtype)
raise ArgumentError, 'Not enough fields on this line.' if row_hash.empty? || (row_hash[:teammembers].empty? && (options[:has_teamname] == 'true_first' || options[:has_teamname] == 'true_last')) || (row_hash[:teammembers].empty? && (options[:has_teamname] == 'true_first' || options[:has_teamname] == 'true_last'))
if options[:has_teamname] == 'true_first' || options[:has_teamname] == 'true_last'
name = row_hash[:teamname].to_s
team = where(['name =? && parent_id =?', name, id]).first
team_exists = !team.nil?
name = handle_duplicate(team, name, id, options[:handle_dups], teamtype)
else
if teamtype.is_a?(CourseTeam)
name = generate_team_name(Course.find(id).name)
elsif teamtype.is_a?(AssignmentTeam)
name = generate_team_name(Assignment.find(id).name)
end
end
if name
team = Object.const_get(teamtype.to_s).create_team_and_node(id)
team.name = name
team.save
end
# insert team members into team unless team was pre-existing & we ignore duplicate teams
team.import_team_members(row_hash) unless team_exists && options[:handle_dups] == 'ignore'
end
# Handle existence of the duplicate team
def self.handle_duplicate(team, name, id, handle_dups, teamtype)
return name if team.nil? # no duplicate
return nil if handle_dups == 'ignore' # ignore: do not create the new team
if handle_dups == 'rename' # rename: rename new team
if teamtype.is_a?(CourseTeam)
return generate_team_name(Course.find(id).name)
elsif teamtype.is_a?(AssignmentTeam)
return generate_team_name(Assignment.find(id).name)
end
end
if handle_dups == 'replace' # replace: delete old team
team.delete
return name
else # handle_dups = "insert"
return nil
end
end
# Export the teams to csv
def self.export(csv, parent_id, options, teamtype)
if teamtype.is_a?(CourseTeam)
teams = CourseTeam.where(parent_id: parent_id)
elsif teamtype.is_a?(AssignmentTeam)
teams = AssignmentTeam.where(parent_id: parent_id)
end
teams.each do |team|
output = []
output.push(team.name)
if options[:team_name] == 'false'
team_members = TeamsUser.where(team_id: team.id)
team_members.each do |user|
output.push(user.name)
end
end
csv << output
end
csv
end
# Create the team with corresponding tree node
def self.create_team_and_node(id)
parent = parent_model id # current_task will be either a course object or an assignment object.
team_name = Team.generate_team_name(parent.name)
team = create(name: team_name, parent_id: id)
# new teamnode will have current_task.id as parent_id and team_id as node_object_id.
TeamNode.create(parent_id: id, node_object_id: team.id)
ExpertizaLogger.info LoggerMessage.new('Model:Team', '', "New TeamNode created with teamname #{team_name}")
team
end
# E1991 : This method allows us to generate
# team names based on whether anonymized view
# is set or not. The logic is similar to
# existing logic of User model.
def name(ip_address = nil)
if User.anonymized_view?(ip_address)
return "Anonymized_Team_#{self[:id]}"
else
return self[:name]
end
end
# REFACTOR END:: class methods import export moved from course_team & assignment_team to here
# Create the team with corresponding tree node and given users
def self.create_team_with_users(parent_id, user_ids)
team = create_team_and_node(parent_id)
user_ids.each do |user_id|
remove_user_from_previous_team(parent_id, user_id)
# Create new team_user and team_user node
team.add_member(User.find(user_id))
end
team
end
# Removes the specified user from any team of the specified assignment
def self.remove_user_from_previous_team(parent_id, user_id)
team_user = TeamsUser.where(user_id: user_id).find { |team_user_obj| team_user_obj.team.parent_id == parent_id }
begin
team_user.destroy
rescue StandardError
nil
end
end
end