Example #1
0
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
Example #5
0
    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
Example #8
0
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
Example #10
0
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
Example #14
0
    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
    }
Example #16
0
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
Example #17
0
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)