예제 #1
0
def details(siret):
    """
    Display the details of an office.
    In case the context of a rome_code is given, display appropriate score value for this rome_code
    """
    fix_csrf_session()
    rome_code = request.args.get('rome_code', None)
    company = Office.query.filter_by(siret=siret).first()
    if not company:
        abort(404)

    # Check if company is hidden by SAVE
    if not company.score:
        abort(404)

    context = {
        'company': company,
        'rome_code': rome_code,
        'next_url_modal': url_for('jepostule.application', siret=siret, rome_code=rome_code),
    }
    activity.log(
        event_name='details',
        siret=siret,
    )
    return render_template('office/details.html', **context)
예제 #2
0
def results_by_commune_and_rome(commune_id, rome_id):
    """
    Convenience function to be used by Pôle Emploi, Bob Emploi and other partners
    Redirects internally to our real user-facing url displaying
    results for his search.

    For more information about the differences between commune_id and zipcode,
    please consult README file
    """
    fix_csrf_session()
    try:
        rome_description = settings.ROME_DESCRIPTIONS[rome_id.upper()]
        slugified_rome_description = slugify(rome_description)
    except KeyError:
        rome_description = None
    city = geocoding.get_city_by_commune_id(commune_id)

    if not city or not rome_description:
        abort(404)

    params = request.args.copy()
    params['city'] = city['slug']
    params['zipcode'] = city['zipcode']
    params['occupation'] = slugified_rome_description
    url = url_for('search.entreprises', **params)
    # Pass all GET params to the redirect URL: this will allow users of the API to build web links
    # roughly equivalent to the result of an API call - see Trello #971.
    return redirect(url)
예제 #3
0
def details(siret):
    """
    Display the details of an office.
    In case the context of a rome_code is given, display appropriate score value for this rome_code
    """
    fix_csrf_session()
    rome_code = request.args.get('rome_code', None)
    company = Office.query.filter_by(siret=siret).first()
    if not company:
        abort(404)

    # Check if company is hidden by SAVE
    if not company.score:
        abort(404)

    context = {
        'company': company,
        'rome_code': rome_code,
        'hide_memo_introjs': True,
    }
    activity.log(
        event_name='details',
        siret=siret,
        # GA tracking is used in PSE 2019-2020 experiment
        utm_medium=request.args.get('utm_medium', ''),
        utm_source=request.args.get('utm_source', ''),
        utm_campaign=request.args.get('utm_campaign', ''),
    )
    return render_template('office/details.html', **context)
예제 #4
0
def home():
    fix_csrf_session()
    activity.log(event_name='home', )

    refresh_token_result = attempt_to_refresh_peam_token()
    if refresh_token_result["token_has_expired"]:
        return redirect(refresh_token_result["redirect_url"])

    return render_template('home.html', form=CompanySearchForm())
예제 #5
0
def home():
    fix_csrf_session()
    activity.log(
        event_name='home',
        # GA tracking is used in PSE 2019-2020 experiment
        utm_medium=request.args.get('utm_medium', ''),
        utm_source=request.args.get('utm_source', ''),
        utm_campaign=request.args.get('utm_campaign', ''),
    )
    return render_template('home.html', form=CompanySearchForm())
