diff --git a/cio/migrations/2022-07-12-150519_add_materials_to_employees/down.sql b/cio/migrations/2022-07-12-150519_add_materials_to_employees/down.sql new file mode 100644 index 000000000..e661484fc --- /dev/null +++ b/cio/migrations/2022-07-12-150519_add_materials_to_employees/down.sql @@ -0,0 +1 @@ +ALTER TABLE users DROP COLUMN materials; \ No newline at end of file diff --git a/cio/migrations/2022-07-12-150519_add_materials_to_employees/up.sql b/cio/migrations/2022-07-12-150519_add_materials_to_employees/up.sql new file mode 100644 index 000000000..0b4e06a6b --- /dev/null +++ b/cio/migrations/2022-07-12-150519_add_materials_to_employees/up.sql @@ -0,0 +1,12 @@ +ALTER TABLE users ADD COLUMN materials VARCHAR NOT NULL DEFAULT ''; + +UPDATE users +SET materials = applicant.materials +FROM ( + SELECT + email, + materials + FROM + applicants +) AS applicant +WHERE users.recovery_email = applicant.email; \ No newline at end of file diff --git a/cio/src/configs.rs b/cio/src/configs.rs index f452e73bc..98183627d 100644 --- a/cio/src/configs.rs +++ b/cio/src/configs.rs @@ -277,6 +277,9 @@ pub struct UserConfig { #[serde(default, skip_serializing_if = "Vec::is_empty")] pub public_ssh_keys: Vec, + #[serde(default, skip_serializing_if = "String::is_empty")] + pub materials: String, + #[serde(default, rename = "type", skip_serializing_if = "String::is_empty")] pub typev: String, @@ -813,14 +816,20 @@ impl UserConfig { self.work_address_formatted = self.work_address_formatted.replace('\n', "\\n"); } + // Looks up an applicant record based on the users recovery_email. There is a historical + // implicit assumption here that employees use the email that they applied with as their + // recovery_email + async fn applicant_record(&self, db: &Database) -> Result { + Ok(applicants::dsl::applicants + .filter(applicants::dsl::email.eq(self.recovery_email.to_string())) + .first_async::(db.pool()) + .await?) + } + pub async fn populate_start_date(&mut self, db: &Database) { // Only populate the start date, if we could not update it from Gusto. if self.start_date == crate::utils::default_date() { - if let Ok(a) = applicants::dsl::applicants - .filter(applicants::dsl::email.eq(self.recovery_email.to_string())) - .first_async::(db.pool()) - .await - { + if let Ok(a) = self.applicant_record(db).await { // Get their start date. if a.start_date.is_some() { self.start_date = a.start_date.unwrap(); @@ -829,6 +838,25 @@ impl UserConfig { } } + // If the user does not yet have a materials url, attempt to look it up from our applicant + // data based on the recovery email. This is performed when a user joins the + // organization and their record is first populated. If these do not match, then materials + // urls will need to be manually assigned + pub async fn populate_materials(&mut self, db: &Database) { + if self.materials.is_empty() { + if let Ok(applicant) = self.applicant_record(db).await { + self.materials = applicant.materials; + } else { + // TODO: When user structs are fixed so they always carry ids, this should be + // updated to log the user id instead + log::info!( + "Unable to find matching applicant when attempting to assign materials to employee starting on {}", + self.start_date + ); + } + } + } + pub fn populate_type(&mut self) { // TODO: make this an enum. self.typev = "full-time".to_string(); @@ -894,6 +922,8 @@ impl UserConfig { self.populate_start_date(db).await; + self.populate_materials(db).await; + // Create the link to the manager. if !self.manager.is_empty() { self.link_to_manager = vec![self.manager.to_string()]; diff --git a/cio/src/interviews.rs b/cio/src/interviews.rs index 762da8c00..c30d84775 100644 --- a/cio/src/interviews.rs +++ b/cio/src/interviews.rs @@ -336,36 +336,23 @@ pub async fn compile_packets(db: &Database, company: &Company) -> Result<()> { .create_folder(&drive_id, "", "interview_packets") .await?; - // Iterate over each user we have in gsuite and download their materials - // locally. + // Iterate over each user we have in gsuite and download their materials locally. + // TODO: This function currently creates local copies of every employees materials prior to + // compiling interview packets. While this is efficient in terms of only requesting each + // materials document once, it is wasteful in that it downloads materials it does not need. let employees = Users::get_from_db(db, company.id).await?; for employee in employees { if employee.is_system_account() { continue; } - // Get their application materials. - let mut materials_url = "".to_string(); - if let Ok(a) = applicants::dsl::applicants - .filter(applicants::dsl::email.eq(employee.recovery_email.to_string())) - .first_async::(db.pool()) - .await - { - materials_url = a.materials; - } - - if materials_url.is_empty() { - if employee.username == "ben.leonard" { - // Add Ben's materials. - materials_url = "https://drive.google.com/open?id=1bOHalcpSyXwaxr4E-_2LeT06HGXQCW0n".to_string(); - } else { - info!("could not find materials for email {}", employee.recovery_email); - continue; - } + if employee.materials.is_empty() { + info!("could not find materials for employee {}", employee.id); + continue; } // Let's download the contents of their materials locally. - if let Err(err) = download_materials_as_pdf(&drive_client, &materials_url, &employee.username).await { + if let Err(err) = download_materials_as_pdf(&drive_client, &employee.materials, &employee.username).await { log::warn!( "Failed to download materials for employee {} when constructing interview packets. err: {:?}", employee.id, diff --git a/cio/src/schema.rs b/cio/src/schema.rs index 08b9a1651..5e2d1ae38 100644 --- a/cio/src/schema.rs +++ b/cio/src/schema.rs @@ -919,6 +919,7 @@ table! { start_date -> Date, birthday -> Date, public_ssh_keys -> Array, + materials -> Varchar, typev -> Varchar, google_anniversary_event_id -> Varchar, email -> Varchar,