Esempio n. 1
0
    def test_mixed_case(self) -> None:
        """First name has various casing."""

        self.assertEqual('Pascal', french.cleanup_firstname('pascal'))
        self.assertEqual('Pascal', french.cleanup_firstname('PASCAL'))
        self.assertEqual('Pascal', french.cleanup_firstname('pASCAL'))
        self.assertEqual('Pascal', french.cleanup_firstname('PaScAl'))
Esempio n. 2
0
    def test_apostrophe(self) -> None:
        """First name contains an apostrophe."""

        self.assertEqual("D'Arles", french.cleanup_firstname("D'ARLES"))
        # This one is not that great, but we'll keep it here to make it obvious
        # what the result is. As it's not really a first name, I think it's ok.
        self.assertEqual("Cap'Tain", french.cleanup_firstname("CAP'TAIN"))
Esempio n. 3
0
    def _pe_connect_authenticate(
            self, auth_request: auth_pb2.AuthRequest) -> auth_pb2.AuthResponse:
        token_data = _get_oauth2_access_token(
            'https://authentification-candidat.pole-emploi.fr/connexion/oauth2/access_token?'
            'realm=/individu',
            code=auth_request.pe_connect_code,
            client_id=_EMPLOI_STORE_CLIENT_ID or '',
            client_secret=_EMPLOI_STORE_CLIENT_SECRET or '',
            auth_name='PE Connect',
        )

        if token_data.get('nonce') != auth_request.pe_connect_nonce:
            flask.abort(403, i18n.flask_translate('Mauvais paramètre nonce'))
        bearer = token_data.get('token_type', 'Bearer')
        access_token = token_data.get('access_token', '')
        authorization_header = f'{bearer} {access_token}'

        user_info_response = requests.get(
            'https://api.emploi-store.fr/partenaire/peconnect-individu/v1/userinfo',
            headers={'Authorization': authorization_header})
        if user_info_response.status_code < 200 or user_info_response.status_code >= 400:
            logging.warning('PE Connect fails (%d): "%s"',
                            user_info_response.status_code,
                            user_info_response.text)
            flask.abort(403, user_info_response.text)

        user_info = typing.cast(dict[str, str], user_info_response.json())

        response = auth_pb2.AuthResponse()
        user_dict = self._user_collection.find_one(
            {'peConnectId': user_info['sub']})
        if proto.parse_from_mongo(user_dict, response.authenticated_user,
                                  'user_id'):
            self._handle_returning_user(response)
        else:
            user = response.authenticated_user
            is_existing_user = self._load_user_from_token_or_email(
                auth_request, user, user_info.get('email'))
            user.pe_connect_id = user_info['sub']
            response.is_new_user = force_update = not user.has_account
            user.has_account = True
            if is_existing_user:
                self._handle_returning_user(response,
                                            force_update=force_update)
            else:
                # TODO(pascal): Handle the case where one of the name is missing.
                user.profile.name = french.cleanup_firstname(
                    user_info.get('given_name', ''))
                user.profile.last_name = french.cleanup_firstname(
                    user_info.get('family_name', ''))
                user.profile.gender = _PE_CONNECT_GENDER.get(
                    user_info.get('gender', ''),
                    user_profile_pb2.UNKNOWN_GENDER)
                self.save_new_user(user, auth_request.user_data)

        response.auth_token = token.create_token(
            response.authenticated_user.user_id, 'auth')

        return response
Esempio n. 4
0
    def test_compound_name(self) -> None:
        """First name is a compound."""

        self.assertEqual('Marie-Laure',
                         french.cleanup_firstname('marie-laure'))
        self.assertEqual('Marie Laure',
                         french.cleanup_firstname('marie laure'))
        self.assertEqual('Marie Laure',
                         french.cleanup_firstname('marie.laure'))
        self.assertEqual('Marie Laure',
                         french.cleanup_firstname('marie   laure'))
Esempio n. 5
0
def get_default_vars(user: user_pb2.User,
                     **unused_kwargs: Any) -> dict[str, Any]:
    """Compute default variables used in all emails: firstName, gender and unsubscribeLink."""

    return {
        'areEmailsAnswerRead':
        product.bob.are_email_answers_read,
        'baseUrl':
        product.bob.base_url,
        'firstName':
        french.cleanup_firstname(user.profile.name),
        'gender':
        user_profile_pb2.Gender.Name(user.profile.gender),
        'highlightColor':
        product.bob.get_config('highlightColor', '#faf453'),
        'productLogoUrl':
        product.bob.get_config(
            'productLogoUrl',
            'https://t.bob-emploi.fr/tplimg/6u2u/b/oirn/2ugx1.png'),
        'productName':
        product.bob.name,
        'unsubscribeLink':
        get_bob_link(
            'unsubscribe.html', {
                'auth':
                auth_token.create_token(user.profile.email,
                                        role='unsubscribe'),
                'hl':
                user.profile.locale,
                'user':
                user.user_id,
            }),
    }
Esempio n. 6
0
def get_default_coaching_email_vars(user: user_pb2.User,
                                    **unused_kwargs: Any) -> dict[str, Any]:
    """Compute default variables used in all coaching emails."""

    return get_default_vars(user) | {
        'changeEmailSettingsUrl':
        get_bob_link(
            'unsubscribe.html', {
                'auth':
                auth_token.create_token(user.user_id, role='settings'),
                'coachingEmailFrequency':
                email_pb2.EmailFrequency.Name(
                    user.profile.coaching_email_frequency),
                'hl':
                user.profile.locale,
                'user':
                user.user_id,
            }),
        'firstName':
        french.cleanup_firstname(user.profile.name),
        'gender':
        user_profile_pb2.Gender.Name(user.profile.gender),
        # TODO(pascal): Harmonize use of URL suffix (instead of link).
        'statusUpdateUrl':
        get_status_update_link(user),
    }
Esempio n. 7
0
def get_default_vars(user: user_pb2.User, **unused_kwargs: Any) -> Dict[str, str]:
    """Compute default variables used in all emails: firstName, gender and unsubscribeLink."""

    unsubscribe_token = parse.quote(auth.create_token(user.profile.email, role='unsubscribe'))
    return {
        'firstName': french.cleanup_firstname(user.profile.name),
        'gender': user_pb2.Gender.Name(user.profile.gender),
        'unsubscribeLink':
        f'{BASE_URL}/unsubscribe.html?user={parse.quote(user.user_id)}&'
        f'auth={unsubscribe_token}&hl={parse.quote(user.profile.locale)}',
    }
Esempio n. 8
0
def employment_vars(user, unused_db=None):
    """Compute vars for a given user for the employment survey.

    Returns:
        a dict with all vars required for the template, or None if no email
        should be sent.
    """

    registered_months_ago = campaign.get_french_months_ago(
        user.registered_at.ToDatetime())
    if not registered_months_ago:
        logging.warning('User registered only recently (%s)',
                        user.registered_at)
        return None
    # If the users have already updated their employment status less than one month ago,
    # ignore them.
    for status in user.employment_status:
        if status.created_at.ToDatetime() > _ONE_MONTH_AGO:
            return None
    survey_token = parse.quote(
        auth.create_token(user.user_id, role='employment-status'))
    unsubscribe_token = parse.quote(
        auth.create_token(user.profile.email, role='unsubscribe'))
    return {
        'firstName':
        french.cleanup_firstname(user.profile.name),
        'registeredMonthsAgo':
        registered_months_ago,
        'seekingUrl':
        '{}/api/employment-status?user={}&token={}&seeking={}&redirect={}'.
        format(
            campaign.BASE_URL,
            user.user_id,
            survey_token,
            'STILL_SEEKING',
            parse.quote('{}/statut/en-recherche'.format(campaign.BASE_URL)),
        ),
        'stopSeekingUrl':
        '{}/api/employment-status?user={}&token={}&seeking={}&redirect={}'.
        format(
            campaign.BASE_URL,
            user.user_id,
            survey_token,
            'STOP_SEEKING',
            parse.quote('{}/statut/ne-recherche-plus'.format(
                campaign.BASE_URL)),
        ),
        'unsubscribeLink':
        '{}/unsubscribe.html?email={}&auth={}'.format(
            campaign.BASE_URL, parse.quote(user.profile.email),
            unsubscribe_token),
    }
Esempio n. 9
0
def _get_nps_vars(user: user_pb2.User,
                  **unused_kwargs: Any) -> Optional[Dict[str, str]]:
    user_id = user.user_id
    nps_form_url = f'{campaign.BASE_URL}/retours?hl={parse.quote(user.profile.locale)}'
    return {
        'baseUrl':
        campaign.BASE_URL,
        'firstName':
        french.cleanup_firstname(user.profile.name),
        'npsFormUrl':
        f'{campaign.BASE_URL}/api/nps?user={user_id}&token={auth.create_token(user_id, "nps")}&'
        f'redirect={parse.quote(nps_form_url)}',
    }
Esempio n. 10
0
def body_language_vars(user, unused_db=None):
    """Compute vars for a given user for the body language email.

    Returns:
        a dict with all vars required for the template, or None if no email
        should be sent.
    """

    if not user.projects:
        logging.info('User has no project')
        return None

    registered_months_ago = campaign.get_french_months_ago(
        user.registered_at.ToDatetime())
    if not registered_months_ago:
        logging.info('User registered only recently (%s)', user.registered_at)
        return None

    has_read_last_focus_email = any(email.status in _READ_EMAIL_STATUSES
                                    for email in user.emails_sent
                                    if email.campaign_id.startswith('focus-'))

    worst_frustration = next(
        (user_pb2.Frustration.Name(frustration)
         for frustration in (user_pb2.SELF_CONFIDENCE, user_pb2.INTERVIEW,
                             user_pb2.ATYPIC_PROFILE)
         if frustration in user.profile.frustrations), '')
    if not worst_frustration:
        return None

    unsubscribe_token = parse.quote(
        auth.create_token(user.profile.email, role='unsubscribe'))

    return {
        'firstName':
        french.cleanup_firstname(user.profile.name),
        'gender':
        user_pb2.Gender.Name(user.profile.gender),
        'hasReadLastFocusEmail':
        campaign.as_template_boolean(has_read_last_focus_email),
        'registeredMonthsAgo':
        registered_months_ago,
        'unsubscribeLink':
        '{}/unsubscribe.html?email={}&auth={}'.format(
            campaign.BASE_URL, parse.quote(user.profile.email),
            unsubscribe_token),
        'worstFrustration':
        worst_frustration,
    }
Esempio n. 11
0
def get_default_coaching_email_vars(user: user_pb2.User, **unused_kwargs: Any) -> Dict[str, str]:
    """Compute default variables used in all coaching emails."""

    settings_token = parse.quote(auth.create_token(user.user_id, role='settings'))
    return dict(get_default_vars(user), **{
        'changeEmailSettingsUrl':
        f'{BASE_URL}/unsubscribe.html?user={parse.quote(user.user_id)}&auth={settings_token}&'
        'coachingEmailFrequency=' +
        user_pb2.EmailFrequency.Name(user.profile.coaching_email_frequency) +
        f'&hl={parse.quote(user.profile.locale)}',
        'firstName': french.cleanup_firstname(user.profile.name),
        'gender': user_pb2.Gender.Name(user.profile.gender),
        # TODO(pascal): Harmonize use of URL suffix (instead of link).
        'statusUpdateUrl': get_status_update_link(user.user_id, user.profile),
    })
Esempio n. 12
0
def new_diagnostic_vars(user, unused_db=None):
    """Compute vars for the "New Diagnostic"."""

    unsubscribe_token = parse.quote(
        auth.create_token(user.profile.email, role='unsubscribe'))
    frustrations_vars = {
        'frustration_{}'.format(user_pb2.Frustration.Name(f)): 'True'
        for f in user.profile.frustrations
    }
    age = datetime.date.today().year - user.profile.year_of_birth
    has_children = user.profile.family_situation in {
        user_pb2.FAMILY_WITH_KIDS,
        user_pb2.SINGLE_PARENT_SITUATION,
    }
    survey_token = parse.quote(
        auth.create_token(user.user_id, role='employment-status'))
    auth_token = parse.quote(
        auth.create_token(user.user_id, is_using_timestamp=True))
    return dict(
        frustrations_vars, **{
            'firstName':
            french.cleanup_firstname(user.profile.name),
            'gender':
            user_pb2.Gender.Name(user.profile.gender),
            'mayHaveSeekingChildren':
            campaign.as_template_boolean(has_children and age >= 45),
            'loginUrl':
            '{}?userId={}&authToken={}'.format(campaign.BASE_URL, user.user_id,
                                               auth_token),
            'stopSeekingUrl':
            '{}/api/employment-status?user={}&token={}&seeking={}&redirect={}'.
            format(
                campaign.BASE_URL,
                user.user_id,
                survey_token,
                'STOP_SEEKING',
                parse.quote('{}/statut/ne-recherche-plus'.format(
                    campaign.BASE_URL)),
            ),
            'unsubscribeLink':
            '{}/unsubscribe.html?email={}&auth={}'.format(
                campaign.BASE_URL, parse.quote(user.profile.email),
                unsubscribe_token),
        })
