Skip to content
This repository has been archived by the owner on Nov 8, 2024. It is now read-only.

Dc airtable #374

Merged
merged 6 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions cio/src/analytics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ use crate::{

#[db {
new_struct_name = "PageView",
airtable_base = "customer_leads",
airtable_table = "AIRTABLE_PAGE_VIEWS_TABLE",
match_on = {
"time" = "DateTime<Utc>",
"user_email" = "String",
Expand All @@ -42,29 +40,6 @@ pub struct NewPageView {
pub cio_company_id: i32,
}

/// Implement updating the Airtable record for a PageView.
#[async_trait]
impl UpdateAirtableRecord<PageView> for PageView {
async fn update_airtable_record(&mut self, _record: PageView) -> Result<()> {
// Get the current auth users in Airtable so we can link to it.
// TODO: make this more dry so we do not call it every single damn time.
let db = Database::new().await;
let auth_users = AuthUsers::get_from_airtable(&db, self.cio_company_id).await?;

// Iterate over the auth_users and see if we find a match.
for (_id, auth_user_record) in auth_users {
if auth_user_record.fields.email == self.user_email {
// Set the link_to_auth_user to the right user.
self.link_to_auth_user = vec![auth_user_record.id];
// Break the loop and return early.
break;
}
}

Ok(())
}
}

impl NewPageView {
pub fn set_page_link(&mut self) {
// Set the link.
Expand All @@ -84,12 +59,3 @@ impl NewPageView {
Ok(())
}
}

pub async fn refresh_analytics(db: &Database, company: &Company) -> Result<()> {
PageViews::get_from_db(db, company.id)
.await?
.update_airtable(db)
.await?;

Ok(())
}
24 changes: 0 additions & 24 deletions cio/src/api_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ use crate::{

#[db {
new_struct_name = "APIToken",
airtable_base = "cio",
airtable_table = "AIRTABLE_API_TOKENS_TABLE",
match_on = {
"auth_company_id" = "i32",
"product" = "String",
Expand Down Expand Up @@ -69,19 +67,6 @@ pub struct NewAPIToken {
pub auth_company_id: i32,
}

/// Implement updating the Airtable record for a APIToken.
#[async_trait]
impl UpdateAirtableRecord<APIToken> for APIToken {
async fn update_airtable_record(&mut self, _record: APIToken) -> Result<()> {
// Link to the correct company.
let db = Database::new().await;
let company = Company::get_by_id(&db, self.auth_company_id).await?;
self.company = vec![company.airtable_record_id];

Ok(())
}
}

impl NewAPIToken {
pub fn expand(&mut self) {
if self.expires_in > 0 {
Expand Down Expand Up @@ -136,12 +121,3 @@ impl APIToken {
// }
}
}

pub async fn refresh_api_tokens(db: &Database, company: &Company) -> Result<()> {
APITokens::get_from_db(db, company.id)
.await?
.update_airtable(db)
.await?;

Ok(())
}
70 changes: 0 additions & 70 deletions cio/src/applicant_reviews.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ use crate::{

#[db {
new_struct_name = "ApplicantReview",
airtable_base = "hiring",
airtable_table = "AIRTABLE_REVIEWS_TABLE",
match_on = {
"name" = "String",
"applicant" = "Vec<String>",
Expand Down Expand Up @@ -105,17 +103,6 @@ pub struct NewApplicantReview {
pub cio_company_id: i32,
}

/// Implement updating the Airtable record for a ApplicantReview.
#[async_trait]
impl UpdateAirtableRecord<ApplicantReview> for ApplicantReview {
async fn update_airtable_record(&mut self, _record: ApplicantReview) -> Result<()> {
// Set name to empty since it is a function we cannot update it.
self.name = "".to_string();

Ok(())
}
}

impl ApplicantReview {
pub async fn expand(&mut self, db: &Database) -> Result<()> {
let company = self.company(db).await?;
Expand Down Expand Up @@ -154,60 +141,3 @@ impl ApplicantReview {
Ok(())
}
}

pub async fn refresh_reviews(db: &Database, company: &Company) -> Result<()> {
if company.airtable_base_id_hiring.is_empty() {
// Return early.
return Ok(());
}

let company_id = company.id;

let client = company.authenticate_airtable(&company.airtable_base_id_hiring);
let mut pages = client.pages::<ApplicantReview>(&ApplicantReview::airtable_table(), "Grid view", vec![]);

while let Some(records) = pages.next().await? {
let records = records
.into_iter()
.filter(|record| !record.fields.name.is_empty() && !record.fields.applicant.is_empty())
.collect::<Vec<_>>();

let chunks: Vec<Vec<_>> = records.chunks(10).map(|chunk| chunk.to_vec()).collect();

for chunk in chunks {
let mut tasks = vec![];

for record in chunk {
let db = db.clone();

tasks.push(tokio::spawn(async move {
let new_review: NewApplicantReview = record.fields.into();

let mut review = new_review.upsert_in_db(&db).await?;
if review.airtable_record_id.is_empty() {
review.airtable_record_id = record.id;
}
review.cio_company_id = company_id;

review.expand(&db).await?;
review.update(&db).await?;

Ok::<(), anyhow::Error>(())
}));
}

futures::future::join_all(tasks)
.await
.into_iter()
.collect::<std::result::Result<Vec<_>, tokio::task::JoinError>>()?;
}
}

// Update them all from the database.
// ApplicantReviews::get_from_db(db, company.id)
// .await?
// .update_airtable(db)
// .await?;

Ok(())
}
106 changes: 37 additions & 69 deletions cio/src/applicants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@ impl From<&OnboardingAssignees> for Vec<String> {
/// The data type for a NewApplicant.
#[db {
new_struct_name = "Applicant",
airtable_base = "hiring",
airtable_table = "AIRTABLE_APPLICATIONS_TABLE",
match_on = {
"email" = "String",
"sheet_id" = "String",
Expand Down Expand Up @@ -461,7 +459,7 @@ impl Applicant {
// Initialize the GSuite sheets client.
let drive_client = company.authenticate_google_drive(db).await?;

self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

// Expand the application.
if let Err(e) = self.expand(db, &drive_client, &app_config.apply).await {
Expand Down Expand Up @@ -565,7 +563,7 @@ impl Applicant {

/// Update applicant reviews counts.
pub async fn update_reviews_scoring(&mut self, db: &Database) -> Result<()> {
self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

// Create the Airtable client.
let company = Company::get_by_id(db, self.cio_company_id).await?;
Expand Down Expand Up @@ -750,7 +748,7 @@ impl Applicant {
/// Send an invite to the applicant to do a background check.
pub async fn send_background_check_invitation(&mut self, db: &Database) -> Result<()> {
// Keep the fields from Airtable we need just in case they changed.
self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

let company = self.company(db).await?;
let checkr_auth = company.authenticate_checkr();
Expand Down Expand Up @@ -1170,21 +1168,6 @@ fn parse_question(q1: &str, q2: &str, materials_contents: &str) -> String {
Default::default()
}

/// Implement updating the Airtable record for an Applicant.
#[async_trait]
impl UpdateAirtableRecord<Applicant> for Applicant {
async fn update_airtable_record(&mut self, record: Applicant) -> Result<()> {
self.interviews = record.interviews;
self.geocode_cache = record.geocode_cache;
self.link_to_reviews = record.link_to_reviews;
self.resume_contents = truncate(&self.resume_contents, 90000);
self.materials_contents = truncate(&self.materials_contents, 90000);
self.question_why_oxide = truncate(&self.question_why_oxide, 90000);

Ok(())
}
}

/// Get the contexts of a file in Google Drive by it's URL as a text string.
pub async fn get_file_contents(drive_client: &GoogleDrive, url: &str) -> Result<String> {
let id = url
Expand Down Expand Up @@ -1291,8 +1274,6 @@ async fn read_pdf(name: &str, path: std::path::PathBuf) -> Result<String> {
/// The data type for a ApplicantReviewer.
#[db {
new_struct_name = "ApplicantReviewer",
airtable_base = "hiring",
airtable_table = "AIRTABLE_REVIEWER_LEADERBOARD_TABLE",
match_on = {
"email" = "String",
},
Expand Down Expand Up @@ -1326,14 +1307,6 @@ pub struct NewApplicantReviewer {
pub cio_company_id: i32,
}

/// Implement updating the Airtable record for an ApplicantReviewer.
#[async_trait]
impl UpdateAirtableRecord<ApplicantReviewer> for ApplicantReviewer {
async fn update_airtable_record(&mut self, _record: ApplicantReviewer) -> Result<()> {
Ok(())
}
}

pub async fn refresh_docusign_for_applicants(db: &Database, company: &Company, config: &AppConfig) -> Result<()> {
if company.airtable_base_id_hiring.is_empty() {
// Return early.
Expand Down Expand Up @@ -1977,7 +1950,7 @@ The applicants Airtable \
new_envelope: docusign::Envelope,
) -> Result<()> {
// Keep the fields from Airtable we need just in case they changed.
self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

// We look for "Onboarding" here as well since we want to make sure we can actually update
// the data for the user.
Expand Down Expand Up @@ -2026,7 +1999,7 @@ The applicants Airtable \
envelope: docusign::Envelope,
) -> Result<()> {
// Keep the fields from Airtable we need just in case they changed.
self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

let company = self.company(db).await?;

Expand Down Expand Up @@ -2182,7 +2155,7 @@ The applicants Airtable \
new_envelope: docusign::Envelope,
) -> Result<()> {
// Keep the fields from Airtable we need just in case they changed.
self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

// We look for "Onboarding" here as well since we want to make sure we can actually update
// the data for the user.
Expand Down Expand Up @@ -2231,7 +2204,7 @@ The applicants Airtable \
new_envelope: docusign::Envelope,
) -> Result<()> {
// Keep the fields from Airtable we need just in case they changed.
self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

// Only allow documents to be re-generated if we are in the process of or have already
// hired this applicant
Expand Down Expand Up @@ -2270,35 +2243,35 @@ The applicants Airtable \
}
}

pub async fn keep_fields_from_airtable(&mut self, db: &Database) {
// Let's get the existing record from Airtable, so we can use it as the source
// of truth for various things.
if let Some(ex) = self.get_existing_airtable_record(db).await {
let existing = ex.fields;
// We keep the scorers from Airtable in case someone assigned someone from the UI.
self.scorers = existing.scorers.clone();
// Keep the interviewers from Airtable since they are updated out of bound by Airtable.
self.interviews = existing.interviews.clone();
// Keep the reviews, since these are updated out of band by Airtable.
self.link_to_reviews = existing.link_to_reviews;

// We want to keep the status and status raw since we might have modified
// it to move a candidate along in the process.
self.status = existing.status.to_string();
self.raw_status = existing.raw_status.to_string();

// Mostly the start date will populate from docusign, but just in case they
// are someone who worked remotely, we might have to manually set it.
// If docusign is incorrect, make sure Airtable always has the source of truth.
self.start_date = existing.start_date;
} else {
log::warn!(
"Could not find existing Airtable record for email -> {}, id -> {}",
self.email,
self.id
);
}
}
// pub async fn keep_fields_from_airtable(&mut self, db: &Database) {
// // Let's get the existing record from Airtable, so we can use it as the source
// // of truth for various things.
// if let Some(ex) = self.get_existing_airtable_record(db).await {
// let existing = ex.fields;
// // We keep the scorers from Airtable in case someone assigned someone from the UI.
// self.scorers = existing.scorers.clone();
// // Keep the interviewers from Airtable since they are updated out of bound by Airtable.
// self.interviews = existing.interviews.clone();
// // Keep the reviews, since these are updated out of band by Airtable.
// self.link_to_reviews = existing.link_to_reviews;

// // We want to keep the status and status raw since we might have modified
// // it to move a candidate along in the process.
// self.status = existing.status.to_string();
// self.raw_status = existing.raw_status.to_string();

// // Mostly the start date will populate from docusign, but just in case they
// // are someone who worked remotely, we might have to manually set it.
// // If docusign is incorrect, make sure Airtable always has the source of truth.
// self.start_date = existing.start_date;
// } else {
// log::warn!(
// "Could not find existing Airtable record for email -> {}, id -> {}",
// self.email,
// self.id
// );
// }
// }

pub async fn update_applicant_from_docusign_piia_envelope(
&mut self,
Expand All @@ -2307,7 +2280,7 @@ The applicants Airtable \
envelope: docusign::Envelope,
) -> Result<()> {
// Keep the fields from Airtable we need just in case they changed.
self.keep_fields_from_airtable(db).await;
// self.keep_fields_from_airtable(db).await;

let company = self.company(db).await?;

Expand Down Expand Up @@ -2481,11 +2454,6 @@ pub async fn refresh_new_applicants_and_reviews(
}
}

// Update Airtable.
// TODO: this might cause some racy problems, maybe only run at night (?)
// Or maybe always get the latest from the database and update airtable with that (?)
// Applicants::get_from_db(db, company.id)?.update_airtable(db).await?;

Ok(())
}

Expand Down
Loading
Loading