def fetch_auth_data(account_id): """ Return the values needed to authenticate to an ATS account. :param int account_id: Primary key of the account. :rtype string: ATS name. :rtype string: Login URL. :rtype: tuple[str] | tuple[str] | tuple[int] | tuple[ATSCredential]: """ # Validate ATS Account account = ATSAccount.get(account_id) if not account: raise InvalidUsage("ATS account {} not found.".format(account_id)) if not account.active: return None, None, None, None ats = ATS.get(account.ats_id) if not ats: raise UnprocessableEntity("Invalid ats id", additional_error_info=dict(id=account.ats_id)) credentials = ATSCredential.get(account.ats_credential_id) if not credentials: raise UnprocessableEntity("No credentials for account", additional_error_info=dict(id=account.ats_id)) return ats.name, ats.login_url, account.user_id, credentials
def match_ats_and_gt_candidates(logger, account_id, method, link=False): """ Search for getTalent condidates which appear to match ATS candidates using a particular method. :param object logger: object to use for logging. :param int account_id: ATS account to search within. :param string method: matching technique to use. :param boolean link: Whether to link the candidates matched or not. :rtype int: Number of matches found. """ if method not in MATCH_DICT: raise UnprocessableEntity("match_ats_and_gt_candidates: Invalid match method", additional_error_info=dict(unsupported_method=method)) match_method = MATCH_DICT[method] account = ATSAccount.get(account_id) if not account: raise NotFoundError('ATS Account id not found', additional_error_info=dict(account_id=account_id)) # Now get the user that owns this account user = User.get(account.user_id) # Get all candidates from user's domain gt_candidate_list = db.session.query(Candidate).join(User).filter(User.domain_id == user.domain_id).all() # Get a list of our ATS candidates ats_candidate_list = ATSCandidate.get_all(account_id) matches = 0 for gt_candidate in gt_candidate_list: for ats_candidate in ats_candidate_list: if match_method(gt_candidate, ats_candidate): matches += 1 if link: link_ats_candidate(gt_candidate.id, ats_candidate.id) return matches
def emails_match(gt_candidate, ats_candidate): """ Determine if there are matching email addresses between a GT candidate and a Workday individual. Workday individuals have only one, but this returns a list so that all ATS may have the same method signature. :param Candidate gt_candidate: getTalent candidate. :param ATSCandidate ats_candidate: Workday individual. :rtype boolean: """ if gt_candidate.is_archived: return False # Get the ATS candidate email address with an ATS-specific static method ats_email_list = ATS_CONSTRUCTORS[ATSAccount.get(ats_candidate.ats_id).name].get_individual_contact_email_addresses(ats_candidate) if not ats_email_list: return False # Compare to GT candidate email address(es) if not gt_candidate.emails: return False # Compare. This makes a 4-deep for loop, but we expect the lists to be very small. For Workday, there'll be only one email address. for gt_email in candidate.emails: for ats_email in ats_email_list: if gt_email == ats_email: return True return False
def new_ats_candidate(account_id, data): """ Register an ATS candidate with an ATS account. :param obj account: an ATS account object. :param dict data: keys and values describing the candidate. :rtype: ATSCandidate """ gt_candidate_id = None if 'gt_candidate_id' in data: gt_candidate_id = data.get('gt_candidate_id', None) account = ATSAccount.get(account_id) if not account: raise InvalidUsage("ATS account {} not found.".format(account_id)) profile = ATSCandidateProfile(active=True, profile_json=data['profile_json'], ats_id=account.ats_id) profile.save() candidate = ATSCandidate(ats_account_id=account.id, ats_remote_id=data['ats_remote_id'], gt_candidate_id=gt_candidate_id, profile_id=profile.id) if 'ats_table_id' in data: candidate.ats_table_id = data.get('ats_table_id', None) candidate.save() update_dict = {} now = datetime.datetime.utcnow() update_dict = {'updated_at' : now} account.update(**update_dict) return candidate
def delete_ats_account(user_id, ats_account_id): """ Remove an ATS account and all of its candidates. :param int ats_account_id: id of the ATS account. :rtype: None """ # First, verify the user and account account = ATSAccount.get(ats_account_id) if not account: raise NotFoundError('delete_ats_account: No such account {}'.format(ats_account_id)) user = User.get(user_id) if not user: raise NotFoundError('delete_ats_account: No such user {}'.format(user_id)) # Next remove all candidates and candidate attributes from the account candidate_list = ATSCandidate.query.filter(ATSCandidate.ats_account_id == ats_account_id).all() for candidate in candidate_list: profile = ATSCandidateProfile.get(candidate.profile_id) ATSCandidateProfile.delete(profile) # Then remove the account credentials credentials = ATSCredential.get(account.ats_credential_id) ATSCredential.delete(credentials) # Remove the account ATSAccount.delete(account) # If this is the only ATS account for this user, mark the user as not ATS enabled all_accounts = ATSAccount.query.filter(ATSAccount.user_id == user_id).all() if not all_accounts: update_dict = {'ats_enabled': False} User.query.filter(User.id == user_id).update(update_dict) db.session.commit()
def emails_and_phones_match(gt_candidate, ats_candidate): """ Determine if there are matching email addresses and phone numbers between a GT candidate and a Workday individual. Workday indidviduals have only one, but this returns a list so that all ATS may have the same method signature. :param Candidate gt_candidate: getTalent candidate. :param ATSCandidate ats_candidate: Workday individual. :rtype boolean: """ if gt_candidate.is_archived: return False # Get the ATS candidate email address with an ATS-specific static method ats_phone_list = ATS_CONSTRUCTORS[ATSAccount.get(ats_candidate.ats_id).name].get_individual_contact_phone_numbers(ats_candidate) if not ats_phone_list: return False # Compare to GT candidate email address(es) if not gt_candidate.phones: return False # Get the ATS candidate email address with an ATS-specific static method ats_email_list = ATS_CONSTRUCTORS[ATSAccount.get(ats_candidate.ats_id).name].get_individual_contact_email_addresses(ats_candidate) if not ats_email_list: return False # Compare to GT candidate email address(es) if not gt_candidate.emails: return False # Compare emails. for gt_email in candidate.emails: for ats_email in ats_email_list: if gt_email == ats_email: email_match = True # Compare phones. phone_match = normalized_phones_match(candidate.phones, ats_phone_list) return email_match and phone_match
def update_ats_account(account_id, new_data): """ Update the values of an ATS account. :param int account_id: primary key of the account. :param dict new_data: New values for the account. :rtype: None """ # Search for the account, complain if it doesn't exist account = ATSAccount.get(account_id) if not account: raise UnprocessableEntity("Invalid ats account id", additional_error_info=dict(id=account_id)) ats = ATS.get(account.ats_id) if not ats: raise UnprocessableEntity("Invalid ats id", additional_error_info=dict(id=account.ats_id)) # Update the ATS info update_dict = {} if 'ats_name' in new_data: update_dict['name'] = new_data['ats_name'] if 'ats_homepage' in new_data: update_dict['homepage_url'] = new_data['ats_homepage'] if 'ats_login' in new_data: update_dict['login_url'] = new_data['ats_login'] if 'ats_auth_type' in new_data: update_dict['auth_type'] = new_data['ats_auth_type'] if len(update_dict) > 0: ats.update(**update_dict) update_dict = {} now = datetime.datetime.utcnow() update_dict = {'updated_at' : now} if 'active' in new_data: if new_data['active'] == "False": update_dict['active'] = False else: update_dict['active'] = True account.update(**update_dict) # If they're changing credentials, find those. Presumably auth_type won't change. if 'ats_credentials' in new_data: credentials = ATSCredential.get(account.ats_credential_id) update_dict = {'credentials_json' : new_data['ats_credentials']} credentials.update(**update_dict)
def delete_ats_candidate(candidate_id): """ Remove an ATS candidate from the database. :param int candidate_id: The id of the candidate. :rtype: None """ candidate = ATSCandidate.get(candidate_id) if not candidate: raise InvalidUsage('delete_ats_candidate: No such candidate {}'.format(candidate_id)) profile = ATSCandidateProfile.get(candidate.profile_id) if profile: ATSCandidateProfile.delete(profile) account = ATSAccount.get(candidate.ats_account_id) if not account: raise InvalidUsage("ATS account {} not found.".format(candidate.ats_account_id)) now = datetime.datetime.utcnow() update_dict = {'updated_at' : now} account.update(**update_dict)
def update_ats_candidate(account_id, candidate_id, new_data): """ Update the profile of an ATS candidate. :param int account_id: primary key of the account the candidate belongs to. :param int candidate_id: primary key of the candidate. :param dict new_data: values to update for the candidate. :rtype: None """ if 'profile_json' not in new_data: raise InvalidUsage("profile_json not found.") # Validate ATS Account account = ATSAccount.get(account_id) if not account: raise InvalidUsage("ATS account {} not found.".format(account_id)) # Validate candidate id candidate = ATSCandidate.get(candidate_id) if not candidate: raise UnprocessableEntity("Invalid candidate id", additional_error_info=dict(id=account.candidate_id)) # Validate profile id profile = ATSCandidateProfile.get(candidate.profile_id) if not profile: raise UnprocessableEntity("Invalid candidate profile id", additional_error_info=dict(id=candidate.profile_id)) if 'ats_table_id' in new_data: candidate.ats_table_id = new_data.get('ats_table_id', None) now = datetime.datetime.utcnow() update_dict = {'profile_json' : new_data['profile_json'], 'updated_at' : now} profile.update(**update_dict) update_dict = {'updated_at' : now} candidate.update(**update_dict) account.update(**update_dict) return candidate