Esempio n. 13
0
def send_email_to_user(user, user_id, base_url):
    """Sends an email to the user to measure the Net Promoter Score."""

    # Renew actions for the day if needed.
    mail_result = mail.send_template(
        _MAILJET_TEMPLATE_ID,
        user.profile,
        {
            'baseUrl': base_url,
            'firstName': french.cleanup_firstname(user.profile.name),
            'npsFormUrl': '{}/api/nps?user={}&token={}&redirect={}'.format(
                base_url, user_id, auth.create_token(user_id, 'nps'),
                parse.quote('{}/retours'.format(base_url)),
            ),
        },
        dry_run=DRY_RUN,
    )
    mail_result.raise_for_status()
    return mail_result
Esempio n. 14
0
def spontaneous_vars(user, previous_email_campaign_id):
    """Compute vars for a given user for the spontaneous email.

    Returns:
        a dict with all vars required for the template, or None if no email
        should be sent.
    """

    if not user.projects:
        logging.info('User has no project')
        return None
    project = user.projects[0]

    job_group_info = jobs.get_group_proto(_DB,
                                          project.target_job.job_group.rome_id)

    def _should_use_spontaneous(modes):
        return any(mode.mode == job_pb2.SPONTANEOUS_APPLICATION
                   and mode.percentage > 20 for mode in modes.modes)

    application_modes = job_group_info.application_modes
    if not any(
            _should_use_spontaneous(modes)
            for modes in application_modes.values()):
        return None

    registered_months_ago = campaign.get_french_months_ago(
        user.registered_at.ToDatetime())
    if not registered_months_ago:
        logging.warning('User registered only recently (%s)',
                        user.registered_at)
        return None

    has_read_previous_email = previous_email_campaign_id and any(
        email.campaign_id == previous_email_campaign_id and email.status in
        (user_pb2.EMAIL_SENT_OPENED, user_pb2.EMAIL_SENT_CLICKED)
        for email in user.emails_sent)

    contact_mode = job_group_info.preferred_application_medium
    if not contact_mode:
        logging.error('There is no contact mode for the job group "%s"',
                      project.target_job.job_group.rome_id)
        return None
    contact_mode = job_pb2.ApplicationMedium.Name(contact_mode).replace(
        'APPLY_', '')

    in_a_workplace = job_group_info.in_a_workplace
    if not in_a_workplace and contact_mode != 'BY_EMAIL':
        logging.error(
            'There is no "in_a_workplace" field for the job group "%s".',
            project.target_job.job_group.rome_id)
        return None

    like_your_workplace = job_group_info.like_your_workplace
    if in_a_workplace and not like_your_workplace:
        logging.error(
            'There is no "like_your_workplace" field for the job group "%s".',
            project.target_job.job_group.rome_id)
        return None

    to_the_workplace = job_group_info.to_the_workplace
    if not to_the_workplace:
        to_the_workplace = "à l'entreprise"

    some_companies = job_group_info.place_plural
    if not some_companies:
        some_companies = 'des entreprises'

    what_i_love_about = job_group_info.what_i_love_about
    if user.profile.gender == user_pb2.FEMININE:
        what_i_love_about_feminine = job_group_info.what_i_love_about_feminine
        if what_i_love_about_feminine:
            what_i_love_about = what_i_love_about_feminine
    if not what_i_love_about and contact_mode == 'BY_EMAIL':
        logging.error(
            'There is no "What I love about" field for the job group "%s".',
            project.target_job.job_group.rome_id)
        return None

    why_specific_company = job_group_info.why_specific_company
    if not why_specific_company:
        logging.error(
            'There is no "Why this specific company" field for the job group "%s".',
            project.target_job.job_group.rome_id)
        return None

    at_various_companies = job_group_info.at_various_companies

    if project.weekly_applications_estimate == project_pb2.SOME:
        weekly_application_count = '5'
    elif project.weekly_applications_estimate > project_pb2.SOME:
        weekly_application_count = '15'
    else:
        weekly_application_count = ''

    survey_token = parse.quote(
        auth.create_token(user.user_id, role='employment-status'))
    unsubscribe_token = parse.quote(
        auth.create_token(user.profile.email, role='unsubscribe'))
    return {
        'applicationComplexity':
        job_pb2.ApplicationProcessComplexity.Name(
            job_group_info.application_complexity),
        'atVariousCompanies':
        at_various_companies,
        'contactMode':
        contact_mode,
        'deepLinkLBB':
        'https://labonneboite.pole-emploi.fr/entreprises/commune/{}/rome/'
        '{}?utm_medium=web&utm_source=bob&utm_campaign=bob-email'.format(
            project.mobility.city.city_id,
            project.target_job.job_group.rome_id),
        'emailInUrl':
        parse.quote(user.profile.email),
        'experienceAsText':
        _EXPERIENCE_AS_TEXT.get(project.seniority, 'peu'),
        'firstName':
        french.cleanup_firstname(user.profile.name),
        'gender':
        user_pb2.Gender.Name(user.profile.gender),
        'hasReadPreviousEmail':
        campaign.as_template_boolean(has_read_previous_email),
        'inWorkPlace':
        in_a_workplace,
        'jobName':
        french.lower_first_letter(
            french.genderize_job(project.target_job, user.profile.gender)),
        'lastName':
        user.profile.last_name,
        'likeYourWorkplace':
        like_your_workplace,
        'registeredMonthsAgo':
        registered_months_ago,
        'someCompanies':
        some_companies,
        # TODO(cyrille): Use campaign.get_status_update_link
        'statusUpdateUrl':
        '{}/statut/mise-a-jour?user={}&token={}&gender={}'.format(
            campaign.BASE_URL, user.user_id, survey_token,
            user_pb2.Gender.Name(user.profile.gender)),
        'toTheWorkplace':
        to_the_workplace,
        'unsubscribeLink':
        '{}/unsubscribe.html?email={}&auth={}'.format(
            campaign.BASE_URL, parse.quote(user.profile.email),
            unsubscribe_token),
        'weeklyApplicationOptions':
        weekly_application_count,
        'whatILoveAbout':
        what_i_love_about,
        'whySpecificCompany':
        why_specific_company,
    }
Esempio n. 15
0
    def _pe_connect_authenticate(self, code, nonce):
        token_data = _get_oauth2_access_token(
            'https://authentification-candidat.pole-emploi.fr/connexion/oauth2/access_token?'
            'realm=/individu',
            code=code,
            client_id=_EMPLOI_STORE_CLIENT_ID,
            client_secret=_EMPLOI_STORE_CLIENT_SECRET,
            auth_name='PE Connect',
        )

        if token_data.get('nonce') != nonce:
            flask.abort(403, 'Mauvais paramètre nonce')
        authorization_header = '{} {}'.format(
            token_data.get('token_type', 'Bearer'),
            token_data.get('access_token', ''))
        scopes = token_data.get('scope', '').split(' ')

        user_info_response = requests.get(
            'https://api.emploi-store.fr/partenaire/peconnect-individu/v1/userinfo',
            headers={'Authorization': authorization_header})
        if user_info_response.status_code < 200 or user_info_response.status_code >= 400:
            logging.warning('PE Connect fails (%d): "%s"',
                            user_info_response.status_code,
                            user_info_response.text)
            flask.abort(403, user_info_response.text)

        user_info = user_info_response.json()

        city = None
        if 'coordonnees' in scopes:
            coordinates_response = requests.get(
                'https://api.emploi-store.fr/partenaire/peconnect-coordonnees/v1/coordonnees',
                headers={
                    'Authorization': authorization_header,
                    'pe-nom-application': 'Bob Emploi',
                })
            if coordinates_response.status_code >= 200 and coordinates_response.status_code < 400:
                coordinates = coordinates_response.json()
                city = geo.get_city_proto(coordinates.get('codeINSEE'))

        job = None
        if 'competences' in scopes:
            competences_response = requests.get(
                'https://api.emploi-store.fr/partenaire/peconnect-competences/v1/competences',
                headers={'Authorization': authorization_header},
            )
            if competences_response.status_code >= 200 and competences_response.status_code < 400:
                competences = competences_response.json()
                job_id, rome_id = next(
                    ((c.get('codeAppellation'), c.get('codeRome'))
                     for c in competences), (None, None))
                job = jobs.get_job_proto(self._db, job_id, rome_id)

        response = user_pb2.AuthResponse()
        user_dict = self._user_db.user.find_one(
            {'peConnectId': user_info['sub']})
        user_id = str(user_dict.pop('_id')) if user_dict else ''
        if proto.parse_from_mongo(user_dict, response.authenticated_user):
            response.authenticated_user.user_id = user_id
            self._handle_returning_user(response)
        else:
            email = user_info.get('email')
            if email:
                self._assert_user_not_existing(email)
                response.authenticated_user.profile.email = email
            user = response.authenticated_user
            user.pe_connect_id = user_info['sub']
            # TODO(pascal): Handle the case where one of the name is missing.
            user.profile.name = french.cleanup_firstname(
                user_info.get('given_name', ''))
            user.profile.last_name = french.cleanup_firstname(
                user_info.get('family_name', ''))
            user.profile.gender = \
                _PE_CONNECT_GENDER.get(user_info.get('gender', ''), user_pb2.UNKNOWN_GENDER)
            if city or job:
                user.projects.add(
                    is_incomplete=True,
                    mobility=geo_pb2.Location(city=city) if city else None,
                    target_job=job)
            self._save_new_user(user)
            response.is_new_user = True

        response.auth_token = create_token(response.authenticated_user.user_id,
                                           'auth')

        return response
