Skip to content

Commit

Permalink
Merge branch 'main' into feat/hackaton-divers
Browse files Browse the repository at this point in the history
  • Loading branch information
alanlr authored Sep 12, 2024
2 parents b57701e + b4a05cb commit 2cf6e09
Show file tree
Hide file tree
Showing 33 changed files with 576 additions and 198 deletions.
6 changes: 4 additions & 2 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ fileignoreconfig:
- filename: server/src/http/controllers/updateRecruteurLba.controller.test.ts
checksum: 71dd55af4f1f44fd2b69dee91f0fc8006f3b9e0bd80d40271c73697d4eea9ea2
- filename: server/src/http/controllers/v2/jobs.controller.v2.test.ts
checksum: 21b3574a8d82bba190ecdf1ace1b5db58db0fa45e6890506de530bc0781380af
checksum: 06366000e6615ce90b5dedbd4792eac0a93ce5a0cf1459273e3cc9bbdf867e4f
- filename: server/src/http/routes/appointmentRequest.controller.ts
checksum: d2770daa97ae332eec0b66497fdb717229895583ac3bfd48af1a830b36504968
- filename: server/src/http/routes/auth/password.controller.ts
Expand Down Expand Up @@ -90,7 +90,7 @@ fileignoreconfig:
- filename: server/src/services/jobs/jobOpportunity/jobOpportunity.service.test.ts
checksum: 7c2a7f5d8ec1c82652296c3271bd435f5998e9c61f9ead2d528a5f2a7195a07d
- filename: server/src/services/recruteurLba.service.test.ts
checksum: 98a1f338a0721200ace9b9a62f755996ad395035621c5830681e721ca58d67c2
checksum: 38c947c87cfb508fec88cbce878f89725cbb6679e833a66bdb7fca60e3685a0a
- filename: server/src/services/referrers.service.ts
checksum: 966b0ece2b18b5a6066df531524c97ba0ad38266e782a334004e8c127b41ade6
- filename: server/src/services/userRecruteur.service.ts
Expand Down Expand Up @@ -157,6 +157,8 @@ fileignoreconfig:
checksum: 477c08911f43c273ad8b257c3885ae98b92e6a74c8ad096c7023201762907414
- filename: ui/components/HomeComponents/FacilitezRDVA.tsx
checksum: cdeae2ef94431bbe8fd1bb0f1bc0c007c938c1a1d4513316cc44cb966b7f4268
- filename: ui/components/InfoBanner/InfoBanner.tsx
checksum: e5f8bd78ea46335fbb8784eabc01391be68922960f7d04ce9e8755484d10ce32
- filename: ui/components/ItemDetail/ItemDetail.tsx
checksum: 1fcc0442306f83b5e45bf7da67304527598d7749b9e2642c6d4628d3b4f15a9c
- filename: ui/components/ItemDetail/LbaJobComponents/LbaJobTechniques.tsx
Expand Down
24 changes: 23 additions & 1 deletion server/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,10 +538,32 @@ program
.option("-parallelism, [parallelism]", "Number of threads", "10")
.action(createJobAction("referentiel-opco:constructys:import"))

program.command("import-hellowork").description("Importe les offres hellowork").option("-q, --queued", "Run job asynchronously", false).action(createJobAction("import-hellowork"))
program
.command("import-hellowork")
.description("Importe les offres hellowork dans la collection raw")
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("import-hellowork"))

program
.command("import-hellowork-to-computed")
.description("Importe les offres hellowork depuis raw vers computed")
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("import-hellowork-to-computed"))

program.command("import-kelio").description("Importe les offres kelio").option("-q, --queued", "Run job asynchronously", false).action(createJobAction("import-kelio"))

program
.command("import-computed-to-jobs-partners")
.description("Met à jour la collection jobs_partners à partir de computed_jobs_partners")
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("import-computed-to-jobs-partners"))

program
.command("cancel-removed-jobs-partners")
.description("Met à jour la collection jobs_partners en mettant à 'Annulé' les offres qui ne sont plus dans computed_jobs_partners")
.option("-q, --queued", "Run job asynchronously", false)
.action(createJobAction("cancel-removed-jobs-partners"))

program
.command("send-applications")
.description("Scanne les virus des pièces jointes et envoie les candidatures. Timeout à 8 minutes.")
Expand Down
52 changes: 6 additions & 46 deletions server/src/http/controllers/v2/jobs.controller.v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useServer } from "@tests/utils/server.test.utils"
import { ObjectId } from "mongodb"
import nock from "nock"
import { generateJobsPartnersOfferPrivate } from "shared/fixtures/jobPartners.fixture"
import { generateLbaConpanyFixture } from "shared/fixtures/recruteurLba.fixture"
import { generateLbaCompanyFixture } from "shared/fixtures/recruteurLba.fixture"
import { clichyFixture, generateReferentielCommuneFixtures, levalloisFixture, marseilleFixture, parisFixture } from "shared/fixtures/referentiel/commune.fixture"
import { IGeoPoint } from "shared/models"
import { IJobsPartnersOfferPrivate, IJobsPartnersWritableApiInput } from "shared/models/jobsPartners.model"
Expand Down Expand Up @@ -39,7 +39,7 @@ const porteDeClichy: IGeoPoint = {
}
const romesQuery = rome.join(",")
const [longitude, latitude] = porteDeClichy.coordinates
const recruteurLba = generateLbaConpanyFixture({ rome_codes: rome, geopoint: clichyFixture.centre, siret: "58006820882692", email: "[email protected]", website: "http://site.fr" })
const recruteurLba = generateLbaCompanyFixture({ rome_codes: rome, geopoint: clichyFixture.centre, siret: "58006820882692", email: "[email protected]", website: "http://site.fr" })
const jobPartnerOffer: IJobsPartnersOfferPrivate = generateJobsPartnersOfferPrivate({
offer_rome_codes: ["D1214"],
workplace_geopoint: parisFixture.centre,
Expand Down Expand Up @@ -164,8 +164,8 @@ describe("GET /jobs/search", () => {
"offer_target_diploma",
"offer_title",
"offer_to_be_acquired_skills",
"partner",
"partner_job_id",
"partner_label",
"workplace_address",
"workplace_brand",
"workplace_description",
Expand Down Expand Up @@ -284,35 +284,15 @@ describe("POST /jobs", async () => {
const inSept = new Date("2024-09-01T00:00:00.000Z")

const data: IJobsPartnersWritableApiInput = {
partner_job_id: null,

contract_start: inSept.toJSON(),
contract_duration: null,
contract_type: null,
contract_remote: null,

offer_title: "Apprentis en développement web",
offer_rome_codes: ["M1602"],
offer_desired_skills: [],
offer_to_be_acquired_skills: [],
offer_access_conditions: [],
offer_creation: null,
offer_expiration: null,
offer_opening_count: 1,
offer_origin: null,
offer_multicast: true,
offer_description: "Envie de devenir développeur web ? Rejoignez-nous !",
offer_diploma_level_european: null,

apply_url: null,
apply_email: "[email protected]",
apply_phone: null,

workplace_siret: apiEntrepriseEtablissementFixture.dinum.data.siret,
workplace_address_label: null,
workplace_description: null,
workplace_website: null,
workplace_name: null,
}

beforeEach(async () => {
Expand Down Expand Up @@ -385,7 +365,7 @@ describe("POST /jobs", async () => {
const doc = await getDbCollection("jobs_partners").findOne({ _id: new ObjectId(responseJson.id as string) })

// Ensure that the job offer is associated to the correct permission
expect(doc?.partner).toBe("Un super Partenaire")
expect(doc?.partner_label).toBe("Un super Partenaire")
})

it("should apply method be defined", async () => {
Expand Down Expand Up @@ -431,38 +411,18 @@ describe("PUT /jobs/:id", async () => {
const now = new Date("2024-06-18T00:00:00.000Z")
const inSept = new Date("2024-09-01T00:00:00.000Z")

const originalJob = generateJobsPartnersOfferPrivate({ _id: id, offer_title: "Old title", partner: "Un super Partenaire" })
const originalJob = generateJobsPartnersOfferPrivate({ _id: id, offer_title: "Old title", partner_label: "Un super Partenaire" })

const data: IJobsPartnersWritableApiInput = {
partner_job_id: null,

contract_start: inSept.toJSON(),
contract_duration: null,
contract_type: null,
contract_remote: null,

offer_title: "Apprentis en développement web",
offer_rome_codes: ["M1602"],
offer_desired_skills: [],
offer_to_be_acquired_skills: [],
offer_access_conditions: [],
offer_creation: null,
offer_expiration: null,
offer_opening_count: 1,
offer_origin: null,
offer_multicast: true,
offer_description: "Envie de devenir développeur web ? Rejoignez-nous !",
offer_diploma_level_european: null,

apply_url: null,
apply_email: "[email protected]",
apply_phone: null,

workplace_siret: apiEntrepriseEtablissementFixture.dinum.data.siret,
workplace_address_label: null,
workplace_description: null,
workplace_website: null,
workplace_name: null,
}

beforeEach(async () => {
Expand Down Expand Up @@ -551,7 +511,7 @@ describe("PUT /jobs/:id", async () => {
expect(response.json()).toEqual({ error: "Forbidden", message: "Unauthorized", statusCode: 403 })
})

it("should return 403 if user is trying to edit other partner job", async () => {
it("should return 403 if user is trying to edit other partner_label job", async () => {
const restrictedToken = getApiApprentissageTestingToken({ email: "[email protected]", organisation: "Un autre", habilitations: { "jobs:write": true } })

const response = await httpClient().inject({
Expand Down
13 changes: 12 additions & 1 deletion server/src/jobs/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ import { pocRomeo } from "./franceTravail/pocRomeo"
import { createJobsCollectionForMetabase } from "./metabase/metabaseJobsCollection"
import { createRoleManagement360 } from "./metabase/metabaseRoleManagement360"
import { runGarbageCollector } from "./misc/runGarbageCollector"
import { importHelloWork } from "./offrePartenaire/importHelloWork"
import { cancelRemovedJobsPartners } from "./offrePartenaire/cancelRemovedJobsPartners"
import { importFromComputedToJobsPartners } from "./offrePartenaire/importFromComputedToJobsPartners"
import { importHelloWork, importRawHelloWorkIntoComputedJobPartners } from "./offrePartenaire/importHelloWork"
import { importKelio } from "./offrePartenaire/importKelio"
import { exportLbaJobsToS3 } from "./partenaireExport/exportJobsToS3"
import { exportToFranceTravail } from "./partenaireExport/exportToFranceTravail"
Expand Down Expand Up @@ -414,9 +416,18 @@ export async function setupJobProcessor() {
"import-hellowork": {
handler: async () => importHelloWork(),
},
"import-hellowork-to-computed": {
handler: async () => importRawHelloWorkIntoComputedJobPartners(),
},
"import-kelio": {
handler: async () => importKelio(),
},
"import-computed-to-jobs-partners": {
handler: async () => importFromComputedToJobsPartners(),
},
"cancel-removed-jobs-partners": {
handler: async () => cancelRemovedJobsPartners(),
},
"send-applications": {
handler: async () => processApplications(),
},
Expand Down
56 changes: 56 additions & 0 deletions server/src/jobs/offrePartenaire/cancelRemovedJobsPartners.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createComputedJobPartner, createJobPartner } from "@tests/utils/jobsPartners.test.utils"
import { useMongo } from "@tests/utils/mongo.test.utils"
import { JOB_STATUS_ENGLISH } from "shared/models"
import { beforeEach, describe, expect, it } from "vitest"

import { getDbCollection } from "@/common/utils/mongodbUtils"

import { cancelRemovedJobsPartners } from "./cancelRemovedJobsPartners"

useMongo()

describe("Canceling jobs_partners that have been removed from computed_jobs_partners", () => {
beforeEach(async () => {
// créations de plusieurs éléments existants dans jobs partners
// création de plusieurs éléments dans computed jobs partners . certains avec validated true, d'autres false
// certains éléments validated de computed sont déjà présents dans jobs partners
// certains éléments dans jobs partners ne sont pas dans computed
await createJobPartner({ partner_job_id: "existing_1", partner_label: "ft", offer_status: JOB_STATUS_ENGLISH.ACTIVE })
await createJobPartner({ partner_job_id: "existing_2", partner_label: "ft", offer_status: JOB_STATUS_ENGLISH.ACTIVE })
await createJobPartner({ partner_job_id: "existing_3", partner_label: "hw", offer_status: JOB_STATUS_ENGLISH.ACTIVE })
await createJobPartner({ partner_job_id: "existing_4", partner_label: "hw", offer_status: JOB_STATUS_ENGLISH.ACTIVE })
await createJobPartner({ partner_job_id: "existing_5", partner_label: "hw", offer_status: JOB_STATUS_ENGLISH.ACTIVE })
await createComputedJobPartner({ partner_job_id: "existing_1", partner_label: "notft", validated: true })
await createComputedJobPartner({ partner_job_id: "computed_1", partner_label: "ft", validated: true })
await createComputedJobPartner({ partner_job_id: "computed_2", partner_label: "ft", validated: false })
await createComputedJobPartner({ partner_job_id: "existing_3", partner_label: "hw", validated: true })
await createComputedJobPartner({ partner_job_id: "existing_4", partner_label: "hw", validated: true })
await createComputedJobPartner({ partner_job_id: "existing_5", partner_label: "hw", validated: false })

return async () => {
await getDbCollection("computed_jobs_partners").deleteMany({})
await getDbCollection("jobs_partners").deleteMany({})
}
})

it("L'annulation dans jobs_partners fonctionne comme attendue : \n- les éléments de jobs_partners qui ne sont plus dans computed doivent être taggés Annulé\n- les éléments de jobs_partners qui sont également dans computed sont toujours présents\n- aucun éléments de jobs_partners n'a été retiré de la collection", async () => {
await cancelRemovedJobsPartners()

// les éléments de jobs_partners qui ne sont plus dans computed doivent être taggés Annulé
const countCanceledJobsPartners = await getDbCollection("jobs_partners").countDocuments({
partner_job_id: { $in: ["existing_1", "existing_2"] },
offer_status: JOB_STATUS_ENGLISH.ANNULEE,
})
expect.soft(countCanceledJobsPartners).toEqual(2)

// les éléments de jobs_partners qui sont également dans computed sont toujours présents
const countRemainingJobsPartners = await getDbCollection("jobs_partners").countDocuments({
partner_job_id: { $in: ["existing_3", "existing_4", "existing_5"] },
})
expect.soft(countRemainingJobsPartners).toEqual(3)

// aucun éléments de jobs_partners n'a été retiré de la collection
const countJobsPartners = await getDbCollection("jobs_partners").countDocuments({})
expect.soft(countJobsPartners).toEqual(5)
})
})
45 changes: 45 additions & 0 deletions server/src/jobs/offrePartenaire/cancelRemovedJobsPartners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { JOB_STATUS_ENGLISH } from "shared/models"

import { getDbCollection } from "@/common/utils/mongodbUtils"

export const cancelRemovedJobsPartners = async () => {
const pipeline = [
{
$lookup: {
from: "computed_jobs_partners",
let: { partnerLabel: "$partner_label", partnerJobId: "$partner_job_id" },
pipeline: [
{
$match: {
$expr: {
$and: [{ $eq: ["$partner_label", "$$partnerLabel"] }, { $eq: ["$partner_job_id", "$$partnerJobId"] }],
},
},
},
],
as: "matched",
},
},
{
$match: {
matched: { $size: 0 },
},
},
{
$set: { offer_status: JOB_STATUS_ENGLISH.ANNULEE },
},
{
$unset: "matched",
},
{
$merge: {
into: "jobs_partners",
on: ["partner_job_id", "partner_label"],
whenMatched: "merge",
whenNotMatched: "discard",
},
},
]

await getDbCollection("jobs_partners").aggregate(pipeline).toArray()
}
4 changes: 2 additions & 2 deletions server/src/jobs/offrePartenaire/helloWorkMapper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe("helloWorkJobToJobsPartners", () => {
}
})

it("should convert a hellowork job to a partner job", () => {
it("should convert a hellowork job to a partner_label job", () => {
expect(
helloWorkJobToJobsPartners({
job_id: "73228597",
Expand Down Expand Up @@ -62,7 +62,7 @@ describe("helloWorkJobToJobsPartners", () => {
).toEqual({
_id: expect.any(ObjectId),
created_at: now,
partner: "Hellowork",
partner_label: "Hello work",
partner_job_id: "73228597",
contract_start: new Date("2024-12-01T00:00:00.000+01:00"),
contract_type: ["Apprentissage", "Professionnalisation"],
Expand Down
5 changes: 3 additions & 2 deletions server/src/jobs/offrePartenaire/helloWorkMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,12 @@ export const helloWorkJobToJobsPartners = (job: IHelloWorkJob): IComputedJobsPar
const { latitude, longitude } = geolocToLatLon(geoloc)
const siretParsing = extensions.siret.safeParse(siret)
const codeRomeParsing = extensions.romeCode().safeParse(code_rome)
const urlParsing = extensions.url().safeParse(url)

const partnerJob: IComputedJobsPartners = {
_id: new ObjectId(),
created_at: new Date(),
partner: JOBPARTNERS_LABEL.HELLOWORK,
partner_label: JOBPARTNERS_LABEL.HELLOWORK,
partner_job_id: job_id,
contract_start: parseDate(contract_start_date),
contract_type: contract.toLowerCase() === "alternance" ? [TRAINING_CONTRACT_TYPE.APPRENTISSAGE, TRAINING_CONTRACT_TYPE.PROFESSIONNALISATION] : undefined,
Expand Down Expand Up @@ -122,7 +123,7 @@ export const helloWorkJobToJobsPartners = (job: IHelloWorkJob): IComputedJobsPar
coordinates: [longitude, latitude],
}
: undefined,
apply_url: url,
apply_url: urlParsing.success ? urlParsing.data : null,
errors: [],
validated: false,
}
Expand Down
Loading

0 comments on commit 2cf6e09

Please sign in to comment.