Ejemplo n.º 1
0
def _get_short_diploma_vars(user: user_pb2.User, *,
                            database: mongo.NoPiiMongoDatabase,
                            **unused_kwargs: Any) -> dict[str, Any]:

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

    project = user.projects[0]

    if user.projects[0].diagnostic.category_id != 'missing-diploma':
        raise campaign.DoNotSend('The user has no missing-diploma category')

    scoring_project = scoring.ScoringProject(project, user, database)

    login_url = campaign.create_logged_url(user.user_id,
                                           f'/projet/{project.project_id}')

    # TODO(sil): Let's check if this is needed to have access to the method.
    if not project.target_job.job_group.rome_id:
        raise scoring.NotEnoughDataException(
            'Need a job group to find trainings',
            # TODO(pascal): Use project_id instead of 0.
            {'projects.0.targetJob.jobGroup.romeId'})

    deep_link_training_url = \
        campaign.get_deep_link_advice(user.user_id, project, 'training')

    return campaign.get_default_coaching_email_vars(user) | {
        'deepTrainingAdviceUrl':
        deep_link_training_url,
        'ofJobName':
        scoring_project.populate_template('%ofJobName'),
        'productUrl':
        f'{login_url}?utm_source=bob-emploi&utm_medium=email',
    }
Ejemplo n.º 2
0
def _get_imt_vars(
        user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase,
        **unused_kwargs: Any) -> dict[str, Any]:
    """Compute vars for the "IMT" email."""

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

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

    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'):
        raise scoring.NotEnoughDataException(
            'User market has no IMT data',
            {f'data.local_diagnosis.{departement_id}:{rome_id}.imt'})
    imt = local_diagnosis.imt

    shown_sections = []

    if market_stress_section := _make_market_stress_section(
            imt.yearly_avg_offers_per_10_candidates):
        shown_sections.append('marketStress')
Ejemplo n.º 3
0
def _get_improve_cv_vars(user: user_pb2.User, *, now: datetime.datetime,
                         **unused_kwargs: Any) -> dict[str, Any]:
    """Compute vars for the "Improve your CV" email."""

    if user_profile_pb2.RESUME not in user.profile.frustrations:
        raise campaign.DoNotSend('User is not frustrated by its CV')

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

    project = user.projects[0]
    if project.kind == project_pb2.FIND_A_FIRST_JOB:
        has_experience = 'False'
    elif project.kind in (project_pb2.FIND_A_NEW_JOB,
                          project_pb2.FIND_ANOTHER_JOB):
        has_experience = 'True'
    else:
        has_experience = ''

    deep_link_advice_url = \
        campaign.get_deep_link_advice(user.user_id, project, 'improve-resume') or \
        campaign.get_deep_link_advice(user.user_id, project, 'fresh-resume')

    return campaign.get_default_coaching_email_vars(user) | {
        'deepLinkAdviceUrl': deep_link_advice_url,
        'hasExperience': has_experience,
        'isSeptember': campaign.as_template_boolean(now.month == 9),
        'loginUrl': campaign.create_logged_url(user.user_id)
    }
Ejemplo n.º 4
0
def _get_in_target_domain(rome_id: str, database: mongo.NoPiiMongoDatabase) -> str:
    if not rome_id:
        raise scoring.NotEnoughDataException(
            "Need a job group to express user's target domain",
            {'projects.0.targetJob.jobGroup.romeId'})
    job_group_info = jobs.get_group_proto(database, rome_id)
    if not job_group_info:
        raise scoring.NotEnoughDataException(
            "Need job group into to express user's target domain",
            {f'data.job_group_info.{rome_id}'})
    in_target_domain = job_group_info.in_domain
    if not in_target_domain:
        raise scoring.NotEnoughDataException(
            "No information about this job group's domain",
            {f'data.job_group_info.{rome_id}.in_target_domain'})
    return in_target_domain
Ejemplo n.º 5
0
def _get_find_diploma_vars(user: user_pb2.User, *,
                           database: mongo.NoPiiMongoDatabase,
                           **unused_kwargs: Any) -> dict[str, Any]:
    """Compute vars for the "Prepare your application" email."""

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

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

    if not any(s.strategy_id == 'get-diploma'
               for s in project.opened_strategies):
        raise campaign.DoNotSend(
            'The user has not started a strategy to get a diploma')

    if not project.target_job.job_group.rome_id:
        raise scoring.NotEnoughDataException(
            'Need a job group to find trainings',
            # TODO(pascal): Use project_id instead of 0.
            {'projects.0.targetJob.jobGroup.romeId'})

    trainings = scoring_project.get_trainings()[:3]

    deep_link_training_url = \
        campaign.get_deep_link_advice(user.user_id, project, 'training')

    return campaign.get_default_coaching_email_vars(user) | {
        'deepTrainingAdviceUrl':
        deep_link_training_url,
        'inDepartement':
        scoring_project.populate_template('%inDepartement'),
        'loginUrl':
        campaign.create_logged_url(user.user_id,
                                   f'/projet/{project.project_id}'),
        'numTrainings':
        len(trainings),
        'ofJobName':
        scoring_project.populate_template('%ofJobName'),
        'trainings': [json_format.MessageToDict(t) for t in trainings],
    }
Ejemplo n.º 6
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,
    }
Ejemplo n.º 7
0
def _get_galita2_short_vars(user: user_pb2.User,
                            **unused_kwargs: Any) -> dict[str, str]:
    if not user.projects:
        raise scoring.NotEnoughDataException(
            'Project is required for galita-2-short.',
            fields={'user.projects.0.kind'})
    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:
        raise campaign.DoNotSend(
            'User is not searching a job in a profession new to them.')
    return campaign.get_default_coaching_email_vars(user)
Ejemplo n.º 8
0
def _get_short_spontaneous_vars(user: user_pb2.User, *, now: datetime.datetime,
                                database: mongo.NoPiiMongoDatabase,
                                **unused_kwargs: Any) -> dict[str, str]:

    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_group_info = scoring_project.job_group_info()

    why_specific_company = job_group_info.why_specific_company
    if not why_specific_company:
        why_specific_company = scoring_project.translate_static_string(
            'vous vous reconnaissez dans leurs valeurs, leur équipe, leur service client ou ce '
            "qu'elles vendent")

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

    if (user.profile.locale or 'fr').startswith('fr'):
        advice_page_url = 'https://labonneboite.pole-emploi.fr/comment-faire-une-candidature-spontanee'
    elif user.profile.locale.startswith('en'):
        advice_page_url = 'https://www.theguardian.com/careers/speculative-applications'
    else:
        logging.warning(
            'No advice webpage given for campaign spontaneous-short in "%s"',
            user.profile.locale)
        advice_page_url = ''

    # If the user receives the email less than 2 months after they registered on Bob and are
    # searching for less than 3 months, we can be happily surprised if they found a job.
    is_job_found_surprising = scoring_project.get_search_length_now() < 3 and \
        (scoring_project.details.created_at.ToDatetime() - now).days / 30 < 2

    return campaign.get_default_coaching_email_vars(user) | {
        'advicePageUrl':
        advice_page_url,
        'atVariousCompanies':
        job_group_info.at_various_companies,
        'isJobFoundSurprising':
        campaign.as_template_boolean(is_job_found_surprising),
        'someCompanies':
        some_companies,
        'whySpecificCompany':
        why_specific_company,
    }
def _get_prepare_your_application_vars(user: user_pb2.User, **unused_kwargs: Any) -> dict[str, Any]:
    """Compute vars for the "Prepare your application" email."""

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

    project = user.projects[0]

    deep_link_motivation_email_url = \
        campaign.get_deep_link_advice(user.user_id, project, 'motivation-email')

    return campaign.get_default_coaching_email_vars(user) | {
        'deepLinkMotivationEmailUrl': deep_link_motivation_email_url,
        'hasInterviewFrustration':
        campaign.as_template_boolean(user_profile_pb2.INTERVIEW in user.profile.frustrations),
        'hasSelfConfidenceFrustration':
        campaign.as_template_boolean(user_profile_pb2.SELF_CONFIDENCE in user.profile.frustrations),
        'loginUrl': campaign.create_logged_url(user.user_id, f'/projet/{project.project_id}'),
    }
Ejemplo n.º 10
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),
    }
Ejemplo n.º 11
0
def _get_post_covid_vars(user: user_pb2.User, *,
                         database: mongo.NoPiiMongoDatabase,
                         **unused_kwargs: Any) -> dict[str, str]:
    if not user.projects:
        raise scoring.NotEnoughDataException(
            'Project is required.', fields={'user.projects.0.advices'})
    project = user.projects[0]
    scoring_project = scoring.ScoringProject(project, user, database)
    if scoring_project.job_group_info().covid_risk != job_pb2.COVID_RISKY:
        raise campaign.DoNotSend("The user's project job is not covid risky.")
    try:
        network_advice_link = next(
            campaign.get_deep_link_advice(user.user_id, project, a.advice_id)
            for a in project.advices
            if a.advice_id.startswith('network-application'))
    except StopIteration:
        raise campaign.DoNotSend('No network-application advice found for the user.')\
            from None
    return campaign.get_default_coaching_email_vars(user) | {
        'deepLinkAdviceUrl': network_advice_link,
        'ofJobName': scoring_project.populate_template('%ofJobName'),
    }
def _get_prepare_your_application_short_vars(user: user_pb2.User, **unused_kwargs: Any)\
        -> dict[str, Any]:
    """Compute vars for the "Prepare your application short" email."""

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

    if (user.profile.locale or 'fr').startswith('fr'):
        advice_page_url = 'https://labonneboite.pole-emploi.fr/comment-faire-une-candidature-spontanee'
    elif user.profile.locale.startswith('en'):
        advice_page_url = 'https://www.theguardian.com/careers/speculative-applications'
    else:
        logging.warning(
            'No advice webpage given for campaign spontaneous-short in "%s"', user.profile.locale)
        advice_page_url = ''

    return campaign.get_default_coaching_email_vars(user) | {
        'advicePageUrl': advice_page_url,
        'hasInterviewFrustration':
        campaign.as_template_boolean(user_profile_pb2.INTERVIEW in user.profile.frustrations),
        'hasSelfConfidenceFrustration':
        campaign.as_template_boolean(user_profile_pb2.SELF_CONFIDENCE in user.profile.frustrations),
    }
Ejemplo n.º 13
0
def _get_jobbing_vars(
        user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase,
        **unused_kwargs: Any) -> dict[str, Any]:
    """Compute vars for the "Jobbing" email."""

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

    project = user.projects[0]

    if not any(s.strategy_id == 'diploma-free-job' for s in project.opened_strategies):
        raise campaign.DoNotSend(
            'The user has not started a strategy to get a job without a diploma')

    scoring_project = scoring.ScoringProject(project, user, database)
    model = scoring.get_scoring_model('advice-reorient-jobbing')
    if not model:
        raise campaign.DoNotSend('The advice-reorient-jobbing model is not implemented')
    reorient_jobs = typing.cast(
        reorient_jobbing_pb2.JobbingReorientJobs,
        model.get_expanded_card_data(scoring_project),
    ).reorient_jobbing_jobs
    if not reorient_jobs:
        raise campaign.DoNotSend("We didn't find any jobbing jobs to reorient to for the user")

    if project.target_job.name:
        of_job_name = scoring_project.populate_template('%ofJobName')
    else:
        # This is not translated to fr@tu because the email templates are only in fr for now.
        of_job_name = 'de definir votre projet professionnel'

    return campaign.get_default_coaching_email_vars(user) | {
        'inDepartement': scoring_project.populate_template('%inDepartement'),
        'jobs': [{'name': job.name} for job in reorient_jobs],
        'loginUrl': campaign.create_logged_url(user.user_id, f'/projet/{project.project_id}'),
        'ofJobName': of_job_name,
    }
Ejemplo n.º 14
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,
    }
Ejemplo n.º 15
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,
    }
Ejemplo n.º 16
0
def _open_classrooms_vars(user: user_pb2.User, *,
                          database: mongo.NoPiiMongoDatabase,
                          **unused_kwargs: Any) -> dict[str, str]:
    """Template variables for open classrooms email."""

    if user.registered_at.ToDatetime() < _SIX_MONTHS_AGO:
        raise campaign.DoNotSend('User registered less than 6 months ago.')

    age = datetime.date.today().year - user.profile.year_of_birth
    if age < 18:
        raise campaign.DoNotSend(
            'User too young to subscribe to OpenClassrooms.')
    if age > 54:
        raise campaign.DoNotSend(
            'User too old to subscribe to OpenClassrooms.')
    if user.profile.highest_degree > job_pb2.BAC_BACPRO:
        raise campaign.DoNotSend('User might have higher education.')

    if user.employment_status and user.employment_status[
            -1].seeking != user_pb2.STILL_SEEKING:
        raise campaign.DoNotSend('User is no more seeking for a job.')
    if not (user.projects and user.projects[0]):
        raise scoring.NotEnoughDataException('Project is required.',
                                             fields={'user.projects.0.kind'})

    project = user.projects[0]
    if project.kind != project_pb2.REORIENTATION and not (
            project.kind == project_pb2.FIND_A_NEW_JOB
            and project.passionate_level == project_pb2.ALIMENTARY_JOB):
        raise campaign.DoNotSend(
            'User is happy with their job (no reorientation and enthusiastic about their job).'
        )

    has_children = user.profile.family_situation in {
        user_profile_pb2.FAMILY_WITH_KIDS,
        user_profile_pb2.SINGLE_PARENT_SITUATION,
    }

    job_group_info = jobs.get_group_proto(database,
                                          project.target_job.job_group.rome_id)
    if not job_group_info:
        raise scoring.NotEnoughDataException(
            'Requires job group info for the difficulty of applying to this kind of job.'
        )

    return campaign.get_default_coaching_email_vars(user) | {
        'hasAtypicProfile':
        campaign.as_template_boolean(
            user_profile_pb2.ATYPIC_PROFILE in user.profile.frustrations),
        'hasFamilyAndManagementIssue':
        campaign.as_template_boolean(
            has_children
            and user_profile_pb2.TIME_MANAGEMENT in user.profile.frustrations),
        'hasSeniority':
        campaign.as_template_boolean(
            project.seniority > project_pb2.INTERMEDIARY),
        'hasSimpleApplication':
        campaign.as_template_boolean(job_group_info.application_complexity ==
                                     job_pb2.SIMPLE_APPLICATION_PROCESS),
        'isReorienting':
        campaign.as_template_boolean(
            project.kind == project_pb2.REORIENTATION),
        'isFrustratedOld':
        campaign.as_template_boolean(age >= 40
                                     and user_profile_pb2.AGE_DISCRIMINATION
                                     in user.profile.frustrations),
        'ofFirstName':
        french.maybe_contract_prefix('de ', "d'", user.profile.name)
    }
Ejemplo n.º 17
0
 def score(self, project: scoring.ScoringProject) -> float:
     raise scoring.NotEnoughDataException(fields={'projects.0'})
Ejemplo n.º 18
0
            _get_best_departements_for_job_group(rome_id, database),
            project.area_type,
            database,
            scoring_project):
        shown_sections.append('departements')

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

    if months_section := _make_months_section(
            imt.active_months, scoring_project.translate_static_string(_FRENCH_MONTHS)):
        shown_sections.append('months')

    if len(shown_sections) < 3:
        raise scoring.NotEnoughDataException(
            f'Not enough IMT data for this user, only {len(shown_sections)}')

    if _BOB_DEPLOYMENT == 'fr':
        imt_link = 'https://candidat.pole-emploi.fr/marche-du-travail/statistiques?' \
            f'codeMetier={project.target_job.code_ogr}&codeZoneGeographique={departement_id}&' \
            'typeZoneGeographique=DEPARTEMENT'
    elif _BOB_DEPLOYMENT == 'usa':
        imt_link = 'https://www.bls.gov/oes/current/' \
            f'oes{project.target_job.job_group.rome_id.replace("-", "")}.htm'
    else:
        imt_link = ''

    if departement_id:
        in_departement = geo.get_in_a_departement_text(
            database, departement_id,
            locale=scoring_project.user_profile.locale, city_hint=project.city)