def _get_short_diploma_vars(user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, Any]: if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] if user.projects[0].diagnostic.category_id != 'missing-diploma': raise campaign.DoNotSend('The user has no missing-diploma category') scoring_project = scoring.ScoringProject(project, user, database) login_url = campaign.create_logged_url(user.user_id, f'/projet/{project.project_id}') # TODO(sil): Let's check if this is needed to have access to the method. if not project.target_job.job_group.rome_id: raise scoring.NotEnoughDataException( 'Need a job group to find trainings', # TODO(pascal): Use project_id instead of 0. {'projects.0.targetJob.jobGroup.romeId'}) deep_link_training_url = \ campaign.get_deep_link_advice(user.user_id, project, 'training') return campaign.get_default_coaching_email_vars(user) | { 'deepTrainingAdviceUrl': deep_link_training_url, 'ofJobName': scoring_project.populate_template('%ofJobName'), 'productUrl': f'{login_url}?utm_source=bob-emploi&utm_medium=email', }
def _get_imt_vars( user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, Any]: """Compute vars for the "IMT" email.""" if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] scoring_project = scoring.ScoringProject(project, user, database) departement_id = project.city.departement_id rome_id = project.target_job.job_group.rome_id local_diagnosis = scoring_project.local_diagnosis() if not local_diagnosis.HasField('imt'): raise scoring.NotEnoughDataException( 'User market has no IMT data', {f'data.local_diagnosis.{departement_id}:{rome_id}.imt'}) imt = local_diagnosis.imt shown_sections = [] if market_stress_section := _make_market_stress_section( imt.yearly_avg_offers_per_10_candidates): shown_sections.append('marketStress')
def _get_improve_cv_vars(user: user_pb2.User, *, now: datetime.datetime, **unused_kwargs: Any) -> dict[str, Any]: """Compute vars for the "Improve your CV" email.""" if user_profile_pb2.RESUME not in user.profile.frustrations: raise campaign.DoNotSend('User is not frustrated by its CV') if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] if project.kind == project_pb2.FIND_A_FIRST_JOB: has_experience = 'False' elif project.kind in (project_pb2.FIND_A_NEW_JOB, project_pb2.FIND_ANOTHER_JOB): has_experience = 'True' else: has_experience = '' deep_link_advice_url = \ campaign.get_deep_link_advice(user.user_id, project, 'improve-resume') or \ campaign.get_deep_link_advice(user.user_id, project, 'fresh-resume') return campaign.get_default_coaching_email_vars(user) | { 'deepLinkAdviceUrl': deep_link_advice_url, 'hasExperience': has_experience, 'isSeptember': campaign.as_template_boolean(now.month == 9), 'loginUrl': campaign.create_logged_url(user.user_id) }
def _get_in_target_domain(rome_id: str, database: mongo.NoPiiMongoDatabase) -> str: if not rome_id: raise scoring.NotEnoughDataException( "Need a job group to express user's target domain", {'projects.0.targetJob.jobGroup.romeId'}) job_group_info = jobs.get_group_proto(database, rome_id) if not job_group_info: raise scoring.NotEnoughDataException( "Need job group into to express user's target domain", {f'data.job_group_info.{rome_id}'}) in_target_domain = job_group_info.in_domain if not in_target_domain: raise scoring.NotEnoughDataException( "No information about this job group's domain", {f'data.job_group_info.{rome_id}.in_target_domain'}) return in_target_domain
def _get_find_diploma_vars(user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, Any]: """Compute vars for the "Prepare your application" email.""" if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] scoring_project = scoring.ScoringProject(project, user, database) if not any(s.strategy_id == 'get-diploma' for s in project.opened_strategies): raise campaign.DoNotSend( 'The user has not started a strategy to get a diploma') if not project.target_job.job_group.rome_id: raise scoring.NotEnoughDataException( 'Need a job group to find trainings', # TODO(pascal): Use project_id instead of 0. {'projects.0.targetJob.jobGroup.romeId'}) trainings = scoring_project.get_trainings()[:3] deep_link_training_url = \ campaign.get_deep_link_advice(user.user_id, project, 'training') return campaign.get_default_coaching_email_vars(user) | { 'deepTrainingAdviceUrl': deep_link_training_url, 'inDepartement': scoring_project.populate_template('%inDepartement'), 'loginUrl': campaign.create_logged_url(user.user_id, f'/projet/{project.project_id}'), 'numTrainings': len(trainings), 'ofJobName': scoring_project.populate_template('%ofJobName'), 'trainings': [json_format.MessageToDict(t) for t in trainings], }
def _get_self_development_vars( user: user_pb2.User, *, now: datetime.datetime, **unused_kwargs: Any) \ -> dict[str, str]: """Computes vars for a given user for the self-development email. Returns a dict with all vars required for the template. """ if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) has_video = (user.profile.locale or 'fr').startswith('fr') project = user.projects[0] job_search_length = campaign.job_search_started_months_ago(project, now) if job_search_length < 0: raise campaign.DoNotSend('No info on user search duration.') if job_search_length > 12: raise campaign.DoNotSend( f'User has been searching for too long ({job_search_length:.2f}).') genderized_job_name = french.lower_first_letter( french.genderize_job(project.target_job, user.profile.gender)) age = datetime.date.today().year - user.profile.year_of_birth max_young = 30 min_old = 50 return campaign.get_default_coaching_email_vars(user) | { 'hasEnoughExperience': campaign.as_template_boolean(project.seniority > project_pb2.JUNIOR), 'hasVideo': campaign.as_template_boolean(has_video), 'isAdministrativeAssistant': campaign.as_template_boolean( project.target_job.job_group.name == 'Secrétariat'), 'isOld': campaign.as_template_boolean(age >= min_old), 'isOldNotWoman': campaign.as_template_boolean( age >= min_old and user.profile.gender != user_profile_pb2.FEMININE), 'isYoung': campaign.as_template_boolean(age <= max_young), 'isYoungNotWoman': campaign.as_template_boolean( age <= max_young and user.profile.gender != user_profile_pb2.FEMININE), 'jobName': genderized_job_name, }
def _get_galita2_short_vars(user: user_pb2.User, **unused_kwargs: Any) -> dict[str, str]: if not user.projects: raise scoring.NotEnoughDataException( 'Project is required for galita-2-short.', fields={'user.projects.0.kind'}) project = user.projects[0] if project.kind not in {project_pb2.FIND_A_FIRST_JOB, project_pb2.REORIENTATION} and \ project.previous_job_similarity != project_pb2.NEVER_DONE: raise campaign.DoNotSend( 'User is not searching a job in a profession new to them.') return campaign.get_default_coaching_email_vars(user)
def _get_short_spontaneous_vars(user: user_pb2.User, *, now: datetime.datetime, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, str]: if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] scoring_project = scoring.ScoringProject(project, user, database, now) job_group_info = scoring_project.job_group_info() why_specific_company = job_group_info.why_specific_company if not why_specific_company: why_specific_company = scoring_project.translate_static_string( 'vous vous reconnaissez dans leurs valeurs, leur équipe, leur service client ou ce ' "qu'elles vendent") some_companies = job_group_info.place_plural if not some_companies: some_companies = scoring_project.translate_static_string( 'des entreprises') if (user.profile.locale or 'fr').startswith('fr'): advice_page_url = 'https://labonneboite.pole-emploi.fr/comment-faire-une-candidature-spontanee' elif user.profile.locale.startswith('en'): advice_page_url = 'https://www.theguardian.com/careers/speculative-applications' else: logging.warning( 'No advice webpage given for campaign spontaneous-short in "%s"', user.profile.locale) advice_page_url = '' # If the user receives the email less than 2 months after they registered on Bob and are # searching for less than 3 months, we can be happily surprised if they found a job. is_job_found_surprising = scoring_project.get_search_length_now() < 3 and \ (scoring_project.details.created_at.ToDatetime() - now).days / 30 < 2 return campaign.get_default_coaching_email_vars(user) | { 'advicePageUrl': advice_page_url, 'atVariousCompanies': job_group_info.at_various_companies, 'isJobFoundSurprising': campaign.as_template_boolean(is_job_found_surprising), 'someCompanies': some_companies, 'whySpecificCompany': why_specific_company, }
def _get_prepare_your_application_vars(user: user_pb2.User, **unused_kwargs: Any) -> dict[str, Any]: """Compute vars for the "Prepare your application" email.""" if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] deep_link_motivation_email_url = \ campaign.get_deep_link_advice(user.user_id, project, 'motivation-email') return campaign.get_default_coaching_email_vars(user) | { 'deepLinkMotivationEmailUrl': deep_link_motivation_email_url, 'hasInterviewFrustration': campaign.as_template_boolean(user_profile_pb2.INTERVIEW in user.profile.frustrations), 'hasSelfConfidenceFrustration': campaign.as_template_boolean(user_profile_pb2.SELF_CONFIDENCE in user.profile.frustrations), 'loginUrl': campaign.create_logged_url(user.user_id, f'/projet/{project.project_id}'), }
def _get_network_vars( user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, str]: """Compute vars for a given user for the network email. Returns: a dict with all vars required for the template, or None if no email should be sent. """ if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] if project.network_estimate != 1: raise campaign.DoNotSend('User has a good enough network') in_target_domain = _get_in_target_domain(project.target_job.job_group.rome_id, database) worst_frustration = next( (f for f in (user_profile_pb2.NO_OFFER_ANSWERS, user_profile_pb2.MOTIVATION) if f in user.profile.frustrations), None) is_hairdresser_or_in_marseille = \ project.target_job.job_group.rome_id.startswith('D') or \ project.city.departement_id == '13' other_job_in_city = 'coiffeur à Marseille' if is_hairdresser_or_in_marseille: other_job_in_city = 'secrétaire à Lyon' job = french.lower_first_letter(french.genderize_job( project.target_job, user.profile.gender)) in_city = french.in_city(strip_district(project.city.name)) return campaign.get_default_coaching_email_vars(user) | { 'inTargetDomain': in_target_domain, 'frustration': user_profile_pb2.Frustration.Name(worst_frustration) if worst_frustration else '', 'otherJobInCity': other_job_in_city, 'jobInCity': f'{job} {in_city}', 'emailInUrl': parse.quote(user.profile.email), }
def _get_post_covid_vars(user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, str]: if not user.projects: raise scoring.NotEnoughDataException( 'Project is required.', fields={'user.projects.0.advices'}) project = user.projects[0] scoring_project = scoring.ScoringProject(project, user, database) if scoring_project.job_group_info().covid_risk != job_pb2.COVID_RISKY: raise campaign.DoNotSend("The user's project job is not covid risky.") try: network_advice_link = next( campaign.get_deep_link_advice(user.user_id, project, a.advice_id) for a in project.advices if a.advice_id.startswith('network-application')) except StopIteration: raise campaign.DoNotSend('No network-application advice found for the user.')\ from None return campaign.get_default_coaching_email_vars(user) | { 'deepLinkAdviceUrl': network_advice_link, 'ofJobName': scoring_project.populate_template('%ofJobName'), }
def _get_prepare_your_application_short_vars(user: user_pb2.User, **unused_kwargs: Any)\ -> dict[str, Any]: """Compute vars for the "Prepare your application short" email.""" if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) if (user.profile.locale or 'fr').startswith('fr'): advice_page_url = 'https://labonneboite.pole-emploi.fr/comment-faire-une-candidature-spontanee' elif user.profile.locale.startswith('en'): advice_page_url = 'https://www.theguardian.com/careers/speculative-applications' else: logging.warning( 'No advice webpage given for campaign spontaneous-short in "%s"', user.profile.locale) advice_page_url = '' return campaign.get_default_coaching_email_vars(user) | { 'advicePageUrl': advice_page_url, 'hasInterviewFrustration': campaign.as_template_boolean(user_profile_pb2.INTERVIEW in user.profile.frustrations), 'hasSelfConfidenceFrustration': campaign.as_template_boolean(user_profile_pb2.SELF_CONFIDENCE in user.profile.frustrations), }
def _get_jobbing_vars( user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, Any]: """Compute vars for the "Jobbing" email.""" if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] if not any(s.strategy_id == 'diploma-free-job' for s in project.opened_strategies): raise campaign.DoNotSend( 'The user has not started a strategy to get a job without a diploma') scoring_project = scoring.ScoringProject(project, user, database) model = scoring.get_scoring_model('advice-reorient-jobbing') if not model: raise campaign.DoNotSend('The advice-reorient-jobbing model is not implemented') reorient_jobs = typing.cast( reorient_jobbing_pb2.JobbingReorientJobs, model.get_expanded_card_data(scoring_project), ).reorient_jobbing_jobs if not reorient_jobs: raise campaign.DoNotSend("We didn't find any jobbing jobs to reorient to for the user") if project.target_job.name: of_job_name = scoring_project.populate_template('%ofJobName') else: # This is not translated to fr@tu because the email templates are only in fr for now. of_job_name = 'de definir votre projet professionnel' return campaign.get_default_coaching_email_vars(user) | { 'inDepartement': scoring_project.populate_template('%inDepartement'), 'jobs': [{'name': job.name} for job in reorient_jobs], 'loginUrl': campaign.create_logged_url(user.user_id, f'/projet/{project.project_id}'), 'ofJobName': of_job_name, }
def _get_spontaneous_vars(user: user_pb2.User, *, now: datetime.datetime, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, str]: """Computes vars for a given user for the spontaneous email. Returns a dict with all vars required for the template. """ if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] scoring_project = scoring.ScoringProject(project, user, database, now) job_search_length = scoring_project.get_search_length_now() if job_search_length < 0: raise campaign.DoNotSend('No info on user search duration') rome_id = project.target_job.job_group.rome_id if not rome_id: raise campaign.DoNotSend('User has no target job yet') job_group_info = scoring_project.job_group_info() if not job_group_info.rome_id: raise scoring.NotEnoughDataException( 'Requires job group info to check if spontaneous application is a good channel.', fields={'projects.0.targetJob.jobGroup.romeId'}) application_modes = job_group_info.application_modes if not application_modes: raise scoring.NotEnoughDataException( 'Requires application modes to check if spontaneous application is a good channel.', fields={f'data.job_group_info.{rome_id}.application_modes'}) def _should_use_spontaneous( modes: job_pb2.RecruitingModesDistribution) -> bool: return any(mode.mode == job_pb2.SPONTANEOUS_APPLICATION and mode.percentage > 20 for mode in modes.modes) if not any( _should_use_spontaneous(modes) for modes in application_modes.values()): raise campaign.DoNotSend( "Spontaneous isn't bigger than 20% of interesting channels.") contact_mode = job_group_info.preferred_application_medium if not contact_mode: raise scoring.NotEnoughDataException( 'Contact mode is required to push people to apply spontaneously', fields={ f'data.job_group_info.{rome_id}.preferred_application_medium' }) in_a_workplace = job_group_info.in_a_workplace if not in_a_workplace and contact_mode != job_pb2.APPLY_BY_EMAIL: raise scoring.NotEnoughDataException( 'To apply in person, the %inAWorkplace template is required', fields={f'data.job_group_info.{rome_id}.in_a_workplace'}) like_your_workplace = job_group_info.like_your_workplace if in_a_workplace and not like_your_workplace: raise scoring.NotEnoughDataException( 'The template %likeYourWorkplace is required', fields={f'data.job_group_info.{rome_id}.like_your_workplace'}) to_the_workplace = job_group_info.to_the_workplace if not to_the_workplace: to_the_workplace = scoring_project.translate_static_string( "à l'entreprise") some_companies = job_group_info.place_plural if not some_companies: some_companies = scoring_project.translate_static_string( 'des entreprises') what_i_love_about = scoring_project.translate_string( job_group_info.what_i_love_about, is_genderized=True) # TODO(cyrille): Drop this behaviour once phrases are translated with gender. if user.profile.gender == user_profile_pb2.FEMININE: what_i_love_about_feminine = job_group_info.what_i_love_about_feminine if what_i_love_about_feminine: what_i_love_about = what_i_love_about_feminine if not what_i_love_about and contact_mode == job_pb2.APPLY_BY_EMAIL: raise scoring.NotEnoughDataException( 'An example about "What I love about" a company is required', fields={f'data.job_group_info.{rome_id}.what_i_love_about'}) why_specific_company = job_group_info.why_specific_company if not why_specific_company: raise scoring.NotEnoughDataException( 'An example about "Why this specific company" is required', fields={f'data.job_group_info.{rome_id}.why_specific_company'}) at_various_companies = job_group_info.at_various_companies if project.weekly_applications_estimate == project_pb2.SOME: weekly_applications_count = '5' elif project.weekly_applications_estimate > project_pb2.SOME: weekly_applications_count = '15' else: weekly_applications_count = '' if project.weekly_applications_estimate: weekly_applications_option = project_pb2.NumberOfferEstimateOption.Name( project.weekly_applications_estimate) else: weekly_applications_option = '' return campaign.get_default_coaching_email_vars(user) | { 'applicationComplexity': job_pb2.ApplicationProcessComplexity.Name( job_group_info.application_complexity), 'atVariousCompanies': at_various_companies, 'contactMode': job_pb2.ApplicationMedium.Name(contact_mode).replace('APPLY_', ''), 'deepLinkLBB': f'https://labonneboite.pole-emploi.fr/entreprises/commune/{project.city.city_id}/rome/' f'{project.target_job.job_group.rome_id}?utm_medium=web&utm_source=bob&' 'utm_campaign=bob-email', 'emailInUrl': parse.quote(user.profile.email), 'experienceAsText': _EXPERIENCE_AS_TEXT.get(project.seniority, 'peu'), 'inWorkPlace': in_a_workplace, 'jobName': french.lower_first_letter( french.genderize_job(project.target_job, user.profile.gender)), 'lastName': user.profile.last_name, 'likeYourWorkplace': like_your_workplace, 'someCompanies': some_companies, 'toTheWorkplace': to_the_workplace, 'weeklyApplicationsCount': weekly_applications_count, 'weeklyApplicationsOption': weekly_applications_option, 'whatILoveAbout': what_i_love_about, 'whySpecificCompany': why_specific_company, }
def network_plus_vars( user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, str]: """Compute vars for a given user for the network email. Returns: a dict with all vars required for the template, or None if no email should be sent. """ if not user.projects: raise scoring.NotEnoughDataException('No project yet', {'projects.0'}) project = user.projects[0] if project.network_estimate < 2: raise campaign.DoNotSend('User does not have a strong network') rome_id = project.target_job.job_group.rome_id in_target_domain = _get_in_target_domain(rome_id, database) job_group_info = jobs.get_group_proto(database, rome_id) assert job_group_info application_modes = job_group_info.application_modes.values() fap_modes = [fap_modes.modes for fap_modes in application_modes if len(fap_modes.modes)] if not fap_modes: raise scoring.NotEnoughDataException( 'No information about application modes for the target job', {f'data.job_group_info.{rome_id}.application_modes'}) flat_fap_modes = [mode for modes in fap_modes for mode in modes] network_percentages = [mode.percentage for mode in flat_fap_modes if ( mode.mode == job_pb2.PERSONAL_OR_PROFESSIONAL_CONTACTS)] # We want to focus on the users for which network, # as an application mode, has a substantial importance. if not network_percentages: raise campaign.DoNotSend( 'User is not targeting a job where networking is a main application mode') scoring_project = scoring.ScoringProject(project, user, database=database) average_network_percentage = sum(network_percentages) / len(network_percentages) if average_network_percentage > 55: network_application_importance = scoring_project.translate_static_string('que la majorité') elif average_network_percentage >= 45: network_application_importance = scoring_project.translate_static_string('que la moitié') elif average_network_percentage >= 25: network_application_importance = scoring_project.translate_static_string("qu'un tiers") else: raise campaign.DoNotSend( 'User is not targeting a job where networking is a main application mode') worst_frustration = next( (f for f in (user_profile_pb2.SELF_CONFIDENCE, user_profile_pb2.MOTIVATION) if f in user.profile.frustrations), None) has_children = user.profile.family_situation in { user_profile_pb2.FAMILY_WITH_KIDS, user_profile_pb2.SINGLE_PARENT_SITUATION} age = datetime.date.today().year - user.profile.year_of_birth max_young = 35 try: in_departement = geo.get_in_a_departement_text( database, project.city.departement_id, city_hint=project.city, locale=user.profile.locale) except KeyError: raise scoring.NotEnoughDataException( 'Need departement info for phrasing', {f'data.departements.{project.city.departement_id}'}) from None job_group_name = french.lower_first_letter(project.target_job.job_group.name) if (user.profile.locale or 'fr').startswith('fr'): in_city = french.in_city(project.city.name) else: # TODO(pascal): Update the English template so that it follows the logic of "in city" and # not "city". For now it's phrased as "near {{inCity}}". in_city = project.city.name return campaign.get_default_coaching_email_vars(user) | { 'frustration': user_profile_pb2.Frustration.Name(worst_frustration) if worst_frustration else '', 'hasChildren': campaign.as_template_boolean(has_children), 'hasHighSchoolDegree': campaign.as_template_boolean( user.profile.highest_degree >= job_pb2.BAC_BACPRO), 'hasLargeNetwork': campaign.as_template_boolean(project.network_estimate >= 2), 'hasWorkedBefore': campaign.as_template_boolean( project.kind != project_pb2.FIND_A_FIRST_JOB), 'inCity': in_city, 'inTargetDomain': in_target_domain, 'isAbleBodied': campaign.as_template_boolean(not user.profile.has_handicap), 'isYoung': campaign.as_template_boolean(age <= max_young), 'jobGroupInDepartement': f'{job_group_name} {in_departement}', 'networkApplicationPercentage': network_application_importance, }
def _open_classrooms_vars(user: user_pb2.User, *, database: mongo.NoPiiMongoDatabase, **unused_kwargs: Any) -> dict[str, str]: """Template variables for open classrooms email.""" if user.registered_at.ToDatetime() < _SIX_MONTHS_AGO: raise campaign.DoNotSend('User registered less than 6 months ago.') age = datetime.date.today().year - user.profile.year_of_birth if age < 18: raise campaign.DoNotSend( 'User too young to subscribe to OpenClassrooms.') if age > 54: raise campaign.DoNotSend( 'User too old to subscribe to OpenClassrooms.') if user.profile.highest_degree > job_pb2.BAC_BACPRO: raise campaign.DoNotSend('User might have higher education.') if user.employment_status and user.employment_status[ -1].seeking != user_pb2.STILL_SEEKING: raise campaign.DoNotSend('User is no more seeking for a job.') if not (user.projects and user.projects[0]): raise scoring.NotEnoughDataException('Project is required.', fields={'user.projects.0.kind'}) project = user.projects[0] if project.kind != project_pb2.REORIENTATION and not ( project.kind == project_pb2.FIND_A_NEW_JOB and project.passionate_level == project_pb2.ALIMENTARY_JOB): raise campaign.DoNotSend( 'User is happy with their job (no reorientation and enthusiastic about their job).' ) has_children = user.profile.family_situation in { user_profile_pb2.FAMILY_WITH_KIDS, user_profile_pb2.SINGLE_PARENT_SITUATION, } job_group_info = jobs.get_group_proto(database, project.target_job.job_group.rome_id) if not job_group_info: raise scoring.NotEnoughDataException( 'Requires job group info for the difficulty of applying to this kind of job.' ) return campaign.get_default_coaching_email_vars(user) | { 'hasAtypicProfile': campaign.as_template_boolean( user_profile_pb2.ATYPIC_PROFILE in user.profile.frustrations), 'hasFamilyAndManagementIssue': campaign.as_template_boolean( has_children and user_profile_pb2.TIME_MANAGEMENT in user.profile.frustrations), 'hasSeniority': campaign.as_template_boolean( project.seniority > project_pb2.INTERMEDIARY), 'hasSimpleApplication': campaign.as_template_boolean(job_group_info.application_complexity == job_pb2.SIMPLE_APPLICATION_PROCESS), 'isReorienting': campaign.as_template_boolean( project.kind == project_pb2.REORIENTATION), 'isFrustratedOld': campaign.as_template_boolean(age >= 40 and user_profile_pb2.AGE_DISCRIMINATION in user.profile.frustrations), 'ofFirstName': french.maybe_contract_prefix('de ', "d'", user.profile.name) }
def score(self, project: scoring.ScoringProject) -> float: raise scoring.NotEnoughDataException(fields={'projects.0'})
_get_best_departements_for_job_group(rome_id, database), project.area_type, database, scoring_project): shown_sections.append('departements') if employment_types_section := _make_employment_type_section( imt.employment_type_percentages, scoring_project): shown_sections.append('employmentTypes') if months_section := _make_months_section( imt.active_months, scoring_project.translate_static_string(_FRENCH_MONTHS)): shown_sections.append('months') if len(shown_sections) < 3: raise scoring.NotEnoughDataException( f'Not enough IMT data for this user, only {len(shown_sections)}') if _BOB_DEPLOYMENT == 'fr': imt_link = 'https://candidat.pole-emploi.fr/marche-du-travail/statistiques?' \ f'codeMetier={project.target_job.code_ogr}&codeZoneGeographique={departement_id}&' \ 'typeZoneGeographique=DEPARTEMENT' elif _BOB_DEPLOYMENT == 'usa': imt_link = 'https://www.bls.gov/oes/current/' \ f'oes{project.target_job.job_group.rome_id.replace("-", "")}.htm' else: imt_link = '' if departement_id: in_departement = geo.get_in_a_departement_text( database, departement_id, locale=scoring_project.user_profile.locale, city_hint=project.city)