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

Commit

Permalink
Disconnecting airtable
Browse files Browse the repository at this point in the history
  • Loading branch information
augustuswm committed Oct 7, 2024
1 parent 0c8838b commit 6eeb5fa
Show file tree
Hide file tree
Showing 28 changed files with 70 additions and 2,331 deletions.
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

0 comments on commit 6eeb5fa

Please sign in to comment.