示例#1
0
def network_vars(user, database):
    """Compute vars for a given user for the network 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.warning('User registered only recently (%s)',
                        user.registered_at)
        return None

    job_group_info = jobs.get_group_proto(database,
                                          project.target_job.job_group.rome_id)
    in_target_domain = job_group_info.in_domain
    if not in_target_domain:
        logging.warning('Could not find a target domain (%s)',
                        project.target_job.job_group)
        return None

    worst_frustration = next(
        (f for f in (user_pb2.NO_OFFER_ANSWERS, user_pb2.MOTIVATION)
         if f in user.profile.frustrations), None)

    is_hairdresser_or_in_marseille = \
        project.target_job.job_group.rome_id.startswith('D') or \
        project.mobility.city.departement_id == '13'
    other_job_in_city = 'coiffeur à Marseille'
    if is_hairdresser_or_in_marseille:
        other_job_in_city = 'secrétaire à Lyon'
    return dict(
        campaign.get_default_vars(user), **{
            'registeredMonthsAgo':
            registered_months_ago,
            'inTargetDomain':
            in_target_domain,
            'frustration':
            user_pb2.Frustration.Name(worst_frustration)
            if worst_frustration else '',
            'otherJobInCity':
            other_job_in_city,
            'jobInCity':
            '{} {}'.format(
                french.lower_first_letter(
                    french.genderize_job(project.target_job,
                                         user.profile.gender)),
                french.in_city(strip_district(project.mobility.city.name))),
            'emailInUrl':
            parse.quote(user.profile.email),
            'statusUpdateUrl':
            campaign.get_status_update_link(user.user_id, user.profile),
        })
示例#2
0
def _get_network_vars(user: user_pb2.User,
                      database: Optional[pymongo.database.Database] = None,
                      **unused_kwargs: Any) -> Optional[Dict[str, str]]:
    """Compute vars for a given user for the network email.

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

    project = user.projects[0]

    if project.network_estimate != 1:
        logging.info('User has a good enough network')
        return None

    assert database
    job_group_info = jobs.get_group_proto(database,
                                          project.target_job.job_group.rome_id)
    if not job_group_info:
        logging.warning('Could not find job group info for "%s"',
                        project.target_job.job_group.rome_id)
        return None

    in_target_domain = job_group_info.in_domain
    if not in_target_domain:
        logging.warning('Could not find a target domain (%s)',
                        project.target_job.job_group)
        return None

    worst_frustration = next(
        (f for f in (user_pb2.NO_OFFER_ANSWERS, user_pb2.MOTIVATION)
         if f in user.profile.frustrations), None)

    is_hairdresser_or_in_marseille = \
        project.target_job.job_group.rome_id.startswith('D') or \
        project.city.departement_id == '13'
    other_job_in_city = 'coiffeur à Marseille'
    if is_hairdresser_or_in_marseille:
        other_job_in_city = 'secrétaire à Lyon'
    job = french.lower_first_letter(
        french.genderize_job(project.target_job, user.profile.gender))
    in_city = french.in_city(strip_district(project.city.name))
    return dict(
        campaign.get_default_coaching_email_vars(user), **{
            'inTargetDomain':
            in_target_domain,
            'frustration':
            user_pb2.Frustration.Name(worst_frustration)
            if worst_frustration else '',
            'otherJobInCity':
            other_job_in_city,
            'jobInCity':
            f'{job} {in_city}',
            'emailInUrl':
            parse.quote(user.profile.email),
        })
示例#3
0
def _get_self_development_vars(
        user: user_pb2.User, *, now: datetime.datetime, **unused_kwargs: Any) \
        -> dict[str, str]:
    """Computes vars for a given user for the self-development email.

    Returns a dict with all vars required for the template.
    """

    if not user.projects:
        raise scoring.NotEnoughDataException('No project yet', {'projects.0'})

    has_video = (user.profile.locale or 'fr').startswith('fr')

    project = user.projects[0]

    job_search_length = campaign.job_search_started_months_ago(project, now)
    if job_search_length < 0:
        raise campaign.DoNotSend('No info on user search duration.')

    if job_search_length > 12:
        raise campaign.DoNotSend(
            f'User has been searching for too long ({job_search_length:.2f}).')

    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

    max_young = 30
    min_old = 50

    return campaign.get_default_coaching_email_vars(user) | {
        'hasEnoughExperience':
        campaign.as_template_boolean(project.seniority > project_pb2.JUNIOR),
        'hasVideo':
        campaign.as_template_boolean(has_video),
        '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_profile_pb2.FEMININE),
        'isYoung':
        campaign.as_template_boolean(age <= max_young),
        'isYoungNotWoman':
        campaign.as_template_boolean(
            age <= max_young
            and user.profile.gender != user_profile_pb2.FEMININE),
        'jobName':
        genderized_job_name,
    }
示例#4
0
def _get_self_development_vars(user: user_pb2.User, now: datetime.datetime, **unused_kwargs: Any) \
        -> Optional[Dict[str, str]]:
    """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.
    """

    project = user.projects[0]

    job_search_length = campaign.job_search_started_months_ago(project, now)
    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

    max_young = 30
    min_old = 50

    return dict(
        campaign.get_default_coaching_email_vars(user), **{
            '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),
        })