Esempio n. 16
0
    def test_extra_blanks(self) -> None:
        """First name contains special unneeded blanks."""

        self.assertEqual('Pascal', french.cleanup_firstname('pascal '))
        self.assertEqual('Pascal', french.cleanup_firstname(' pascal'))
Esempio n. 17
0
    def test_special_chars(self) -> None:
        """First name contains special chars."""

        self.assertEqual('Éloïse', french.cleanup_firstname('éloïse'))
        self.assertEqual('Éloïse', french.cleanup_firstname('ÉLOÏSE'))
Esempio n. 18
0
    def _pe_connect_authenticate(
            self, auth_request: auth_pb2.AuthRequest) -> auth_pb2.AuthResponse:
        token_data = _get_oauth2_access_token(
            'https://authentification-candidat.pole-emploi.fr/connexion/oauth2/access_token?'
            'realm=/individu',
            code=auth_request.pe_connect_code,
            client_id=_EMPLOI_STORE_CLIENT_ID or '',
            client_secret=_EMPLOI_STORE_CLIENT_SECRET or '',
            auth_name='PE Connect',
        )

        if token_data.get('nonce') != auth_request.pe_connect_nonce:
            flask.abort(403, 'Mauvais paramètre nonce')
        bearer = token_data.get('token_type', 'Bearer')
        access_token = token_data.get('access_token', '')
        authorization_header = f'{bearer} {access_token}'
        scopes = token_data.get('scope', '').split(' ')

        user_info_response = requests.get(
            'https://api.emploi-store.fr/partenaire/peconnect-individu/v1/userinfo',
            headers={'Authorization': authorization_header})
        if user_info_response.status_code < 200 or user_info_response.status_code >= 400:
            logging.warning('PE Connect fails (%d): "%s"',
                            user_info_response.status_code,
                            user_info_response.text)
            flask.abort(403, user_info_response.text)

        user_info = typing.cast(Dict[str, str], user_info_response.json())

        city = None
        if 'coordonnees' in scopes:
            coordinates_response = requests.get(
                'https://api.emploi-store.fr/partenaire/peconnect-coordonnees/v1/coordonnees',
                headers={
                    'Authorization': authorization_header,
                    'pe-nom-application': 'Bob Emploi',
                })
            if coordinates_response.status_code >= 200 and coordinates_response.status_code < 400:
                coordinates = typing.cast(Dict[str, str],
                                          coordinates_response.json())
                code_insee = coordinates.get('codeINSEE')
                if code_insee:
                    clean_code_insee = _replace_arrondissement_insee_to_city(
                        code_insee)
                    city = geo.get_city_proto(clean_code_insee)

        job = None
        if 'competences' in scopes:
            competences_response = requests.get(
                'https://api.emploi-store.fr/partenaire/peconnect-competences/v1/competences',
                headers={'Authorization': authorization_header},
            )
            if competences_response.status_code >= 200 and competences_response.status_code < 400:
                competences = typing.cast(List[Dict[str, str]],
                                          competences_response.json())
                job_id, rome_id = next(
                    ((c.get('codeAppellation'), c.get('codeRome'))
                     for c in competences), (None, None))
                if job_id and rome_id:
                    job = jobs.get_job_proto(self._db, job_id, rome_id)

        response = auth_pb2.AuthResponse()
        user_dict = self._user_collection.find_one(
            {'peConnectId': user_info['sub']})
        if proto.parse_from_mongo(user_dict, response.authenticated_user,
                                  'user_id'):
            self._handle_returning_user(response)
        else:
            user = response.authenticated_user
            is_existing_user, had_email = self._load_user_from_token_or_email(
                auth_request, user, user_info.get('email'))
            user.pe_connect_id = user_info['sub']
            response.is_new_user = force_update = not user.has_account
            user.has_account = True
            if city or job and not user.projects:
                force_update = True
                user.projects.add(is_incomplete=True,
                                  city=city or None,
                                  target_job=job)
            if is_existing_user:
                self._handle_returning_user(response,
                                            force_update=force_update,
                                            had_email=had_email)
            else:
                # TODO(pascal): Handle the case where one of the name is missing.
                user.profile.name = french.cleanup_firstname(
                    user_info.get('given_name', ''))
                user.profile.last_name = french.cleanup_firstname(
                    user_info.get('family_name', ''))
                user.profile.gender = \
                    _PE_CONNECT_GENDER.get(user_info.get('gender', ''), user_pb2.UNKNOWN_GENDER)
                self.save_new_user(user, auth_request.user_data)

        response.auth_token = create_token(response.authenticated_user.user_id,
                                           'auth')

        return response
