From 05d77329ab8b9ffce6a520076ebeaf11ae7a6394 Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Wed, 23 Nov 2022 20:17:14 +0100 Subject: [PATCH] Add bulma css --- backend/src/backend/api/schema/mutation.cr | 2 +- .../src/backend/worker/jobs/assignment_job.cr | 109 +----------------- frontend/Dockerfile | 5 +- frontend/graphql/mutations/login.graphql | 3 + .../mutations/start_assignment.graphql | 3 + frontend/graphql/schema.graphql | 2 +- frontend/index.html | 4 + frontend/nginx.conf | 6 + frontend/src/components/logged_in_handler.rs | 4 +- frontend/src/cookie_names.rs | 4 - frontend/src/cookies.rs | 14 +++ frontend/src/graphql/mutations/mod.rs | 1 + .../src/graphql/mutations/start_assignment.rs | 10 ++ frontend/src/lib.rs | 2 +- frontend/src/main.rs | 1 + frontend/src/routes/login.rs | 21 +++- frontend/src/routes/mod.rs | 22 +++- frontend/src/routes/settings/assignments.rs | 82 +++++++++++++ frontend/src/routes/settings/mod.rs | 7 ++ frontend/src/routes/settings/tokens.rs | 4 +- 20 files changed, 175 insertions(+), 131 deletions(-) create mode 100644 frontend/graphql/mutations/start_assignment.graphql delete mode 100644 frontend/src/cookie_names.rs create mode 100644 frontend/src/cookies.rs create mode 100644 frontend/src/graphql/mutations/start_assignment.rs create mode 100644 frontend/src/routes/settings/assignments.rs diff --git a/backend/src/backend/api/schema/mutation.cr b/backend/src/backend/api/schema/mutation.cr index fe1dcc2..b475bbf 100644 --- a/backend/src/backend/api/schema/mutation.cr +++ b/backend/src/backend/api/schema/mutation.cr @@ -103,7 +103,7 @@ module Backend @[GraphQL::Field] # Starts assignment job of mentors to students - def assign_students(context : Context) : Bool? + def start_assignment(context : Context) : Bool? context.admin! Worker::Jobs::AssignStudentsJob.new.enqueue diff --git a/backend/src/backend/worker/jobs/assignment_job.cr b/backend/src/backend/worker/jobs/assignment_job.cr index 8dfd2fc..372b4ba 100644 --- a/backend/src/backend/worker/jobs/assignment_job.cr +++ b/backend/src/backend/worker/jobs/assignment_job.cr @@ -54,115 +54,8 @@ module Backend teachers = Db::Teacher.query .where do raw("EXISTS (SELECT 1 FROM teacher_votes WHERE teacher_id = teachers.id)") & - (max_students > 0) + max_students > 0 end - .with_teacher_votes - .to_a - vote_index = Hash.zip(teachers.map(&.id), [0] * teachers.size) - teacher_votes : Hash(Int32, Array(TeacherVote)) = Hash.zip( - teachers.map(&.id), - teachers.map do |t| - t.teacher_votes.map do |tv| - vote_index[t.id] += 1 - - { - student: tv.vote.student.id, - priority: tv.priority, - } - end - end - ) - teachers.sort_by! { |t| vote_index[t.id] } - - students = Db::Student.query - .with_vote(&.with_teacher_votes(&.order_by(priority: :asc))) - .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, - teacher_max_students: tv.teacher.max_students, - } - end - end - ) - - best_assignment = { - assignment: {} of Int32 => Assignment, - score: Float32::INFINITY, - } - - Backend.config.assignment_possibility_count.times.each do - assignment = {} of Int32 => Assignment - assignment_count = Hash.zip(teachers.map(&.id), [0] * teachers.size) - # teachers.each do |t| - # queue = Deque.new(teacher_votes[t.id].shuffle) - - # count = 1 - # while count < t.max_students - # break unless x = queue.shift? - # tv = x.not_nil! - - # if assignment[tv[:student]]?.nil? || assignment[tv[:student]][:priority] <= tv[:priority] - # assignment[tv[:student]] = {teacher: t.id, priority: tv[:priority]} - # count += 1 - # end - # end - # end - votes.to_a.shuffle.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} - end - end - end - - pp! assignment, assignment_count - - score = 0_f32 - # positivity = 0 - # assignment.each do |s, a| - # ratio = (vote_sizes[s] - a[:priority]) / vote_sizes[s] - # score += 2 ** ratio - # positivity += ratio > 0.5 ? 1 : -1 - # end - assignment.each do |s, a| - size = votes[s].size - p! a[:priority], (votes[s].size - a[:priority]) / size - # score += 1 if ((votes[s].size - a[:priority]) / size) >= 0.5 - score += a[:priority] - end - - # full_score = score ** positivity - if score < best_assignment[:score] - best_assignment = { - assignment: assignment, - score: score, - } - end - end - - pp! best_assignment - - str = String.build do |str| - str << "===========================\n" - best_assignment[:assignment].each do |s, a| - str << "#{Db::Student.query.find!(s).user.username} : #{Db::Teacher.query.find!(a[:teacher]).user.username} (#{a[:priority]} / #{votes[s].size})\n" - end - str << "===========================\n" - end - print str end end end diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 8de8f74..35bb5a1 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -2,7 +2,6 @@ FROM lukemathwalker/cargo-chef:latest-rust-1.65.0 as chef WORKDIR /usr/src/frontend FROM chef as planner -WORKDIR /usr/src/frontend RUN mkdir src && touch src/main.rs COPY ./Cargo.toml . RUN cargo chef prepare --recipe-path recipe.json @@ -11,10 +10,10 @@ FROM chef as builder WORKDIR /usr/local/bin ARG TRUNK_VERSION="v0.16.0" RUN wget -qO- https://github.com/thedodd/trunk/releases/download/${TRUNK_VERSION}/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf- -WORKDIR /usr/src/frontend RUN rustup target add wasm32-unknown-unknown -COPY ./.cargo ./.cargo +WORKDIR /usr/src/frontend COPY --from=planner /usr/src/frontend/recipe.json . +COPY ./.cargo ./.cargo RUN cargo chef cook --release --recipe-path recipe.json COPY ./index.html . COPY ./graphql ./graphql diff --git a/frontend/graphql/mutations/login.graphql b/frontend/graphql/mutations/login.graphql index 64e3cd4..2ba6200 100644 --- a/frontend/graphql/mutations/login.graphql +++ b/frontend/graphql/mutations/login.graphql @@ -1,5 +1,8 @@ mutation Login($username: String!, $password: String!) { login(username: $username, password: $password) { token + user { + admin + } } } diff --git a/frontend/graphql/mutations/start_assignment.graphql b/frontend/graphql/mutations/start_assignment.graphql new file mode 100644 index 0000000..f26cf9e --- /dev/null +++ b/frontend/graphql/mutations/start_assignment.graphql @@ -0,0 +1,3 @@ +mutation StartAssignment { + startAssignment +} diff --git a/frontend/graphql/schema.graphql b/frontend/graphql/schema.graphql index c55dc98..224a00b 100644 --- a/frontend/graphql/schema.graphql +++ b/frontend/graphql/schema.graphql @@ -86,7 +86,6 @@ type LoginPayload { } type Mutation { - assignStudents: Boolean createUser(checkLdap: Boolean! = true, input: UserCreateInput!): User createVote(input: VoteCreateInput!): Vote deleteUser(id: Int!): Int @@ -94,6 +93,7 @@ type Mutation { logout: UUID registerTeacher(input: TeacherInput!): Teacher revokeToken(token: UUID!): UUID! + startAssignment: Boolean } input TeacherInput { diff --git a/frontend/index.html b/frontend/index.html index aefc1fe..42304f0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -5,6 +5,10 @@ + + diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 8b2b43d..80007c3 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -3,6 +3,12 @@ events { } http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + sendfile on; + tcp_nopush on; + server_tokens off; more_clear_headers Server; diff --git a/frontend/src/components/logged_in_handler.rs b/frontend/src/components/logged_in_handler.rs index d6205fc..aef86bf 100644 --- a/frontend/src/components/logged_in_handler.rs +++ b/frontend/src/components/logged_in_handler.rs @@ -3,7 +3,7 @@ use yew_agent::{Bridge, Bridged}; use yew_router::prelude::*; use crate::agents; -use crate::cookie_names; +use crate::cookies; use crate::routes; pub enum Msg { @@ -38,7 +38,7 @@ impl Component for LoggedInHandler { Msg::LoggedIn(x) => { if self.logged_in && !x { log::info!("Global logout!"); - wasm_cookies::delete(cookie_names::TOKEN); + cookies::logout_clear(); ctx.link().history().unwrap().push(routes::Route::Login); } self.logged_in = x; diff --git a/frontend/src/cookie_names.rs b/frontend/src/cookie_names.rs deleted file mode 100644 index 18e5aa0..0000000 --- a/frontend/src/cookie_names.rs +++ /dev/null @@ -1,4 +0,0 @@ -use const_format::concatcp; - -pub const BASE: &str = "mentorenwahl_"; -pub const TOKEN: &str = concatcp!(BASE, "token"); diff --git a/frontend/src/cookies.rs b/frontend/src/cookies.rs new file mode 100644 index 0000000..ac61f64 --- /dev/null +++ b/frontend/src/cookies.rs @@ -0,0 +1,14 @@ +use const_format::concatcp; + +const BASE: &str = "mentorenwahl_"; + +pub const TOKEN: &str = concatcp!(BASE, "token"); +pub const ADMIN: &str = concatcp!(BASE, "admin"); + +pub const DELETE_ON_LOGOUT: [&str; 2] = [TOKEN, ADMIN]; + +pub fn logout_clear() { + for x in DELETE_ON_LOGOUT { + wasm_cookies::delete(x); + } +} diff --git a/frontend/src/graphql/mutations/mod.rs b/frontend/src/graphql/mutations/mod.rs index da438a2..48cf68f 100644 --- a/frontend/src/graphql/mutations/mod.rs +++ b/frontend/src/graphql/mutations/mod.rs @@ -2,4 +2,5 @@ pub mod login; pub mod logout; pub mod register_teacher; pub mod revoke_token; +pub mod start_assignment; pub mod vote; diff --git a/frontend/src/graphql/mutations/start_assignment.rs b/frontend/src/graphql/mutations/start_assignment.rs new file mode 100644 index 0000000..a8c7829 --- /dev/null +++ b/frontend/src/graphql/mutations/start_assignment.rs @@ -0,0 +1,10 @@ +use graphql_client::GraphQLQuery; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "graphql/schema.graphql", + query_path = "graphql/mutations/start_assignment.graphql", + response_derives = "Debug", + skip_serializing_none +)] +pub struct StartAssignment; diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index b0dc0bd..7f6b0f7 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -1,6 +1,6 @@ pub mod agents; pub mod components; -pub mod cookie_names; +pub mod cookies; pub mod graphql; pub mod layouts; pub mod routes; diff --git a/frontend/src/main.rs b/frontend/src/main.rs index c9911f6..49bd18d 100644 --- a/frontend/src/main.rs +++ b/frontend/src/main.rs @@ -33,6 +33,7 @@ impl Component for App { } fn main() { + #[cfg(target_arch = "wasm32")] wasm_logger::init(wasm_logger::Config::default()); yew::start_app::(); diff --git a/frontend/src/routes/login.rs b/frontend/src/routes/login.rs index 4e5c680..07724c7 100644 --- a/frontend/src/routes/login.rs +++ b/frontend/src/routes/login.rs @@ -5,7 +5,7 @@ use yew_router::prelude::*; use yew_side_effect::title::Title; use crate::components; -use crate::cookie_names; +use crate::cookies; use crate::graphql; use crate::routes; @@ -50,13 +50,24 @@ impl Component for Login { .unwrap(); if response.errors.is_some() { - wasm_cookies::delete(cookie_names::TOKEN); + cookies::logout_clear(); } else { + let data = response.data.unwrap().login.unwrap(); + wasm_cookies::set( - cookie_names::TOKEN, - &response.data.unwrap().login.unwrap().token, + cookies::TOKEN, + &data.token, &wasm_cookies::CookieOptions::default(), - ) + ); + if data.user.admin { + wasm_cookies::set( + cookies::ADMIN, + "1", + &wasm_cookies::CookieOptions::default(), + ); + } else { + wasm_cookies::delete(cookies::ADMIN); + } } Msg::Login(components::graphql_errors::convert(response.errors)) diff --git a/frontend/src/routes/mod.rs b/frontend/src/routes/mod.rs index 3f1f21b..e51490d 100644 --- a/frontend/src/routes/mod.rs +++ b/frontend/src/routes/mod.rs @@ -1,7 +1,7 @@ use yew::prelude::*; use yew_router::prelude::*; -use crate::cookie_names; +use crate::cookies; use crate::layouts; pub mod home; @@ -24,12 +24,12 @@ pub enum Route { pub fn switch(routes: &Route) -> Html { let token = { - let tmp = wasm_cookies::get(cookie_names::TOKEN); + let tmp = wasm_cookies::get(cookies::TOKEN); if let Some(x) = tmp { if let Ok(y) = x { Some(y) } else { - wasm_cookies::delete(cookie_names::TOKEN); + wasm_cookies::delete(cookies::TOKEN); None } } else { @@ -37,6 +37,20 @@ pub fn switch(routes: &Route) -> Html { } }; let logged_in = token.is_some(); + let admin = { + let tmp = wasm_cookies::get(cookies::ADMIN); + if let Some(x) = tmp { + if let Ok(_) = x { + true + } else { + wasm_cookies::delete(cookies::ADMIN); + false + } + } else { + false + } + }; + log::debug!("admin = {:?}", admin); match routes { Route::Home => { @@ -52,7 +66,7 @@ pub fn switch(routes: &Route) -> Html { html! { - + } diff --git a/frontend/src/routes/settings/assignments.rs b/frontend/src/routes/settings/assignments.rs new file mode 100644 index 0000000..5079b7b --- /dev/null +++ b/frontend/src/routes/settings/assignments.rs @@ -0,0 +1,82 @@ +use graphql_client::reqwest::post_graphql; +use yew::prelude::*; + +use crate::components; +use crate::graphql; + +pub enum Msg { + StartAssignment, + StartAssignmentDone(Option>), +} + +#[derive(Properties, PartialEq, Eq)] +pub struct AssignmentsProps { + pub token: String, +} + +pub struct Assignments { + errors: Option>, +} + +impl Component for Assignments { + type Message = Msg; + type Properties = AssignmentsProps; + + fn create(_ctx: &Context) -> Self { + Self { errors: None } + } + + fn update(&mut self, ctx: &Context, msg: Self::Message) -> bool { + match msg { + Msg::StartAssignment => { + let client = graphql::client(Some(&ctx.props().token)).unwrap(); + ctx.link().send_future(async move { + let response = + post_graphql::( + &client, + graphql::URL.as_str(), + graphql::mutations::start_assignment::start_assignment::Variables, + ) + .await + .unwrap(); + + Msg::StartAssignmentDone(components::graphql_errors::convert(response.errors)) + }); + + false + } + Msg::StartAssignmentDone(errors) => { + self.errors = errors; + true + } + } + } + + fn view(&self, ctx: &Context) -> Html { + html! { +
+ { "Zuweisungen" } + + + + + + + + + +
{ "Aktion" }{ "Optionen" }
{ "Zuweisung starten" } +
    +
  • + +
  • +
+
+ + +
+ } + } +} diff --git a/frontend/src/routes/settings/mod.rs b/frontend/src/routes/settings/mod.rs index 94a7b86..790f43d 100644 --- a/frontend/src/routes/settings/mod.rs +++ b/frontend/src/routes/settings/mod.rs @@ -1,11 +1,13 @@ use yew::prelude::*; use yew_side_effect::title::Title; +pub mod assignments; pub mod tokens; #[derive(Properties, PartialEq, Eq)] pub struct SettingsProps { pub token: Option, + pub admin: bool, } pub struct Settings; @@ -25,6 +27,11 @@ impl Component for Settings {
+ if ctx.props().admin { +
+ +
+ } } } diff --git a/frontend/src/routes/settings/tokens.rs b/frontend/src/routes/settings/tokens.rs index f32d68a..b4acb63 100644 --- a/frontend/src/routes/settings/tokens.rs +++ b/frontend/src/routes/settings/tokens.rs @@ -82,7 +82,7 @@ impl Component for Tokens { } }); - true + false } Msg::RevokeDone { errors, id } => { self.errors = errors; @@ -122,7 +122,7 @@ impl Component for Tokens { }) } - + } }