Esempio n. 1
0
def _find_best_departements(unused_: Any, project: scoring_base.ScoringProject) \
        -> list[project_pb2.DepartementScore]:
    """Find which are the best departement to relocate for a given job group."""

    own_departement_offers = project.imt_proto(
    ).yearly_avg_offers_per_10_candidates

    # If we do not have data about our own departement, we choose not to say anything.
    if not own_departement_offers:
        return []

    best_departements = project.job_group_info().departement_scores

    result: list[project_pb2.DepartementScore] = []
    for dep in itertools.islice(best_departements, 10):
        if dep.local_stats.imt.yearly_avg_offers_per_10_candidates <= own_departement_offers:
            return result
        offer_ratio = \
            dep.local_stats.imt.yearly_avg_offers_per_10_candidates / own_departement_offers
        result.append(
            project_pb2.DepartementScore(name=project.translate_string(
                geo.get_departement_name(project.database,
                                         dep.departement_id)),
                                         offer_ratio=offer_ratio))

    return result
Esempio n. 2
0
    def score_and_explain(self, project: scoring_base.ScoringProject) \
            -> scoring_base.ExplainedScore:
        """Compute a score for the given ScoringProject."""

        local_jobbing = self.get_local_jobbing(project)
        if len(local_jobbing.reorient_jobbing_jobs) < 2:
            return scoring_base.NULL_EXPLAINED_SCORE
        score_modifier = 0
        reasons: list[str] = []

        if project.details.passionate_level == project_pb2.LIFE_GOAL_JOB:
            score_modifier = -2
            if project.job_group_info().growth_2012_2022 < .1:
                score_modifier = -1
        if score_modifier >= 0:
            reasons.append(
                project.translate_static_string(
                    'votre métier ne vous tient pas trop à cœur'))

        if project.user_profile.highest_degree <= job_pb2.CAP_BEP:
            return scoring_base.ExplainedScore(3 + score_modifier, reasons)
        if project.user_profile.highest_degree <= job_pb2.BAC_BACPRO:
            return scoring_base.ExplainedScore(max(2 + score_modifier, 1),
                                               reasons)
        if project.user_profile.highest_degree <= job_pb2.BTS_DUT_DEUG:
            return scoring_base.ExplainedScore(1, reasons)
        return scoring_base.NULL_EXPLAINED_SCORE
Esempio n. 3
0
def _score_and_explain_after_filters(project: scoring_base.ScoringProject) \
        -> scoring_base.ExplainedScore:
    """A helper function to give a score and an explanation for all advices in the module,
    once some prerequisite filters have been met.
    """

    if project.user_profile.has_car_driving_license != boolean_pb2.FALSE:
        return scoring_base.NULL_EXPLAINED_SCORE
    reasons = []
    license_required = next(
        (license.percent_required
         for license in project.job_group_info().requirements.driving_licenses
         if license.driving_license == job_pb2.CAR), 0)
    if license_required:
        reasons.append(
            project.translate_static_string(
                'le permis est important dans votre métier'))
    score_modifier = 0
    if _license_helps_mobility(project.details):
        reasons.append(
            project.translate_static_string(
                'le permis augmenterait votre mobilité'))
        score_modifier = 1
    if not reasons:
        return scoring_base.NULL_EXPLAINED_SCORE
    score = min(
        3,
        score_modifier + (
            # Example at 80% is civil engineer F1106.
            3 if license_required > 80 else
            # Example at 67% is translator E1108.
            2 if license_required > 67 else
            # Example at 50% is chiropractor J1408.
            1 if license_required > 50 else 0))
    return scoring_base.ExplainedScore(score, reasons)
Esempio n. 4
0
def _max_monthly_interviews(project: scoring_base.ScoringProject) -> int:
    """Maximum number of monthly interviews one should have."""

    if project.job_group_info(
    ).application_complexity == job_pb2.COMPLEX_APPLICATION_PROCESS:
        return 5
    return 3
Esempio n. 5
0
    def score(self, project: scoring_base.ScoringProject) -> float:
        application_modes = project.job_group_info().application_modes.values()

        missing_fields: Set[str] = set()
        # User's job has no application modes info.
        if not application_modes:
            rome_id = project.details.target_job.job_group.rome_id
            missing_fields.add(
                f'data.job_group_info.{rome_id}.application_modes')
            raise scoring_base.NotEnoughDataException(
                "User's job has no application modes info. We cannot say whether this is blocking.",
                fields=missing_fields)

        user_application_mode = project.details.preferred_application_mode
        if not user_application_mode:
            missing_fields.add('projects.0.preferredApplicationMode')
            raise scoring_base.NotEnoughDataException(
                "Missing some information about user's application modes",
                fields=missing_fields)

        first_modes = project.get_fap_modes()
        second_modes = project.get_fap_modes(rank='second')

        if user_application_mode in first_modes:
            # User uses the correct application mode.
            return 0

        if user_application_mode in second_modes:
            # User uses one of the best application modes.
            return 1

        return 3
Esempio n. 6
0
    def score_to_hundred(self, project: scoring_base.ScoringProject) -> float:
        """Compute a percentage score for the given ScoringProject."""

        growth_2012_2022 = project.job_group_info().growth_2012_2022
        if not growth_2012_2022:
            raise scoring_base.NotEnoughDataException()
        return _interpolate_points(growth_2012_2022, [(-.17, 0), (.07, 50),
                                                      (.29, 100)])
Esempio n. 7
0
    def score_and_explain(self, project: scoring_base.ScoringProject) \
            -> scoring_base.ExplainedScore:
        """Compute a score for the given ScoringProject."""

        has_any_covid_risk_info = jobs.has_covid_risk_info(project.database)
        has_any_automation_risk_info = jobs.has_automation_risk_info(
            project.database)
        if not has_any_covid_risk_info and not has_any_automation_risk_info:
            raise scoring_base.NotEnoughDataException(
                'No data about jobs being affected by Covid or automation', {
                    'data.job_group_info.covid_risk',
                    'data.job_group_info.automation_risk'
                })

        # Total risk from 0 to 100.
        total_risk = 0

        # Covid risk: 0 if safe or no covid data at all, 25 if unknown, 50 if risky.
        covid_risk = project.job_group_info().covid_risk
        if covid_risk == job_pb2.COVID_RISKY:
            total_risk += 50
        elif not covid_risk and has_any_covid_risk_info:
            total_risk += 25

        # Automation risk: 0 if super safe or no covid data at all, 25 if unknown, 50 if very risky.
        automation_risk = project.job_group_info().automation_risk
        if automation_risk:
            total_risk += automation_risk // 2
        elif has_any_automation_risk_info:
            total_risk += 25

        if total_risk <= 15:
            # This job is as safe as it can be, no need to explore for more.
            return scoring_base.NULL_EXPLAINED_SCORE

        # 81+ => 3
        return scoring_base.ExplainedScore(min((total_risk - 15) / 22, 3), [
            project.translate_static_string(
                "il existe des métiers avec peu de risques d'automatisation",
            ),
        ])
Esempio n. 8
0
 def score_to_hundred(self, project: scoring_base.ScoringProject) -> int:
     # Only score if user is not sure about this.
     if (project.details.training_fulfillment_estimate
             is not project_pb2.TRAINING_FULFILLMENT_NOT_SURE):
         raise scoring_base.NotEnoughDataException()
     max_requirement = -1
     for diploma in project.job_group_info().requirements.diplomas:
         if diploma.percent_required > max_requirement:
             max_requirement = diploma.percent_required
     if max_requirement >= 0:
         return 100 - max_requirement
     raise scoring_base.NotEnoughDataException()
Esempio n. 9
0
    def _filter(self, project: scoring_base.ScoringProject) -> bool:
        """the filtering function for a given ScoringProject."""

        application_modes = project.job_group_info().application_modes.values()
        for fap_modes in application_modes:
            if fap_modes.modes and fap_modes.modes[
                    0].mode == self.application_mode:
                return True
            if len(fap_modes.modes) > 1 \
                    and fap_modes.modes[1].mode == self.application_mode \
                    and fap_modes.modes[0].percentage - fap_modes.modes[1].percentage <= \
                    self.delta_percentage:
                return True

        return False
Esempio n. 10
0
    def score_and_explain(self, project: scoring_base.ScoringProject) \
            -> scoring_base.ExplainedScore:
        """Compute a score for the given ScoringProject."""

        if project.details.network_estimate != self._network_level:
            return scoring_base.NULL_EXPLAINED_SCORE

        application_modes = project.job_group_info().application_modes.values()
        first_modes = set(fap_modes.modes[0].mode
                          for fap_modes in application_modes)
        first_modes.discard(job_pb2.UNDEFINED_APPLICATION_MODE)
        if first_modes == {job_pb2.PERSONAL_OR_PROFESSIONAL_CONTACTS}:
            return scoring_base.ExplainedScore(3, [
                'le réseau est le canal n°1 pour trouver un métier %inDomain'
            ])
        return scoring_base.ExplainedScore(2, [])
Esempio n. 11
0
def _get_handcrafted_job_requirements(project: scoring_base.ScoringProject) \
        -> Optional[job_pb2.JobRequirements]:
    """Handcrafted job requirements for the target job."""

    handcrafted_requirements = job_pb2.JobRequirements()
    all_requirements = project.job_group_info().requirements
    handcrafted_fields = [
        field for field in job_pb2.JobRequirements.DESCRIPTOR.fields_by_name.keys()
        if field.endswith('_short_text')]
    has_requirements = False
    for field in handcrafted_fields:
        field_requirements = getattr(all_requirements, field)
        if field_requirements:
            has_requirements = True
            setattr(handcrafted_requirements, field, field_requirements)
    if not has_requirements:
        return None
    return handcrafted_requirements
Esempio n. 12
0
    def score_and_explain(self, project: scoring_base.ScoringProject) \
            -> scoring_base.ExplainedScore:
        """Compute a score for the given ScoringProject."""

        application_modes = project.job_group_info().application_modes.values()
        first_modes = set(fap_modes.modes[0].mode
                          for fap_modes in application_modes)
        first_modes.discard(job_pb2.UNDEFINED_APPLICATION_MODE)
        if first_modes == {job_pb2.PERSONAL_OR_PROFESSIONAL_CONTACTS}:
            return scoring_base.ExplainedScore(2, [
                project.translate_string(
                    'les embauches se font surtout par les contacts personnels ou professionnels dans'
                    ' votre métier')
            ])

        return scoring_base.ExplainedScore(1, [
            project.translate_string(
                "c'est un bon moyen d'étendre votre réseau")
        ])
Esempio n. 13
0
 def score(self, project: scoring_base.ScoringProject) -> float:
     if not project.details.target_job.job_group.rome_id:
         raise scoring_base.NotEnoughDataException(
             'Need a job group to determine if the user has enough diplomas',
             {'projects.0.targetJob.jobGroup.romeId'})
     required_diploma = min(
         (r.diploma.level for r in project.job_group_info().requirements.diplomas),
         default=job_pb2.UNKNOWN_DEGREE) or \
         _DIPLOMA_REQUIREMENTS.get(project.details.target_job.job_group.rome_id[:5])
     # TODO(pascal): Check the is_diploma_strictly_required bool.
     if not required_diploma:
         raise scoring_base.NotEnoughDataException(
             'No information about this job group diploma requirements',
             {'data.job_group_info.requirements.diplomas'})
     if required_diploma == job_pb2.NO_DEGREE:
         return 0
     if project.user_profile.highest_degree >= required_diploma:
         return 0
     return 3
Esempio n. 14
0
    def score_and_explain(self, project: scoring_base.ScoringProject) \
            -> scoring_base.ExplainedScore:
        """Compute a score for the given ScoringProject."""

        application_modes = project.job_group_info().application_modes.values()
        first_modes = set(fap_modes.modes[0].mode
                          for fap_modes in application_modes
                          if len(fap_modes.modes))
        if job_pb2.SPONTANEOUS_APPLICATION in first_modes:
            return scoring_base.ExplainedScore(3, [
                project.translate_string(
                    "c'est le canal de recrutement n°1 pour votre métier")
            ])

        # In the category missing-diploma, we always have the alternance strategy which requires
        # spontaneous application data.
        if project.details.diagnostic.category_id == 'missing-diploma':
            return scoring_base.ExplainedScore(2, [
                project.translate_string(
                    "c'est le meilleur moyen de trouver un contrat en alternance"
                )
            ])

        second_modes = set(fap_modes.modes[1].mode
                           for fap_modes in application_modes
                           if len(fap_modes.modes) > 1)
        if job_pb2.SPONTANEOUS_APPLICATION in second_modes:
            return scoring_base.ExplainedScore(2, [
                project.translate_string(
                    "c'est un des meilleurs canaux de recrutement pour votre métier"
                )
            ])

        if project.details.diagnostic.category_id == 'bravo' and \
                user_pb2.NO_OFFERS in project.user_profile.frustrations:
            return scoring_base.ExplainedScore(2, [
                project.translate_string(
                    "vous nous avez dit ne pas trouver assez d'offres.")
            ])
        return scoring_base.NULL_EXPLAINED_SCORE
Esempio n. 15
0
    def _get_skills(self, project: scoring_base.ScoringProject) \
            -> Sequence[skill_pb2.Skill]:
        """Return a list of skills recommendation for the project's target job."""

        return project.job_group_info().skills_for_future
Esempio n. 16
0
def _what_i_love_about(project: scoring_base.ScoringProject) -> str:
    return project.user_profile.gender == user_profile_pb2.FEMININE and \
        project.job_group_info().what_i_love_about_feminine or \
        project.job_group_info().what_i_love_about