Esempio n. 19
0
def self_development_vars(user, unused_db=None):
    """Compute vars for a given user for the self-development email.

    Returns:
        a dict with all vars required for the template, or None if no email
        should be sent.
    """

    if not user.projects:
        logging.info('User has no project')
        return None
    project = user.projects[0]

    registered_months_ago = campaign.get_french_months_ago(
        user.registered_at.ToDatetime())
    if not registered_months_ago:
        logging.info('User registered only recently (%s)', user.registered_at)
        return None

    job_search_length = campaign.job_search_started_months_ago(project)
    if job_search_length < 0:
        logging.info('No info on user search duration')
        return None

    if job_search_length >= 12:
        logging.info('User has been searching for too long (%s)',
                     job_search_length)
        return None

    genderized_job_name = french.lower_first_letter(
        french.genderize_job(project.target_job, user.profile.gender))
    age = datetime.date.today().year - user.profile.year_of_birth
    unsubscribe_token = parse.quote(
        auth.create_token(user.profile.email, role='unsubscribe'))

    max_young = 30
    min_old = 50

    return {
        'firstName':
        french.cleanup_firstname(user.profile.name),
        'gender':
        user_pb2.Gender.Name(user.profile.gender),
        'hasEnoughExperience':
        campaign.as_template_boolean(project.seniority > project_pb2.JUNIOR),
        'isAdministrativeAssistant':
        campaign.as_template_boolean(
            project.target_job.job_group.name == 'Secrétariat'),
        'isOld':
        campaign.as_template_boolean(age >= min_old),
        'isOldNotWoman':
        campaign.as_template_boolean(
            age >= min_old and user.profile.gender != user_pb2.FEMININE),
        'isYoung':
        campaign.as_template_boolean(age <= max_young),
        'isYoungNotWoman':
        campaign.as_template_boolean(
            age <= max_young and user.profile.gender != user_pb2.FEMININE),
        'jobName':
        genderized_job_name,
        'ofJobName':
        french.maybe_contract_prefix('de ', "d'", genderized_job_name),
        'registeredMonthsAgo':
        registered_months_ago,
        'unsubscribeLink':
        '{}/unsubscribe.html?email={}&auth={}'.format(
            campaign.BASE_URL, parse.quote(user.profile.email),
            unsubscribe_token),
    }