Update
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Dominic Grimm 2022-12-29 22:24:29 +01:00
parent 125bbf7ff1
commit 6620dea812
No known key found for this signature in database
GPG Key ID: 6F294212DEAAC530
10 changed files with 92 additions and 71 deletions

View File

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

View File

@ -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`

View File

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

View File

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

View File

@ -1,4 +1,5 @@
body {
// bulma fix
overflow-x: hidden;
display: flex;

View File

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

View File

@ -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);
}

View File

@ -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>
}
}
}

View File

@ -1,2 +1,3 @@
pub mod base;
pub mod logged_in;
pub mod main;

View File

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