示例#5
0
 def _get_template_variable(self, name: str) -> str:
     if name in self._template_variables:
         return self._template_variables[name]
     try:
         cache = _TEMPLATE_VARIABLES[name](self)
     except KeyError:
         # name[0] should always be %.
         lower_name = name[0] + french.lower_first_letter(name[1:])
         if lower_name not in _TEMPLATE_VARIABLES:
             logging.info('Wrong case in template variable "%s", cannot replace it.', name)
             cache = name
         else:
             # Recursion cannot run in a loop thanks to the test just before.
             cache = french.upper_first_letter(self._get_template_variable(lower_name))
     self._template_variables[name] = cache
     return cache
示例#6
0
def _get_galita2_vars(user: user_pb2.User,
                      **unused_kwargs: Any) -> Optional[Dict[str, str]]:
    if not user.projects:
        return None
    project = user.projects[0]
    if project.kind not in {project_pb2.FIND_A_FIRST_JOB, project_pb2.REORIENTATION} and \
            project.previous_job_similarity != project_pb2.NEVER_DONE:
        return None
    genderized_job_name = french.lower_first_letter(
        french.genderize_job(project.target_job, user.profile.gender))
    return dict(
        campaign.get_default_coaching_email_vars(user), **{
            'isReorienting':
            campaign.as_template_boolean(
                project.kind == project_pb2.REORIENTATION),
            'ofJobName':
            french.maybe_contract_prefix('de ', "d'", genderized_job_name)
        })
示例#7
0
def _get_network_vars(
        user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase,
        **unused_kwargs: Any) -> dict[str, str]:
    """Compute vars for a given user for the network email.

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

    if not user.projects:
        raise scoring.NotEnoughDataException('No project yet', {'projects.0'})

    project = user.projects[0]

    if project.network_estimate != 1:
        raise campaign.DoNotSend('User has a good enough network')

    in_target_domain = _get_in_target_domain(project.target_job.job_group.rome_id, database)
    worst_frustration = next(
        (f for f in (user_profile_pb2.NO_OFFER_ANSWERS, user_profile_pb2.MOTIVATION)
         if f in user.profile.frustrations),
        None)

    is_hairdresser_or_in_marseille = \
        project.target_job.job_group.rome_id.startswith('D') or \
        project.city.departement_id == '13'
    other_job_in_city = 'coiffeur à Marseille'
    if is_hairdresser_or_in_marseille:
        other_job_in_city = 'secrétaire à Lyon'
    job = french.lower_first_letter(french.genderize_job(
        project.target_job, user.profile.gender))
    in_city = french.in_city(strip_district(project.city.name))
    return campaign.get_default_coaching_email_vars(user) | {
        'inTargetDomain': in_target_domain,
        'frustration':
        user_profile_pb2.Frustration.Name(worst_frustration) if worst_frustration else '',
        'otherJobInCity': other_job_in_city,
        'jobInCity': f'{job} {in_city}',
        'emailInUrl': parse.quote(user.profile.email),
    }
示例#8
0
def _send_activation_email(user, project, database, base_url):
    """Send an email to the user just after we have defined their diagnosis."""

    advice_modules = {a.advice_id: a for a in _advice_modules(database)}
    advices = [a for a in project.advices if a.advice_id in advice_modules]
    if not advices:
        logging.error(  # pragma: no-cover
            'Weird: the advices that just got created do not exist in DB.'
        )  # pragma: no-cover
        return  # pragma: no-cover
    data = {
        'baseUrl':
        base_url,
        'projectId':
        project.project_id,
        'firstName':
        user.profile.name,
        'ofProjectTitle':
        french.maybe_contract_prefix(
            'de ', "d'",
            french.lower_first_letter(
                french.genderize_job(project.target_job,
                                     user.profile.gender))),
        'advices': [{
            'adviceId': a.advice_id,
            'title': advice_modules[a.advice_id].title
        } for a in advices],
    }
    response = mail.send_template(
        # https://app.mailjet.com/template/168827/build
        '168827',
        user.profile,
        data,
        dry_run=not _EMAIL_ACTIVATION_ENABLED)
    if response.status_code != 200:
        logging.warning('Error while sending diagnostic email: %s\n%s',
                        response.status_code, response.text)
示例#9
0
def _get_spontaneous_vars(user: user_pb2.User, *, now: datetime.datetime,
                          database: mongo.NoPiiMongoDatabase,
                          **unused_kwargs: Any) -> dict[str, str]:
    """Computes vars for a given user for the spontaneous email.

    Returns a dict with all vars required for the template.
    """

    if not user.projects:
        raise scoring.NotEnoughDataException('No project yet', {'projects.0'})

    project = user.projects[0]
    scoring_project = scoring.ScoringProject(project, user, database, now)

    job_search_length = scoring_project.get_search_length_now()
    if job_search_length < 0:
        raise campaign.DoNotSend('No info on user search duration')

    rome_id = project.target_job.job_group.rome_id
    if not rome_id:
        raise campaign.DoNotSend('User has no target job yet')

    job_group_info = scoring_project.job_group_info()
    if not job_group_info.rome_id:
        raise scoring.NotEnoughDataException(
            'Requires job group info to check if spontaneous application is a good channel.',
            fields={'projects.0.targetJob.jobGroup.romeId'})

    application_modes = job_group_info.application_modes
    if not application_modes:
        raise scoring.NotEnoughDataException(
            'Requires application modes to check if spontaneous application is a good channel.',
            fields={f'data.job_group_info.{rome_id}.application_modes'})

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

    if not any(
            _should_use_spontaneous(modes)
            for modes in application_modes.values()):
        raise campaign.DoNotSend(
            "Spontaneous isn't bigger than 20% of interesting channels.")

    contact_mode = job_group_info.preferred_application_medium
    if not contact_mode:
        raise scoring.NotEnoughDataException(
            'Contact mode is required to push people to apply spontaneously',
            fields={
                f'data.job_group_info.{rome_id}.preferred_application_medium'
            })

    in_a_workplace = job_group_info.in_a_workplace
    if not in_a_workplace and contact_mode != job_pb2.APPLY_BY_EMAIL:
        raise scoring.NotEnoughDataException(
            'To apply in person, the %inAWorkplace template is required',
            fields={f'data.job_group_info.{rome_id}.in_a_workplace'})

    like_your_workplace = job_group_info.like_your_workplace
    if in_a_workplace and not like_your_workplace:
        raise scoring.NotEnoughDataException(
            'The template %likeYourWorkplace is required',
            fields={f'data.job_group_info.{rome_id}.like_your_workplace'})

    to_the_workplace = job_group_info.to_the_workplace
    if not to_the_workplace:
        to_the_workplace = scoring_project.translate_static_string(
            "à l'entreprise")

    some_companies = job_group_info.place_plural
    if not some_companies:
        some_companies = scoring_project.translate_static_string(
            'des entreprises')

    what_i_love_about = scoring_project.translate_string(
        job_group_info.what_i_love_about, is_genderized=True)
    # TODO(cyrille): Drop this behaviour once phrases are translated with gender.
    if user.profile.gender == user_profile_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 == job_pb2.APPLY_BY_EMAIL:
        raise scoring.NotEnoughDataException(
            'An example about "What I love about" a company is required',
            fields={f'data.job_group_info.{rome_id}.what_i_love_about'})

    why_specific_company = job_group_info.why_specific_company
    if not why_specific_company:
        raise scoring.NotEnoughDataException(
            'An example about "Why this specific company" is required',
            fields={f'data.job_group_info.{rome_id}.why_specific_company'})

    at_various_companies = job_group_info.at_various_companies

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

    if project.weekly_applications_estimate:
        weekly_applications_option = project_pb2.NumberOfferEstimateOption.Name(
            project.weekly_applications_estimate)
    else:
        weekly_applications_option = ''

    return campaign.get_default_coaching_email_vars(user) | {
        'applicationComplexity':
        job_pb2.ApplicationProcessComplexity.Name(
            job_group_info.application_complexity),
        'atVariousCompanies':
        at_various_companies,
        'contactMode':
        job_pb2.ApplicationMedium.Name(contact_mode).replace('APPLY_', ''),
        'deepLinkLBB':
        f'https://labonneboite.pole-emploi.fr/entreprises/commune/{project.city.city_id}/rome/'
        f'{project.target_job.job_group.rome_id}?utm_medium=web&utm_source=bob&'
        'utm_campaign=bob-email',
        'emailInUrl':
        parse.quote(user.profile.email),
        'experienceAsText':
        _EXPERIENCE_AS_TEXT.get(project.seniority, 'peu'),
        '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,
        'someCompanies':
        some_companies,
        'toTheWorkplace':
        to_the_workplace,
        'weeklyApplicationsCount':
        weekly_applications_count,
        'weeklyApplicationsOption':
        weekly_applications_option,
        'whatILoveAbout':
        what_i_love_about,
        'whySpecificCompany':
        why_specific_company,
    }
示例#10
0
def network_plus_vars(user: user_pb2.User,
                      database: Optional[pymongo.database.Database] = None,
                      **unused_kwargs: Any) -> Optional[Dict[str, str]]:
    """Compute vars for a given user for the network email.

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

    project = user.projects[0]

    if project.network_estimate < 2:
        logging.info('User does not have a strong network')
        return None

    assert database
    job_group_info = jobs.get_group_proto(database,
                                          project.target_job.job_group.rome_id)
    if not job_group_info:
        logging.warning('Could not find job group info for "%s"',
                        project.target_job.job_group.rome_id)
        return None
    in_target_domain = job_group_info.in_domain
    application_modes = job_group_info.application_modes.values()
    if not in_target_domain:
        logging.warning('Could not find a target domain (%s)',
                        project.target_job.job_group)
        return None

    fap_modes = [
        fap_modes.modes for fap_modes in application_modes
        if len(fap_modes.modes)
    ]
    if not fap_modes:
        return None
    flat_fap_modes = [mode for modes in fap_modes for mode in modes]
    network_percentages = [
        mode.percentage for mode in flat_fap_modes
        if (mode.mode == job_pb2.PERSONAL_OR_PROFESSIONAL_CONTACTS)
    ]
    # We want to focus on the user for which network,
    # as an application mode, has a substantial importance.
    if not network_percentages:
        return None
    average_network_percentage = sum(network_percentages) / len(
        network_percentages)
    if average_network_percentage > 55:
        network_application_importance = 'que la majorité'
    elif average_network_percentage >= 45:
        network_application_importance = 'que la moitié'
    elif average_network_percentage >= 25:
        network_application_importance = "qu'un tiers"
    else:
        return None

    worst_frustration = next(
        (f for f in (user_pb2.SELF_CONFIDENCE, user_pb2.MOTIVATION)
         if f in user.profile.frustrations), None)
    has_children = user.profile.family_situation in {
        user_pb2.FAMILY_WITH_KIDS, user_pb2.SINGLE_PARENT_SITUATION
    }

    age = datetime.date.today().year - user.profile.year_of_birth
    max_young = 35

    try:
        in_departement = geo.get_in_a_departement_text(
            database, project.city.departement_id)
    except KeyError:
        logging.warning('Could not find departement (%s)',
                        project.city.departement_id)
        return None

    job_group_name = french.lower_first_letter(
        project.target_job.job_group.name)

    return dict(
        campaign.get_default_coaching_email_vars(user), **{
            'frustration':
            user_pb2.Frustration.Name(worst_frustration)
            if worst_frustration else '',
            'hasChildren':
            campaign.as_template_boolean(has_children),
            'hasHighSchoolDegree':
            campaign.as_template_boolean(
                user.profile.highest_degree >= job_pb2.BAC_BACPRO),
            'hasLargeNetwork':
            campaign.as_template_boolean(project.network_estimate >= 2),
            'hasWorkedBefore':
            campaign.as_template_boolean(
                project.kind != project_pb2.FIND_A_FIRST_JOB),
            'inCity':
            french.in_city(project.city.name),
            'inTargetDomain':
            in_target_domain,
            'isAbleBodied':
            campaign.as_template_boolean(not user.profile.has_handicap),
            'isYoung':
            campaign.as_template_boolean(age <= max_young),
            'jobGroupInDepartement':
            f'{job_group_name} {in_departement}',
            'networkApplicationPercentage':
            network_application_importance,
        })
示例#11
0
def network_plus_vars(user, database):
    """Compute vars for a given user for the network 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.warning('User registered only recently (%s)',
                        user.registered_at)
        return None

    job_group_info = jobs.get_group_proto(database,
                                          project.target_job.job_group.rome_id)
    in_target_domain = job_group_info.in_domain
    application_modes = job_group_info.application_modes.values()
    if not in_target_domain:
        logging.warning('Could not find a target domain (%s)',
                        project.target_job.job_group)
        return None

    fap_modes = [
        fap_modes.modes for fap_modes in application_modes
        if len(fap_modes.modes)
    ]
    if not fap_modes:
        return None
    flat_fap_modes = [mode for modes in fap_modes for mode in modes]
    network_percentages = [
        mode.percentage for mode in flat_fap_modes
        if (mode.mode == job_pb2.PERSONAL_OR_PROFESSIONAL_CONTACTS)
    ]
    # We want to focus on the user for which network,
    # as an application mode, has a substantial importance.
    if not network_percentages:
        return None
    average_network_percentage = sum(network_percentages) / len(
        network_percentages)
    if average_network_percentage < 55:
        network_application_importance = 'que la majorité'
    if average_network_percentage >= 45 and average_network_percentage <= 55:
        network_application_importance = 'que la moitié'
    if average_network_percentage >= 25 and average_network_percentage < 45:
        network_application_importance = "qu'un tiers"
    else:
        return None

    worst_frustration = next(
        (f for f in (user_pb2.SELF_CONFIDENCE, user_pb2.MOTIVATION)
         if f in user.profile.frustrations), None)
    has_children = user.profile.family_situation in {
        user_pb2.FAMILY_WITH_KIDS, user_pb2.SINGLE_PARENT_SITUATION
    }

    age = datetime.date.today().year - user.profile.year_of_birth
    max_young = 35

    return dict(
        campaign.get_default_vars(user), **{
            'frustration':
            user_pb2.Frustration.Name(worst_frustration)
            if worst_frustration else '',
            'hasChildren':
            campaign.as_template_boolean(has_children),
            'hasHandicap':
            campaign.as_template_boolean(user.profile.has_handicap),
            'hasHighSchoolDegree':
            campaign.as_template_boolean(
                user.profile.highest_degree >= job_pb2.BAC_BACPRO),
            'hasLargeNetwork':
            campaign.as_template_boolean(project.network_estimate >= 2),
            'hasWorkedBefore':
            campaign.as_template_boolean(
                project.kind != project_pb2.FIND_A_FIRST_JOB),
            'inCity':
            french.in_city(project.mobility.city.name),
            'inTargetDomain':
            in_target_domain,
            'isYoung':
            campaign.as_template_boolean(age <= max_young),
            'jobGroupInDepartement':
            '{} {}'.format(
                french.lower_first_letter(project.target_job.job_group.name),
                geo.get_in_a_departement_text(
                    database, project.mobility.city.departement_id)),
            'networkApplicationPercentage':
            network_application_importance,
        })
示例#12
0
def quick_diagnose(
        user: user_pb2.User, project: project_pb2.Project,
        user_diff: user_pb2.User,
        database: mongo.NoPiiMongoDatabase) -> diagnostic_pb2.QuickDiagnostic:
    """Create a quick diagnostic of a project or user profile focused on the given field."""

    scoring_project = scoring.ScoringProject(project or project_pb2.Project(),
                                             user,
                                             database,
                                             now=now.get())

    response = diagnostic_pb2.QuickDiagnostic()
    has_departement_diff = user_diff.projects and user_diff.projects[
        0].city.departement_id
    if has_departement_diff:
        all_counts = get_users_counts(database)
        if all_counts:
            departement_count = all_counts.departement_counts[
                project.city.departement_id]
            if departement_count and departement_count > 50:
                response.comments.add(
                    field=diagnostic_pb2.CITY_FIELD,
                    comment=_create_bolded_string(
                        scoring_project.translate_static_string(
                            'Super, <strong>{count}</strong> personnes dans ce département ont déjà '
                            'testé le diagnostic de Bob\xa0!', ).format(
                                count=str(departement_count))),
                )

    has_rome_id_diff = user_diff.projects and user_diff.projects[
        0].target_job.job_group.rome_id
    if has_rome_id_diff:
        all_counts = get_users_counts(database)
        if all_counts:
            job_group_count = all_counts.job_group_counts[
                project.target_job.job_group.rome_id]
            if job_group_count and job_group_count > 50:
                response.comments.add(
                    field=diagnostic_pb2.TARGET_JOB_FIELD,
                    comment=_create_bolded_string(
                        scoring_project.translate_static_string(
                            "Ça tombe bien, j'ai déjà accompagné <strong>{count}</strong> personnes "
                            'pour ce métier\xa0!', ).format(
                                count=str(job_group_count))),
                )

    if user_diff.profile.year_of_birth or has_rome_id_diff or has_departement_diff:
        if user.profile.year_of_birth:
            local_diagnosis = scoring_project.local_diagnosis()
            is_senior = scoring_project.get_user_age() >= 35
            if is_senior:
                salary_estimation = local_diagnosis.imt.senior_salary
            else:
                salary_estimation = local_diagnosis.imt.junior_salary
            if salary_estimation.short_text:
                response.comments.add(
                    field=diagnostic_pb2.SALARY_FIELD,
                    is_before_question=True,
                    comment=diagnostic_pb2.BoldedString(string_parts=[
                        scoring_project.translate_static_string(
                            'En général les gens demandent un salaire {of_salary} par mois.',
                        ).format(of_salary=french.lower_first_letter(
                            salary_estimation.short_text)),
                    ]),
                )

    if has_rome_id_diff:
        required_diplomas = sorted(
            (
                d for d in scoring_project.requirements().diplomas
                # Only mention real diplomas that are required in 10% or more of job offers.
                if d.diploma.level != job_pb2.NO_DEGREE
                and d.percent_required > 10),
            key=lambda d: d.percent_required,
            reverse=True,
            # Only take the 2 biggest ones.
        )[:2]
        if len(required_diplomas) == 2:
            if required_diplomas[0].percent_required >= 70:
                # The first one is doing more than 70% of requirements, just keep one.
                required_diplomas = required_diplomas[:1]
            else:
                # Sort by degree level.
                required_diplomas.sort(key=lambda d: d.diploma.level)

        if required_diplomas:
            diplomas = ', '.join(diploma.name for diploma in required_diplomas)
            response.comments.add(
                field=diagnostic_pb2.REQUESTED_DIPLOMA_FIELD,
                is_before_question=True,
                comment=diagnostic_pb2.BoldedString(string_parts=[
                    scoring_project.translate_static_string(
                        'Les offres demandent souvent un {diplomas} ou équivalent.',
                    ).format(diplomas=diplomas),
                ]))

    if has_rome_id_diff or has_departement_diff:
        local_diagnosis = scoring_project.local_diagnosis()
        if local_diagnosis.imt.employment_type_percentages:
            main_employment_type_percentage = local_diagnosis.imt.employment_type_percentages[
                0]
            if main_employment_type_percentage.percentage > 98:
                comment = scoring_project.translate_static_string(
                    'La plupart des offres sont en {employment_type}.', )
            else:
                comment = scoring_project.translate_static_string(
                    'Plus de {percentage}% des offres sont en {employment_type}.',
                )
            if main_employment_type_percentage.employment_type in jobs.EMPLOYMENT_TYPES:
                employment_type = scoring_project.translate_static_string(
                    jobs.EMPLOYMENT_TYPES[
                        main_employment_type_percentage.employment_type])
                response.comments.add(
                    field=diagnostic_pb2.EMPLOYMENT_TYPE_FIELD,
                    is_before_question=True,
                    comment=_create_bolded_string(
                        comment.format(
                            percentage=str(
                                int(main_employment_type_percentage.percentage)
                            ),
                            employment_type=employment_type,
                        )),
                )

    return response
示例#13
0
 _an_application_mode,
 '%cityId':
 lambda scoring_project: scoring_project.details.city.city_id,
 # TODO(pascal): Investigate who's using that template and rename it to someting with URL in it.
 '%cityName':
 lambda scoring_project: parse.quote(scoring_project.details.city.name),
 '%departementId':
 lambda scoring_project: scoring_project.details.city.departement_id,
 '%eFeminine':
 lambda scoring_project:
 ('e' if scoring_project.user_profile.gender == user_pb2.FEMININE else ''),
 '%experienceDuration':
 lambda scoring_project: _EXPERIENCE_DURATION.get(
     scoring_project.details.seniority, ''),
 '%feminineJobName':
 lambda scoring_project: french.lower_first_letter(
     scoring_project.details.target_job.feminine_name),
 '%inAreaType':
 _in_area_type,
 '%inAWorkplace':
 lambda scoring_project: scoring_project.job_group_info().in_a_workplace,
 '%inCity':
 _in_city,
 '%inDepartement':
 _in_departement,
 '%inDomain':
 lambda scoring_project: scoring_project.job_group_info().in_domain,
 '%inRegion':
 _in_region,
 # TODO(pascal): Don't use Url as a prefix, as this makes %jobGroupName forbidden (no variable
 # can be the prefix of another variable).
 '%jobGroupNameUrl':
示例#14
0
    def test_empty(self) -> None:
        """Empty string."""

        sentence = french.lower_first_letter('')
        self.assertEqual('', sentence)
示例#15
0
    def test_with_uppercase(self) -> None:
        """All upper case."""

        sentence = french.lower_first_letter('This contains UPPERCASE')
        self.assertEqual('this contains UPPERCASE', sentence)
示例#16
0
def _get_spontaneous_vars(user: user_pb2.User,
                          now: datetime.datetime,
                          database: Optional[pymongo.database.Database] = None,
                          **unused_kwargs: Any) -> Optional[Dict[str, str]]:
    """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.
    """

    project = user.projects[0]

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

    assert database
    job_group_info = jobs.get_group_proto(database,
                                          project.target_job.job_group.rome_id)
    if not job_group_info:
        logging.warning('Could not find job group info for "%s"',
                        project.target_job.job_group.rome_id)
        return None

    def _should_use_spontaneous(
            modes: job_pb2.RecruitingModesDistribution) -> bool:
        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

    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

    in_a_workplace = job_group_info.in_a_workplace
    if not in_a_workplace and contact_mode != job_pb2.APPLY_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 == job_pb2.APPLY_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_applications_count = '5'
    elif project.weekly_applications_estimate > project_pb2.SOME:
        weekly_applications_count = '15'
    else:
        weekly_applications_count = ''

    if project.weekly_applications_estimate:
        weekly_applications_option = project_pb2.NumberOfferEstimateOption.Name(
            project.weekly_applications_estimate)
    else:
        weekly_applications_option = ''

    return dict(
        campaign.get_default_coaching_email_vars(user), **{
            'applicationComplexity':
            job_pb2.ApplicationProcessComplexity.Name(
                job_group_info.application_complexity),
            'atVariousCompanies':
            at_various_companies,
            'contactMode':
            job_pb2.ApplicationMedium.Name(contact_mode).replace('APPLY_', ''),
            'deepLinkLBB':
            f'https://labonneboite.pole-emploi.fr/entreprises/commune/{project.city.city_id}/rome/'
            f'{project.target_job.job_group.rome_id}?utm_medium=web&utm_source=bob&'
            'utm_campaign=bob-email',
            'emailInUrl':
            parse.quote(user.profile.email),
            'experienceAsText':
            _EXPERIENCE_AS_TEXT.get(project.seniority, 'peu'),
            '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,
            'someCompanies':
            some_companies,
            'toTheWorkplace':
            to_the_workplace,
            'weeklyApplicationsCount':
            weekly_applications_count,
            'weeklyApplicationsOption':
            weekly_applications_option,
            'whatILoveAbout':
            what_i_love_about,
            'whySpecificCompany':
            why_specific_company,
        })
示例#17
0
def imt_vars(user, database):
    """Compute vars for the "IMT" email."""

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

    genderized_job_name = french.lower_first_letter(
        french.genderize_job(project.target_job, user.profile.gender))

    departement_id = project.mobility.city.departement_id
    rome_id = project.target_job.job_group.rome_id
    diagnosis_key = '{}:{}'.format(departement_id, rome_id)
    local_diagnosis = _LOCAL_DIAGNOSIS.get_collection(database).get(
        diagnosis_key)
    if not local_diagnosis:
        logging.info('User market does not exist')
        return None
    imt = local_diagnosis.imt
    if not imt:
        logging.info('User market has no IMT data')
        return None

    shown_sections = 0

    market_stress_section = _make_market_stress_section(
        imt.yearly_avg_offers_per_10_candidates)
    if market_stress_section:
        shown_sections += 1

    application_modes_section = _make_application_mode_section(
        campaign.get_application_modes(rome_id, database), project.advices,
        user.user_id)
    if application_modes_section:
        shown_sections += 1

    departements_section = _make_departements_section(
        project.mobility.city.departement_id,
        _get_best_departements_for_job_group(rome_id, database),
        project.mobility.area_type, database)
    if departements_section:
        shown_sections += 1

    employment_types_section = _make_employment_type_section(
        sorted(imt.employment_type_percentages, key=lambda e: e.percentage))
    if employment_types_section:
        shown_sections += 1

    months_section = _make_months_section(imt.active_months)
    if months_section:
        shown_sections += 1

    if shown_sections < 3:
        logging.info('Only %d section(s) to be shown for user.',
                     shown_sections)
        return None

    imt_link = 'http://candidat.pole-emploi.fr/marche-du-travail/statistiques?' + \
        'codeMetier={}&codeZoneGeographique={}&typeZoneGeographique=DEPARTEMENT'.format(
            project.target_job.code_ogr, departement_id)

    job_name_in_departement = '{} {}'.format(
        genderized_job_name,
        geo.get_in_a_departement_text(database,
                                      project.mobility.city.departement_id))

    return dict(
        campaign.get_default_vars(user), **{
            'applicationModes':
            _make_section(application_modes_section),
            'departements':
            _make_section(departements_section),
            'employmentType':
            _make_section(employment_types_section),
            'imtLink':
            imt_link,
            'inCity':
            french.in_city(project.mobility.city.name),
            'jobNameInDepartement':
            job_name_in_departement,
            'loginUrl':
            campaign.create_logged_url(user.user_id),
            'marketStress':
            _make_section(market_stress_section),
            'months':
            _make_section(months_section),
            'ofJobNameInDepartement':
            french.maybe_contract_prefix('de ', "d'", job_name_in_departement),
            'ofJobName':
            french.maybe_contract_prefix('de ', "d'", genderized_job_name),
            'showPs':
            campaign.as_template_boolean(
                _can_go_to_arles_hotellerie_event(rome_id, project.mobility)),
            'statusUpdateUrl':
            campaign.get_status_update_link(user.user_id, user.profile),
        })
示例#18
0
    def test_all_uppercase(self) -> None:
        """All upper case."""

        sentence = french.lower_first_letter('THIS IS ALL UPPERCASE')
        self.assertEqual('THIS IS ALL UPPERCASE', sentence)
示例#19
0
def network_plus_vars(
        user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase,
        **unused_kwargs: Any) -> dict[str, str]:
    """Compute vars for a given user for the network email.

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

    if not user.projects:
        raise scoring.NotEnoughDataException('No project yet', {'projects.0'})

    project = user.projects[0]

    if project.network_estimate < 2:
        raise campaign.DoNotSend('User does not have a strong network')

    rome_id = project.target_job.job_group.rome_id
    in_target_domain = _get_in_target_domain(rome_id, database)
    job_group_info = jobs.get_group_proto(database, rome_id)
    assert job_group_info
    application_modes = job_group_info.application_modes.values()

    fap_modes = [fap_modes.modes for fap_modes in application_modes if len(fap_modes.modes)]
    if not fap_modes:
        raise scoring.NotEnoughDataException(
            'No information about application modes for the target job',
            {f'data.job_group_info.{rome_id}.application_modes'})
    flat_fap_modes = [mode for modes in fap_modes for mode in modes]
    network_percentages = [mode.percentage for mode in flat_fap_modes if (
        mode.mode == job_pb2.PERSONAL_OR_PROFESSIONAL_CONTACTS)]
    # We want to focus on the users for which network,
    # as an application mode, has a substantial importance.
    if not network_percentages:
        raise campaign.DoNotSend(
            'User is not targeting a job where networking is a main application mode')
    scoring_project = scoring.ScoringProject(project, user, database=database)
    average_network_percentage = sum(network_percentages) / len(network_percentages)
    if average_network_percentage > 55:
        network_application_importance = scoring_project.translate_static_string('que la majorité')
    elif average_network_percentage >= 45:
        network_application_importance = scoring_project.translate_static_string('que la moitié')
    elif average_network_percentage >= 25:
        network_application_importance = scoring_project.translate_static_string("qu'un tiers")
    else:
        raise campaign.DoNotSend(
            'User is not targeting a job where networking is a main application mode')

    worst_frustration = next(
        (f for f in (user_profile_pb2.SELF_CONFIDENCE, user_profile_pb2.MOTIVATION)
         if f in user.profile.frustrations),
        None)
    has_children = user.profile.family_situation in {
        user_profile_pb2.FAMILY_WITH_KIDS, user_profile_pb2.SINGLE_PARENT_SITUATION}

    age = datetime.date.today().year - user.profile.year_of_birth
    max_young = 35

    try:
        in_departement = geo.get_in_a_departement_text(
            database, project.city.departement_id,
            city_hint=project.city, locale=user.profile.locale)
    except KeyError:
        raise scoring.NotEnoughDataException(
            'Need departement info for phrasing',
            {f'data.departements.{project.city.departement_id}'}) from None

    job_group_name = french.lower_first_letter(project.target_job.job_group.name)

    if (user.profile.locale or 'fr').startswith('fr'):
        in_city = french.in_city(project.city.name)
    else:
        # TODO(pascal): Update the English template so that it follows the logic of "in city" and
        # not "city". For now it's phrased as "near {{inCity}}".
        in_city = project.city.name

    return campaign.get_default_coaching_email_vars(user) | {
        'frustration':
        user_profile_pb2.Frustration.Name(worst_frustration) if worst_frustration else '',
        'hasChildren': campaign.as_template_boolean(has_children),
        'hasHighSchoolDegree': campaign.as_template_boolean(
            user.profile.highest_degree >= job_pb2.BAC_BACPRO),
        'hasLargeNetwork': campaign.as_template_boolean(project.network_estimate >= 2),
        'hasWorkedBefore': campaign.as_template_boolean(
            project.kind != project_pb2.FIND_A_FIRST_JOB),
        'inCity': in_city,
        'inTargetDomain': in_target_domain,
        'isAbleBodied': campaign.as_template_boolean(not user.profile.has_handicap),
        'isYoung': campaign.as_template_boolean(age <= max_young),
        'jobGroupInDepartement': f'{job_group_name} {in_departement}',
        'networkApplicationPercentage': network_application_importance,
    }
示例#20
0
    def test_one_letter(self) -> None:
        """Only one letter."""

        sentence = french.lower_first_letter('T')
        self.assertEqual('t', sentence)
示例#21
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),
    }
示例#22
0
    def test_acronym(self) -> None:
        """Starts with an acronym."""

        sentence = french.lower_first_letter('SPA manager')
        self.assertEqual('SPA manager', sentence)
示例#23
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,
    }
示例#24
0
文件: imt.py 项目: b3rday/bob-emploi
def _get_imt_vars(user: user_pb2.User,
                  database: Optional[pymongo.database.Database] = None,
                  **unused_kwargs: Any) -> Optional[Dict[str, Any]]:
    """Compute vars for the "IMT" email."""

    project = user.projects[0]
    assert database
    scoring_project = scoring.ScoringProject(project, user, database)

    genderized_job_name = french.lower_first_letter(
        french.genderize_job(project.target_job, user.profile.gender))

    departement_id = project.city.departement_id
    rome_id = project.target_job.job_group.rome_id
    local_diagnosis = scoring_project.local_diagnosis()
    if not local_diagnosis.HasField('imt'):
        logging.info('User market has no IMT data')
        return None
    imt = local_diagnosis.imt

    shown_sections = []

    market_stress_section = _make_market_stress_section(
        imt.yearly_avg_offers_per_10_candidates)
    if market_stress_section:
        shown_sections.append('marketStress')

    application_modes_section = _make_application_mode_section(
        scoring_project.get_best_application_mode(), project, user.user_id)
    if application_modes_section:
        shown_sections.append('applicationModes')

    departements_section = _make_departements_section(
        departement_id,
        _get_best_departements_for_job_group(rome_id, database),
        project.area_type, database)
    if departements_section:
        shown_sections.append('departements')

    employment_types_section = _make_employment_type_section(
        imt.employment_type_percentages)
    if employment_types_section:
        shown_sections.append('employmentTypes')

    months_section = _make_months_section(imt.active_months)
    if months_section:
        shown_sections.append('months')

    if len(shown_sections) < 3:
        logging.info('Only %d section(s) to be shown for user (%s).',
                     len(shown_sections), shown_sections)
        return None

    imt_link = 'http://candidat.pole-emploi.fr/marche-du-travail/statistiques?' \
        f'codeMetier={project.target_job.code_ogr}&codeZoneGeographique={departement_id}&' \
        'typeZoneGeographique=DEPARTEMENT'

    in_departement = geo.get_in_a_departement_text(database, departement_id)
    job_name_in_departement = f'{genderized_job_name} {in_departement}'

    return dict(
        campaign.get_default_coaching_email_vars(user), **{
            'applicationModes':
            _make_section(application_modes_section),
            'departements':
            _make_section(departements_section),
            'employmentType':
            _make_section(employment_types_section),
            'imtLink':
            imt_link,
            'inCity':
            french.in_city(project.city.name),
            'jobNameInDepartement':
            job_name_in_departement,
            'loginUrl':
            campaign.create_logged_url(user.user_id),
            'marketStress':
            _make_section(market_stress_section),
            'months':
            _make_section(months_section),
            'ofJobNameInDepartement':
            french.maybe_contract_prefix('de ', "d'", job_name_in_departement),
            'ofJobName':
            french.maybe_contract_prefix('de ', "d'", genderized_job_name),
        })
示例#25
0
def _job_name(scoring_project):
    return french.lower_first_letter(
        french.genderize_job(scoring_project.details.target_job,
                             scoring_project.user_profile.gender))