Add temporary stat meassuring
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
d6dbd18090
commit
8bfd9a4e2a
|
@ -69,18 +69,18 @@ RUN if [ "${BUILD_ENV}" = "development" ]; then \
|
|||
xargs -I % sh -c 'mkdir -p $(dirname deps%); cp % deps%;'; \
|
||||
fi
|
||||
|
||||
FROM alpine as config
|
||||
WORKDIR /usr/src/config
|
||||
RUN mkdir ./tmp
|
||||
RUN chmod -R 1777 ./tmp
|
||||
# FROM alpine as config
|
||||
# WORKDIR /usr/src/config
|
||||
# RUN mkdir ./tmp
|
||||
# RUN chmod -R 1777 ./tmp
|
||||
|
||||
FROM scratch as runner
|
||||
FROM busybox as runner
|
||||
LABEL maintainer="Dominic Grimm <dominic@dergrimm.net>" \
|
||||
org.opencontainers.image.description="Backend of Mentorenwahl" \
|
||||
org.opencontainers.image.licenses="GPL-3.0" \
|
||||
org.opencontainers.image.source="https://git.dergrimm.net/mentorenwahl/mentorenwahl" \
|
||||
org.opencontainers.image.url="https://git.dergrimm.net/mentorenwahl/mentorenwahl"
|
||||
COPY --from=config /usr/src/config/tmp /tmp
|
||||
# COPY --from=config /usr/src/config/tmp /tmp
|
||||
WORKDIR /usr/src/mentorenwahl
|
||||
COPY --from=micrate-builder /usr/src/micrate/bin/micrate ./bin/micrate
|
||||
COPY --from=builder /usr/src/mentorenwahl/db ./db
|
||||
|
|
|
@ -68,6 +68,14 @@ CREATE TABLE teacher_votes(
|
|||
|
||||
CREATE TABLE assignments(
|
||||
id serial PRIMARY KEY,
|
||||
active boolean NOT NULL,
|
||||
priority_score bigint NOT NULL,
|
||||
teacher_score bigint NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE student_assignments(
|
||||
id serial PRIMARY KEY,
|
||||
assignment_id int NULL NULL REFERENCES assignments(id),
|
||||
student_id int NOT NULL REFERENCES students(id) UNIQUE,
|
||||
teacher_id int NOT NULL REFERENCES teachers(id)
|
||||
);
|
||||
|
|
|
@ -222,7 +222,7 @@ module Backend
|
|||
def set_voting(context : Context, state : Bool) : Bool?
|
||||
context.admin!
|
||||
|
||||
config = Db::Config.query.where { active == true }.first!
|
||||
config = Db::Config.query.where { active }.first!
|
||||
config.can_vote = state
|
||||
config.save!
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ module Backend
|
|||
# teacher_role_count = Db::User.query.where(role: Db::UserRole::Teacher).count
|
||||
|
||||
# teacher_role_count > 0 && teacher_role_count == Db::Teacher.query.count
|
||||
Db::Config.query.select(:can_vote).where { active == true }.first!.can_vote
|
||||
Db::Config.query.select(:can_vote).where { active }.first!.can_vote
|
||||
end
|
||||
|
||||
@[GraphQL::Field]
|
||||
|
|
|
@ -1,8 +1,124 @@
|
|||
require "commander"
|
||||
require "tallboy"
|
||||
require "wannabe_bool"
|
||||
require "csv"
|
||||
|
||||
module Backend
|
||||
alias TeacherVote = {student: Int32, priority: Int32}
|
||||
alias Assignment = {teacher: Int32, priority: Int32}
|
||||
|
||||
def do_assignment(roll_count : UInt32) : Time::Span
|
||||
teachers = Db::Teacher.query
|
||||
.where do
|
||||
raw("EXISTS (SELECT 1 FROM teacher_votes WHERE teacher_id = teachers.id)") &
|
||||
(max_students > 0)
|
||||
end
|
||||
.with_teacher_votes
|
||||
.to_a
|
||||
teacher_ids = teachers.map(&.id)
|
||||
teacher_max_students = Hash.zip(teacher_ids, teachers.map(&.max_students))
|
||||
teacher_votes : Hash(Int32, Array(TeacherVote)) = Hash.zip(
|
||||
teacher_ids,
|
||||
teachers.map do |t|
|
||||
t.teacher_votes.map do |tv|
|
||||
{
|
||||
student: tv.vote.student.id,
|
||||
priority: tv.priority,
|
||||
}
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
students = Db::Student.query
|
||||
.with_vote(&.with_teacher_votes(&.order_by(priority: :desc)))
|
||||
.to_a
|
||||
student_ids = students.map(&.id)
|
||||
votes = Hash.zip(
|
||||
student_ids,
|
||||
students.map do |s|
|
||||
s.vote.not_nil!.teacher_votes
|
||||
.to_a
|
||||
.select! { |tv| teacher_votes.has_key?(tv.teacher.id) }
|
||||
.map do |tv|
|
||||
{
|
||||
teacher: tv.teacher.id,
|
||||
priority: tv.priority,
|
||||
}
|
||||
end
|
||||
end
|
||||
)
|
||||
votes_a = votes.to_a
|
||||
|
||||
t1 = Time.utc
|
||||
best : {assignment: Hash(Int32, Assignment), priority_score: Int64, teacher_score: Int64}? = nil
|
||||
empty_assignment_count = Hash.zip(teachers.map(&.id), [0] * teachers.size)
|
||||
roll_count.times.each do |i|
|
||||
p! i
|
||||
assignment = {} of Int32 => Assignment
|
||||
assignment_count = empty_assignment_count.clone
|
||||
votes_a.shuffle(Random::Secure).each do |s, tvs|
|
||||
tvs.each do |tv|
|
||||
if assignment_count[tv[:teacher]] < teacher_max_students[tv[:teacher]]
|
||||
if assignment[s]?.nil?
|
||||
assignment_count[tv[:teacher]] += 1
|
||||
assignment[s] = {teacher: tv[:teacher], priority: tv[:priority]}
|
||||
else
|
||||
assignment_count[assignment[s][:teacher]] -= 1
|
||||
assignment_count[tv[:teacher]] += 1
|
||||
assignment[s] = {teacher: tv[:teacher], priority: tv[:priority]}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
priority_score = 0_i64
|
||||
assignment.each do |_, a|
|
||||
priority_score += a[:priority] ** 2
|
||||
end
|
||||
|
||||
teacher_score = 0_i64
|
||||
assignment_count.each do |t, c|
|
||||
teacher_score += (teacher_max_students[t] ** c) * teacher_max_students[t]
|
||||
end
|
||||
|
||||
if best.nil?
|
||||
best = {
|
||||
assignment: assignment,
|
||||
priority_score: priority_score,
|
||||
teacher_score: teacher_score,
|
||||
}
|
||||
elsif priority_score < best.not_nil![:priority_score] && teacher_score < best.not_nil![:teacher_score]
|
||||
best = {
|
||||
assignment: assignment,
|
||||
priority_score: priority_score,
|
||||
teacher_score: teacher_score,
|
||||
}
|
||||
end
|
||||
end
|
||||
t2 = Time.utc
|
||||
|
||||
pp! best
|
||||
|
||||
# Db::Assignment.import(best.not_nil![:assignment].map { |s, a| Db::Assignment.new({student_id: s, teacher_id: a[:teacher]}) })
|
||||
Db::Assignment.query.where { active }.to_update.set(active: false).execute
|
||||
assignment_id = Db::Assignment.create!({
|
||||
active: true,
|
||||
priority_score: best.not_nil![:priority_score],
|
||||
teacher_score: best.not_nil![:teacher_score],
|
||||
}).id
|
||||
Db::StudentAssignment.import(
|
||||
best.not_nil![:assignment].map do |s, a|
|
||||
Db::StudentAssignment.new({
|
||||
assignment_id: assignment_id,
|
||||
student_id: s,
|
||||
teacher_id: a[:teacher],
|
||||
})
|
||||
end
|
||||
)
|
||||
|
||||
t2 - t1
|
||||
end
|
||||
|
||||
CLI = Commander::Command.new do |cmd|
|
||||
cmd.use = "backend"
|
||||
cmd.short = "Mentorenwahl backend CLI"
|
||||
|
@ -249,5 +365,118 @@ module Backend
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# ameba:disable Lint/ShadowingOuterLocalVar
|
||||
cmd.commands.add do |cmd|
|
||||
cmd.use = "stats"
|
||||
cmd.short = "Runs tests and outputs statistics for analysis"
|
||||
cmd.long = cmd.short
|
||||
|
||||
cmd.run do
|
||||
result = CSV.build do |csv|
|
||||
csv.row "roll_count", "students", "time", "priority_score", "teacher_score"
|
||||
|
||||
(2..100).each do |i|
|
||||
p! i
|
||||
p! student_count = 0...i * 10
|
||||
p! teacher_count = 0...i * 5
|
||||
|
||||
Db::User.import(
|
||||
student_count.map do |x|
|
||||
Db::User.new({
|
||||
username: "student#{x}",
|
||||
password_hash: "$2a$12$eEkFG9OAfaAgwPSHTIVfMedH9VIijpRIz1jddkxuJnbe5zwfVIQ6y",
|
||||
initial_password: "12345",
|
||||
password_changed: false,
|
||||
first_name: "Student#{x}",
|
||||
last_name: "Student#{x}",
|
||||
role: Db::UserRole::Student,
|
||||
admin: false,
|
||||
})
|
||||
end.concat(
|
||||
teacher_count.map do |x|
|
||||
Db::User.new({
|
||||
username: "teacher#{x}",
|
||||
password_hash: "$2a$12$eEkFG9OAfaAgwPSHTIVfMedH9VIijpRIz1jddkxuJnbe5zwfVIQ6y",
|
||||
initial_password: "12345",
|
||||
password_changed: false,
|
||||
first_name: "Teacher#{x}",
|
||||
last_name: "Teacher#{x}",
|
||||
role: Db::UserRole::Teacher,
|
||||
admin: false,
|
||||
})
|
||||
end
|
||||
)
|
||||
)
|
||||
|
||||
class_id = Db::Class.create!({name: "Default class"}).id
|
||||
student_user_ids = Db::User.query.select(:id).where { role == Db::UserRole::Student }.map(&.id)
|
||||
Db::Student.import(
|
||||
student_user_ids.map do |id|
|
||||
Db::Student.new({
|
||||
user_id: id,
|
||||
class_id: class_id,
|
||||
})
|
||||
end
|
||||
)
|
||||
student_ids = Db::Student.query.select(:id).map(&.id)
|
||||
|
||||
teacher_user_ids = Db::User.query.select(:id).where { role == Db::UserRole::Teacher }.map(&.id)
|
||||
Db::Teacher.import(
|
||||
teacher_user_ids.map do |id|
|
||||
Db::Teacher.new({
|
||||
user_id: id,
|
||||
max_students: id % 5,
|
||||
})
|
||||
end
|
||||
)
|
||||
teacher_ids = Db::Teacher.query.select(:id).map(&.id)
|
||||
|
||||
Db::Vote.import(
|
||||
student_ids.map do |id|
|
||||
Db::Vote.new({student_id: id})
|
||||
end
|
||||
)
|
||||
vote_ids = Db::Vote.query.select(:id).map(&.id)
|
||||
Db::TeacherVote.import(
|
||||
vote_ids.flat_map do |id|
|
||||
size = 6 + id % 4
|
||||
tvs = [] of Int32
|
||||
while tvs.size < size
|
||||
t_id = teacher_ids.sample
|
||||
tvs << t_id unless t_id.in?(tvs)
|
||||
end
|
||||
|
||||
tvs.map_with_index do |t, j|
|
||||
Db::TeacherVote.new({
|
||||
vote_id: id,
|
||||
teacher_id: t,
|
||||
priority: j,
|
||||
})
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
dt = do_assignment(Backend.config.assignment_possibility_count)
|
||||
a = Db::Assignment.query.where { active }.first!
|
||||
csv.row Backend.config.assignment_possibility_count, student_ids.size, dt.seconds, a.priority_score, a.teacher_score
|
||||
|
||||
puts csv
|
||||
|
||||
Db::StudentAssignment.query.to_delete.execute
|
||||
Db::Assignment.query.to_delete.execute
|
||||
Db::TeacherVote.query.to_delete.execute
|
||||
Db::Vote.query.to_delete.execute
|
||||
Db::Teacher.query.to_delete.execute
|
||||
Db::Student.query.to_delete.execute
|
||||
Db::Class.query.to_delete.execute
|
||||
Db::User.query.to_delete.execute
|
||||
end
|
||||
end
|
||||
|
||||
puts result
|
||||
File.write("stats.csv", result.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,9 +19,12 @@ module Backend::Db
|
|||
include Clear::Model
|
||||
self.table = :assignments
|
||||
|
||||
primary_key type: :serial
|
||||
primary_key type: serial
|
||||
|
||||
belongs_to student : Student
|
||||
belongs_to teacher : Teacher
|
||||
column active : Bool
|
||||
column priority_score : Int64
|
||||
column teacher_score : Int64
|
||||
|
||||
has_many student_assignments : StudentAssignment, foreign_key: :assignment_id
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
module Backend::Db
|
||||
class StudentAssignment
|
||||
include Clear::Model
|
||||
self.table = :student_assignments
|
||||
|
||||
primary_key type: :serial
|
||||
|
||||
belongs_to assignment : Assignment
|
||||
belongs_to student : Student
|
||||
belongs_to teacher : Teacher
|
||||
end
|
||||
end
|
|
@ -68,6 +68,7 @@ module Backend
|
|||
end
|
||||
end
|
||||
)
|
||||
p! "Got teachers"
|
||||
|
||||
students = Db::Student.query
|
||||
.with_vote(&.with_teacher_votes(&.order_by(priority: :desc)))
|
||||
|
@ -88,9 +89,11 @@ module Backend
|
|||
end
|
||||
)
|
||||
votes_a = votes.to_a
|
||||
p! "Got students' votes"
|
||||
|
||||
best : {assignment: Hash(Int32, Assignment), priority_score: UInt32, teacher_score: UInt32}? = nil
|
||||
best : {assignment: Hash(Int32, Assignment), priority_score: Int64, teacher_score: Int64}? = nil
|
||||
empty_assignment_count = Hash.zip(teachers.map(&.id), [0] * teachers.size)
|
||||
p! "Starting assignment"
|
||||
Backend.config.assignment_possibility_count.times.each do
|
||||
assignment = {} of Int32 => Assignment
|
||||
assignment_count = empty_assignment_count.clone
|
||||
|
@ -109,12 +112,12 @@ module Backend
|
|||
end
|
||||
end
|
||||
|
||||
priority_score = 0_u32
|
||||
priority_score = 0_i64
|
||||
assignment.each do |_, a|
|
||||
priority_score += a[:priority] ** 2
|
||||
end
|
||||
|
||||
teacher_score = 0_u32
|
||||
teacher_score = 0_i64
|
||||
assignment_count.each do |t, c|
|
||||
teacher_score += (teacher_max_students[t] ** c) * teacher_max_students[t]
|
||||
end
|
||||
|
@ -136,7 +139,23 @@ module Backend
|
|||
|
||||
pp! best
|
||||
|
||||
Db::Assignment.import(best.not_nil![:assignment].map { |s, a| Db::Assignment.new({student_id: s, teacher_id: a[:teacher]}) })
|
||||
p! "Saving best assignment into database"
|
||||
# Db::Assignment.import(best.not_nil![:assignment].map { |s, a| Db::Assignment.new({student_id: s, teacher_id: a[:teacher]}) })
|
||||
Db::Assignment.query.where { active }.to_update.set(active: false).execute
|
||||
assignment_id = Db::Assignment.create!({
|
||||
active: true,
|
||||
priority_score: best.not_nil![:priority_score],
|
||||
teacher_score: best.not_nil![:teacher_score],
|
||||
})
|
||||
Db::StudentAssignment.import(
|
||||
best.not_nil![:assignment].map do |s, a|
|
||||
Db::StudentAssignment.new({
|
||||
assignment_id: assignment_id,
|
||||
student_id: s,
|
||||
teacher_id: a[:teacher],
|
||||
})
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue