Exemple #1
0
    def test_dynamic_sticky_steps(self):
        """Check that dynamic fields of sticky steps are populated."""
        new_action = action_pb2.Action()
        database = mongomock.MongoClient().test
        database.sticky_action_steps.insert_one({
            '_id': 'step1',
            'title': 'Trouver un job de %masculineJobName',
            'content': 'Regarder sur le [ROME](http://go/rome/%romeId).',
            'link': 'http://lbb.fr/city/%cityId/rome/%romeId',
            'linkName': 'Les bonnes boites de %cityName',
            'finishCheckboxCaption': "J'ai trouvé un job de %masculineJobName",
        })
        action.instantiate(
            new_action,
            user_pb2.User(),
            project_pb2.Project(
                mobility=geo_pb2.Location(city=geo_pb2.FrenchCity(
                    city_id='45123',
                    name='Orléans',
                    departement_id='45',
                    region_id='84')),
                target_job=job_pb2.Job(
                    masculine_name='Pompier',
                    code_ogr='78910',
                    job_group=job_pb2.JobGroup(rome_id='A1101', name='Combattants')),
            ),
            action_pb2.ActionTemplate(
                action_template_id='my-sticky',
                step_ids=['step1']),
            set(), database, None)

        step = new_action.steps[0]
        self.assertEqual('http://lbb.fr/city/45123/rome/A1101', step.link)
        self.assertEqual('Regarder sur le [ROME](http://go/rome/A1101).', step.content)
    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)
Exemple #3
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.local_diagnosis.insert_one({
            '_id': '14:A1234',
            'imt': {'applicationModes': {'Foo': {'first': 'SPONTANEOUS_APPLICATION'}}},
        })
        self.database.advice_modules.insert_one({
            'adviceId': 'my-advice',
            'triggerScoringModel': 'chantier-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])
Exemple #4
0
    def _find_closest_events(
            self,
            project: scoring_base.ScoringProject) -> event_pb2.CloseByEvents:
        """Find the events nearest to the project's location."""

        now_date = project.now.strftime('%Y-%m-%d')
        event_collection = [
            event for event in self.event_db.get_collection(project.database)
            if not event.start_date or event.start_date >= now_date
        ]

        city_name = self.find_closest_city_with_events(project)
        if not city_name:
            all_events = list(event_collection)
            target_city = geo.get_city_location(
                project.database,
                project.details.city.city_id) or geo_pb2.FrenchCity()
            if not target_city.latitude:
                return event_pb2.CloseByEvents(
                    events=random.sample(all_events, min(5, len(all_events))))
            sorted_events = sorted(
                all_events,
                key=lambda event: _compute_square_degree_distance(
                    event, target_city))
            return event_pb2.CloseByEvents(events=sorted_events[:5])

        return event_pb2.CloseByEvents(
            city=city_name,
            events=[
                event for event in event_collection
                if event.city_name == city_name
            ],
        )
Exemple #5
0
def get_city_proto(city_id: str) -> Optional[geo_pb2.FrenchCity]:
    """Compute a full FrenchCity proto from a simple city_id."""

    if not city_id:
        return None

    try:
        algolia_city = _get_algolia_index().get_object(city_id)
    except exceptions.AlgoliaException as err:
        logging.warning('Error in algolia: %s for city ID %s', err, city_id)
        return None
    if not algolia_city:
        return None

    # Keep in sync with frontend/src/components/suggestions.jsx
    if 'urban' in algolia_city:
        urban = algolia_city.pop('urban')
        algolia_city['urbanScore'] = urban if urban else -1
    if 'transport' in algolia_city:
        algolia_city['publicTransportationScore'] = algolia_city.pop(
            'transport')
    if 'zipCode' in algolia_city:
        algolia_city['postcodes'] = algolia_city.pop('zipCode')

    city = geo_pb2.FrenchCity(city_id=city_id)
    try:
        json_format.ParseDict(algolia_city, city, ignore_unknown_fields=True)
    except json_format.ParseError as error:
        logging.warning(
            'Error %s while parsing a city proto from Algolia:\n%s', error,
            algolia_city)
        return None

    return city
    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])
    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)
Exemple #8
0
    def test_get_in_a_departement_text_city_hint(self) -> None:
        """Test get_in_a_departement_text func with a city hing."""

        self.assertEqual(
            'in Illinois',
            geo.get_in_a_departement_text(self._db,
                                          '19',
                                          city_hint=geo_pb2.FrenchCity(
                                              departement_id='19',
                                              departement_name='Illinois',
                                              departement_prefix='in ',
                                          )))
    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)
    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)
    def test_arrondissements(
            self, mock_requests: requests_mock.Mocker, mock_get_city_proto: mock.MagicMock) -> None:
        """Test that arrondissements IDs from PE are converted to city IDs."""

        mock_get_city_proto.side_effect = lambda c: geo_pb2.FrenchCity(name='City', city_id=c)

        # Sanity check.
        self.assertEqual('31555', self._test_new_user_in_city(mock_requests, '31555'))

        # Lyon.
        self.assertEqual('69123', self._test_new_user_in_city(mock_requests, '69381'))
        self.assertEqual('69123', self._test_new_user_in_city(mock_requests, '69386'))
        # Marseille.
        self.assertEqual('13055', self._test_new_user_in_city(mock_requests, '13201'))
        self.assertEqual('13055', self._test_new_user_in_city(mock_requests, '13212'))
        # Paris.
        self.assertEqual('75056', self._test_new_user_in_city(mock_requests, '75101'))
        self.assertEqual('75056', self._test_new_user_in_city(mock_requests, '75113'))
Exemple #12
0
    def list_nearby_cities(self, project):
        """Compute and store all interesting cities that are not too close and not too far.

        Those cities will be used by the Commute advice.
        """
        job_group = project.details.target_job.job_group.rome_id

        all_cities = commute_pb2.HiringCities()
        proto.parse_from_mongo(
            project.database.hiring_cities.find_one({'_id': job_group}),
            all_cities)
        interesting_cities_for_rome = all_cities.hiring_cities

        if not interesting_cities_for_rome:
            return []

        target_city = geo_pb2.FrenchCity()
        mongo_city = project.database.cities.find_one(
            {'_id': project.details.mobility.city.city_id})
        if not mongo_city:
            return []
        proto.parse_from_mongo(mongo_city, target_city)

        commuting_cities = list(
            _get_commuting_cities(interesting_cities_for_rome, target_city))

        obvious_cities = [
            city for city in commuting_cities
            if city.distance_km < _MIN_CITY_DISTANCE
        ]

        interesting_cities = [
            city for city in commuting_cities
            if city.distance_km >= _MIN_CITY_DISTANCE
        ]

        # If there is only one city nearby and no obvious city, the nearby city becomes obvious, so
        # we do not recommend it.
        if len(interesting_cities) == 1 and not obvious_cities:
            return []

        return interesting_cities
Exemple #13
0
def get_city_proto(city_id: str) -> Optional[geo_pb2.FrenchCity]:
    """Compute a full FrenchCity proto from a simple city_id."""

    if not city_id:
        return None
    if not _ALGOLIA_INDEX:
        _ALGOLIA_INDEX.append(
            search_client.SearchClient.create(
                os.getenv('ALGOLIA_APP_ID', 'K6ACI9BKKT'),
                os.getenv(
                    'ALGOLIA_API_KEY',
                    'da4db0bf437e37d6d49cefcb8768c67a')).init_index('cities'))

    try:
        algolia_city = _ALGOLIA_INDEX[0].get_object(city_id)
    except exceptions.AlgoliaException as err:
        logging.warning('Error in algolia: %s for city ID %s', err, city_id)
        return None
    if not algolia_city:
        return None

    # Keep in sync with frontend/src/components/suggestions.jsx
    if 'urban' in algolia_city:
        urban = algolia_city.pop('urban')
        algolia_city['urbanScore'] = urban if urban else -1
    if 'transport' in algolia_city:
        algolia_city['publicTransportationScore'] = algolia_city.pop(
            'transport')
    if 'zipCode' in algolia_city:
        algolia_city['postcodes'] = algolia_city.pop('zipCode')

    city = geo_pb2.FrenchCity(city_id=city_id)
    try:
        json_format.ParseDict(algolia_city, city, ignore_unknown_fields=True)
    except json_format.ParseError as error:
        logging.warning(
            'Error %s while parsing a city proto from Algolia:\n%s', error,
            algolia_city)
        return None

    return city
    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))
    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)
    def get_expanded_card_data(
            self, project: scoring_base.ScoringProject) -> geo_pb2.FrenchCity:
        """Retrieve data for the expanded card."""

        return geo.get_city_location(project.database, project.details.city.city_id) or \
            geo_pb2.FrenchCity()
Exemple #17
0
def _find_state(state_name: str) -> geo_pb2.FrenchCity:
    return geo_pb2.FrenchCity(region_id=STATE_MAP[state_name],
                              region_name=state_name)
    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:])