예제 #6
0
def change_info():
    """
    Let a user fill a form to identify himself/herself in order to request changes
    about an office.
    """
    fix_csrf_session()
    form = forms.OfficeIdentificationForm()

    # Clear session if user comes from 'I don't have recruiter account'
    if request.args.get('no_pe_connect', ''):
        peam_recruiter.clear_pe_connect_recruiter_session()

    # Siret information is present when coming from change_info route.
    # Apply it only if user has not typed anything else yet.
    params = request.args.copy()

    action_name = get_action_name()
    if not action_name:
        flash('Une erreur inattendue est survenue, veuillez sélectionner à nouveau une action', 'error')
        return redirect(url_for('contact_form.ask_action'))

    form.last_name.data = form.last_name.data or session.get(peam_recruiter.SessionKeys.LASTNAME.value)
    form.first_name.data = form.first_name.data or session.get(peam_recruiter.SessionKeys.FIRSTNAME.value)
    form.email.data = form.email.data or session.get(peam_recruiter.SessionKeys.EMAIL.value)

    siret = params.get('siret')
    if siret and not form.data['siret']:
        form.siret.data = siret

    if form.validate_on_submit():
        office = models.Office.query.filter(models.Office.siret == form.siret.data).first()
        if not office:
            flash(unknown_siret_message(), 'error')
        else:
            params = {key: form.data[key] for key in ['siret', 'last_name', 'first_name', 'phone', 'email']}
            if is_recruiter_from_lba():
                params.update({"origin":"labonnealternance"})
            action_form_url = "contact_form.%s_form" % action_name
            url = url_for(action_form_url, **params)
            return redirect(url)

    return render_template('contact_form/form.html',
        title='Identifiez-vous',
        submit_text='suivant',
        extra_submit_class='identification-form',
        form=form,
        is_certified_recruiter=peam_recruiter.is_certified_recruiter(),
        is_recruiter=peam_recruiter.is_recruiter(),
        use_lba_template=is_recruiter_from_lba(),
        show_disclaimer=True,
        hide_return=True,
        custom_ga_pageview='/recruteur/%s/identification' % action_name,
    )
예제 #7
0
def account():
    """
    The current user account main page.
    """
    fix_csrf_session()
    user_social_auth = get_user_social_auth(current_user.id)
    context = {}
    if user_social_auth:
        context['token'] = user_social_auth.extra_data['access_token']
        context['token_age_in_seconds'] = int(
            time.time()) - user_social_auth.extra_data['auth_time'],
    return render_template('user/account.html', **context)
예제 #8
0
파일: views.py 프로젝트: PBBM/labonneboite
def results_by_departments_and_rome(department_code, rome_id):
    """
    http://localhost:8080/entreprises/departments/57/rome/M1805
    Redirects internally to our real user-facing url displaying
    results for his search.

    The partners may use our API to get companies count, which comes with the URL to this route. @see labonneboite/web/api/views.py:compute_frontend_url
    """
    fix_csrf_session()
    url = get_url_for_rome(rome_id, department_code)
    if url is None:
        abort(400, 'Department or rome not found')
    return redirect(url)
예제 #9
0
def results(city, zipcode, occupation):
    """
    All this does is a redirect to the 'search.entreprises' view with
    city-related location parameters. This view is preserved so that older urls
    still work.
    """
    fix_csrf_session()
    params = request.args.copy()
    params['city'] = city
    params['zipcode'] = zipcode
    params['occupation'] = occupation

    redirect_url = url_for('search.entreprises', **params)
    return redirect(redirect_url)
예제 #10
0
def logout_from_peam_callback():
    """
    The route where a user is redirected after a log out through the PEAM website.
    """

    # Quick ugly but useful fix
    referer = request.referrer
    if referer and len(referer) > 1700:
        # If the referer is too long, the redirect will return a 502 status code.
        # I did not find how to change the referrer. See https://github.com/pallets/flask/issues/3310
        fix_csrf_session()
        return render_template('home.html', form=CompanySearchForm())

    return redirect(url_for('root.home'))
예제 #11
0
def details(siret):
    """
    Display the details of an office.
    In case the context of a rome_code is given, display appropriate score value for this rome_code
    This code is very similar to the code in labonneboite/web/api/views.py
    """
    fix_csrf_session()
    rome_code = request.args.get('rome_code', None)
    company = Office.query.filter_by(siret=siret).first()

    if not company:
        abort(404)

    # Alternance case
    alternance = 'contract' in request.args and request.args['contract'] == 'alternance'
    # If an office score equals 0 it means it is not supposed
    # to be shown on LBB frontend/api
    # and/or it was specifically removed via SAVE,
    # and thus it should not be accessible by siret.
    if not alternance and not company.score:
        # The company is hidden by SAVE
        abort(404)

    # Offices having score_alternance equal 0 may still be accessed
    # by siret in case of LBA offices from the visible market (i.e. having
    # at least one job offer obtained from the API Offers V2).
    # However we should not show them if they were specifically removed via SAVE.
    if alternance and company.is_removed_from_lba:
        abort(404)

    context = {
        'company': company,
        'rome_code': rome_code,
        'next_url_modal': url_for('jepostule.application', siret=siret, rome_code=rome_code),
    }
    activity.log(
        event_name='details',
        siret=siret,
    )
    return render_template('office/details.html', **context)
예제 #12
0
def favorites_list():
    """
    List the favorited offices of a user.
    """
    fix_csrf_session()
    try:
        page = int(request.args.get('page'))
    except (TypeError, ValueError):
        page = 1

    favorites = UserFavoriteOffice.query.filter(
        UserFavoriteOffice.user_id == current_user.id)
    limit = FAVORITES_PER_PAGE
    pagination = Pagination(page, limit, favorites.count())
    if page > 1:
        favorites = favorites.offset((page - 1) * limit)
    favorites = favorites.limit(limit)

    context = {
        'favorites': favorites,
        'pagination': pagination,
        'show_favorites': True,
    }
    return render_template('user/favorites_list.html', **context)
예제 #13
0
def account():
    """
    The current user account main page.
    """
    fix_csrf_session()
    return render_template('user/account.html')
예제 #14
0
def home():
    fix_csrf_session()
    return render_template('home.html', form=CompanySearchForm())
예제 #15
0
def entreprises():
    """
    This view takes arguments as a query string.

    Expected arguments are those returned by get_parameters and expected by the
    selected office search form.
    """
    fix_csrf_session()
    session['search_args'] = request.args
    location, named_location = get_location(request.args)

    occupation = request.args.get('occupation', '')
    if not occupation and 'j' in request.args:
        suggestion = search_util.build_job_label_suggestions(request.args['j'],
                                                             size=1)
        occupation = suggestion[0]['occupation'] if suggestion else None

    rome = mapping_util.SLUGIFIED_ROME_LABELS.get(occupation)
    job_doesnt_exist = not rome

    # Build form
    form_kwargs = {key: val for key, val in list(request.args.items()) if val}
    form_kwargs['j'] = settings.ROME_DESCRIPTIONS.get(rome, occupation)

    if 'occupation' not in form_kwargs:
        form_kwargs['occupation'] = occupation

    if not form_kwargs.get('l') and named_location:
        # Override form location only if it is not available (e.g when user has
        # removed it from the url)
        form_kwargs['l'] = named_location.name

    if location:
        form_kwargs['lat'] = location.latitude
        form_kwargs['lon'] = location.longitude

    form = make_company_search_form(**form_kwargs)

    # Render different template if it's an ajax call
    template = 'search/results.html' if not request.is_xhr else 'search/results_content.html'

    activity_log_properties = dict(
        emploi=occupation,
        localisation={
            'nom': named_location.name if named_location else None,
            'ville': named_location.city if named_location else None,
            'codepostal': named_location.zipcode if named_location else None,
            'latitude': location.latitude if location else None,
            'longitude': location.longitude if location else None,
        },
    )

    # Stop here in case of invalid arguments
    if not form.validate() or job_doesnt_exist:
        log_search_activity(activity_log_properties)
        return render_template(template,
                               job_doesnt_exist=job_doesnt_exist,
                               form=form)

    # Convert request arguments to fetcher parameters
    parameters = get_parameters(request.args)

    # Fetch offices and alternatives
    fetcher = search_util.HiddenMarketFetcher(
        location,
        romes=[rome],
        distance=parameters['distance'],
        travel_mode=parameters['travel_mode'],
        duration=parameters['duration'],
        sort=parameters['sort'],
        from_number=parameters['from_number'],
        to_number=parameters['to_number'],
        public=parameters.get('public'),
        headcount=parameters['headcount'],
        naf=parameters['naf'],
        naf_codes=None,
        aggregate_by=['naf'],
        departments=None,
    )
    alternative_rome_descriptions = []
    naf_codes_with_descriptions = []
    offices = []
    office_count = 0
    alternative_distances = {}

    # Aggregations
    offices, aggregations = fetcher.get_offices(add_suggestions=True)
    office_count = fetcher.office_count
    alternative_distances = fetcher.alternative_distances
    alternative_rome_descriptions = fetcher.get_alternative_rome_descriptions()

    # If a filter or more are selected, the aggregations returned by fetcher.get_offices()
    # will be filtered too... To avoid that, we are doing additionnal calls (one by filter activated)
    if aggregations:
        fetcher.update_aggregations(aggregations)

    # Generates values for the NAF filter
    # aggregations could be empty if errors or empty results
    if aggregations:
        for naf_aggregate in aggregations['naf']:
            naf_description = '%s (%s)' % (settings.NAF_CODES.get(
                naf_aggregate["code"]), naf_aggregate["count"])
            naf_codes_with_descriptions.append(
                (naf_aggregate["code"], naf_description))

    duration_filter_enabled = fetcher.duration is not None

    # Pagination.
    pagination_manager = pagination.PaginationManager(
        office_count,
        fetcher.from_number,
        fetcher.to_number,
        request.full_path,
    )
    current_page = pagination_manager.get_current_page()

    # Anticipate future calls by pre-computing duration-related searches
    if location:
        precompute.isochrones((location.latitude, location.longitude))

    form.naf.choices = [('', 'Tous les secteurs')] + sorted(
        naf_codes_with_descriptions, key=lambda t: t[1])
    form.validate()

    canonical_url = get_canonical_results_url(
        named_location.zipcode, named_location.city,
        occupation) if named_location else ''

    context = {
        'alternative_distances':
        alternative_distances,
        'alternative_rome_descriptions':
        alternative_rome_descriptions,
        'canonical_url':
        canonical_url,
        'companies':
        list(offices),
        'companies_per_page':
        pagination.OFFICES_PER_PAGE,
        'company_count':
        office_count,
        'distance':
        fetcher.distance,
        'doorbell_tags':
        doorbell.get_tags('results'),
        'form':
        form,
        'headcount':
        fetcher.headcount,
        'job_doesnt_exist':
        False,
        'naf':
        fetcher.naf,
        'location':
        location,
        'city_name':
        named_location.city if named_location else '',
        'location_name':
        named_location.name if named_location else '',
        'page':
        current_page,
        'pagination':
        pagination_manager,
        'rome_code':
        rome,
        'rome_description':
        settings.ROME_DESCRIPTIONS.get(rome, ''),
        'show_favorites':
        True,
        'sort':
        fetcher.sort,
        'tile_server_url':
        settings.TILE_SERVER_URL,
        'travel_mode':
        fetcher.travel_mode,
        'travel_modes':
        maps_constants.TRAVEL_MODES,
        'travel_modes_french':
        maps_constants.TRAVEL_MODES_FRENCH,
        'duration_filter_enabled':
        duration_filter_enabled,
        'user_favs_as_sirets':
        UserFavoriteOffice.user_favs_as_sirets(current_user),
    }

    activity_log_properties['distance'] = fetcher.distance
    activity_log_properties['effectif'] = fetcher.headcount
    activity_log_properties['tri'] = fetcher.sort
    activity_log_properties['naf'] = fetcher.naf
    activity_log_sirets = [office.siret for office in offices]
    activity.log_search(sirets=activity_log_sirets,
                        count=office_count,
                        page=current_page,
                        **activity_log_properties)

    return render_template(template, **context)
예제 #16
0
파일: views.py 프로젝트: PBBM/labonneboite
def entreprises():
    """
    This view takes arguments as a query string.

    Expected arguments are those returned by get_parameters and expected by the
    selected office search form.
    """
    fix_csrf_session()

    refresh_token_result = attempt_to_refresh_peam_token()
    if refresh_token_result["token_has_expired"]:
        return redirect(refresh_token_result["redirect_url"])

    # filter empty url params
    args = {
        key: request.args[key]
        for key in request.args if request.args[key] != ''
    }

    # get structured location data from url params
    location, named_location, departments = get_location(args)

    occupation = args.get('occupation', '')
    if not occupation and 'j' in args:
        suggestion = autocomplete.build_job_label_suggestions(args['j'],
                                                              size=1)
        occupation = suggestion[0]['occupation'] if suggestion else None

    rome = mapping_util.SLUGIFIED_ROME_LABELS.get(occupation)
    job_doesnt_exist = not rome

    # Related romes
    if settings.ENABLE_RELATED_ROMES:
        related_romes = None
        hide_suggestions = False
        if (named_location):
            with start_transaction(op='related_romes_get',
                                   name='rome_' + rome):
                related_area = RELATED_ROMES_AREAS.get(
                    named_location.city_code, None)
                if (related_area):
                    # Hide suggestions for places which may have suggestions
                    hide_suggestions = True
                    romes = RELATED_ROMES.get(related_area, [])
                    if (rome in romes):
                        # Case where there are suggestions for this rome and this location
                        related_romes = romes.get(rome)
                        # related_romes = list(map(add_nafs, related_romes))
                        related_romes = list(
                            map(add_descriptions, related_romes))
                        # sort and limit size
                        related_romes.sort(
                            key=lambda rome_: rome_.get('score'))
                        related_romes = related_romes[:settings.
                                                      MAX_RELATED_ROMES]
                        if (len(related_romes) > 0):
                            flash(
                                'Nouvelle fonctionnalité : Grâce aux nouveaux filtres, élargissez votre recherche aux métiers qui recrutent !',
                                'info')
    else:
        related_romes = []
        hide_suggestions = False

    # Build form
    form_kwargs = {key: val for key, val in list(args.items()) if val}
    form_kwargs['j'] = settings.ROME_DESCRIPTIONS.get(rome, occupation)

    if 'occupation' not in form_kwargs:
        form_kwargs['occupation'] = occupation

    if not form_kwargs.get('l') and named_location:
        # Override form location only if it is not available (e.g when user has
        # removed it from the url)
        form_kwargs['l'] = named_location.name

    if location:
        form_kwargs['lat'] = location.latitude
        form_kwargs['lon'] = location.longitude

    if departments:
        form_kwargs['departments'] = departments

    form = make_company_search_form(**form_kwargs)

    # Render different template if it's an ajax call
    template = 'search/results.html' if not request.is_xhr else 'search/results_content.html'

    activity_log_properties = dict(
        emploi=occupation,
        localisation={
            'nom': named_location.name if named_location else None,
            'ville': named_location.city if named_location else None,
            'codepostal': named_location.zipcode if named_location else None,
            'latitude': location.latitude if location else None,
            'longitude': location.longitude if location else None,
            'departments': departments if departments else None,
        },
    )

    # Stop here in case of invalid arguments
    if not form.validate() or job_doesnt_exist:
        log_search_activity(activity_log_properties)
        return render_template(template,
                               job_doesnt_exist=job_doesnt_exist,
                               form=form)

    # Convert request arguments to fetcher parameters
    parameters = get_parameters(args)

    # Fetch offices and alternatives
    fetcher = HiddenMarketFetcher(
        location.longitude if location is not None else None,
        location.latitude if location is not None else None,
        departments=departments.split(',') if departments else None,
        romes=[rome],
        distance=parameters['distance'],
        travel_mode=parameters['travel_mode'],
        duration=parameters['duration'],
        sort=parameters['sort'],
        from_number=parameters['from_number'],
        to_number=parameters['to_number'],
        audience=parameters['audience'],
        headcount=parameters['headcount'],
        naf=parameters['naf'],
        naf_codes=None,
        aggregate_by=['naf'],
    )
    alternative_rome_descriptions = []
    naf_codes_with_descriptions = []
    offices = []
    office_count = 0
    alternative_distances = {}

    # Aggregations
    offices, aggregations = fetcher.get_offices(add_suggestions=True)
    office_count = fetcher.office_count
    alternative_distances = fetcher.alternative_distances
    alternative_rome_descriptions = fetcher.get_alternative_rome_descriptions()

    # If a filter or more are selected, the aggregations returned by fetcher.get_offices()
    # will be filtered too... To avoid that, we are doing additionnal calls (one by filter activated)
    if aggregations:
        fetcher.update_aggregations(aggregations)

    # Generates values for the NAF filter
    # aggregations could be empty if errors or empty results
    if aggregations:
        for naf_aggregate in aggregations['naf']:
            naf_description = '%s (%s)' % (settings.NAF_CODES.get(
                naf_aggregate["code"]), naf_aggregate["count"])
            naf_codes_with_descriptions.append(
                (naf_aggregate["code"], naf_description))

    duration_filter_enabled = fetcher.duration is not None

    # Pagination.
    pagination_manager = pagination.PaginationManager(
        office_count,
        fetcher.from_number,
        fetcher.to_number,
        request.full_path,
    )
    current_page = pagination_manager.get_current_page()

    form.naf.choices = [('', 'Tous les secteurs')] + sorted(
        naf_codes_with_descriptions, key=lambda t: t[1])
    form.validate()

    canonical_url = get_canonical_results_url(
        named_location.zipcode, named_location.city,
        occupation) if named_location else ''

    context = {
        'alternative_distances':
        alternative_distances,
        'alternative_rome_descriptions':
        alternative_rome_descriptions,
        'related_romes':
        related_romes,
        'related_rome_initial':
        parameters.get('related_rome_initial', ''),
        'hide_suggestions':
        hide_suggestions,
        'canonical_url':
        canonical_url,
        'companies':
        list(offices),
        'companies_per_page':
        pagination.OFFICES_PER_PAGE,
        'company_count':
        office_count,
        'distance':
        fetcher.distance,
        'form':
        form,
        'headcount':
        fetcher.headcount,
        'job_doesnt_exist':
        False,
        'naf':
        fetcher.naf,
        'location':
        location,
        'departments':
        departments,
        'city_name':
        named_location.city if named_location else '',
        'location_name':
        named_location.name if named_location else '',
        'page':
        current_page,
        'pagination':
        pagination_manager,
        'rome_code':
        rome,
        'rome_description':
        settings.ROME_DESCRIPTIONS.get(rome, ''),
        'show_favorites':
        True,
        'sort':
        fetcher.sort,
        'tile_server_url':
        settings.TILE_SERVER_URL,
        'travel_mode':
        fetcher.travel_mode,
        'travel_modes':
        maps_constants.TRAVEL_MODES,
        'travel_modes_french':
        maps_constants.TRAVEL_MODES_FRENCH,
        'duration_filter_enabled':
        duration_filter_enabled,
        'user_favs_as_sirets':
        UserFavoriteOffice.user_favs_as_sirets(current_user),
    }

    activity_log_properties['distance'] = fetcher.distance
    activity_log_properties['effectif'] = fetcher.headcount
    activity_log_properties['tri'] = fetcher.sort
    activity_log_properties['naf'] = fetcher.naf
    activity_log_sirets = [office.siret for office in offices]
    activity.log_search(sirets=activity_log_sirets,
                        count=office_count,
                        page=current_page,
                        **activity_log_properties)

    return render_template(template, **context)