diff --git a/bvplan/Dockerfile b/bvplan/Dockerfile index 6440809..741ff79 100644 --- a/bvplan/Dockerfile +++ b/bvplan/Dockerfile @@ -26,6 +26,7 @@ LABEL maintainer="Dominic Grimm " \ RUN apt update RUN apt install -y libpq5 RUN apt install -y ca-certificates +RUN apt install -y tzdata RUN apt-get clean RUN apt-get autoremove -y RUN rm -rf /var/lib/{apt,dpkg,cache,log}/ diff --git a/bvplan/migrations/2022-12-03-124501_init/up.sql b/bvplan/migrations/2022-12-03-124501_init/up.sql index a8a36a5..519ff5d 100644 --- a/bvplan/migrations/2022-12-03-124501_init/up.sql +++ b/bvplan/migrations/2022-12-03-124501_init/up.sql @@ -125,7 +125,7 @@ CREATE TABLE timegrid_time_unit ( CREATE TYPE week_type AS ENUM ('a', 'b'); CREATE TABLE substitution_queries ( - id SERIAL PRIMARY KEY, + id BIGSERIAL PRIMARY KEY, schoolyear_id INTEGER NOT NULL REFERENCES schoolyears(id), date DATE NOT NULL, week_type week_type NOT NULL, @@ -153,8 +153,8 @@ CREATE TYPE substitution_type AS ENUM ( ); CREATE TABLE substitutions( - id SERIAL PRIMARY KEY, - substitution_query_id INTEGER NOT NULL REFERENCES substitution_queries(id), + id BIGSERIAL PRIMARY KEY, + substitution_query_id BIGINT NOT NULL REFERENCES substitution_queries(id), subst_type substitution_type NOT NULL, lesson_id INTEGER NOT NULL, start_time TIME NOT NULL, @@ -165,8 +165,8 @@ CREATE TABLE substitutions( ); CREATE TABLE substitution_classes ( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), + id BIGSERIAL PRIMARY KEY, + substitution_id BIGINT NOT NULL REFERENCES substitutions(id), position SMALLINT NOT NULL, class_id INTEGER NOT NULL REFERENCES classes(id), original_id INTEGER REFERENCES classes(id), @@ -175,8 +175,8 @@ CREATE TABLE substitution_classes ( ); CREATE TABLE substitution_teachers ( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), + id BIGSERIAL PRIMARY KEY, + substitution_id BIGINT NOT NULL REFERENCES substitutions(id), position SMALLINT NOT NULL, teacher_id INTEGER REFERENCES teachers(id), original_id INTEGER REFERENCES teachers(id), @@ -185,8 +185,8 @@ CREATE TABLE substitution_teachers ( ); CREATE TABLE substitution_subjects ( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), + id BIGSERIAL PRIMARY KEY, + substitution_id BIGINT NOT NULL REFERENCES substitutions(id), position SMALLINT NOT NULL, subject_id INTEGER NOT NULL REFERENCES subjects(id), original_id INTEGER REFERENCES subjects(id), @@ -195,8 +195,8 @@ CREATE TABLE substitution_subjects ( ); CREATE TABLE substitution_rooms ( - id SERIAL PRIMARY KEY, - substitution_id INTEGER NOT NULL REFERENCES substitutions(id), + id BIGSERIAL PRIMARY KEY, + substitution_id BIGINT NOT NULL REFERENCES substitutions(id), position SMALLINT NOT NULL, room_id INTEGER REFERENCES rooms(id), original_id INTEGER REFERENCES rooms(id), diff --git a/bvplan/src/db/models.rs b/bvplan/src/db/models.rs index 5fe1480..95dc9a8 100644 --- a/bvplan/src/db/models.rs +++ b/bvplan/src/db/models.rs @@ -388,7 +388,7 @@ impl diesel::deserialize::FromSql f #[diesel(table_name = schema::substitution_queries)] #[diesel(belongs_to(Schoolyear))] pub struct SubstitutionQuery { - pub id: i32, + pub id: i64, pub schoolyear_id: i32, pub date: NaiveDate, pub week_type: WeekType, @@ -508,8 +508,8 @@ impl From for SubstitutionType { #[diesel(table_name = schema::substitutions)] #[diesel(belongs_to(SubstitutionQuery))] pub struct Substitution { - pub id: i32, - pub substitution_query_id: i32, + pub id: i64, + pub substitution_query_id: i64, pub subst_type: SubstitutionType, pub lesson_id: i32, pub start_time: NaiveTime, @@ -522,7 +522,7 @@ pub struct Substitution { #[derive(Insertable, Debug)] #[diesel(table_name = schema::substitutions)] pub struct NewSubstitution<'a> { - pub substitution_query_id: i32, + pub substitution_query_id: i64, pub subst_type: SubstitutionType, pub lesson_id: i32, pub start_time: NaiveTime, @@ -535,8 +535,8 @@ pub struct NewSubstitution<'a> { #[diesel(belongs_to(Substitution))] #[diesel(belongs_to(Class))] pub struct SubstitutionClass { - pub id: i32, - pub substitution_id: i32, + pub id: i64, + pub substitution_id: i64, pub position: i16, pub class_id: i32, pub original_id: Option, @@ -547,7 +547,7 @@ pub struct SubstitutionClass { #[derive(Insertable, Debug)] #[diesel(table_name = schema::substitution_classes)] pub struct NewSubstitutionClass { - pub substitution_id: i32, + pub substitution_id: i64, pub position: i16, pub class_id: i32, pub original_id: Option, @@ -558,8 +558,8 @@ pub struct NewSubstitutionClass { #[diesel(belongs_to(Substitution))] #[diesel(belongs_to(Teacher))] pub struct SubstitutionTeacher { - pub id: i32, - pub substitution_id: i32, + pub id: i64, + pub substitution_id: i64, pub position: i16, pub teacher_id: Option, pub original_id: Option, @@ -570,7 +570,7 @@ pub struct SubstitutionTeacher { #[derive(Insertable, Debug)] #[diesel(table_name = schema::substitution_teachers)] pub struct NewSubstitutionTeacher { - pub substitution_id: i32, + pub substitution_id: i64, pub position: i16, pub teacher_id: Option, pub original_id: Option, @@ -581,8 +581,8 @@ pub struct NewSubstitutionTeacher { #[diesel(belongs_to(Substitution))] #[diesel(belongs_to(Subject))] pub struct SubstitutionSubject { - pub id: i32, - pub substitution_id: i32, + pub id: i64, + pub substitution_id: i64, pub position: i16, pub subject_id: i32, pub original_id: Option, @@ -593,7 +593,7 @@ pub struct SubstitutionSubject { #[derive(Insertable, Debug)] #[diesel(table_name = schema::substitution_subjects)] pub struct NewSubstitutionSubject { - pub substitution_id: i32, + pub substitution_id: i64, pub position: i16, pub subject_id: i32, pub original_id: Option, @@ -604,8 +604,8 @@ pub struct NewSubstitutionSubject { #[diesel(belongs_to(Substitution))] #[diesel(belongs_to(Room))] pub struct SubstitutionRoom { - pub id: i32, - pub substitution_id: i32, + pub id: i64, + pub substitution_id: i64, pub position: i16, pub room_id: Option, pub original_id: Option, @@ -616,7 +616,7 @@ pub struct SubstitutionRoom { #[derive(Insertable, Debug)] #[diesel(table_name = schema::substitution_rooms)] pub struct NewSubstitutionRoom { - pub substitution_id: i32, + pub substitution_id: i64, pub position: i16, pub room_id: Option, pub original_id: Option, diff --git a/bvplan/src/db/schema.rs b/bvplan/src/db/schema.rs index 4a9c322..12813db 100644 --- a/bvplan/src/db/schema.rs +++ b/bvplan/src/db/schema.rs @@ -152,7 +152,7 @@ diesel::table! { use super::sql_types::*; substitution_queries { - id -> Integer, + id -> BigInt, schoolyear_id -> Integer, date -> Date, week_type -> WeekType, @@ -168,8 +168,8 @@ diesel::table! { use super::sql_types::*; substitutions { - id -> Integer, - substitution_query_id -> Integer, + id -> BigInt, + substitution_query_id -> BigInt, subst_type -> SubstitutionType, lesson_id -> Integer, start_time -> Time, @@ -182,8 +182,8 @@ diesel::table! { diesel::table! { substitution_classes { - id -> Integer, - substitution_id -> Integer, + id -> BigInt, + substitution_id -> BigInt, position -> SmallInt, class_id -> Integer, original_id -> Nullable, @@ -194,8 +194,8 @@ diesel::table! { diesel::table! { substitution_teachers { - id -> Integer, - substitution_id -> Integer, + id -> BigInt, + substitution_id -> BigInt, position -> SmallInt, teacher_id -> Nullable, original_id -> Nullable, @@ -206,8 +206,8 @@ diesel::table! { diesel::table! { substitution_subjects { - id -> Integer, - substitution_id -> Integer, + id -> BigInt, + substitution_id -> BigInt, position -> SmallInt, subject_id -> Integer, original_id -> Nullable, @@ -218,8 +218,8 @@ diesel::table! { diesel::table! { substitution_rooms { - id -> Integer, - substitution_id -> Integer, + id -> BigInt, + substitution_id -> BigInt, position -> SmallInt, room_id -> Nullable, original_id -> Nullable, diff --git a/bvplan/src/worker/cleanup.rs b/bvplan/src/worker/cleanup.rs new file mode 100644 index 0000000..7ddc0e2 --- /dev/null +++ b/bvplan/src/worker/cleanup.rs @@ -0,0 +1,89 @@ +use anyhow::Result; +use celery::{error::TaskError, task::TaskResult}; +use chrono::prelude::*; +use diesel::prelude::*; + +use crate::db; + +fn do_cleanup(db_conn: &mut db::Connection) -> Result<()> { + let timegrid_ids = db::schema::timegrids::table + .select(db::schema::timegrids::id) + .filter(db::schema::timegrids::active.eq(false)) + .load::(db_conn)?; + let timegrid_day_ids = db::schema::timegrid_days::table + .select(db::schema::timegrid_days::id) + .filter(db::schema::timegrid_days::timegrid_id.eq_any(&timegrid_ids)) + .load::(db_conn)?; + + diesel::delete( + db::schema::timegrid_time_unit::table + .filter(db::schema::timegrid_time_unit::timegrid_day_id.eq_any(&timegrid_day_ids)), + ) + .execute(db_conn)?; + diesel::delete( + db::schema::timegrid_days::table + .filter(db::schema::timegrid_days::id.eq_any(timegrid_day_ids)), + ) + .execute(db_conn)?; + diesel::delete( + db::schema::timegrids::table.filter(db::schema::timegrids::id.eq_any(timegrid_ids)), + ) + .execute(db_conn)?; + + let subst_query_ids = db::schema::substitution_queries::table + .select(db::schema::substitution_queries::id) + .filter( + db::schema::substitution_queries::queried_at + .lt(Local::now().naive_local() - chrono::Duration::hours(1)), + ) + .load::(db_conn)?; + let subst_ids = db::schema::substitutions::table + .select(db::schema::substitutions::id) + .filter(db::schema::substitutions::substitution_query_id.eq_any(&subst_query_ids)) + .load::(db_conn)?; + + diesel::delete( + db::schema::substitution_classes::table + .filter(db::schema::substitution_classes::substitution_id.eq_any(&subst_ids)), + ) + .execute(db_conn)?; + diesel::delete( + db::schema::substitution_teachers::table + .filter(db::schema::substitution_teachers::substitution_id.eq_any(&subst_ids)), + ) + .execute(db_conn)?; + diesel::delete( + db::schema::substitution_subjects::table + .filter(db::schema::substitution_subjects::substitution_id.eq_any(&subst_ids)), + ) + .execute(db_conn)?; + diesel::delete( + db::schema::substitution_rooms::table + .filter(db::schema::substitution_rooms::substitution_id.eq_any(&subst_ids)), + ) + .execute(db_conn)?; + diesel::delete( + db::schema::substitutions::table.filter(db::schema::substitutions::id.eq_any(subst_ids)), + ) + .execute(db_conn)?; + diesel::delete( + db::schema::substitution_queries::table + .filter(db::schema::substitution_queries::id.eq_any(subst_query_ids)), + ) + .execute(db_conn)?; + + Ok(()) +} + +#[celery::task] +pub async fn cleanup() -> TaskResult<()> { + let db_conn = &mut match db::POOL.get() { + Ok(x) => x, + Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))), + }; + if let Err(e) = do_cleanup(db_conn) { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + + Ok(()) +} diff --git a/bvplan/src/worker/update_info.rs b/bvplan/src/worker/get_substitutions.rs similarity index 65% rename from bvplan/src/worker/update_info.rs rename to bvplan/src/worker/get_substitutions.rs index 2a29bce..823fc9d 100644 --- a/bvplan/src/worker/update_info.rs +++ b/bvplan/src/worker/get_substitutions.rs @@ -25,286 +25,11 @@ async fn get_schoolyear(client: &untis::Client, db_conn: &mut db::Connection) -> .first(db_conn)?) } -async fn fetch_current_tenant( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let tenant = client.current_tenant().await?; - if diesel::select(diesel::dsl::not(diesel::dsl::exists( - db::schema::tenants::table.filter(db::schema::tenants::untis_id.eq(tenant.id)), - ))) - .get_result::(db_conn)? - { - diesel::update(db::schema::tenants::table) - .filter(db::schema::tenants::active) - .set(db::schema::tenants::active.eq(false)) - .execute(db_conn)?; - diesel::insert_into(db::schema::tenants::table) - .values(db::models::NewTenant { - untis_id: tenant.id, - schoolyear_id, - name: &tenant.display_name, - active: true, - }) - .execute(db_conn)?; - } else if diesel::select(diesel::dsl::exists( - db::schema::tenants::table - .filter(db::schema::tenants::untis_id.eq(tenant.id)) - .filter(db::schema::tenants::active.eq(false)), - )) - .get_result::(db_conn)? - { - diesel::update(db::schema::tenants::table) - .filter(db::schema::tenants::active) - .set(( - db::schema::tenants::active.eq(false), - db::schema::tenants::updated_at.eq(diesel::dsl::now), - )) - .execute(db_conn)?; - diesel::update(db::schema::tenants::table) - .filter(db::schema::tenants::untis_id.eq(tenant.id)) - .set(( - db::schema::tenants::active.eq(true), - db::schema::tenants::updated_at.eq(diesel::dsl::now), - )) - .execute(db_conn)?; - } - - Ok(()) -} - -async fn fetch_timegrid( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let days = client.timegrid().await?; - - diesel::update(db::schema::timegrids::table) - .filter(db::schema::timegrids::active) - .set(db::schema::timegrids::active.eq(false)) - .execute(db_conn)?; - let timegrid_id = diesel::insert_into(db::schema::timegrids::table) - .values(db::models::NewTimegrid { - schoolyear_id, - active: true, - }) - .returning(db::schema::timegrids::id) - .get_result::(db_conn)?; - - for day in days { - let timegrid_day_id = diesel::insert_into(db::schema::timegrid_days::table) - .values(db::models::NewTimegridDay { - timegrid_id, - weekday: day.day.try_into()?, - }) - .returning(db::schema::timegrid_days::id) - .get_result::(db_conn)?; - diesel::insert_into(db::schema::timegrid_time_unit::table) - .values( - day.time_units - .into_iter() - .map(|x| db::models::NewTimegridTimeUnit { - timegrid_day_id, - start_time: x.start_time, - end_time: x.end_time, - }) - .collect::>(), - ) - .execute(db_conn)?; - } - - Ok(()) -} - -async fn fetch_teachers( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let existing_teachers = db::schema::teachers::table - .select(db::schema::teachers::untis_id) - .filter(db::schema::teachers::schoolyear_id.eq(schoolyear_id)) - .load::(db_conn)?; - diesel::insert_into(db::schema::teachers::table) - .values( - &client - .teachers() - .await? - .iter() - .filter(|t| !existing_teachers.contains(&t.id)) - .map(|t| db::models::NewTeacher { - untis_id: t.id, - schoolyear_id, - name: &t.name, - forename: if t.forename.is_empty() { - None - } else { - Some(&t.forename) - }, - display_name: &t.display_name, - }) - .collect::>(), - ) - .execute(db_conn)?; - - Ok(()) -} - -async fn fetch_classes( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let existing_classes = db::schema::classes::table - .select(db::schema::classes::untis_id) - .filter(db::schema::classes::schoolyear_id.eq(schoolyear_id)) - .load::(db_conn)?; - diesel::insert_into(db::schema::classes::table) - .values( - &client - .classes() - .await? - .iter() - .filter(|c| !existing_classes.contains(&c.id)) - .map(|c| db::models::NewClass { - untis_id: c.id, - schoolyear_id, - name: &c.name, - long_name: &c.long_name, - active: c.active, - }) - .collect::>(), - ) - .execute(db_conn)?; - - Ok(()) -} - -async fn fetch_subjects( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let existing_classes = db::schema::subjects::table - .select(db::schema::subjects::untis_id) - .filter(db::schema::subjects::schoolyear_id.eq(schoolyear_id)) - .load::(db_conn)?; - diesel::insert_into(db::schema::subjects::table) - .values( - &client - .subjects() - .await? - .iter() - .filter(|c| !existing_classes.contains(&c.id)) - .map(|c| db::models::NewSubject { - untis_id: c.id, - schoolyear_id, - name: &c.name, - long_name: &c.long_name, - }) - .collect::>(), - ) - .execute(db_conn)?; - - Ok(()) -} - -async fn fetch_rooms( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let existing_classes = db::schema::rooms::table - .select(db::schema::rooms::untis_id) - .filter(db::schema::rooms::schoolyear_id.eq(schoolyear_id)) - .load::(db_conn)?; - diesel::insert_into(db::schema::rooms::table) - .values( - &client - .rooms() - .await? - .iter() - .filter(|c| !existing_classes.contains(&c.id)) - .map(|c| db::models::NewRoom { - untis_id: c.id, - schoolyear_id, - name: &c.name, - long_name: &c.long_name, - }) - .collect::>(), - ) - .execute(db_conn)?; - - Ok(()) -} - -async fn fetch_departments( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let existing_classes = db::schema::departments::table - .select(db::schema::departments::untis_id) - .filter(db::schema::departments::schoolyear_id.eq(schoolyear_id)) - .load::(db_conn)?; - diesel::insert_into(db::schema::departments::table) - .values( - &client - .departments() - .await? - .iter() - .filter(|c| !existing_classes.contains(&c.id)) - .map(|c| db::models::NewDepartment { - untis_id: c.id, - schoolyear_id, - name: &c.name, - long_name: &c.long_name, - }) - .collect::>(), - ) - .execute(db_conn)?; - - Ok(()) -} - -async fn fetch_holidays( - client: &untis::Client, - db_conn: &mut PgConnection, - schoolyear_id: i32, -) -> Result<()> { - let existing_classes = db::schema::holidays::table - .select(db::schema::holidays::untis_id) - .filter(db::schema::holidays::schoolyear_id.eq(schoolyear_id)) - .load::(db_conn)?; - diesel::insert_into(db::schema::holidays::table) - .values( - &client - .holidays() - .await? - .iter() - .filter(|c| !existing_classes.contains(&c.id)) - .map(|c| db::models::NewHoliday { - untis_id: c.id, - schoolyear_id, - name: &c.name, - long_name: &c.long_name, - start_date: c.start_date, - end_date: c.end_date, - }) - .collect::>(), - ) - .execute(db_conn)?; - - Ok(()) -} - async fn fetch_substitutions( client: &untis::Client, - db_conn: &mut PgConnection, + db_conn: &mut db::Connection, schoolyear_id: i32, -) -> Result { +) -> Result { lazy_static! { static ref TITLE_SELECTOR: scraper::Selector = scraper::Selector::parse(".mon_title").unwrap(); @@ -362,7 +87,7 @@ async fn fetch_substitutions( queried_at: Utc::now().naive_utc(), }) .returning(db::schema::substitution_queries::id) - .get_result::(db_conn)?; + .get_result::(db_conn)?; for substitution in client.substitutions(&date, &date, None).await? { let substitution_id = diesel::insert_into(db::schema::substitutions::table) @@ -375,7 +100,7 @@ async fn fetch_substitutions( text: substitution.text.as_deref(), }) .returning(db::schema::substitutions::id) - .get_result::(db_conn)?; + .get_result::(db_conn)?; diesel::insert_into(db::schema::substitution_classes::table) .values( @@ -525,7 +250,7 @@ fn get_period(times: &Vec, start: bool, time: NaiveTime) -> Option fn cache_substitutions( db_conn: &mut PgConnection, redis_conn: &mut cache::Connection, - substitution_query_id: i32, + substitution_query_id: i64, last_import_time: NaiveDateTime, ) -> Result<()> { let (queried_at, date, week_type) = db::schema::substitution_queries::table @@ -717,7 +442,7 @@ fn cache_substitutions( } #[celery::task] -pub async fn update_info() -> TaskResult<()> { +pub async fn get_substitutions() -> TaskResult<()> { let dur = Duration::from_secs(2); thread::sleep(dur); @@ -744,38 +469,6 @@ pub async fn update_info() -> TaskResult<()> { Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))), }; thread::sleep(dur); - if let Err(e) = fetch_current_tenant(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); - if let Err(e) = fetch_timegrid(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); - if let Err(e) = fetch_teachers(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); - if let Err(e) = fetch_classes(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); - if let Err(e) = fetch_subjects(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); - if let Err(e) = fetch_rooms(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); - if let Err(e) = fetch_departments(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); - if let Err(e) = fetch_holidays(&client, db_conn, schoolyear_id).await { - return Err(TaskError::UnexpectedError(format!("{:?}", e))); - } - thread::sleep(dur); let last_import_time = match client.last_import_time().await { Ok(x) => x, Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))), diff --git a/bvplan/src/worker/mod.rs b/bvplan/src/worker/mod.rs index baa771f..f91e81b 100644 --- a/bvplan/src/worker/mod.rs +++ b/bvplan/src/worker/mod.rs @@ -7,7 +7,8 @@ use stdext::duration::DurationExt; use crate::config; -pub mod update_info; +pub mod cleanup; +pub mod get_substitutions; pub mod update_meta; pub const QUEUE_NAME: &str = "celery"; @@ -22,8 +23,9 @@ pub async fn init() { celery::app!( broker = AMQPBroker { &config::CONFIG.amqp_url }, tasks = [ - update_info::update_info, + get_substitutions::get_substitutions, update_meta::update_meta, + cleanup::cleanup, ], task_routes = [ "*" => QUEUE_NAME, @@ -45,15 +47,20 @@ pub fn beat() -> impl std::future::Future< celery::beat!( broker = AMQPBroker { &config::CONFIG.amqp_url }, tasks = [ - "update_info" => { - update_info::update_info, - schedule = DeltaSchedule::new(Duration::from_minutes(999)), + "get_substitutions" => { + get_substitutions::get_substitutions, + schedule = DeltaSchedule::new(Duration::from_minutes(5)), args = (), }, "update_meta" => { update_meta::update_meta, schedule = DeltaSchedule::new(Duration::from_hours(6)), args = (), + }, + "cleanup" => { + cleanup::cleanup, + schedule = DeltaSchedule::new(Duration::from_hours(1)), + args = (), } ], task_routes = [ diff --git a/bvplan/src/worker/update_meta.rs b/bvplan/src/worker/update_meta.rs index bed54d5..354a516 100644 --- a/bvplan/src/worker/update_meta.rs +++ b/bvplan/src/worker/update_meta.rs @@ -43,6 +43,281 @@ async fn fetch_schoolyears(client: &untis::Client, db_conn: &mut db::Connection) Ok(id) } +async fn fetch_current_tenant( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let tenant = client.current_tenant().await?; + if diesel::select(diesel::dsl::not(diesel::dsl::exists( + db::schema::tenants::table.filter(db::schema::tenants::untis_id.eq(tenant.id)), + ))) + .get_result::(db_conn)? + { + diesel::update(db::schema::tenants::table) + .filter(db::schema::tenants::active) + .set(db::schema::tenants::active.eq(false)) + .execute(db_conn)?; + diesel::insert_into(db::schema::tenants::table) + .values(db::models::NewTenant { + untis_id: tenant.id, + schoolyear_id, + name: &tenant.display_name, + active: true, + }) + .execute(db_conn)?; + } else if diesel::select(diesel::dsl::exists( + db::schema::tenants::table + .filter(db::schema::tenants::untis_id.eq(tenant.id)) + .filter(db::schema::tenants::active.eq(false)), + )) + .get_result::(db_conn)? + { + diesel::update(db::schema::tenants::table) + .filter(db::schema::tenants::active) + .set(( + db::schema::tenants::active.eq(false), + db::schema::tenants::updated_at.eq(diesel::dsl::now), + )) + .execute(db_conn)?; + diesel::update(db::schema::tenants::table) + .filter(db::schema::tenants::untis_id.eq(tenant.id)) + .set(( + db::schema::tenants::active.eq(true), + db::schema::tenants::updated_at.eq(diesel::dsl::now), + )) + .execute(db_conn)?; + } + + Ok(()) +} + +async fn fetch_timegrid( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let days = client.timegrid().await?; + + diesel::update(db::schema::timegrids::table) + .filter(db::schema::timegrids::active) + .set(db::schema::timegrids::active.eq(false)) + .execute(db_conn)?; + let timegrid_id = diesel::insert_into(db::schema::timegrids::table) + .values(db::models::NewTimegrid { + schoolyear_id, + active: true, + }) + .returning(db::schema::timegrids::id) + .get_result::(db_conn)?; + + for day in days { + let timegrid_day_id = diesel::insert_into(db::schema::timegrid_days::table) + .values(db::models::NewTimegridDay { + timegrid_id, + weekday: day.day.try_into()?, + }) + .returning(db::schema::timegrid_days::id) + .get_result::(db_conn)?; + diesel::insert_into(db::schema::timegrid_time_unit::table) + .values( + day.time_units + .into_iter() + .map(|x| db::models::NewTimegridTimeUnit { + timegrid_day_id, + start_time: x.start_time, + end_time: x.end_time, + }) + .collect::>(), + ) + .execute(db_conn)?; + } + + Ok(()) +} + +async fn fetch_teachers( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let existing_teachers = db::schema::teachers::table + .select(db::schema::teachers::untis_id) + .filter(db::schema::teachers::schoolyear_id.eq(schoolyear_id)) + .load::(db_conn)?; + diesel::insert_into(db::schema::teachers::table) + .values( + &client + .teachers() + .await? + .iter() + .filter(|t| !existing_teachers.contains(&t.id)) + .map(|t| db::models::NewTeacher { + untis_id: t.id, + schoolyear_id, + name: &t.name, + forename: if t.forename.is_empty() { + None + } else { + Some(&t.forename) + }, + display_name: &t.display_name, + }) + .collect::>(), + ) + .execute(db_conn)?; + + Ok(()) +} + +async fn fetch_classes( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let existing_classes = db::schema::classes::table + .select(db::schema::classes::untis_id) + .filter(db::schema::classes::schoolyear_id.eq(schoolyear_id)) + .load::(db_conn)?; + diesel::insert_into(db::schema::classes::table) + .values( + &client + .classes() + .await? + .iter() + .filter(|c| !existing_classes.contains(&c.id)) + .map(|c| db::models::NewClass { + untis_id: c.id, + schoolyear_id, + name: &c.name, + long_name: &c.long_name, + active: c.active, + }) + .collect::>(), + ) + .execute(db_conn)?; + + Ok(()) +} + +async fn fetch_subjects( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let existing_classes = db::schema::subjects::table + .select(db::schema::subjects::untis_id) + .filter(db::schema::subjects::schoolyear_id.eq(schoolyear_id)) + .load::(db_conn)?; + diesel::insert_into(db::schema::subjects::table) + .values( + &client + .subjects() + .await? + .iter() + .filter(|c| !existing_classes.contains(&c.id)) + .map(|c| db::models::NewSubject { + untis_id: c.id, + schoolyear_id, + name: &c.name, + long_name: &c.long_name, + }) + .collect::>(), + ) + .execute(db_conn)?; + + Ok(()) +} + +async fn fetch_rooms( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let existing_classes = db::schema::rooms::table + .select(db::schema::rooms::untis_id) + .filter(db::schema::rooms::schoolyear_id.eq(schoolyear_id)) + .load::(db_conn)?; + diesel::insert_into(db::schema::rooms::table) + .values( + &client + .rooms() + .await? + .iter() + .filter(|c| !existing_classes.contains(&c.id)) + .map(|c| db::models::NewRoom { + untis_id: c.id, + schoolyear_id, + name: &c.name, + long_name: &c.long_name, + }) + .collect::>(), + ) + .execute(db_conn)?; + + Ok(()) +} + +async fn fetch_departments( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let existing_classes = db::schema::departments::table + .select(db::schema::departments::untis_id) + .filter(db::schema::departments::schoolyear_id.eq(schoolyear_id)) + .load::(db_conn)?; + diesel::insert_into(db::schema::departments::table) + .values( + &client + .departments() + .await? + .iter() + .filter(|c| !existing_classes.contains(&c.id)) + .map(|c| db::models::NewDepartment { + untis_id: c.id, + schoolyear_id, + name: &c.name, + long_name: &c.long_name, + }) + .collect::>(), + ) + .execute(db_conn)?; + + Ok(()) +} + +async fn fetch_holidays( + client: &untis::Client, + db_conn: &mut PgConnection, + schoolyear_id: i32, +) -> Result<()> { + let existing_classes = db::schema::holidays::table + .select(db::schema::holidays::untis_id) + .filter(db::schema::holidays::schoolyear_id.eq(schoolyear_id)) + .load::(db_conn)?; + diesel::insert_into(db::schema::holidays::table) + .values( + &client + .holidays() + .await? + .iter() + .filter(|c| !existing_classes.contains(&c.id)) + .map(|c| db::models::NewHoliday { + untis_id: c.id, + schoolyear_id, + name: &c.name, + long_name: &c.long_name, + start_date: c.start_date, + end_date: c.end_date, + }) + .collect::>(), + ) + .execute(db_conn)?; + + Ok(()) +} + #[celery::task] pub async fn update_meta() -> TaskResult<()> { let dur = Duration::from_secs(2); @@ -50,6 +325,11 @@ pub async fn update_meta() -> TaskResult<()> { Ok(x) => x, Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))), }; + if let Err(e) = client.login().await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + let db_conn = &mut match db::POOL.get() { Ok(x) => x, Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))), @@ -60,8 +340,38 @@ pub async fn update_meta() -> TaskResult<()> { Ok(x) => x, Err(e) => return Err(TaskError::UnexpectedError(format!("{:?}", e))), }; - dbg!(&schoolyear_id); thread::sleep(dur); + if let Err(e) = fetch_current_tenant(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + if let Err(e) = fetch_timegrid(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + if let Err(e) = fetch_teachers(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + if let Err(e) = fetch_classes(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + if let Err(e) = fetch_subjects(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + if let Err(e) = fetch_rooms(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + if let Err(e) = fetch_departments(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } + thread::sleep(dur); + if let Err(e) = fetch_holidays(&client, db_conn, schoolyear_id).await { + return Err(TaskError::UnexpectedError(format!("{:?}", e))); + } if let Err(e) = client.logout().await { return Err(TaskError::UnexpectedError(format!("{:?}", e))); diff --git a/bvplan/templates/bvplan.html b/bvplan/templates/bvplan.html index ae90394..68401d9 100644 --- a/bvplan/templates/bvplan.html +++ b/bvplan/templates/bvplan.html @@ -8,7 +8,7 @@ - + diff --git a/docker-compose.yml b/docker-compose.yml index 2750bf2..f50d09b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,6 +83,8 @@ services: <<: *bvplan command: worker volumes: + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro - ./data/backups:/backups web: @@ -93,6 +95,9 @@ services: - rabbitmq - worker - redis + volumes: + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro volumes: db: