Update
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
125bbf7ff1
commit
6620dea812
|
@ -84,6 +84,9 @@ module Backend
|
|||
true
|
||||
end
|
||||
user = Db::User.create!(username: input.username, role: input.role.to_db, admin: input.admin)
|
||||
if input.role.student?
|
||||
Db::Student.create!(user_id: user.id)
|
||||
end
|
||||
Worker::Jobs::CacheLdapUserJob.new(user.id.not_nil!.to_i).enqueue
|
||||
|
||||
User.new(user)
|
||||
|
@ -105,7 +108,7 @@ module Backend
|
|||
def start_assignment(context : Context) : Bool?
|
||||
context.admin!
|
||||
|
||||
Worker::Jobs::AssignStudentsJob.new.enqueue
|
||||
Worker::Jobs::AssignmentJob.new.enqueue
|
||||
|
||||
true
|
||||
end
|
||||
|
|
|
@ -62,7 +62,7 @@ module Backend
|
|||
getter minimum_teacher_selection_count : Int32
|
||||
|
||||
# Assignment possibility count
|
||||
getter assignment_possibility_count : Int32
|
||||
getter assignment_possibility_count : UInt32
|
||||
|
||||
@[EnvConfig::Setting(key: "api")]
|
||||
# Configuration for `Api`
|
||||
|
|
|
@ -24,11 +24,9 @@ module Backend
|
|||
|
||||
alias Raw = Hash(String, Array(String))
|
||||
|
||||
@[JSON::Field(key: "givenName")]
|
||||
# First name
|
||||
getter first_name : String
|
||||
|
||||
@[JSON::Field(key: "sn")]
|
||||
# Last name
|
||||
getter last_name : String
|
||||
|
||||
|
|
|
@ -18,13 +18,9 @@ module Backend
|
|||
module Worker
|
||||
module Jobs
|
||||
# Assigns students to teachers when all students voted
|
||||
class AssignStudentsJob < Mosquito::QueuedJob
|
||||
class AssignmentJob < Mosquito::QueuedJob
|
||||
# run_every 1.minute
|
||||
|
||||
def rescheduleable? : Bool
|
||||
false
|
||||
end
|
||||
|
||||
alias TeacherVote = {student: Int32, priority: Int32}
|
||||
alias Assignment = {teacher: Int32, priority: Int32}
|
||||
|
||||
|
@ -33,7 +29,6 @@ module Backend
|
|||
teacher_count = Db::Teacher.query.count
|
||||
student_count = Db::Student.query.count
|
||||
vote_count = Db::Vote.query.count
|
||||
|
||||
if teacher_count == 0
|
||||
log "No teachers found, skipping assignment"
|
||||
fail
|
||||
|
@ -58,13 +53,12 @@ module Backend
|
|||
end
|
||||
.with_teacher_votes
|
||||
.to_a
|
||||
vote_index = Hash.zip(teachers.map(&.id), [0] * teachers.size)
|
||||
teacher_ids = teachers.map(&.id)
|
||||
teacher_max_students = Hash.zip(teacher_ids, teachers.map(&.max_students))
|
||||
teacher_votes : Hash(Int32, Array(TeacherVote)) = Hash.zip(
|
||||
teachers.map(&.id),
|
||||
teacher_ids,
|
||||
teachers.map do |t|
|
||||
t.teacher_votes.map do |tv|
|
||||
vote_index[t.id] += 1
|
||||
|
||||
{
|
||||
student: tv.vote.student.id,
|
||||
priority: tv.priority,
|
||||
|
@ -72,78 +66,75 @@ module Backend
|
|||
end
|
||||
end
|
||||
)
|
||||
teachers.sort_by! { |t| vote_index[t.id] }
|
||||
|
||||
students = Db::Student.query
|
||||
.with_vote(&.with_teacher_votes(&.order_by(priority: :desc)))
|
||||
.to_a
|
||||
students.each do |s|
|
||||
p! s
|
||||
pp! s.vote.not_nil!.teacher_votes
|
||||
end
|
||||
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) }
|
||||
.select! { |tv| teacher_votes.has_key?(tv.teacher.id) }
|
||||
.map do |tv|
|
||||
{
|
||||
teacher: tv.teacher.id,
|
||||
teacher_max_students: tv.teacher.max_students,
|
||||
teacher: tv.teacher.id,
|
||||
priority: tv.priority,
|
||||
}
|
||||
end
|
||||
end
|
||||
)
|
||||
votes_a = votes.to_a
|
||||
|
||||
best : {assignment: Hash(Int32, Assignment), score: UInt32}? = nil
|
||||
best : {assignment: Hash(Int32, Assignment), priority_score: UInt32, teacher_score: UInt32}? = nil
|
||||
empty_assignment_count = Hash.zip(teachers.map(&.id), [0] * teachers.size)
|
||||
Backend.config.assignment_possibility_count.times.each do
|
||||
assignment = {} of Int32 => Assignment
|
||||
assignment_count = Hash.zip(teachers.map(&.id), [0] * teachers.size)
|
||||
assignment_count = empty_assignment_count.clone
|
||||
votes_a.shuffle(Random::Secure).each do |s, tvs|
|
||||
tvs.each_with_index do |tv, i|
|
||||
if assignment[s]?.nil?
|
||||
assignment_count[tv[:teacher]] += 1
|
||||
assignment[s] = {teacher: tv[:teacher], priority: i}
|
||||
elsif assignment_count[tv[:teacher]] < tv[:teacher_max_students]
|
||||
assignment_count[assignment[s][:teacher]] -= 1
|
||||
assignment_count[tv[:teacher]] += 1
|
||||
assignment[s] = {teacher: tv[:teacher], priority: i}
|
||||
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
|
||||
|
||||
score = 0_u32
|
||||
priority_score = 0_u32
|
||||
assignment.each do |_, a|
|
||||
score += a[:priority] ** 2
|
||||
priority_score += a[:priority] ** 2
|
||||
end
|
||||
|
||||
teacher_score = 0_u32
|
||||
assignment_count.each do |t, c|
|
||||
teacher_score += (teacher_max_students[t] ** c) * teacher_max_students[t]
|
||||
end
|
||||
|
||||
if best.nil?
|
||||
best = {
|
||||
assignment: assignment,
|
||||
score: score,
|
||||
assignment: assignment,
|
||||
priority_score: priority_score,
|
||||
teacher_score: teacher_score,
|
||||
}
|
||||
elsif score < best.not_nil![:score]
|
||||
elsif priority_score < best.not_nil![:priority_score] && teacher_score < best.not_nil![:teacher_score]
|
||||
best = {
|
||||
assignment: assignment,
|
||||
score: score,
|
||||
assignment: assignment,
|
||||
priority_score: priority_score,
|
||||
teacher_score: teacher_score,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
pp! best
|
||||
|
||||
# str = String.build do |str|
|
||||
# str << "===========================\n"
|
||||
# best.not_nil![:assignment].each do |s, a|
|
||||
# pp! a
|
||||
# str << "#{Db::Student.query.find!(s).user.username} #{s} : #{Db::Teacher.query.find!(a[:teacher]).user.username} #{a[:priority]} (#{votes[s].size - a[:priority]} / #{votes[s].size})\n"
|
||||
# end
|
||||
# str << "===========================\n"
|
||||
# end
|
||||
# print str
|
||||
Db::Assignment.import(best.not_nil![:assignment].map { |s, a| Db::Assignment.new({student_id: s, teacher_id: a[:teacher]}) })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
body {
|
||||
// bulma fix
|
||||
overflow-x: hidden;
|
||||
|
||||
display: flex;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use crate::components;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct BaseProps {
|
||||
pub token: Option<String>,
|
||||
pub logged_in: bool,
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
pub struct Base;
|
||||
|
||||
impl Component for Base {
|
||||
type Message = ();
|
||||
type Properties = BaseProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<components::logged_in_handler::LoggedInHandler logged_in={ctx.props().logged_in} />
|
||||
|
||||
<div id="wrapper">
|
||||
<components::navbar::Navbar token={ctx.props().token.to_owned()} logged_in={ctx.props().logged_in} />
|
||||
{ for ctx.props().children.iter() }
|
||||
</div>
|
||||
<components::footer::Footer />
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,7 +46,6 @@ impl Component for LoggedIn {
|
|||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
if !self.logged_in {
|
||||
log::info!("Viewing logged in required site while not logged in!");
|
||||
ctx.link().history().unwrap().push(routes::Route::Login);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
use yew::prelude::*;
|
||||
|
||||
use crate::components;
|
||||
|
||||
pub enum Msg {
|
||||
LogOutClicked,
|
||||
LogOut(Option<Vec<String>>),
|
||||
}
|
||||
// use crate::components;
|
||||
use crate::layouts;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct MainProps {
|
||||
|
@ -18,7 +14,7 @@ pub struct MainProps {
|
|||
pub struct Main;
|
||||
|
||||
impl Component for Main {
|
||||
type Message = Msg;
|
||||
type Message = ();
|
||||
type Properties = MainProps;
|
||||
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
|
@ -27,17 +23,13 @@ impl Component for Main {
|
|||
|
||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<components::logged_in_handler::LoggedInHandler logged_in={ctx.props().logged_in} />
|
||||
|
||||
<div id="wrapper">
|
||||
<components::navbar::Navbar token={ctx.props().token.to_owned()} logged_in={ctx.props().logged_in} />
|
||||
<main>
|
||||
<layouts::base::Base token={ctx.props().token.to_owned()} logged_in={ctx.props().logged_in}>
|
||||
<div class={classes!("columns")}>
|
||||
<main class={classes!("column", "is-four-fifths", "mx-auto")}>
|
||||
{ for ctx.props().children.iter() }
|
||||
</main>
|
||||
</div>
|
||||
<components::footer::Footer />
|
||||
</>
|
||||
</layouts::base::Base>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod base;
|
||||
pub mod logged_in;
|
||||
pub mod main;
|
||||
|
|
|
@ -24,14 +24,14 @@ impl Component for Settings {
|
|||
html! {
|
||||
<>
|
||||
<Title value="Settings" />
|
||||
<section>
|
||||
<tokens::Tokens token={ctx.props().token.as_ref().unwrap().to_owned()} />
|
||||
</section>
|
||||
if ctx.props().admin {
|
||||
<section>
|
||||
<assignments::Assignments token={ctx.props().token.as_ref().unwrap().to_owned()} />
|
||||
<section>
|
||||
<tokens::Tokens token={ctx.props().token.as_ref().unwrap().to_owned()} />
|
||||
</section>
|
||||
}
|
||||
if ctx.props().admin {
|
||||
<section>
|
||||
<assignments::Assignments token={ctx.props().token.as_ref().unwrap().to_owned()} />
|
||||
</section>
|
||||
}
|
||||
</>
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue