Beispiel #1
0
    def test_advice_job_boards_extra_data(self):
        """Test that the advisor computes extra data for the "Find a Job Board" advice."""
        project = project_pb2.Project(
            target_job=job_pb2.Job(job_group=job_pb2.JobGroup(
                rome_id='A1234')),
            mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                departement_id='14')),
            job_search_length_months=7,
            weekly_applications_estimate=project_pb2.A_LOT,
            total_interview_count=1,
        )
        self.database.jobboards.insert_one({
            'title': 'Indeed',
            'filters': ['for-departement(14)']
        })
        self.database.advice_modules.insert_one({
            'adviceId': 'job-boards',
            'triggerScoringModel': 'advice-job-boards',
            'extraDataFieldName': 'job_boards_data',
            'isReadyForProd': True,
        })
        advisor.clear_cache()

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices
                      if a.advice_id == 'job-boards')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual('Indeed', advice.job_boards_data.job_board_title)
        self.assertFalse(advice.job_boards_data.is_specific_to_job_group)
        self.assertTrue(advice.job_boards_data.is_specific_to_region)
Beispiel #2
0
    def test_categorize_advice_missing(self):
        """Test that the advisor does not choke on an ancient advice that does not exist anymore."""

        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
            advices=[
                project_pb2.Advice(advice_id='spontaneous-application',
                                   status=project_pb2.ADVICE_RECOMMENDED,
                                   num_stars=2),
                project_pb2.Advice(advice_id='advice-does-no-exist-anymore',
                                   status=project_pb2.ADVICE_RECOMMENDED,
                                   num_stars=2),
            ])
        self.database.advice_modules.insert_many([
            {
                'adviceId': 'spontaneous-application',
                'categories': ['first', 'second'],
                'triggerScoringModel': 'constant(2)',
                'isReadyForProd': True,
            },
        ])

        advisor.maybe_categorize_advice(self.user, project, self.database)
        # Point check that some categorization happened.
        self.assertEqual(['spontaneous-application'],
                         project.advice_categories[0].advice_ids)
Beispiel #3
0
    def test_advice_other_work_env_extra_data(self):
        """Test that the advisor computes extra data for the work environment advice."""
        project = project_pb2.Project(target_job=job_pb2.Job(
            job_group=job_pb2.JobGroup(rome_id='A1234')), )
        self.user.features_enabled.alpha = True
        self.database.job_group_info.insert_one({
            '_id': 'A1234',
            'workEnvironmentKeywords': {
                'structures': ['A', 'B'],
                'sectors': ['sector Toise', 'sector Gal'],
            },
        })
        self.database.advice_modules.insert_one({
            'adviceId': 'other-work-env',
            'triggerScoringModel': 'advice-other-work-env',
            'extraDataFieldName': 'other_work_env_advice_data',
            'isReadyForProd': True,
        })

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices
                      if a.advice_id == 'other-work-env')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual(['A', 'B'], advice.other_work_env_advice_data.
                         work_environment_keywords.structures)
        self.assertEqual([
            'sector Toise', 'sector Gal'
        ], advice.other_work_env_advice_data.work_environment_keywords.sectors)
Beispiel #4
0
    def test_advice_spontaneous_application_extra_data(self,
                                                       mock_get_lbb_companies):
        """Test that the advisor computes extra data for the "Spontaneous Application" advice."""

        project = project_pb2.Project(
            target_job=job_pb2.Job(job_group=job_pb2.JobGroup(
                rome_id='A1234')),
            mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                departement_id='14')),
            job_search_length_months=7,
            weekly_applications_estimate=project_pb2.A_LOT,
            total_interview_count=1,
        )
        self.database.job_group_info.insert_one({
            '_id': 'A1234',
            'applicationModes': {
                'R4Z92': {
                    'modes': [{
                        'percentage': 36.38,
                        'mode': 'SPONTANEOUS_APPLICATION'
                    }, {
                        'percentage': 29.46,
                        'mode': 'PERSONAL_OR_PROFESSIONAL_CONTACTS'
                    }, {
                        'percentage': 18.38,
                        'mode': 'PLACEMENT_AGENCY'
                    }, {
                        'percentage': 15.78,
                        'mode': 'UNDEFINED_APPLICATION_MODE'
                    }],
                }
            },
        })
        self.database.advice_modules.insert_one({
            'adviceId': 'my-advice',
            'categories': ['first'],
            'triggerScoringModel': 'advice-spontaneous-application',
            'extraDataFieldName': 'spontaneous_application_data',
            'isReadyForProd': True,
        })
        mock_get_lbb_companies.return_value = iter([
            {
                'name': 'EX NIHILO'
            },
            {
                'name': 'M.F.P MULTIMEDIA FRANCE PRODUCTIONS'
            },
        ])
        advisor.clear_cache()

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices if a.advice_id == 'my-advice')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual(
            ['EX NIHILO', 'M.F.P MULTIMEDIA FRANCE PRODUCTIONS'],
            [c.name for c in advice.spontaneous_application_data.companies])
Beispiel #5
0
    def compute_extra_data(self, project):
        """Compute extra data for this module to render a card in the client."""

        # The number of job names is restricted to 2 because some job names
        # are very long e.g. Conducteur / Conductrice d'engins d'exploitation.
        recommended_jobs = self.get_local_jobbing(
            project).reorient_jobbing_jobs[:2]
        return project_pb2.ReorientData(
            jobs=[job_pb2.Job(name=job.name) for job in recommended_jobs])
    def test_advice_better_job_in_group_extra_data(self):
        """Test that the advisor computes extra data for the "Better Job in Group" advice."""
        project = project_pb2.Project(
            target_job=job_pb2.Job(
                code_ogr='1234', job_group=job_pb2.JobGroup(rome_id='A1234')),
            mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                departement_id='14')),
            job_search_length_months=7,
            weekly_applications_estimate=project_pb2.A_LOT,
            total_interview_count=1,
        )
        self.database.job_group_info.insert_one({
            '_id':
            'A1234',
            'jobs': [
                {
                    'codeOgr': '1234',
                    'name': 'Pilote'
                },
                {
                    'codeOgr': '5678',
                    'name': 'Pompier'
                },
                {
                    'codeOgr': '9012',
                    'name': 'Facteur'
                },
            ],
            'requirements': {
                'specificJobs': [
                    {
                        'codeOgr': '5678',
                        'percentSuggested': 55,
                    },
                    {
                        'codeOgr': '1234',
                        'percentSuggested': 45,
                    },
                ],
            },
        })
        self.database.advice_modules.insert_one({
            'adviceId': 'my-advice',
            'triggerScoringModel': 'advice-better-job-in-group',
            'extraDataFieldName': 'better_job_in_group_data',
            'isReadyForProd': True,
        })
        advisor.clear_cache()

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices if a.advice_id == 'my-advice')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual('Pompier',
                         advice.better_job_in_group_data.better_job.name)
        self.assertEqual(1, advice.better_job_in_group_data.num_better_jobs)
Beispiel #7
0
    def test_advice_specific_to_job_override(self) -> None:
        """Test that the advisor overrides some advice data with the "Specific to Job" module."""

        project = project_pb2.Project(target_job=job_pb2.Job(
            job_group=job_pb2.JobGroup(rome_id='D1102')), )
        self.database.advice_modules.insert_one({
            'adviceId': 'custom-advice-id',
            'categories': ['first'],
            'triggerScoringModel': 'advice-specific-to-job',
            'isReadyForProd': True,
        })
        self.database.specific_to_job_advice.insert_one({
            'title':
            'Présentez-vous au chef boulanger dès son arrivée tôt le matin',
            'shortTitle':
            'Astuces de boulanger',
            'goal':
            'impressionner le patron',
            'diagnosticTopics': [
                diagnostic_pb2.MARKET_DIAGNOSTIC,
                diagnostic_pb2.PROJECT_DIAGNOSTIC
            ],
            'filters': ['for-job-group(D1102)', 'not-for-job(12006)'],
            'cardText':
            'Allez à la boulangerie la veille pour savoir à quelle '
            'heure arrive le chef boulanger.',
            'expandedCardHeader':
            "Voilà ce qu'il faut faire",
            'expandedCardItems': [
                'Se présenter aux boulangers entre 4h et 7h du matin.',
                'Demander au vendeur / à la vendeuse à quelle heure arrive le chef le matin',
                'Contacter les fournisseurs de farine locaux : ils connaissent '
                'tous les boulangers du coin et sauront où il y a des '
                'embauches.',
            ],
        })

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices
                      if a.advice_id == 'custom-advice-id')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual(
            'Présentez-vous au chef boulanger dès son arrivée tôt le matin',
            advice.title)
        self.assertEqual("Voilà ce qu'il faut faire",
                         advice.expanded_card_header)
        self.assertTrue(advice.card_text)
        self.assertTrue(advice.expanded_card_items)
        self.assertEqual('Astuces de boulanger', advice.short_title)
        self.assertEqual([
            diagnostic_pb2.MARKET_DIAGNOSTIC, diagnostic_pb2.PROJECT_DIAGNOSTIC
        ], advice.diagnostic_topics)
        self.assertEqual('impressionner le patron', advice.goal)
Beispiel #8
0
    def test_advice_spontaneous_application_extra_data(
            self, mock_get_lbb_companies: mock.MagicMock) -> None:
        """Test that the advisor computes extra data for the "Spontaneous Application" advice."""

        persona = self._random_persona().clone()
        persona.project.CopyFrom(
            project_pb2.Project(
                target_job=job_pb2.Job(job_group=job_pb2.JobGroup(
                    rome_id='A1234')),
                weekly_applications_estimate=project_pb2.A_LOT,
                employment_types=[job_pb2.CDI],
                total_interview_count=1,
            ))
        persona.project.job_search_started_at.FromDatetime(
            persona.project.created_at.ToDatetime() -
            datetime.timedelta(days=210))
        persona.project.city.departement_id = '14'

        self.database.job_group_info.insert_one({
            '_id': 'A1234',
            'applicationModes': {
                'R4Z92': {
                    'modes': [{
                        'percentage': 36.38,
                        'mode': 'SPONTANEOUS_APPLICATION'
                    }, {
                        'percentage': 29.46,
                        'mode': 'PERSONAL_OR_PROFESSIONAL_CONTACTS'
                    }, {
                        'percentage': 18.38,
                        'mode': 'PLACEMENT_AGENCY'
                    }, {
                        'percentage': 15.78,
                        'mode': 'UNDEFINED_APPLICATION_MODE'
                    }],
                }
            },
        })
        mock_get_lbb_companies.return_value = iter([
            {
                'name': 'EX NIHILO'
            },
            {
                'name': 'M.F.P MULTIMEDIA FRANCE PRODUCTIONS'
            },
        ])

        extra_data = typing.cast(project_pb2.SpontaneousApplicationData,
                                 self._compute_expanded_card_data(persona))
        self.assertEqual(['EX NIHILO', 'M.F.P MULTIMEDIA FRANCE PRODUCTIONS'],
                         [c.name for c in extra_data.companies])
        self.assertEqual(10, extra_data.max_distance_to_companies_km)
Beispiel #9
0
    def test_categorize_all_pieces_of_advice(self):
        """Test that the advisor categorize all advice modules."""

        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
            advices=[
                project_pb2.Advice(advice_id='spontaneous-application',
                                   status=project_pb2.ADVICE_RECOMMENDED,
                                   num_stars=3),
                project_pb2.Advice(advice_id='other-work-env',
                                   status=project_pb2.ADVICE_RECOMMENDED,
                                   num_stars=2),
                project_pb2.Advice(advice_id='one-star',
                                   status=project_pb2.ADVICE_RECOMMENDED,
                                   num_stars=1),
            ])
        self.database.advice_modules.insert_many([
            {
                'adviceId': 'spontaneous-application',
                'categories': ['first', 'second'],
                'triggerScoringModel': 'constant(2)',
                'isReadyForProd': True,
            },
            {
                'adviceId': 'other-work-env',
                'categories': ['second'],
                'triggerScoringModel': 'constant(0)',
                'isReadyForProd': True,
            },
            {
                'adviceId': 'one-star',
                'categories': ['second'],
                'triggerScoringModel': 'constant(0)',
                'isReadyForProd': True,
            },
        ])

        advisor.maybe_categorize_advice(self.user, project, self.database)
        # First category is the category with all the 3-stars advice.
        self.assertEqual(['spontaneous-application'],
                         project.advice_categories[0].advice_ids)
        self.assertEqual('three-stars',
                         project.advice_categories[0].category_id)
        self.assertEqual(
            ['other-work-env', 'spontaneous-application', 'one-star'],
            project.advice_categories[1].advice_ids)
        self.assertEqual('second', project.advice_categories[1].category_id)
    def compute_extra_data(self, project):
        """Compute extra data for this module to render a card in the client."""

        # The number of job names is restricted to 2.
        all_close_jobs = self.get_close_jobs(project)
        recommended_jobs = itertools.islice(
            itertools.chain(all_close_jobs.close_jobs,
                            all_close_jobs.evolution_jobs), 2)
        return project_pb2.ReorientData(jobs=[
            job_pb2.Job(
                name=french.genderize_job(job, project.user_profile.gender))
            for job in recommended_jobs
        ])
Beispiel #11
0
    def find_job(self, job_title: str) -> Optional[job_pb2.Job]:
        """Find a job in Algolia index by its name."""

        try:
            return job_pb2.Job(
                name=job_title,
                job_group=job_pb2.JobGroup(rome_id=self._mapping[job_title]))
        except KeyError:
            logging.exception('Could not find job "%s" in Excel file',
                              job_title)
            self._missed.add(job_title)

        return None
Beispiel #12
0
    def test_generate_actions(self) -> None:
        """Test that the advisor generates actions if the action_plan feature is active."""

        self.database.action_templates.insert_many([
            {
                'actionTemplateId': 'review-me',
                'triggerScoringModel': 'constant(1)',
                'tags': ['chrome-tool'],
                'duration': 'FIFTEEN_TO_30_MIN',
                'resourceUrl': 'https://bar',
            },
            {
                'actionTemplateId': 'not-for-you',
                'triggerScoringModel': 'constant(0)',
            },
            {
                'actionTemplateId': 'finish-the-sprint',
                'triggerScoringModel': 'constant(2)',
            },
        ])
        self.database.translations.insert_many([
            {'string': 'actionTemplates:review-me:title', 'en': 'Review me!'},
            {'string': 'actionTemplates:review-me:short_description', 'en': 'Please review me now'},
            {
                'string': 'actionTemplates:review-me:short_description_FEMININE',
                'en': 'Please review me now, Madam',
            },
            {'string': 'actionTemplates:tags:chrome-tool', 'en': 'Chrome Tool'},
            {'string': 'actionTemplates:review-me:resource_url_usa', 'en': 'https://foo'},
        ])
        self.user.features_enabled.action_plan = features_pb2.ACTIVE
        self.user.profile.locale = 'en'
        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
        )

        advisor.maybe_advise(self.user, project, self.database)

        self.assertEqual(['finish-the-sprint', 'review-me'], [a.action_id for a in project.actions])
        review_me = project.actions[1]
        self.assertEqual('Review me!', review_me.title)
        self.assertEqual('Please review me now, Madam', review_me.short_description)
        self.assertEqual(['Chrome Tool'], review_me.tags)
        self.assertEqual(action_pb2.FIFTEEN_TO_30_MIN, review_me.duration)
        self.assertEqual('https://foo', review_me.resource_url)
    def test_advice_improve_success_rate_extra_data(self):
        """Test that the advisor computes extra data for the "Improve Success Rate" advice."""
        project = project_pb2.Project(
            target_job=job_pb2.Job(job_group=job_pb2.JobGroup(
                rome_id='A1234')),
            mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                departement_id='14')),
            job_search_length_months=6,
            weekly_applications_estimate=project_pb2.A_LOT,
            total_interview_count=1,
        )
        self.database.local_diagnosis.insert_one({
            '_id': '14:A1234',
            'imt': {
                'yearlyAvgOffersDenominator': 10,
                'yearlyAvgOffersPer10Candidates': 2,
            },
        })
        self.database.job_group_info.insert_one({
            '_id': 'A1234',
            'requirements': {
                'skills': [{
                    'name': 'Humour'
                }, {
                    'name': 'Empathie'
                }],
                'skillsShortText': '**Humour** et **empathie**',
            },
        })
        self.database.advice_modules.insert_one({
            'adviceId': 'improve-success',
            'triggerScoringModel': 'advice-improve-resume',
            'extraDataFieldName': 'improve_success_rate_data',
            'isReadyForProd': True,
        })
        advisor.clear_cache()

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices
                      if a.advice_id == 'improve-success')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertGreater(
            advice.improve_success_rate_data.num_interviews_increase, 50)
        self.assertFalse(advice.improve_success_rate_data.requirements.skills)
        self.assertEqual(
            '**Humour** et **empathie**',
            advice.improve_success_rate_data.requirements.skills_short_text)
Beispiel #14
0
    def test_find_all_pieces_of_advice(self, mock_now: mock.MagicMock) -> None:
        """Test that the advisor scores all advice modules."""

        mock_now.return_value = datetime.datetime(2018, 6, 10)

        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
            actions=[
                action_pb2.Action(
                    action_id='be-happy',
                    title='Be happy',
                    status=action_pb2.ACTION_UNREAD,
                ),
                action_pb2.Action(
                    action_id='dont-worry',
                    title="Don't worry",
                    status=action_pb2.ACTION_CURRENT,
                ),
                action_pb2.Action(
                    action_id='sing-along',
                    title='Sing along',
                    status=action_pb2.ACTION_UNREAD,
                ),
            ],
        )
        self.database.advice_modules.insert_many([
            {
                'adviceId': 'spontaneous-application',
                'categories': ['first'],
                'triggerScoringModel': 'constant(2)',
                'isReadyForProd': True,
            },
            {
                'adviceId': 'other-work-env',
                'categories': ['first'],
                'triggerScoringModel': 'constant(0)',
                'isReadyForProd': True,
            },
        ])

        advisor.maybe_advise(self.user, project, self.database)
        self.assertEqual(['spontaneous-application'], [a.advice_id for a in project.advices])
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, project.advices[0].status)
Beispiel #15
0
def get_job_proto(database, job_id, rome_id):
    """Get a Job proto corresponding to the job ID if it is found in the ROME job group."""

    job_group = get_group_proto(database, rome_id)
    if not job_group or not job_id:
        return None

    for job_proto in job_group.jobs:
        if job_proto.code_ogr == job_id:
            job = job_pb2.Job()
            job.CopyFrom(job_proto)
            job.job_group.rome_id = job_group.rome_id
            job.job_group.name = job_group.name
            return job

    return None
    def test_advice_events_extra_data(self, mock_now):
        """Test that the advisor computes extra data for the "Events" advice."""
        mock_now.return_value = datetime.datetime(2017, 8, 15)
        project = project_pb2.Project(
            target_job=job_pb2.Job(
                code_ogr='1234', job_group=job_pb2.JobGroup(rome_id='A1234')),
            mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                departement_id='75')),
            job_search_length_months=7,
            weekly_applications_estimate=project_pb2.A_LOT,
            total_interview_count=1,
        )
        self.database.advice_modules.insert_one({
            'adviceId': 'my-advice',
            'triggerScoringModel': 'advice-event',
            'extraDataFieldName': 'events_data',
            'isReadyForProd': True,
        })
        self.database.events.insert_many([
            {
                'title': 'AP HEROS CANDIDATS MADIRCOM - BORDEAUX',
                'link':
                'https://www.workuper.com/events/ap-heros-candidats-madircom-bordeaux',
                'organiser': 'MADIRCOM',
                'startDate': '2017-08-29',
            },
            {
                'title':
                'Le Salon du Travail et de la Mobilité Professionnelle',
                'link':
                'https://www.workuper.com/events/le-salon-du-travail-et-de-la-mobilite-'
                'professionnelle',
                'organiser':
                'Altice Media Events',
                'startDate':
                '2018-01-19',
            },
        ])
        advisor.clear_cache()
        self.user.features_enabled.alpha = True

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices if a.advice_id == 'my-advice')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual('AP HEROS CANDIDATS MADIRCOM - BORDEAUX',
                         advice.events_data.event_name)
Beispiel #17
0
    def test_find_all_pieces_of_advice(self, mock_send_template):
        """Test that the advisor scores all advice modules."""

        mock_send_template().status_code = 200
        mock_send_template.reset_mock()
        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
        )
        self.database.advice_modules.insert_many([
            {
                'adviceId': 'spontaneous-application',
                'categories': ['first'],
                'triggerScoringModel': 'constant(2)',
                'isReadyForProd': True,
            },
            {
                'adviceId': 'other-work-env',
                'categories': ['first'],
                'triggerScoringModel': 'constant(0)',
                'isReadyForProd': True,
            },
        ])

        advisor.maybe_advise(self.user, project, self.database,
                             'http://base.example.com')

        self.assertEqual(['spontaneous-application'],
                         [a.advice_id for a in project.advices])
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED,
                         project.advices[0].status)

        mock_send_template.assert_called_once()
        data = mock_send_template.call_args[0][2]
        self.assertEqual(
            ['advices', 'baseUrl', 'firstName', 'ofProjectTitle', 'projectId'],
            sorted(data.keys()))
        self.assertEqual('http://base.example.com', data['baseUrl'])
        self.assertEqual('Margaux', data['firstName'])
        self.assertEqual("d'hôtesse", data['ofProjectTitle'])
        self.assertEqual('1234', data['projectId'])
Beispiel #18
0
    def test_jobgroup_info_fr_locale(self) -> None:
        """A scoring project can be represented as a meaningful string."""

        user = user_pb2.User()
        user.profile.locale = ''
        database = mongomock.MongoClient().test
        database.job_group_info.insert_many([{
            '_id': 'A1234',
            'name': 'french'
        }, {
            '_id': 'nl:A1234',
            'name': 'dutch'
        }])
        project = project_pb2.Project(target_job=job_pb2.Job(
            job_group=job_pb2.JobGroup(rome_id='A1234')))
        scoring_project = scoring.ScoringProject(project, user, database)
        job_group_info = scoring_project.job_group_info()
        self.assertEqual('french', job_group_info.name)
Beispiel #19
0
    def test_avoid_double_first_advice(self):
        """The advisor avoids having the same first advice for two neighbor categories."""

        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
            advices=[
                project_pb2.Advice(advice_id='spontaneous-application',
                                   status=project_pb2.ADVICE_RECOMMENDED,
                                   num_stars=2),
                project_pb2.Advice(advice_id='other-work-env',
                                   status=project_pb2.ADVICE_RECOMMENDED,
                                   num_stars=1)
            ])
        self.database.advice_modules.insert_many([
            {
                'adviceId': 'spontaneous-application',
                'categories': ['first', 'second', 'third'],
                'triggerScoringModel': 'constant(2)',
                'isReadyForProd': True,
            },
            {
                'adviceId': 'other-work-env',
                'categories': ['first', 'second', 'third'],
                'triggerScoringModel': 'constant(0)',
                'isReadyForProd': True,
            },
        ])

        advisor.maybe_categorize_advice(self.user, project, self.database)
        # First category is the category with all the 3-stars advice.
        self.assertEqual(3,
                         len(project.advice_categories),
                         msg=project.advice_categories)
        self.assertEqual(['spontaneous-application', 'other-work-env'],
                         project.advice_categories[0].advice_ids)
        self.assertEqual(['other-work-env', 'spontaneous-application'],
                         project.advice_categories[1].advice_ids)
        self.assertEqual(['spontaneous-application', 'other-work-env'],
                         project.advice_categories[2].advice_ids)
Beispiel #20
0
    def test_advice_specific_to_job_override_i18n(self) -> None:
        """Test that the advisor translate overrides with the "Specific to Job" module."""

        self.user.profile.locale = 'en'
        self.database.translations.insert_many([
            {'string': 'specificToJobAdvice:baker:title', 'en': 'Get there early'},
            {'string': 'Astuces de boulanger', 'en': 'Baker tips'},
        ])
        project = project_pb2.Project(
            target_job=job_pb2.Job(job_group=job_pb2.JobGroup(rome_id='D1102')),
        )
        self.database.advice_modules.insert_one({
            'adviceId': 'custom-advice-id',
            'categories': ['first'],
            'triggerScoringModel': 'advice-specific-to-job',
            'isReadyForProd': True,
        })
        self.database.specific_to_job_advice.insert_one({
            'id': 'baker',
            'title': 'Présentez-vous au chef boulanger dès son arrivée tôt le matin',
            'shortTitle': 'Astuces de boulanger',
            'goal': 'impressionner le patron',
            'filters': ['for-job-group(D1102)', 'not-for-job(12006)'],
            'cardText':
            'Allez à la boulangerie la veille pour savoir à quelle '
            'heure arrive le chef boulanger.',
            'expandedCardHeader': "Voilà ce qu'il faut faire",
            'expandedCardItems': [
                'Se présenter aux boulangers entre 4h et 7h du matin.',
                'Demander au vendeur / à la vendeuse à quelle heure arrive le chef le matin',
                'Contacter les fournisseurs de farine locaux : ils connaissent '
                'tous les boulangers du coin et sauront où il y a des '
                'embauches.',
            ],
        })

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices if a.advice_id == 'custom-advice-id')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual('Get there early', advice.title)
        self.assertEqual('Baker tips', advice.short_title)
    def test_advice_volunteer_extra_data(self):
        """Test that the advisor computes extra data for the "Volunteer" advice."""
        project = project_pb2.Project(
            target_job=job_pb2.Job(
                code_ogr='1234', job_group=job_pb2.JobGroup(rome_id='A1234')),
            mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                departement_id='75')),
            job_search_length_months=7,
            weekly_applications_estimate=project_pb2.A_LOT,
            total_interview_count=1,
        )
        self.database.volunteering_missions.insert_one({
            '_id':
            '75',
            'missions': [
                {
                    'associationName': 'BackUp Rural'
                },
                {
                    'associationName': 'Construisons Ensemble Comment Faire'
                },
            ],
        })
        self.database.advice_modules.insert_one({
            'adviceId': 'my-advice',
            'triggerScoringModel': 'advice-volunteer',
            'extraDataFieldName': 'volunteer_data',
            'isReadyForProd': True,
        })
        advisor.clear_cache()

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices if a.advice_id == 'my-advice')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual(
            ['BackUp Rural', 'Construisons Ensemble Comment Faire'],
            sorted(advice.volunteer_data.association_names))
Beispiel #22
0
    def test_redacted_email_address(self) -> None:
        """Test that we do not send any email if the email address is already redacted."""

        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
        )
        self.database.advice_modules.insert_many([
            {
                'adviceId': 'spontaneous-application',
                'categories': ['first'],
                'triggerScoringModel': 'constant(2)',
                'isReadyForProd': True,
            },
            {
                'adviceId': 'other-work-env',
                'categories': ['first'],
                'triggerScoringModel': 'constant(0)',
                'isReadyForProd': True,
            },
        ])
        self.user.profile.email = 'REDACTED'

        advisor.maybe_advise(self.user, project, self.database,
                             'http://base.example.com')

        self.assertEqual(['spontaneous-application'],
                         [a.advice_id for a in project.advices])
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED,
                         project.advices[0].status)

        mails_sent = mailjetmock.get_all_sent_messages()
        self.assertFalse(mails_sent)
    def test_advice_association_help_extra_data(self):
        """Test that the advisor computes extra data for the "Find an association" advice."""
        project = project_pb2.Project(
            target_job=job_pb2.Job(
                code_ogr='1234', job_group=job_pb2.JobGroup(rome_id='A1234')),
            mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                departement_id='14')),
            job_search_length_months=7,
            weekly_applications_estimate=project_pb2.A_LOT,
            total_interview_count=1,
        )
        self.database.associations.insert_many([
            {
                'name': 'Pôle emploi'
            },
            {
                'name': 'SNC',
                'filters': ['for-departement(14,15,16)']
            },
            {
                'name': 'Ressort',
                'filters': ['for-departement(69)']
            },
        ])
        self.database.advice_modules.insert_one({
            'adviceId': 'my-advice',
            'triggerScoringModel': 'advice-association-help',
            'extraDataFieldName': 'associations_data',
            'isReadyForProd': True,
        })
        advisor.clear_cache()

        advisor.maybe_advise(self.user, project, self.database)

        advice = next(a for a in project.advices if a.advice_id == 'my-advice')
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED, advice.status)
        self.assertEqual('SNC', advice.associations_data.association_name)
Beispiel #24
0
def _find_job(job_title: str) -> Optional[job_pb2.Job]:
    if job_group := _JOB_MAP.get(job_title):
        return job_pb2.Job(name=job_title, job_group=job_group)
Beispiel #25
0
    job_pb2.JobGroup(
        name='Health Informatics Specialists',
        rome_id='15-1211',
    ),
}


@functools.lru_cache()
def _find_job(job_title: str) -> Optional[job_pb2.Job]:
    if job_group := _JOB_MAP.get(job_title):
        return job_pb2.Job(name=job_title, job_group=job_group)
    for finder in _JOB_FINDERS:
        job = finder(job_title)
        if job:
            return job
    return job_pb2.Job(name=job_title)


@functools.lru_cache()
def _find_state(state_name: str) -> geo_pb2.FrenchCity:
    return geo_pb2.FrenchCity(region_id=STATE_MAP[state_name],
                              region_name=state_name)


class _AlgoliaJobFinder:
    def __init__(self) -> None:
        client = search_client.SearchClient.create('K6ACI9BKKT')
        self._index = client.init_index('jobs_en')
        self._missed: set[str] = set()

    def find_job(self, job_title: str) -> Optional[job_pb2.Job]:
Beispiel #26
0
    def test_find_all_pieces_of_advice(self, mock_now: mock.MagicMock) -> None:
        """Test that the advisor scores all advice modules."""

        mock_now.return_value = datetime.datetime(2018, 6, 10)

        project = project_pb2.Project(
            project_id='1234',
            target_job=job_pb2.Job(
                name='Steward/ Hôtesse',
                feminine_name='Hôtesse',
                masculine_name='Steward',
            ),
        )
        self.database.advice_modules.insert_many([
            {
                'adviceId': 'spontaneous-application',
                'categories': ['first'],
                'triggerScoringModel': 'constant(2)',
                'isReadyForProd': True,
            },
            {
                'adviceId': 'other-work-env',
                'categories': ['first'],
                'triggerScoringModel': 'constant(0)',
                'isReadyForProd': True,
            },
        ])

        advisor.maybe_advise(self.user, project, self.database,
                             'http://base.example.com')

        self.assertEqual(['spontaneous-application'],
                         [a.advice_id for a in project.advices])
        self.assertEqual(project_pb2.ADVICE_RECOMMENDED,
                         project.advices[0].status)

        mails_sent = mailjetmock.get_all_sent_messages()
        self.assertEqual(1, len(mails_sent), msg=mails_sent)
        data = mails_sent[0].properties['Variables']
        with open(path.join(_TEMPLATE_PATH, 'activation-email', 'vars.txt'),
                  'r') as vars_file:
            template_vars = {v.strip() for v in vars_file}
        self.assertEqual(template_vars, set(data.keys()))

        self.assertEqual('10 juin 2018', data['date'])
        self.assertEqual('Margaux', data['firstName'])
        self.assertEqual('FEMININE', data['gender'])
        self.assertEqual("d'hôtesse", data['ofJob'])
        login_url = data.pop('loginUrl')
        base_url = re.escape(
            f'http://base.example.com?userId={self.user.user_id}')
        self.assertRegex(login_url, rf'^{base_url}&authToken=\d+\.[a-f0-9]+$')
        email_settings_url = data.pop('changeEmailSettingsUrl')
        base_url = re.escape(
            f'http://base.example.com/unsubscribe.html?user={self.user.user_id}'
        )
        self.assertRegex(
            email_settings_url,
            rf'^{base_url}&auth=\d+\.[a-f0-9]+&coachingEmailFrequency=UNKNOWN_EMAIL_FREQUENCY&'
            r'hl=fr%40tu$')
        self.assertEqual('', data['isCoachingEnabled'])
    def test_new_user(
            self, mock_requests: requests_mock.Mocker, mock_get_job_proto: mock.MagicMock,
            mock_get_city_proto: mock.MagicMock) -> None:
        """Auth request with PE Connect for a new user."""

        def _match_correct_code(request: 'requests_mock._RequestObjectProxy') -> bool:
            return 'code=correct-code' in (request.text or '')

        mock_requests.post(
            'https://authentification-candidat.pole-emploi.fr/connexion/oauth2/access_token?'
            'realm=/individu',
            json={
                'access_token': '123456',
                'nonce': 'correct-nonce',
                'scope':
                    'api_peconnect-individuv1 openid profile email api_peconnect-coordonneesv1 '
                    'coordonnees competences',
                'token_type': 'Bearer',
            },
            additional_matcher=_match_correct_code)
        mock_requests.get(
            'https://api.emploi-store.fr/partenaire/peconnect-competences/v1/competences',
            headers={'Authorization': 'Bearer 123456'},
            json=[
                {
                    'codeAppellation': '86420',
                    'codeRome': 'A1234',
                },
                {
                    'codeAppellation': '86421',
                    'codeRome': 'A1235',
                },
            ],
        )
        mock_requests.get(
            'https://api.emploi-store.fr/partenaire/peconnect-individu/v1/userinfo',
            headers={'Authorization': 'Bearer 123456'},
            json={
                'email': '*****@*****.**',
                'family_name': 'CORPET',
                'gender': 'male',
                'given_name': 'PASCAL',
                'sub': 'pe-connect-user-id-1',
            })
        mock_requests.get(
            'https://api.emploi-store.fr/partenaire/peconnect-coordonnees/v1/coordonnees',
            headers={'Authorization': 'Bearer 123456'},
            json={
                'codeINSEE': '69386',
                'address1': '55 rue du lac',
            })
        mock_get_city_proto.return_value = geo_pb2.FrenchCity(name='Lyon', city_id='69123')
        mock_get_job_proto.return_value = job_pb2.Job(name='Plombier')

        response = self.app.post(
            '/api/user/authenticate',
            data='{"peConnectCode": "correct-code", "peConnectNonce": "correct-nonce"}',
            content_type='application/json')
        auth_response = self.json_from_response(response)

        self.assertTrue(auth_response['isNewUser'])
        user = auth_response['authenticatedUser']
        self.assertEqual('pe-connect-user-id-1', user.get('peConnectId'))
        self.assertTrue(user.get('hasAccount'))
        self.assertEqual('Pascal', user.get('profile', {}).get('name'))
        self.assertEqual('Corpet', user.get('profile', {}).get('lastName'))
        self.assertEqual('MASCULINE', user.get('profile', {}).get('gender'))
        self.assertEqual([True], [p.get('isIncomplete') for p in user.get('projects', [])])
        self.assertEqual('Lyon', user['projects'][0].get('city', {}).get('name'))
        self.assertEqual('69123', user['projects'][0].get('city', {}).get('cityId'))
        self.assertEqual('Plombier', user['projects'][0].get('targetJob', {}).get('name'))
        user_id = user['userId']
        self.assertEqual([user_id], [str(u['_id']) for u in self._user_db.user.find()])

        mock_get_city_proto.assert_called_once_with('69123')
        mock_get_job_proto.assert_called_once()
        self.assertEqual(('86420', 'A1234'), mock_get_job_proto.call_args[0][1:])