def get_recipient_email_addresses(notification, resend=False): """Given a Notification, return a list of email addresses as strings.""" all_emails = [] if notification.recipient_profile: collect_contact_info_for_profile(notification.recipient_profile) all_emails.extend(ProfileEmail.all_sane().filter( profile_id=notification.recipient_profile.id).values_list( "value", flat=True)) if notification.recipient_email: sane_exists = ProfileEmail.all_sane().filter( value=notification.recipient_email).exists() exists_generally = ProfileEmail.objects.filter( value=notification.recipient_email).exists() if sane_exists or not exists_generally: all_emails.append(notification.recipient_email) if not all_emails: if notification.recipient_profile: raise EmailNotFoundException( message="", profile_id=notification.recipient_profile.id) else: raise UnspecifiedRecipientException() return all_emails
def test_profile_email_sanity(): p = Profile() p.save() VALID_EMAIL = "*****@*****.**" INVALID_EMAIL = "*****@*****.**" # Invalid emails a = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, unsubscribed=False, reported_spam=False) b = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=False, unsubscribed=True, reported_spam=False) c = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=False, unsubscribed=False, reported_spam=True) d = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, unsubscribed=True, reported_spam=False) e = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=False, unsubscribed=True, reported_spam=True) f = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, unsubscribed=True, reported_spam=True) g = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, is_correct_person=True) # Valid emails h = ProfileEmail(value=VALID_EMAIL, profile=p, bounces=False, unsubscribed=False, reported_spam=False) for email in [a,b,c,d,e,f,g,h]: email.save() assert not a.sane assert not b.sane assert not c.sane assert not d.sane assert not e.sane assert not f.sane assert not g.sane assert h.sane sane = ProfileEmail.all_sane() assert len(sane) == 1 assert sane.first().value == VALID_EMAIL
def test_collect_email_data(): p = Profile() p.save() email = "*****@*****.**" pe = ProfileEmail(value=email, profile=p) pe.save() # First, test a bounced email. NotificationBouncedEmail(email=email).save() collect_email_data(pe) assert pe.bounces # unbounce it NotificationRecipientEvent(email=email, action="delivered").save() collect_email_data(pe) assert not pe.bounces # re-bounce it NotificationBouncedEmail(email=email).save() collect_email_data(pe) assert pe.bounces # open it - opening sets opens to True and bounces to False NotificationRecipientEvent(email=email, action="open").save() collect_email_data(pe) assert not pe.bounces and pe.opens # open it again. Make sure we're idempotent. NotificationRecipientEvent(email=email, action="open").save() collect_email_data(pe) assert not pe.bounces and pe.opens # re-bounce it, but differently NotificationRecipientEvent(email=email, action="bounce").save() collect_email_data(pe) assert pe.bounces # test a click. NotificationRecipientEvent(email=email, action="click").save() collect_email_data(pe) assert not pe.bounces and pe.responds # try a couple unreleated events, make sure they work NotificationRecipientEvent(email=email, action="spamreport").save() NotificationRecipientEvent(email=email, action="unsubscribe").save() collect_email_data(pe) assert pe.reported_spam assert pe.unsubscribed # resubscribe NotificationRecipientEvent(email=email, action="group_resubscribe").save() collect_email_data(pe) assert not pe.unsubscribed
def test_get_sender_email(): p = Profile() p.save() p_invalid = Profile() p_invalid.save() valid = ProfileEmail(value="*****@*****.**", profile=p) valid.save() invalid = ProfileEmail(value="*****@*****.**", profile=p_invalid, bounces=True) invalid.save() noti_a = Notification(type="email", sender_profile=p) noti_a.save() noti_b = Notification(type="email", sender_profile=p_invalid) noti_b.save() noti_c = Notification(type="email", sender_profile=p_invalid, sender_email="*****@*****.**") assert get_sender_email(noti_a) == valid.value assert get_sender_email(noti_b, "fallback") == "fallback" assert get_sender_email(noti_c, "fallback") == noti_c.sender_email
def get(self, request, employer_candidate_guid): template_name = "notifications/page_after_candidate_accepts.html" try: candidate = EmployerCandidate.objects.get( guid=employer_candidate_guid) except EmployerCandidate.DoesNotExist: return render(request, template_name, { "success": False, "no_such_guid": True }) collect_contact_info_for_profile(candidate.profile) candidate_email = ProfileEmail.to_reach(candidate.profile.id) if candidate_email: candidate_email = candidate_email.value candidate_phone = ProfilePhoneNumber.to_reach(candidate.profile.id) if candidate_phone: candidate_phone = candidate_phone.value suggest_claim = (candidate_email is not None and candidate.profile.user_id is None and (request.user is None or not request.user.is_authenticated)) if suggest_claim: request.session['appl_accepted_email'] = candidate_email handle_candidate_accept(candidate) return render( request, template_name, { "success": True, "employer_candidate_guid": employer_candidate_guid, "candidate_email": candidate_email, "candidate_phone": candidate_phone, "suggest_claim": suggest_claim })
def test_add_contact(): p = Profile() p.save() # Test input existentiality invariants assert add_contact(ProfileEmail, "", p.id) is None assert add_contact(ProfileEmail, "", p.id, is_correct_person = True) is None assert add_contact(ProfileEmail, "*****@*****.**", None) is None assert add_contact(ProfileEmail, "*****@*****.**", None, is_correct_person = True) is None # Test add_contact() when the email already exists. a = ProfileEmail(value="*****@*****.**", profile=p) b = ProfileEmail(value="*****@*****.**", profile=p) for email in [a,b]: email.save() ap = add_contact(ProfileEmail, a.value, p.id) bp = add_contact(ProfileEmail, b.value, p.id) assert ap and ap.id is a.id and ap.value == a.value and ap.is_correct_person == a.is_correct_person assert bp and bp.id is b.id and bp.value == b.value and bp.is_correct_person == b.is_correct_person # Test add_contact() for a new email. c = add_contact(ProfileEmail, "*****@*****.**", p.id) assert c and c.id and c.value == "*****@*****.**" and c.id not in [a.id, b.id] # Test add_contact() is_correct_person parameter d = ProfileEmail(value="*****@*****.**", profile=p) e = ProfileEmail(value="*****@*****.**", profile=p, is_correct_person = True) f = ProfileEmail(value="*****@*****.**", profile=p, is_correct_person = True) for email in [d,e]: email.save() dp = add_contact(ProfileEmail, d.value, p.id, is_correct_person = True) ep = add_contact(ProfileEmail, e.value, p.id, is_correct_person = False) fp = add_contact(ProfileEmail, f.value, p.id, is_correct_person = True) assert dp.is_correct_person is not d.is_correct_person assert ep.is_correct_person is not e.is_correct_person assert fp.is_correct_person is f.is_correct_person
def test_email_to_reach_with_valid_is_correct_person(): """Test to make sure ProfileEmail.to_reach returns `is_correct_person = True` first.""" p = Profile() p.save() invalid = ProfileEmail(value="*****@*****.**", profile=p, bounces=True) valid = ProfileEmail(value="*****@*****.**", profile=p) correct_person = ProfileEmail(value="*****@*****.**", profile=p, is_correct_person=True) for email in [invalid, valid, correct_person]: email.save() test = ProfileEmail.to_reach(p.id) assert test.value == correct_person.value test_two = ProfileEmail.to_reach(p.id, strict=True) assert test_two.value == correct_person.value
def get_sender_email(notification, fallback='*****@*****.**'): from_email = None if notification.sender_profile: collect_contact_info_for_profile(notification.sender_profile) from_email = ProfileEmail.to_reach(notification.sender_profile_id) if from_email: from_email = from_email.value return from_email or notification.sender_email or fallback
def test_email_to_reach_valid_before_invalid(): """Test to make sure ProfileEmail.to_reach only returns sane emails.""" p = Profile() p.save() a = ProfileEmail(value="*****@*****.**", profile=p, bounces=True, unsubscribed=False, reported_spam=False) b = ProfileEmail(value="*****@*****.**", profile=p, bounces=True, is_correct_person=True) valid = ProfileEmail(value="*****@*****.**", profile=p) for email in [a, b, valid]: email.save() dubious = ProfileEmail.to_reach(p.id) assert dubious is not None assert dubious.value == valid.value assert ProfileEmail.to_reach(p.id, strict=True) is None
def call_pipl(profile=None, search_pointer=None): api_key = os.environ.get("PIPL_API_KEY") if not api_key: print("call_pipl(): Warning: Missing API Key.") params = {'api_key': api_key, 'hide_sponsored': True, 'use_https': True} if search_pointer: params["search_pointer"] = search_pointer elif profile: # TODO: Look into using advanced search: # https://docs.pipl.com/reference/#using-search-parameters # TODO: look this up from the profile params['country'] = 'US' email = ProfileEmail.to_reach(profile.id, strict=True) if email: params["email"] = email.value if profile.state_id: params["state"] = profile.state.state_code if profile.city: params["city"] = profile.city if profile.first_name: params["first_name"] = profile.first_name if profile.last_name: params["last_name"] = profile.last_name else: print("Abiguous Lookup: Not enough information to make a PIPL query.") raise AmbiguousLookupException() try: response = SearchAPIRequest(**params).send() except ValueError: # ValueError's get raised when there's not enough information in the query print("Sparse PIPL query.") raise AmbiguousLookupException() if response and response.http_status_code == 200: if response.persons_count > 1: print("Ambiguous Lookup: Too many results") raise AmbiguousLookupException() # NS 11.13.18 - disable because search pointer logic is FUBAR # search_pointer = get_valid_search_pointer_from_pipl_response(response, job=profile.job) # if search_pointer: # return call_pipl(search_pointer=search_pointer) return json.loads(response.raw_json.replace('\n', '')) else: print("Bad HTTP response.") raise LookupFailedException() return None
def test_verify_sane(): p = Profile() p.save() SANE_EMAIL = "*****@*****.**" INSANE_EMAIL = "*****@*****.**" UNUSED_EMAIL = "*****@*****.**" a = ProfileEmail(value=SANE_EMAIL, profile=p) b = ProfileEmail(value=SANE_EMAIL, profile=p, is_correct_person=True) c = ProfileEmail(value=INSANE_EMAIL, profile=p, bounces=True) d = ProfileEmail(value=INSANE_EMAIL, profile=p, bounces=True, is_correct_person=True) for email in [a,b,c,d]: email.save() assert verify_sane(ProfileEmail, SANE_EMAIL) assert not verify_sane(ProfileEmail, INSANE_EMAIL) assert verify_sane(ProfileEmail, UNUSED_EMAIL, assume_sane=True) assert not verify_sane(ProfileEmail, UNUSED_EMAIL, assume_sane=False)
def test_no_valid_emails(): p = Profile() p.save() INVALID_EMAIL = "*****@*****.**" # Invalid emails a = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, unsubscribed=False, reported_spam=False) b = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=False, unsubscribed=True, reported_spam=False) c = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=False, unsubscribed=False, reported_spam=True) d = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, unsubscribed=True, reported_spam=False) e = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=False, unsubscribed=True, reported_spam=True) f = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, unsubscribed=True, reported_spam=True) g = ProfileEmail(value=INVALID_EMAIL, profile=p, bounces=True, is_correct_person=True) for email in [a,b,c,d,e,f,g]: email.save() assert ProfileEmail.to_reach(p.id) is None
def _make_notification(set_recipient_email=False, set_recipient_profile=False): n = Notification(type="email") if set_recipient_email: # This profile won't be used. It's a means of appeasing # the DB schema. p = Profile() p.save() pe = ProfileEmail(value="*****@*****.**", profile=p) pe.save() n.recipient_email = pe.value if set_recipient_profile: p = Profile() p.save() n.recipient_profile = p for addr in [ "*****@*****.**", "*****@*****.**", "*****@*****.**" ]: pe = ProfileEmail(value=addr, profile=p) pe.save() n.save() return n
def generate_context(self, *args, **kwargs): employer_candidate = kwargs.get('employer_candidate') if not employer_candidate: raise ValueError("Missing keyword argument: employer_candidate") emails = ProfileEmail.all_sane().filter( profile_id=employer_candidate.profile_id).values_list('value', flat=True) phones = ProfilePhoneNumber.all_sane().filter( profile_id=employer_candidate.profile_id).values_list('value', flat=True) context = { "candidate": employer_candidate.profile, "employer_user": employer_candidate.employer_user, "job": employer_candidate.employer_job.job, "employer_job": employer_candidate.employer_job, "view_profile_url": "{}/profile/pdf/{}?bkgen=1".format(get_website_root_url(), employer_candidate.profile.id), "candidate_status_url": "{}/employer/candidate_status_detail/{}".format( get_website_root_url(), employer_candidate.id), "email_addresses": emails, "phone_numbers": phones, "website_root_url": get_website_root_url() } if employer_candidate.response: context[ 'candidate_response'] = employer_candidate.response.response return context
def profile_to_json_icims(profile_id, icims_job_id=None, hide_details=False, hide_bscore=False, hide_job=False, hide_profile_id=True): details_visible = not hide_details profile = Profile.objects.get(id=profile_id) certification_set = (profile.profilecertification_set.extra( select={ "issued_date_non_null": "COALESCE(issued_date, '1970-01-01')" }).order_by('-issued_date_non_null', 'certification_name')) emails = [] phones = [] if details_visible: collect_contact_info_for_profile(profile) phones_raw = list( ProfilePhoneNumber.objects.filter( profile_id=profile_id).values_list('value', flat=True)) phones = list(filter(bool, map(format_phone_number, phones_raw))) emails = list(ProfileEmail.all_sane().filter( profile_id=profile_id).values_list('value', flat=True)) job_id = None job_name = None if details_visible and not hide_job: if icims_job_id: job = IcimsJobData.objects.get(pk=icims_job_id) job_id = job.id job_name = job.job_title elif icims_job_id: job = IcimsJobData.objects.get(pk=icims_job_id) job_id = job.job_id job_name = job.job_title else: mapping = ProfileJobMapping.objects.filter( profile_id=profile_id).order_by("id").first() if mapping: job_id = mapping.job_id job_name = mapping.job.job_name if profile.last_updated_date: last_updated_date_display = profile.last_updated_date.date().strftime( "%m/%d/%y") else: last_updated_date_display = None score = None if details_visible and not hide_bscore: score = int((Score.objects.filter( profile_id=profile_id, job_id=job_id).order_by('-date_created').values_list( 'score_value', flat=True).first()) or 0) or None name = None if profile.name_verification_completed and (profile.first_name or profile.last_name): name = ' '.join(filter(bool, [profile.first_name, profile.last_name])) if not name: name = make_initials_for_profile_id(profile.id) print("retuning-----------------") return { "profile": profile, "name": name, "job_name": job_name, "emails": emails, "phones": phones, "experience_set": list((profile.profileexperience_set.order_by( '-is_current_position', F('end_date').desc(nulls_first=True), F('start_date').desc(nulls_last=True)).select_related('state'))), "education_set": list( map( annotate_education_record, profile.profileeducation_set.order_by( F('degree_date').desc(nulls_last=True), '-start_date').select_related('state', 'degree_major', 'degree_type', 'degree_name'))), "certification_set": list(certification_set), "last_updated_date_display": last_updated_date_display, "score": score, "show_profile_id": details_visible and not hide_profile_id }
def _test_auto_respond_functions(is_bakround_employee): test_candidate_queue.set_up_test(is_bakround_employee) employer_user = EmployerUser.objects.first() employer_job = EmployerJob.objects.first() user = employer_user.user user.first_name, user.last_name = "Bob", "Loblaw" user.save() assert full_name_of_employer_user(employer_user) == "Bob Loblaw" assert get_recruiter_for_job(employer_job) == employer_user profile = Profile.objects.first() collect_contact_info_for_profile(profile) candidate = EmployerCandidate(profile=profile, employer_job=employer_job, employer_user=employer_user) candidate.save() # First, test the case where old data is used. cd = ProfileEmail(profile=profile, value="*****@*****.**") cd.save() assert get_email_address_for_responding_candidate(candidate) is None NotificationRecipientEvent(action="open", email=cd.value).save() collect_email_data(cd) assert get_email_address_for_responding_candidate(candidate) == cd.value NotificationRecipientEvent(action="unsubscribe", email=cd.value).save() collect_email_data(cd) assert get_email_address_for_responding_candidate(candidate) is None # Then, test the case where new data is used. e = ProfileEmail(profile=profile, value="*****@*****.**") e.save() assert get_email_address_for_responding_candidate(candidate) is None e.opens = True e.save() assert get_email_address_for_responding_candidate(candidate) == e.value e.unsubscribed = True e.save() assert get_email_address_for_responding_candidate(candidate) is None
def get_email_address_for_responding_candidate(candidate): # Turning strict on excludes all emails that didn't open emails we sent them. collect_contact_info_for_profile(candidate.profile) pe = ProfileEmail.to_reach(candidate.profile, strict=True) if pe: return pe.value return None
def handle_message(self, body): message = json.loads(body) force_reverification = message.get('force_reverification') or False request_id = message.get('request_id') if not request_id: raise ValueError( "Missing ProfileVerificationRequest id (request_id)") request = ProfileVerificationRequest.objects.filter( id=int(request_id)).first() if not request: raise ValueError( "ProfileVerificationRequest id {} does not exist.".format( request_id)) if request.use_manual and not force_reverification: self.logger.info( "ProfileVerificationRequest id {}'s use_manual = True. Exiting." .format(request_id)) return profile = request.profile collect_contact_info_for_profile(profile) if not profile.name_verification_completed or force_reverification: if rescrape_authenticated(profile): self.logger.info( "Successfully re-scraped Profile id {}.".format( profile.id)) profile.name_verification_completed = True profile.save() else: self.logger.info( "Failed to re-scrape Profile id {} authenticated.".format( profile.id)) if (profile.name_verification_completed and not ProfileEmail.to_reach(profile.id)) or force_reverification: if lookup_profile(profile, use_cache=not force_reverification): self.logger.info( "Successful external lookup for Profile id {}!".format( profile.id)) else: self.logger.info( "Unsuccessful external lookup for Profile id {}.".format( profile.id)) if not profile.name_verification_completed or not ProfileEmail.to_reach( profile.id): self.logger.info( "Failed to find sufficient information for ProfileVerificationRequest id {} (Profile id {})." .format(request.id, request.profile.id)) request.use_manual = True request.save() # natesymer 11.15.18 - Stop using a headless browser to contact people on Indeed. # It gets our accounts banned. # if not request.contacted: # message = get_message_for(request) # if not message: # self.logger.info("ProfileVerificationRequest id {} didn't have necessary metadata to contact through Indeed.".format(request.id)) # elif is_production(): # # TODO: this is hardcoded for expedience. Generalize. # self.logger.info("Attempting external contact via Indeed.") # user_name = os.environ.get("INDEED_EMAIL", default="*****@*****.**") # try: # scraper_login = ScraperLogin.objects.get(user_name=user_name, source='indeed') # not_already_contacted = indeed_external_contact(profile, message, scraper_login).result # if not_already_contacted: # self.logger.info("Sucessfully contacted Profile id {} via Indeed".format(profile.id)) # else: # self.logger.info("We've already contacted Profile id {} via Indeed.".format(profile.id)) # request.contacted = True # request.save() # except ScraperLogin.DoesNotExist: # self.logger.error("No ScraperLogin exists with the username {}. Check your INDEED_EMAIL environment variable.".format(user_name)) # except (BrowserActionFatalError, BrowserFatalError): # self.logger.error("Encountered fatal error from the browsing infrastructure:\n", exc_info=True) # else: # self.logger.info("Not contacting because we're not in production!") self.logger.info("Falling back to manual.") return # TODO: Phone number validation here - use a service. self.logger.info( "Successfully automatically verified ProfileVerificationRequest id {} (Profile id {})" .format(request.id, profile.id)) request.verified = True request.save() if request.callback_queue and request.callback_message: self.send_message(request.callback_queue, request.callback_message)