toulouse-foederis: add candidature endpoint (#77524)
gitea/passerelle/pipeline/head This commit looks good Details

This commit is contained in:
Corentin Sechet 2023-05-11 22:38:08 +02:00 committed by Corentin Sechet
parent 816da0f6b6
commit 7395fa5560
2 changed files with 403 additions and 0 deletions

View File

@ -30,6 +30,229 @@ from passerelle.utils.api import endpoint
from passerelle.utils.json import datasource_array_schema, datasource_schema, response_schema
def boolean_field(description):
return {
'description': description,
'oneOf': [
{'type': 'boolean'},
{
'type': 'string',
'pattern': '[Oo]|[Nn]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|1|0',
'pattern_description': _(
'Values "0", "1", "O", "N", "true" or "false" are allowed (case insensitive).'
),
},
],
}
def get_bool(obj):
if obj is True or str(obj).lower() in ['true', 'o', '1']:
return True
if obj is False or str(obj).lower() in ['false', 'n', '0']:
return False
return obj
APPLICATION_SCHEMA = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Toulouse Foederis application',
'description': '',
'type': 'object',
'properties': {
'type': {
'description': _('Application Type (External or Internal).'),
'type': 'string',
},
'announce_id': {
'description': _('ID of the concerned job offer.'),
'type': 'string',
'pattern': '^[0-9]*$',
},
'civility': {
'description': _("ID of an element of the data source 'civilite'."),
'type': 'string',
'pattern': '^[0-9]+$',
},
'first_name': {
'description': _('Applicant first name.'),
'type': 'string',
},
'last_name': {
'description': _('Applicant last name.'),
'type': 'string',
},
'gender': {
'description': _('Applicant gender.'),
'type': 'string',
'enum': ['H', 'F', ''],
},
'birth_date': {
'description': _('Applicant birth date.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'nationality': {
'description': _("ID of an element of the data source 'nationalite'."),
'type': 'string',
'pattern': '^[0-9]+$',
},
'work_authorization_end_date': {
'description': _("Applicant end of working authorization, if nationality is 'other'."),
'example_value': '2023-04-05',
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'rqth': boolean_field(_('RQTH.')),
'rqth_end_date': {
'description': _('End of RQTH, or none if not applicable.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'driving_license': {
'description': _('Driving license.'),
'type': 'string',
},
'fimo': boolean_field(_('FIMO licence.')),
'fimo_delivrance_date': {
'description': _('FIMO licence delivrance date.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'fimo_end_validity_date': {
'description': _('FIMO licence end validity date.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'current_situation': {
'description': _("ID of an element of the data source 'situation-actuelle'."),
'type': 'string',
'pattern': '^[0-9]+$',
},
'agent_collectivity': {
'description': _("Agent's collectivity"),
'type': 'string',
},
'availability_start_date': {
'description': _('Applicant availability start date.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'availability_end_date': {
'description': _('Applicant availability end date.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'salary_expectations': {
'description': _('Applicant salary expectations.'),
'type': 'string',
},
'address': {
'description': _('Applicant address.'),
'type': 'string',
},
'address_complement': {
'description': _('Applicant address complement.'),
'type': 'string',
},
'zip': {
'description': _('Applicant zip code.'),
'type': 'string',
},
'city': {
'description': _('Applicant city.'),
'type': 'string',
},
'phone': {
'description': _('Applicant phone number.'),
'type': 'string',
},
'email': {
'description': _('Applicant email.'),
'type': 'string',
},
'contract_start_date': {
'description': _('Applicant contract start date.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'contract_end_date': {
'description': _('Applicant contract end date.'),
'type': 'string',
'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2})?$',
},
'additional_informations': {
'description': _('Application information complement.'),
'type': 'string',
},
'origin': {
'description': _("ID of an element of the data source 'origine-candidature'."),
'pattern': '^[0-9]*$',
'type': 'string',
},
'origin_precisions': {
'description': _("Precisions if 'origine' is 'other'."),
'type': 'string',
},
'rgpd_agreement': boolean_field(_('RGPD agreement.')),
'job_type': {
'description': _("ID of an element of the data source 'type-emploi'."),
'type': 'string',
'pattern': '^[0-9]*$',
},
'job_realm': {
'description': _("ID of an element of the data source 'domaine-emploi'."),
'type': 'string',
'pattern': '^[0-9]*$',
},
'job_family': {
'description': _("ID of an element of the data source 'sous-domaine-emploi'."),
'type': 'string',
'pattern': '^[0-9]*$',
},
'job': {
'description': _("ID of an element of the data source 'emploi'."),
'type': 'string',
'pattern': '^[0-9]*$',
},
'desired_work_time': {
'description': _('TC / TNC.'),
'type': 'string',
'enum': ['TC', 'TNC'],
},
'internship_duration': {
'description': _('Duration of the desired internship.'),
'type': 'string',
},
'school_name': {
'description': _("Candidate trainee's school name."),
'type': 'string',
},
'diploma_name': {
'description': _("Candidate trainee's diploma name."),
'type': 'string',
},
'diploma_speciality': {
'description': _("Candidate trainee's diploma speciality."),
'type': 'string',
},
'aimed_diploma_level': {
'description': _("ID of an element of the data source 'niveau-diplome'."),
'type': 'string',
'pattern': '^[0-9]*$',
},
'last_obtained_diploma': {
'description': _("Candidate trainee's last obtained diploma."),
'type': 'string',
},
'last_course_taken': {
'description': _("Candidate trainee's last taken course."),
'type': 'string',
},
},
}
class UpdateError(Exception):
pass
@ -295,6 +518,78 @@ class Resource(BaseResource, HTTPResource):
with document.pdf.open() as fd:
return HttpResponse(fd, content_type='application/pdf')
@endpoint(
perm="can_access",
name="create-application",
post={
"description": _("Creates an application"),
"request_body": {"schema": {"application/json": APPLICATION_SCHEMA}},
},
)
def create_application(self, request, post_data):
def _get_id(field_name):
id = post_data.get(field_name, None)
if id is None or id == '':
return None
return int(id)
request_data = {
'type_de_candidature': post_data.get('type', 'E'),
'annonce': _get_id('announce_id'),
self.REFERENTIELS_FKEYS['civilite']: _get_id('civility'),
'firstName': post_data.get('first_name', None),
'lastName': post_data.get('last_name', None),
'sexe': post_data.get('gender', None),
'date_de_naissance': post_data.get('birth_date', None),
self.REFERENTIELS_FKEYS['nationalite']: _get_id('nationality'),
'date_fin_autorisation_de_travail': post_data.get('work_authorization_end_date', None),
'rqth': 'O' if get_bool(post_data.get('rqth', False)) else 'N',
'date_fin_rqth': post_data.get('rqth_end_date', None),
'permis_de_conduire': post_data.get('driving_license', None),
'fimo': 'O' if get_bool(post_data.get('fimo', False)) else 'N',
'Date_delivrance_fimo': post_data.get('fimo_delivrance_date', None),
'date_fin_validite_fimo': post_data.get('fimo_end_validity_date', None),
self.REFERENTIELS_FKEYS['situation_actuelle']: _get_id('current_situation'),
'collectivite_agent': post_data.get('agent_collectivity', None),
'date_debut_disponibilite': post_data.get('availability_start_date', None),
'date_fin_disponibilite': post_data.get('availability_end_date', None),
'pretentions_salariales': post_data.get('salary_expectations', None),
'adresse': post_data.get('address', None),
'adresse_ligne_2': post_data.get('address_complement', None),
'code_postal': post_data.get('zip', None),
'ville': post_data.get('city', None),
'telephone': post_data.get('phone', None),
'email': post_data.get('email', None),
'date_debut_contrat': post_data.get('contract_start_date', None),
'date_fin_contrat': post_data.get('contract_end_date', None),
'complement_information_candidature': post_data.get('additional_informations', None),
self.REFERENTIELS_FKEYS['origine_candidature']: _get_id('origin'),
'precision_origine_candidature': post_data.get('origin_precisions', None),
'accord_RGPD': get_bool(post_data.get('rgpd_agreement', False)),
self.REFERENTIELS_FKEYS['type_emploi']: _get_id('job_type'),
self.REFERENTIELS_FKEYS['domaine_emploi']: _get_id('job_realm'),
self.REFERENTIELS_FKEYS['sous_domaine_emploi']: _get_id('job_family'),
self.REFERENTIELS_FKEYS['emploi']: _get_id('job'),
'temps_de_travail_souhaite': post_data.get('desired_work_time', None),
'duree_du_contrat_de_stage_apprentissage': post_data.get('internship_duration', None),
'ecole_centre_de_formation_mission_loc': post_data.get('school_name', None),
'intitule_diplome_vise': post_data.get('diploma_name', None),
'specialite_diplome': post_data.get('diploma_speciality', None),
self.REFERENTIELS_FKEYS['niveau_diplome']: _get_id('aimed_diploma_level'),
'dernier_diplome_obtenu': post_data.get('last_obtained_diploma', None),
'derniere_classe_suivie': post_data.get('last_course_taken', None),
}
request_data = {k: v for k, v in request_data.items() if v is not None and v != ''}
results = self.http_request(
'POST', 'data/candidature?viewIntegrationName=api_publik', json=request_data
)
return {
'err': 0,
'data': {'application_id': results[0]['id']},
}
@endpoint(
description=_('List announces'),
long_description=_(

View File

@ -405,6 +405,114 @@ class TestEndpoints:
app.get('/toulouse-foederis/foederis/announce/111/pdf/', status=404)
assert response.headers['content-type'] == 'application/pdf'
def test_create_application(self, resource, app):
@httmock.urlmatch(path=r'^.*/data/candidature$')
def handler(url, request):
assert request.headers['content-type'] == 'application/json'
assert request.headers['api-key'] == APIKEY
payload = json.loads(request.body)
assert payload == {
"type_de_candidature": "E",
"annonce": 524522,
"R60284409": 170013,
"firstName": "John",
"lastName": "Doe",
"sexe": "H",
"date_de_naissance": "1985-03-06",
"R1249730": 93421,
"date_fin_autorisation_de_travail": "2023-05-09",
"rqth": "N",
"date_fin_rqth": "2023-05-08",
"permis_de_conduire": "A,B",
"fimo": "O",
"Date_delivrance_fimo": "2023-05-07",
"date_fin_validite_fimo": "2023-05-08",
"R1258320": 1258319,
"collectivite_agent": "Mairie de Toulouse",
"date_debut_disponibilite": "2023-05-02",
"date_fin_disponibilite": "2023-05-01",
"pretentions_salariales": "1000",
"adresse": "12 Sesame Street",
"code_postal": "77710",
"ville": "Nemours",
"telephone": "+33 636656565",
"email": "csechet@entrouvert.com",
"date_debut_contrat": "2023-05-06",
"date_fin_contrat": "2023-05-04",
"complement_information_candidature": "I need money.",
"R1261279": 1561049,
"accord_RGPD": True,
"R1249707": 157193,
"R60845221": 5776395,
"R60845244": 5776394,
"temps_de_travail_souhaite": "TC",
"duree_du_contrat_de_stage_apprentissage": "2h",
"ecole_centre_de_formation_mission_loc": "Ecole de la vie",
"intitule_diplome_vise": "BE",
"specialite_diplome": "Curling",
"R1249737": 1124022,
"dernier_diplome_obtenu": "BAC",
"derniere_classe_suivie": "Terminale",
}
return httmock.response(200, json.dumps({"code": 200, "results": [{"id": 42}]}))
@httmock.urlmatch()
def error_handler(url, request):
assert False, 'should not be reached'
with httmock.HTTMock(handler, error_handler):
response = app.post_json(
'/toulouse-foederis/foederis/create-application',
params={
"additional_informations": "I need money.",
"address": "12 Sesame Street",
"address_complement": "",
"agent_collectivity": "Mairie de Toulouse",
"aimed_diploma_level": "1124022",
"announce_id": "0524522",
"availability_end_date": "2023-05-01",
"availability_start_date": "2023-05-02",
"birth_date": "1985-03-06",
"city": "Nemours",
"civility": "170013",
"contract_end_date": "2023-05-04",
"contract_start_date": "2023-05-06",
"current_situation": "1258319",
"desired_work_time": "TC",
"diploma_name": "BE",
"diploma_speciality": "Curling",
"driving_license": "A,B",
"email": "csechet@entrouvert.com",
"fimo": "o",
"fimo_delivrance_date": "2023-05-07",
"fimo_end_validity_date": "2023-05-08",
"first_name": "John",
"gender": "H",
"internship_duration": "2h",
"job_family": "5776394",
"job_realm": "5776395",
"job_type": "157193",
"last_course_taken": "Terminale",
"last_name": "Doe",
"last_obtained_diploma": "BAC",
"nationality": "93421",
"origin": "1561049",
"origin_precisions": "",
"phone": "+33 636656565",
"rgpd_agreement": "tRuE",
"rqth": False,
"rqth_end_date": "2023-05-08",
"salary_expectations": "1000",
"school_name": "Ecole de la vie",
"type": "E",
"work_authorization_end_date": "2023-05-09",
"zip": "77710",
},
)
assert response.json["data"]["application_id"] == 42
def test_migration_0003_no_null_no_charfield(migration):
with connection.cursor() as cur: