From 33b7818086d4dfe972f7086477d477f612a53f1b Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Fri, 10 Nov 2023 12:55:57 +0100 Subject: [PATCH 01/11] :white_check_mark: api: Split test as there can be only one complementary per certifification --- .../repositories/jury-certification-repository_test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js b/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js index dd237d37cfc..f0c40b5c4de 100644 --- a/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js @@ -37,6 +37,7 @@ describe('Integration | Infrastructure | Repository | Jury Certification', funct complementaryCertificationBadgeId: 3453, level: 1, label: 'Badge for complementary certification without external jury', + complementaryCertificationCourse: null, }); databaseBuilder.factory.buildUser({ id: 789 }); From 58909bf16fc7ce044205ca0b8f75c1f4fd676bd3 Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Tue, 21 Nov 2023 22:52:56 +0100 Subject: [PATCH 02/11] :recycle: api: Simplify repo for single complementary per certification --- .../certification-result-repository.js | 78 +++--- .../certification-result-repository_test.js | 230 +++++------------- 2 files changed, 103 insertions(+), 205 deletions(-) diff --git a/api/lib/infrastructure/repositories/certification-result-repository.js b/api/lib/infrastructure/repositories/certification-result-repository.js index 929f3664a0c..0642e59b8c0 100644 --- a/api/lib/infrastructure/repositories/certification-result-repository.js +++ b/api/lib/infrastructure/repositories/certification-result-repository.js @@ -1,9 +1,6 @@ import { knex } from '../../../db/knex-database-connection.js'; import { ComplementaryCertificationCourseResult } from '../../domain/models/ComplementaryCertificationCourseResult.js'; import { CertificationResult } from '../../domain/models/CertificationResult.js'; -import lodash from 'lodash'; - -const { isEmpty } = lodash; const findBySessionId = async function ({ sessionId }) { const certificationResultDTOs = await _selectCertificationResults() @@ -16,13 +13,9 @@ const findBySessionId = async function ({ sessionId }) { sessionId, }); - return certificationResultDTOs.map((certificationResultDTO) => { - certificationResultDTO.complementaryCertificationCourseResults = - complementaryCertificationCourseResultsByCertificationCourseId.find( - ({ certificationCourseId }) => certificationCourseId === certificationResultDTO.id, - )?.complementaryCertificationCourseResults; - return _toDomain(certificationResultDTO); - }); + return certificationResultDTOs.map((certificationResultDTO) => + _toDomain({ certificationResultDTO, complementaryCertificationCourseResultsByCertificationCourseId }), + ); }; const findByCertificationCandidateIds = async function ({ certificationCandidateIds }) { @@ -36,21 +29,18 @@ const findByCertificationCandidateIds = async function ({ certificationCandidate .orderBy('certification-courses.lastName', 'ASC') .orderBy('certification-courses.firstName', 'ASC'); - let complementaryCertificationCourseResultsByCertificationCourseId = []; - if (!isEmpty(certificationResultDTOs)) { - complementaryCertificationCourseResultsByCertificationCourseId = - await _selectComplementaryCertificationCourseResultsBySessionId({ - sessionId: certificationResultDTOs[0].sessionId, - }); + if (!certificationResultDTOs.length) { + return []; } - return certificationResultDTOs.map((certificationResultDTO) => { - certificationResultDTO.complementaryCertificationCourseResults = - complementaryCertificationCourseResultsByCertificationCourseId.find( - ({ certificationCourseId }) => certificationCourseId === certificationResultDTO.id, - )?.complementaryCertificationCourseResults; - return _toDomain(certificationResultDTO); - }); + const complementaryCertificationCourseResultsByCertificationCourseId = + await _selectComplementaryCertificationCourseResultsBySessionId({ + sessionId: certificationResultDTOs[0].sessionId, + }); + + return certificationResultDTOs.map((certificationResultDTO) => + _toDomain({ certificationResultDTO, complementaryCertificationCourseResultsByCertificationCourseId }), + ); }; export { findBySessionId, findByCertificationCandidateIds }; @@ -94,20 +84,16 @@ function _selectCertificationResults() { function _selectComplementaryCertificationCourseResultsBySessionId({ sessionId }) { return knex('complementary-certification-course-results') - .select({ certificationCourseId: 'certification-courses.id' }) - .select( - knex.raw(` - array_agg(json_build_object( - 'complementaryCertificationCourseId', "complementary-certification-course-results"."complementaryCertificationCourseId", - 'id', "complementary-certification-course-results"."id", - 'partnerKey', "complementary-certification-course-results"."partnerKey", - 'acquired', "complementary-certification-course-results"."acquired", - 'source', "complementary-certification-course-results"."source", - 'label', "complementary-certification-badges"."label", - 'order', "complementary-certifications".id - ) order by "complementary-certifications".id, "complementary-certification-badges".level) as "complementaryCertificationCourseResults" - `), - ) + .select({ + certificationCourseId: 'certification-courses.id', + complementaryCertificationCourseId: + 'complementary-certification-course-results.complementaryCertificationCourseId', + id: 'complementary-certification-course-results.id', + partnerKey: 'complementary-certification-course-results.partnerKey', + acquired: 'complementary-certification-course-results.acquired', + source: 'complementary-certification-course-results.source', + label: 'complementary-certification-badges.label', + }) .join( 'complementary-certification-courses', 'complementary-certification-courses.id', @@ -115,6 +101,7 @@ function _selectComplementaryCertificationCourseResultsBySessionId({ sessionId } ) .join('badges', 'badges.key', 'complementary-certification-course-results.partnerKey') .join('complementary-certification-badges', 'complementary-certification-badges.badgeId', 'badges.id') + .join( 'complementary-certifications', 'complementary-certifications.id', @@ -125,13 +112,20 @@ function _selectComplementaryCertificationCourseResultsBySessionId({ sessionId } 'certification-courses.id', 'complementary-certification-courses.certificationCourseId', ) - .where({ sessionId }) - .where('complementary-certification-course-results.source', ComplementaryCertificationCourseResult.sources.PIX) - .groupBy('certification-courses.id'); + .where({ + sessionId, + 'complementary-certification-course-results.source': ComplementaryCertificationCourseResult.sources.PIX, + }); } -function _toDomain(certificationResultDTO) { +function _toDomain({ certificationResultDTO, complementaryCertificationCourseResultsByCertificationCourseId }) { + const complementaryCertificationCourseResult = complementaryCertificationCourseResultsByCertificationCourseId.find( + (results) => results.certificationCourseId === certificationResultDTO.id, + ); return CertificationResult.from({ - certificationResultDTO, + certificationResultDTO: { + ...certificationResultDTO, + complementaryCertificationCourseResults: [complementaryCertificationCourseResult], + }, }); } diff --git a/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js b/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js index 5c96860580a..f5b28568557 100644 --- a/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js @@ -387,136 +387,91 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun expect(certificationResults).to.deepEqualArray(expectedResult); }); - it(`should return complementary certification results ordered by complementaryCertificationId and level`, async function () { + it(`should return complementary certification results`, async function () { // given const sessionId = databaseBuilder.factory.buildSession().id; - const { certificationCourseId } = await _buildCertificationResultInSession(sessionId); - databaseBuilder.factory.buildBadge({ key: 'First badge expert', id: 123 }); - databaseBuilder.factory.buildBadge({ key: 'First badge maître', id: 456 }); - databaseBuilder.factory.buildBadge({ key: 'Second badge expert', id: 789 }); - databaseBuilder.factory.buildBadge({ key: 'Second badge maître', id: 101112 }); + const { certificationCourseId: certificationCourseId1 } = await _buildCertificationResultInSession(sessionId); + const { certificationCourseId: certificationCourseId2 } = await _buildCertificationResultInSession(sessionId); databaseBuilder.factory.buildComplementaryCertification({ id: 123, }); - databaseBuilder.factory.buildComplementaryCertification({ - id: 456, - }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - id: 101, - badgeId: 789, - complementaryCertificationId: 456, - level: 1, - label: 'Second badge expert', - }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - id: 102, - badgeId: 456, - complementaryCertificationId: 123, - level: 2, - label: 'First badge maître', - }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - id: 103, - badgeId: 101112, - complementaryCertificationId: 456, - level: 2, - label: 'Second badge maître', - }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - id: 104, - badgeId: 123, - complementaryCertificationId: 123, - level: 1, - label: 'First badge expert', - }); - databaseBuilder.factory.buildComplementaryCertificationCourse({ - id: 996, - certificationCourseId, - complementaryCertificationId: 123, - complementaryCertificationBadgeId: 104, - }); - databaseBuilder.factory.buildComplementaryCertificationCourse({ - id: 997, - certificationCourseId, - complementaryCertificationId: 456, - complementaryCertificationBadgeId: 103, - }); - databaseBuilder.factory.buildComplementaryCertificationCourse({ - id: 998, + const buildComplementaryResult = ({ certificationCourseId, + complementaryCertificationId, + complementaryCertificationBadgeId, + partnerKey, + complementaryCertificationCourseId, + acquired = true, + label, + level, + }) => { + const { id: badgeId } = databaseBuilder.factory.buildBadge({ key: partnerKey }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: complementaryCertificationBadgeId, + badgeId, + complementaryCertificationId, + level, + label, + }); + databaseBuilder.factory.buildComplementaryCertificationCourse({ + id: complementaryCertificationCourseId, + certificationCourseId, + complementaryCertificationId, + complementaryCertificationBadgeId, + }); + databaseBuilder.factory.buildComplementaryCertificationCourseResult({ + complementaryCertificationCourseId, + partnerKey, + acquired, + source: ComplementaryCertificationCourseResult.sources.PIX, + }); + }; + buildComplementaryResult({ + certificationCourseId: certificationCourseId1, complementaryCertificationId: 123, - complementaryCertificationBadgeId: 102, - }); - databaseBuilder.factory.buildComplementaryCertificationCourse({ - id: 999, - certificationCourseId, - complementaryCertificationId: 456, complementaryCertificationBadgeId: 101, - }); - databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 998, - partnerKey: 'First badge expert', - acquired: true, - source: ComplementaryCertificationCourseResult.sources.PIX, - }); - databaseBuilder.factory.buildComplementaryCertificationCourseResult({ - complementaryCertificationCourseId: 996, - partnerKey: 'First badge maître', - acquired: true, - source: ComplementaryCertificationCourseResult.sources.PIX, + label: 'First badge expert', + partnerKey: 'EXPERT', }); - databaseBuilder.factory.buildComplementaryCertificationCourseResult({ + + buildComplementaryResult({ + certificationCourseId: certificationCourseId2, + complementaryCertificationId: 123, + complementaryCertificationBadgeId: 102, complementaryCertificationCourseId: 999, - partnerKey: 'Second badge expert', - acquired: true, - source: ComplementaryCertificationCourseResult.sources.PIX, - }); - databaseBuilder.factory.buildComplementaryCertificationCourseResult({ - complementaryCertificationCourseId: 997, - partnerKey: 'Second badge maître', - acquired: true, - source: ComplementaryCertificationCourseResult.sources.PIX, + label: 'First badge avance', + partnerKey: 'AVANCE', + acquired: false, }); await databaseBuilder.commit(); // when - const [result] = await certificationResultRepository.findBySessionId({ + const [results1, results2] = await certificationResultRepository.findBySessionId({ sessionId, }); // then - const expectedResult = [ + + expect(results1.complementaryCertificationCourseResults[0]).to.deepEqualInstance( domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 998, - partnerKey: 'First badge expert', + partnerKey: 'EXPERT', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'First badge expert', }), + ); + expect(results2.complementaryCertificationCourseResults[0]).to.deepEqualInstance( domainBuilder.buildComplementaryCertificationCourseResult({ - acquired: true, - complementaryCertificationCourseId: 996, - partnerKey: 'First badge maître', - source: ComplementaryCertificationCourseResult.sources.PIX, - label: 'First badge maître', - }), - domainBuilder.buildComplementaryCertificationCourseResult({ - acquired: true, + acquired: false, complementaryCertificationCourseId: 999, - partnerKey: 'Second badge expert', + partnerKey: 'AVANCE', source: ComplementaryCertificationCourseResult.sources.PIX, - label: 'Second badge expert', + label: 'First badge avance', }), - domainBuilder.buildComplementaryCertificationCourseResult({ - acquired: true, - complementaryCertificationCourseId: 997, - partnerKey: 'Second badge maître', - source: ComplementaryCertificationCourseResult.sources.PIX, - label: 'Second badge maître', - }), - ]; - expect(result.complementaryCertificationCourseResults).to.deepEqualArray(expectedResult); + ); }); }); @@ -727,36 +682,10 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun it(`should return only complementary certifications of Pix source`, async function () { // given - const sessionId = databaseBuilder.factory.buildSession().id; const userId = databaseBuilder.factory.buildUser().id; - const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ - firstName: 'Buffy', - lastName: 'Summers', - birthdate: '1981-01-19', - birthplace: 'Torreilles', - isPublished: true, - isCancelled: false, - externalId: 'VAMPIRES_SUCK', - createdAt: new Date('2020-01-01'), - sessionId, - userId, - }).id; - const assessmentResultId = databaseBuilder.factory.buildAssessmentResult.last({ - certificationCourseId, - pixScore: 123, - status: CertificationResult.status.VALIDATED, - commentForOrganization: 'Un commentaire orga 1', - }).id; + const sessionId = databaseBuilder.factory.buildSession().id; + const { certificationCourseId } = await _buildCertificationResultInSession(sessionId, userId); - databaseBuilder.factory.buildCompetenceMark({ - id: 123, - score: 10, - level: 4, - competence_code: '2.3', - area_code: '2', - competenceId: 'recComp23', - assessmentResultId, - }); const oneComplementaryCertificationId = databaseBuilder.factory.buildComplementaryCertification({ label: 'PARTNER_LABEL', }).id; @@ -786,47 +715,21 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun await databaseBuilder.commit(); // when - const certificationResults = await certificationResultRepository.findByCertificationCandidateIds({ + const [certificationResults] = await certificationResultRepository.findByCertificationCandidateIds({ certificationCandidateIds: [certificationCandidateId], }); // then const expectedResult = [ - domainBuilder.buildCertificationResult({ - id: certificationCourseId, - firstName: 'Buffy', - lastName: 'Summers', - externalId: 'VAMPIRES_SUCK', - pixScore: 123, - sessionId, - status: 'validated', - birthdate: '1981-01-19', - birthplace: 'Torreilles', - createdAt: new Date('2020-01-01T00:00:00Z'), - commentForOrganization: 'Un commentaire orga 1', - competencesWithMark: [ - domainBuilder.buildCompetenceMark({ - id: 123, - score: 10, - level: 4, - competence_code: '2.3', - area_code: '2', - competenceId: 'recComp23', - assessmentResultId, - }), - ], - complementaryCertificationCourseResults: [ - domainBuilder.buildComplementaryCertificationCourseResult({ - acquired: true, - complementaryCertificationCourseId: 997, - partnerKey: 'PARTNER_KEY', - source: ComplementaryCertificationCourseResult.sources.PIX, - label: 'PARTNER_LABEL', - }), - ], + domainBuilder.buildComplementaryCertificationCourseResult({ + acquired: true, + complementaryCertificationCourseId: 997, + partnerKey: 'PARTNER_KEY', + source: ComplementaryCertificationCourseResult.sources.PIX, + label: 'PARTNER_LABEL', }), ]; - expect(certificationResults).to.deepEqualArray(expectedResult); + expect(certificationResults.complementaryCertificationCourseResults).to.deepEqualArray(expectedResult); }); it(`should return complementary certification linked to the candidates`, async function () { @@ -907,9 +810,10 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun }); }); -async function _buildCertificationResultInSession(sessionId) { +async function _buildCertificationResultInSession(sessionId, userId) { const certificationCourseId = databaseBuilder.factory.buildCertificationCourse({ sessionId, + userId, isPublished: true, }).id; const assessmentResultId = databaseBuilder.factory.buildAssessmentResult.last({ From 62260517855faa0e5b9b08004872c350469edef6 Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Mon, 6 Nov 2023 22:18:07 +0100 Subject: [PATCH 03/11] :card_file_box: api: Add ccc-results foreign key cc-badges --- ...c-course-results-to-cc-badges-reference.js | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 api/db/migrations/20231106161726_add-cc-course-results-to-cc-badges-reference.js diff --git a/api/db/migrations/20231106161726_add-cc-course-results-to-cc-badges-reference.js b/api/db/migrations/20231106161726_add-cc-course-results-to-cc-badges-reference.js new file mode 100644 index 00000000000..148c90ec1ce --- /dev/null +++ b/api/db/migrations/20231106161726_add-cc-course-results-to-cc-badges-reference.js @@ -0,0 +1,29 @@ +const TABLE_NAME = 'complementary-certification-course-results'; +const REF_TABLE_NAME = 'complementary-certification-badges'; +const COLUMN_NAME = 'complementaryCertificationBadgeId'; + +const up = async function (knex) { + await knex.schema.table(TABLE_NAME, function (table) { + table + .integer(COLUMN_NAME) + .defaultTo(null) + .references(`${REF_TABLE_NAME}.id`) + .withKeyName('cccresults-ccbadgeId_foreignkey'); + }); + + await knex(TABLE_NAME).update({ + [COLUMN_NAME]: knex(`${REF_TABLE_NAME}`) + .select(`${REF_TABLE_NAME}.id`) + .innerJoin('badges', 'badges.id', `${REF_TABLE_NAME}.badgeId`) + // eslint-disable-next-line knex/avoid-injections + .where('badges.key', '=', knex.raw(`"${TABLE_NAME}"."partnerKey"`)), + }); +}; + +const down = async function (knex) { + await knex.schema.table(TABLE_NAME, function (table) { + table.dropColumn(COLUMN_NAME); + }); +}; + +export { up, down }; From 1287cb37ad809d7dbc13fd69096ad248983845dd Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Mon, 6 Nov 2023 23:44:52 +0100 Subject: [PATCH 04/11] :recycle: api: add complementaryCertificationBadgeId to result --- ...mplementary-certification-course-result.js | 14 ++++- .../data/common/tooling/session-tooling.js | 2 + ...le-complementary-certifications-scoring.js | 6 +++ api/lib/domain/models/CertificationResult.js | 4 -- .../ComplementaryCertificationCourseResult.js | 31 +++++++++-- ...mplementaryCertificationScoringCriteria.js | 2 + ...tionScoringWithComplementaryReferential.js | 2 + ...nScoringWithoutComplementaryReferential.js | 3 +- .../models/PartnerCertificationScoring.js | 9 +++- .../certification-result-repository.js | 1 + ...-certification-course-result-repository.js | 10 +++- ...rtification-scoring-criteria-repository.js | 3 ++ ...mplementary-certifications-scoring_test.js | 1 + .../certification-result-repository_test.js | 54 +++++++++++++------ ...ification-course-result-repository_test.js | 49 +++++++++++++---- ...cation-scoring-criteria-repository_test.js | 2 + ...mplementary-certification-course-result.js | 2 + ...ementary-certification-scoring-criteria.js | 2 + ...oring-without-complementary-referential.js | 2 + .../build-pix-plus-certification-scoring.js | 2 + ...mplementary-certifications-scoring_test.js | 18 +++++++ ...coringWithComplementaryReferential_test.js | 4 +- .../PartnerCertificationScoring_test.js | 1 + 23 files changed, 182 insertions(+), 42 deletions(-) diff --git a/api/db/database-builder/factory/build-complementary-certification-course-result.js b/api/db/database-builder/factory/build-complementary-certification-course-result.js index 55b88504c1d..42159e321ca 100644 --- a/api/db/database-builder/factory/build-complementary-certification-course-result.js +++ b/api/db/database-builder/factory/build-complementary-certification-course-result.js @@ -4,10 +4,13 @@ import { buildComplementaryCertification } from './build-complementary-certifica import { buildCertificationCourse } from './build-certification-course.js'; import _ from 'lodash'; import { ComplementaryCertificationCourseResult } from '../../../lib/domain/models/ComplementaryCertificationCourseResult.js'; +import { buildBadge } from './build-badge.js'; +import { buildComplementaryCertificationBadge } from './build-complementary-certification-badge.js'; const buildComplementaryCertificationCourseResult = function ({ id, complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey, source = ComplementaryCertificationCourseResult.sources.PIX, acquired = true, @@ -16,9 +19,12 @@ const buildComplementaryCertificationCourseResult = function ({ complementaryCertificationCourseId = _.isUndefined(complementaryCertificationCourseId) ? _buildComplementaryCertificationCourse().id : complementaryCertificationCourseId; + complementaryCertificationBadgeId = _.isUndefined(complementaryCertificationBadgeId) + ? _buildComplementaryCertificationBadge().id + : complementaryCertificationBadgeId; return databaseBuffer.pushInsertable({ tableName: 'complementary-certification-course-results', - values: { id, complementaryCertificationCourseId, partnerKey, source, acquired }, + values: { id, complementaryCertificationCourseId, partnerKey, complementaryCertificationBadgeId, source, acquired }, }); }; @@ -32,3 +38,9 @@ function _buildComplementaryCertificationCourse() { certificationCourseId, }); } + +function _buildComplementaryCertificationBadge() { + const { id: badgeId } = buildBadge(); + const { id: complementaryCertificationId } = buildComplementaryCertification(); + return buildComplementaryCertificationBadge({ badgeId, complementaryCertificationId }); +} diff --git a/api/db/seeds/data/common/tooling/session-tooling.js b/api/db/seeds/data/common/tooling/session-tooling.js index 961ca2d4247..b4b80bdeae5 100644 --- a/api/db/seeds/data/common/tooling/session-tooling.js +++ b/api/db/seeds/data/common/tooling/session-tooling.js @@ -1094,6 +1094,8 @@ function _makeCandidatesPassCertification({ }).id; databaseBuilder.factory.buildComplementaryCertificationCourseResult({ partnerKey: certificationCandidate.complementaryCertificationBadgeInfo.partnerKey, + complementaryCertificationBadgeId: + certificationCandidate.complementaryCertificationBadgeInfo.complementaryCertificationBadgeId, acquired: true, source: 'PIX', complementaryCertificationCourseId, diff --git a/api/lib/domain/events/handle-complementary-certifications-scoring.js b/api/lib/domain/events/handle-complementary-certifications-scoring.js index d2a8b7700c7..5286b078ca3 100644 --- a/api/lib/domain/events/handle-complementary-certifications-scoring.js +++ b/api/lib/domain/events/handle-complementary-certifications-scoring.js @@ -30,6 +30,7 @@ async function handleComplementaryCertificationsScoring({ const { minimumReproducibilityRate, complementaryCertificationCourseId, + complementaryCertificationBadgeId, complementaryCertificationBadgeKey, hasComplementaryReferential, minimumEarnedPix, @@ -49,6 +50,7 @@ async function handleComplementaryCertificationsScoring({ _buildComplementaryCertificationScoringWithReferential( minimumReproducibilityRate, complementaryCertificationCourseId, + complementaryCertificationBadgeId, pixPlusChallenges, pixPlusAnswers, complementaryCertificationBadgeKey, @@ -58,6 +60,7 @@ async function handleComplementaryCertificationsScoring({ complementaryCertificationScoringWithComplementaryReferential = new ComplementaryCertificationScoringWithoutComplementaryReferential({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, complementaryCertificationBadgeKey, reproducibilityRate: assessmentResult.reproducibilityRate, pixScore: assessmentResult.pixScore, @@ -69,6 +72,7 @@ async function handleComplementaryCertificationsScoring({ await complementaryCertificationCourseResultRepository.save( ComplementaryCertificationCourseResult.from({ ...complementaryCertificationScoringWithComplementaryReferential, + source: ComplementaryCertificationCourseResult.sources.PIX, acquired: complementaryCertificationScoringWithComplementaryReferential.isAcquired(), }), ); @@ -78,6 +82,7 @@ async function handleComplementaryCertificationsScoring({ function _buildComplementaryCertificationScoringWithReferential( minimumReproducibilityRate, complementaryCertificationCourseId, + complementaryCertificationBadgeId, challenges, answers, complementaryCertificationBadgeKey, @@ -92,6 +97,7 @@ function _buildComplementaryCertificationScoringWithReferential( return new ComplementaryCertificationScoringWithComplementaryReferential({ minimumReproducibilityRate, complementaryCertificationCourseId, + complementaryCertificationBadgeId, reproducibilityRate, complementaryCertificationBadgeKey, hasAcquiredPixCertification: assessmentResult.isValidated(), diff --git a/api/lib/domain/models/CertificationResult.js b/api/lib/domain/models/CertificationResult.js index 235cb52ce9f..3dee1c41a89 100644 --- a/api/lib/domain/models/CertificationResult.js +++ b/api/lib/domain/models/CertificationResult.js @@ -127,10 +127,6 @@ class CertificationResult { ), ]; } - - _getCertificationCourseResultByPartnerKeys(partnerKeys) { - return this.complementaryCertificationCourseResults.find(({ partnerKey }) => partnerKeys.includes(partnerKey)); - } } CertificationResult.status = status; diff --git a/api/lib/domain/models/ComplementaryCertificationCourseResult.js b/api/lib/domain/models/ComplementaryCertificationCourseResult.js index db846a4a4cc..73aab6e5822 100644 --- a/api/lib/domain/models/ComplementaryCertificationCourseResult.js +++ b/api/lib/domain/models/ComplementaryCertificationCourseResult.js @@ -9,17 +9,33 @@ const juryOptions = { }; class ComplementaryCertificationCourseResult { - constructor({ complementaryCertificationCourseId, partnerKey, source, acquired, label } = {}) { + constructor({ + complementaryCertificationCourseId, + complementaryCertificationBadgeId, + partnerKey, + source, + acquired, + label, + } = {}) { this.complementaryCertificationCourseId = complementaryCertificationCourseId; + this.complementaryCertificationBadgeId = complementaryCertificationBadgeId; this.partnerKey = partnerKey; this.acquired = acquired; this.source = source; this.label = label; } - static from({ complementaryCertificationCourseId, partnerKey, acquired, source, label }) { + static from({ + complementaryCertificationCourseId, + complementaryCertificationBadgeId, + partnerKey, + acquired, + source, + label, + }) { return new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey, acquired, source, @@ -27,10 +43,16 @@ class ComplementaryCertificationCourseResult { }); } - static buildFromJuryLevel({ complementaryCertificationCourseId, juryLevel, pixPartnerKey }) { - if (juryLevel === juryOptions.REJECTED) { + static buildFromJuryLevel({ + complementaryCertificationCourseId, + complementaryCertificationBadgeId, + juryLevel, + pixPartnerKey, + }) { + if (juryLevel === 'REJECTED') { return new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey: pixPartnerKey, acquired: false, source: sources.EXTERNAL, @@ -39,6 +61,7 @@ class ComplementaryCertificationCourseResult { return new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey: juryLevel, acquired: true, source: sources.EXTERNAL, diff --git a/api/lib/domain/models/ComplementaryCertificationScoringCriteria.js b/api/lib/domain/models/ComplementaryCertificationScoringCriteria.js index ca42199568a..952ea2ec8e6 100644 --- a/api/lib/domain/models/ComplementaryCertificationScoringCriteria.js +++ b/api/lib/domain/models/ComplementaryCertificationScoringCriteria.js @@ -3,11 +3,13 @@ class ComplementaryCertificationScoringCriteria { complementaryCertificationCourseId, minimumReproducibilityRate, complementaryCertificationBadgeKey, + complementaryCertificationBadgeId, hasComplementaryReferential, minimumEarnedPix, } = {}) { this.complementaryCertificationCourseId = complementaryCertificationCourseId; this.minimumReproducibilityRate = minimumReproducibilityRate; + this.complementaryCertificationBadgeId = complementaryCertificationBadgeId; this.complementaryCertificationBadgeKey = complementaryCertificationBadgeKey; this.hasComplementaryReferential = hasComplementaryReferential; this.minimumEarnedPix = minimumEarnedPix; diff --git a/api/lib/domain/models/ComplementaryCertificationScoringWithComplementaryReferential.js b/api/lib/domain/models/ComplementaryCertificationScoringWithComplementaryReferential.js index 85e532faf99..22376fdec1c 100644 --- a/api/lib/domain/models/ComplementaryCertificationScoringWithComplementaryReferential.js +++ b/api/lib/domain/models/ComplementaryCertificationScoringWithComplementaryReferential.js @@ -4,6 +4,7 @@ import { PartnerCertificationScoring } from './PartnerCertificationScoring.js'; class ComplementaryCertificationScoringWithComplementaryReferential extends PartnerCertificationScoring { constructor({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, complementaryCertificationBadgeKey, reproducibilityRate, hasAcquiredPixCertification, @@ -11,6 +12,7 @@ class ComplementaryCertificationScoringWithComplementaryReferential extends Part } = {}) { super({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey: complementaryCertificationBadgeKey, source: ComplementaryCertificationCourseResult.sources.PIX, }); diff --git a/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js b/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js index 9bdb5068b27..f649258fa6d 100644 --- a/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js +++ b/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js @@ -3,6 +3,7 @@ import { PartnerCertificationScoring } from './PartnerCertificationScoring.js'; class ComplementaryCertificationScoringWithoutComplementaryReferential extends PartnerCertificationScoring { constructor({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, complementaryCertificationBadgeKey, reproducibilityRate, pixScore, @@ -11,10 +12,10 @@ class ComplementaryCertificationScoringWithoutComplementaryReferential extends P } = {}) { super({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey: complementaryCertificationBadgeKey, }); - this.complementaryCertificationCourseId = complementaryCertificationCourseId; this.reproducibilityRate = reproducibilityRate; this.pixScore = pixScore; this.minimumEarnedPix = minimumEarnedPix; diff --git a/api/lib/domain/models/PartnerCertificationScoring.js b/api/lib/domain/models/PartnerCertificationScoring.js index dd4dea33630..1ef760c17a0 100644 --- a/api/lib/domain/models/PartnerCertificationScoring.js +++ b/api/lib/domain/models/PartnerCertificationScoring.js @@ -10,12 +10,19 @@ const SOURCES = { }; class PartnerCertificationScoring { - constructor({ complementaryCertificationCourseId, partnerKey, source = SOURCES.PIX } = {}) { + constructor({ + complementaryCertificationCourseId, + complementaryCertificationBadgeId, + partnerKey, + source = SOURCES.PIX, + } = {}) { this.complementaryCertificationCourseId = complementaryCertificationCourseId; + this.complementaryCertificationBadgeId = complementaryCertificationBadgeId; this.partnerKey = partnerKey; this.source = source; const schema = Joi.object({ complementaryCertificationCourseId: Joi.number().integer().required(), + complementaryCertificationBadgeId: Joi.number().integer().required(), partnerKey: Joi.string().allow(null).required(), source: Joi.string() .required() diff --git a/api/lib/infrastructure/repositories/certification-result-repository.js b/api/lib/infrastructure/repositories/certification-result-repository.js index 0642e59b8c0..fe3e8bff127 100644 --- a/api/lib/infrastructure/repositories/certification-result-repository.js +++ b/api/lib/infrastructure/repositories/certification-result-repository.js @@ -89,6 +89,7 @@ function _selectComplementaryCertificationCourseResultsBySessionId({ sessionId } complementaryCertificationCourseId: 'complementary-certification-course-results.complementaryCertificationCourseId', id: 'complementary-certification-course-results.id', + complementaryCertificationBadgeId: 'complementary-certification-course-results.complementaryCertificationBadgeId', partnerKey: 'complementary-certification-course-results.partnerKey', acquired: 'complementary-certification-course-results.acquired', source: 'complementary-certification-course-results.source', diff --git a/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js b/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js index 52e485ff6f4..56e2be992a1 100644 --- a/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js +++ b/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js @@ -19,9 +19,15 @@ const getAllowedJuryLevelByBadgeKey = async function ({ key }) { .where('targetProfileId', '=', knex('badges').select('targetProfileId').where({ key })); }; -const save = async function ({ complementaryCertificationCourseId, partnerKey, acquired, source }) { +const save = async function ({ + complementaryCertificationCourseId, + complementaryCertificationBadgeId, + partnerKey, + acquired, + source, +}) { return knex('complementary-certification-course-results') - .insert({ partnerKey, acquired, complementaryCertificationCourseId, source }) + .insert({ partnerKey, complementaryCertificationBadgeId, acquired, complementaryCertificationCourseId, source }) .onConflict(['complementaryCertificationCourseId', 'source']) .merge(); }; diff --git a/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js b/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js index 435d3519a83..50cf5df9701 100644 --- a/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js +++ b/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js @@ -5,6 +5,7 @@ const findByCertificationCourseId = async function ({ certificationCourseId }) { const results = await knex('complementary-certification-courses') .select({ complementaryCertificationCourseId: 'complementary-certification-courses.id', + complementaryCertificationBadgeId: 'complementary-certification-courses.complementaryCertificationBadgeId', minimumReproducibilityRate: 'complementary-certifications.minimumReproducibilityRate', complementaryCertificationBadgeKey: 'badges.key', hasComplementaryReferential: 'complementary-certifications.hasComplementaryReferential', @@ -26,6 +27,7 @@ const findByCertificationCourseId = async function ({ certificationCourseId }) { return results.map( ({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, minimumReproducibilityRate, complementaryCertificationBadgeKey, hasComplementaryReferential, @@ -33,6 +35,7 @@ const findByCertificationCourseId = async function ({ certificationCourseId }) { }) => new ComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, minimumReproducibilityRate: Number(minimumReproducibilityRate), complementaryCertificationBadgeKey, hasComplementaryReferential, diff --git a/api/tests/integration/domain/event/handle-complementary-certifications-scoring_test.js b/api/tests/integration/domain/event/handle-complementary-certifications-scoring_test.js index 37863581e02..c4a347ca909 100644 --- a/api/tests/integration/domain/event/handle-complementary-certifications-scoring_test.js +++ b/api/tests/integration/domain/event/handle-complementary-certifications-scoring_test.js @@ -90,6 +90,7 @@ describe('Integration | Event | Handle Complementary Certifications Scoring', fu expect(_.omit(complementaryCertificationCourseResults, ['id'])).to.deep.equal({ acquired: true, complementaryCertificationCourseId, + complementaryCertificationBadgeId: 501, partnerKey: 'badge_key', source: 'PIX', }); diff --git a/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js b/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js index f5b28568557..7e18f371f90 100644 --- a/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/certification-result-repository_test.js @@ -186,6 +186,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun key: 'PARTNER_KEY', }).id; const oneComplementaryCertificationBadgeId = databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 100, badgeId: oneBadgeId, complementaryCertificationId: oneComplementaryCertificationId, label: 'PARTNER_LABEL', @@ -198,6 +199,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 998, + complementaryCertificationBadgeId: 100, partnerKey: 'PARTNER_KEY', acquired: true, source: ComplementaryCertificationCourseResult.sources.PIX, @@ -211,6 +213,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun const otherComplementaryCertificationId = databaseBuilder.factory.buildComplementaryCertification().id; databaseBuilder.factory.buildBadge({ id: 12345, key: 'OTHER_PARTNER_KEY' }); const otherComplementaryCertificationBadgeId = databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 101, badgeId: '12345', complementaryCertificationId: otherComplementaryCertificationId, label: 'OTHER_PARTNER_LABEL', @@ -223,6 +226,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 101, partnerKey: 'OTHER_PARTNER_KEY', acquired: false, source: ComplementaryCertificationCourseResult.sources.PIX, @@ -262,6 +266,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 998, + complementaryCertificationBadgeId: 100, partnerKey: 'PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'PARTNER_LABEL', @@ -295,6 +300,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun domainBuilder.buildComplementaryCertificationCourseResult({ acquired: false, complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 101, partnerKey: 'OTHER_PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'OTHER_PARTNER_LABEL', @@ -320,28 +326,32 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun id: 997, certificationCourseId: oneCertificationCourseId, }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 100, + badgeId: 12345, + complementaryCertificationId: oneComplementaryCertificationId, + label: 'PARTNER_LABEL_PIX', + }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 101, + badgeId: 12346, + complementaryCertificationId: oneComplementaryCertificationId, + label: 'PARTNER_LABEL_EXTERNAL', + }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 100, partnerKey: 'PARTNER_KEY_PIX', acquired: true, source: ComplementaryCertificationCourseResult.sources.PIX, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 101, partnerKey: 'PARTNER_KEY_EXTERNAL', acquired: true, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - badgeId: 12345, - complementaryCertificationId: oneComplementaryCertificationId, - label: 'PARTNER_LABEL_PIX', - }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - badgeId: 12346, - complementaryCertificationId: oneComplementaryCertificationId, - label: 'PARTNER_LABEL_EXTERNAL', - }); await databaseBuilder.commit(); @@ -377,6 +387,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 100, partnerKey: 'PARTNER_KEY_PIX', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'PARTNER_LABEL_PIX', @@ -421,6 +432,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey, acquired, source: ComplementaryCertificationCourseResult.sources.PIX, @@ -458,18 +470,20 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 998, + complementaryCertificationBadgeId: 101, partnerKey: 'EXPERT', - source: ComplementaryCertificationCourseResult.sources.PIX, label: 'First badge expert', + source: ComplementaryCertificationCourseResult.sources.PIX, }), ); expect(results2.complementaryCertificationCourseResults[0]).to.deepEqualInstance( domainBuilder.buildComplementaryCertificationCourseResult({ acquired: false, complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 102, partnerKey: 'AVANCE', - source: ComplementaryCertificationCourseResult.sources.PIX, label: 'First badge avance', + source: ComplementaryCertificationCourseResult.sources.PIX, }), ); }); @@ -694,23 +708,26 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun id: 997, certificationCourseId, }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 789, + badgeId: 12345, + complementaryCertificationId: oneComplementaryCertificationId, + label: 'PARTNER_LABEL', + }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 789, partnerKey: 'PARTNER_KEY', acquired: true, source: ComplementaryCertificationCourseResult.sources.PIX, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 789, partnerKey: 'PARTNER_KEY', acquired: true, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - badgeId: 12345, - complementaryCertificationId: oneComplementaryCertificationId, - label: 'PARTNER_LABEL', - }); const certificationCandidateId = databaseBuilder.factory.buildCertificationCandidate({ userId, sessionId }).id; await databaseBuilder.commit(); @@ -724,6 +741,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 997, + complementaryCertificationBadgeId: 789, partnerKey: 'PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'PARTNER_LABEL', @@ -757,6 +775,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 998, + complementaryCertificationBadgeId: 100, partnerKey: 'PARTNER_KEY', acquired: true, source: ComplementaryCertificationCourseResult.sources.PIX, @@ -798,6 +817,7 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 998, + complementaryCertificationBadgeId: 100, partnerKey: 'PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'PARTNER_LABEL', diff --git a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js index 020ba6606ce..359f635a3bb 100644 --- a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js @@ -21,9 +21,15 @@ describe('Integration | Repository | complementary-certification-courses-result- certificationCourseId: 99, complementaryCertificationId: 1, }); - databaseBuilder.factory.buildBadge({ key: 'PIX_TEST_1' }); + databaseBuilder.factory.buildBadge({ id: 51, key: 'PIX_TEST_1' }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 42, + badgeId: 51, + complementaryCertificationId: 1, + }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 42, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: true, @@ -43,6 +49,7 @@ describe('Integration | Repository | complementary-certification-courses-result- domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 42, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }), @@ -112,6 +119,10 @@ describe('Integration | Repository | complementary-certification-courses-result- }); describe('#save', function () { + afterEach(function () { + return knex('complementary-certification-course-results').delete(); + }); + describe('when the ComplementaryCertificationCourseResult does not exist', function () { it('saves the ComplementaryCertificationCourseResult', async function () { // given @@ -127,14 +138,21 @@ describe('Integration | Repository | complementary-certification-courses-result- }); databaseBuilder.factory.buildBadge({ + id: 10, key: 'PIX_TEST_1', }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 60, + badgeId: 10, + complementaryCertificationId: 1, + }); await databaseBuilder.commit(); const complementaryCertificationCourseResult = domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 60, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -143,18 +161,14 @@ describe('Integration | Repository | complementary-certification-courses-result- await complementaryCertificationCourseResultRepository.save(complementaryCertificationCourseResult); // then - const savedComplementaryCertificationCourseResult = await knex('complementary-certification-course-results') - .where({ - acquired: true, - complementaryCertificationCourseId: 999, - partnerKey: 'PIX_TEST_1', - source: ComplementaryCertificationCourseResult.sources.PIX, - }) - .first(); + const savedComplementaryCertificationCourseResult = await knex( + 'complementary-certification-course-results', + ).first(); expect(_.omit(savedComplementaryCertificationCourseResult, 'id')).to.deep.equal({ acquired: true, complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 60, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -175,12 +189,23 @@ describe('Integration | Repository | complementary-certification-courses-result- complementaryCertificationId: 1, }); - databaseBuilder.factory.buildBadge({ key: 'PIX_TEST_1' }); - databaseBuilder.factory.buildBadge({ key: 'PIX_TEST_2' }); + databaseBuilder.factory.buildBadge({ id: 10, key: 'PIX_TEST_1' }); + databaseBuilder.factory.buildBadge({ id: 11, key: 'PIX_TEST_2' }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 60, + badgeId: 10, + complementaryCertificationId: 1, + }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 61, + badgeId: 11, + complementaryCertificationId: 1, + }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 61, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -190,6 +215,7 @@ describe('Integration | Repository | complementary-certification-courses-result- const complementaryCertificationCourseResult = domainBuilder.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 61, partnerKey: 'PIX_TEST_2', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -208,6 +234,7 @@ describe('Integration | Repository | complementary-certification-courses-result- expect(_.omit(results[0], 'id')).to.deep.equal({ acquired: true, complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 61, partnerKey: 'PIX_TEST_2', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); diff --git a/api/tests/integration/infrastructure/repositories/complementary-certification-scoring-criteria-repository_test.js b/api/tests/integration/infrastructure/repositories/complementary-certification-scoring-criteria-repository_test.js index 1ec0a949320..fde7effb1ea 100644 --- a/api/tests/integration/infrastructure/repositories/complementary-certification-scoring-criteria-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/complementary-certification-scoring-criteria-repository_test.js @@ -57,12 +57,14 @@ describe('Integration | Repository | complementary certification scoring criteri expect(results).to.deep.equal([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: complementaryCertificationCourse1.id, + complementaryCertificationBadgeId: complementaryCertificationCourse1.complementaryCertificationBadgeId, minimumReproducibilityRate: complementaryCertification1.minimumReproducibilityRate, complementaryCertificationBadgeKey: badge1.key, hasComplementaryReferential: complementaryCertification1.hasComplementaryReferential, }), domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: complementaryCertificationCourse2.id, + complementaryCertificationBadgeId: complementaryCertificationCourse2.complementaryCertificationBadgeId, minimumReproducibilityRate: complementaryCertification2.minimumReproducibilityRate, complementaryCertificationBadgeKey: badge2.key, hasComplementaryReferential: complementaryCertification2.hasComplementaryReferential, diff --git a/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result.js b/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result.js index 2ba9ff52f3f..1553d5dd8d8 100644 --- a/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result.js +++ b/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result.js @@ -2,6 +2,7 @@ import { ComplementaryCertificationCourseResult } from './../../../../lib/domain const buildComplementaryCertificationCourseResult = function ({ complementaryCertificationCourseId = 2, + complementaryCertificationBadgeId = 2, partnerKey = 'PARTNER_KEY', acquired = false, source = ComplementaryCertificationCourseResult.sources.PIX, @@ -9,6 +10,7 @@ const buildComplementaryCertificationCourseResult = function ({ } = {}) { return new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey, acquired, source, diff --git a/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-criteria.js b/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-criteria.js index 6f31856f0fd..a6216b97c2e 100644 --- a/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-criteria.js +++ b/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-criteria.js @@ -3,12 +3,14 @@ import { ComplementaryCertificationScoringCriteria } from '../../../../lib/domai function buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId = 123, minimumReproducibilityRate = 70, + complementaryCertificationBadgeId = 89, complementaryCertificationBadgeKey = 'badge_key', hasComplementaryReferential = false, minimumEarnedPix = 0, } = {}) { return new ComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, minimumReproducibilityRate, complementaryCertificationBadgeKey, hasComplementaryReferential, diff --git a/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-without-complementary-referential.js b/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-without-complementary-referential.js index 6306747b5a1..e5c20f4dd03 100644 --- a/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-without-complementary-referential.js +++ b/api/tests/tooling/domain-builder/factory/build-complementary-certification-scoring-without-complementary-referential.js @@ -2,6 +2,7 @@ import { ComplementaryCertificationScoringWithoutComplementaryReferential } from const buildComplementaryCertificationScoringWithoutComplementaryReferential = function ({ complementaryCertificationCourseId = 99, + complementaryCertificationBadgeId = 60, certificationCourseId = 42, reproducibilityRate = 50, complementaryCertificationBadgeKey = 'badge_key', @@ -11,6 +12,7 @@ const buildComplementaryCertificationScoringWithoutComplementaryReferential = fu } = {}) { return new ComplementaryCertificationScoringWithoutComplementaryReferential({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, certificationCourseId, reproducibilityRate, complementaryCertificationBadgeKey, diff --git a/api/tests/tooling/domain-builder/factory/build-pix-plus-certification-scoring.js b/api/tests/tooling/domain-builder/factory/build-pix-plus-certification-scoring.js index 60f5612afa7..c91943ca023 100644 --- a/api/tests/tooling/domain-builder/factory/build-pix-plus-certification-scoring.js +++ b/api/tests/tooling/domain-builder/factory/build-pix-plus-certification-scoring.js @@ -3,6 +3,7 @@ import { buildReproducibilityRate } from './build-reproducibility-rate.js'; const buildComplementaryCertificationScoringWithComplementaryReferential = function ({ complementaryCertificationCourseId = 999, + complementaryCertificationBadgeId = 100, complementaryCertificationBadgeKey = 'PIX_PLUS_TEST', reproducibilityRate = buildReproducibilityRate({ value: 100 }), hasAcquiredPixCertification = true, @@ -10,6 +11,7 @@ const buildComplementaryCertificationScoringWithComplementaryReferential = funct } = {}) { return new ComplementaryCertificationScoringWithComplementaryReferential({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, complementaryCertificationBadgeKey, reproducibilityRate, hasAcquiredPixCertification, diff --git a/api/tests/unit/domain/events/handle-complementary-certifications-scoring_test.js b/api/tests/unit/domain/events/handle-complementary-certifications-scoring_test.js index 6e74d9a3a17..b0a35b2e91f 100644 --- a/api/tests/unit/domain/events/handle-complementary-certifications-scoring_test.js +++ b/api/tests/unit/domain/events/handle-complementary-certifications-scoring_test.js @@ -85,6 +85,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat .resolves([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 70, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: true, @@ -106,6 +107,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: true, @@ -141,6 +143,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat const complementaryCertificationScoringCriteria = [ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 100, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: true, @@ -159,6 +162,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: false, @@ -197,6 +201,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat .resolves([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 75, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: true, @@ -217,6 +222,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: false, @@ -255,6 +261,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat .resolves([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 75, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: true, @@ -275,6 +282,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: true, @@ -312,6 +320,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat .resolves([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 70, minimumEarnedPix: 50, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', @@ -335,6 +344,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: true, @@ -373,6 +383,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat const complementaryCertificationScoringCriteria = [ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 75, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: false, @@ -392,6 +403,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: false, @@ -430,6 +442,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat .resolves([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 75, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: false, @@ -453,6 +466,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: false, @@ -491,6 +505,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat .resolves([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 70, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: false, @@ -514,6 +529,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: false, @@ -552,6 +568,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat .resolves([ domainBuilder.buildComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, minimumReproducibilityRate: 70, complementaryCertificationBadgeKey: 'PIX_PLUS_TEST', hasComplementaryReferential: false, @@ -575,6 +592,7 @@ describe('Unit | Domain | Events | handle-complementary-certification-certificat expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( ComplementaryCertificationCourseResult.from({ complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 888, partnerKey: 'PIX_PLUS_TEST', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: true, diff --git a/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js b/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js index 5bdb6378fbc..02ec77969db 100644 --- a/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js +++ b/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js @@ -8,6 +8,7 @@ describe('Unit | Domain | Models | ComplementaryCertificationScoringWithCompleme const complementaryCertificationScoringWithComplementaryReferential = new ComplementaryCertificationScoringWithComplementaryReferential({ complementaryCertificationCourseId: 99, + complementaryCertificationBadgeId: 89, complementaryCertificationBadgeKey: 'BADGE', reproducibilityRate: 71, hasAcquiredPixCertification: false, @@ -17,8 +18,9 @@ describe('Unit | Domain | Models | ComplementaryCertificationScoringWithCompleme expect(complementaryCertificationScoringWithComplementaryReferential).to.deepEqualInstance( new ComplementaryCertificationScoringWithComplementaryReferential({ complementaryCertificationCourseId: 99, - reproducibilityRate: 71, + complementaryCertificationBadgeId: 89, complementaryCertificationBadgeKey: 'BADGE', + reproducibilityRate: 71, hasAcquiredPixCertification: false, minimumReproducibilityRate: undefined, partnerKey: 'BADGE', diff --git a/api/tests/unit/domain/models/PartnerCertificationScoring_test.js b/api/tests/unit/domain/models/PartnerCertificationScoring_test.js index 04a855075d8..dd77e2bf18a 100644 --- a/api/tests/unit/domain/models/PartnerCertificationScoring_test.js +++ b/api/tests/unit/domain/models/PartnerCertificationScoring_test.js @@ -8,6 +8,7 @@ describe('Unit | Domain | Models | PartnerCertificationScoring', function () { beforeEach(function () { validArguments = { complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: 60, certificationCourseId: 123, partnerKey: 'partnerKey', }; From 8b95d8866a44f9ff982ce56eb7ecfcd464a6fb21 Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Tue, 7 Nov 2023 23:40:53 +0100 Subject: [PATCH 05/11] :recycle: api: Replace join from partnerKey to complementaryCertificationBadgeId --- .../repositories/certificate-repository.js | 12 +- ...mentary-certification-course-repository.js | 5 +- .../certification-attestation-route_test.js | 99 ++-- .../certificate-repository_test.js | 450 ++++++++++++++++++ .../certificate-repository_private_test.js | 1 + .../certificate-repository_shareable_test.js | 1 + ...ification-course-result-repository_test.js | 34 +- 7 files changed, 549 insertions(+), 53 deletions(-) diff --git a/api/lib/infrastructure/repositories/certificate-repository.js b/api/lib/infrastructure/repositories/certificate-repository.js index 9fc8449c60f..efaf8a80161 100644 --- a/api/lib/infrastructure/repositories/certificate-repository.js +++ b/api/lib/infrastructure/repositories/certificate-repository.js @@ -71,7 +71,7 @@ export { getPrivateCertificate, findPrivateCertificatesByUserId, getShareableCer async function _getCertifiedBadges(certificationCourseId) { const complementaryCertificationCourseResults = await knex .select( - 'complementary-certification-course-results.partnerKey', + 'badges.key as partnerKey', 'complementary-certification-course-results.source', 'complementary-certification-course-results.acquired', 'complementary-certification-course-results.complementaryCertificationCourseId', @@ -89,15 +89,19 @@ async function _getCertifiedBadges(certificationCourseId) { 'complementary-certification-courses.id', 'complementary-certification-course-results.complementaryCertificationCourseId', ) - .innerJoin('badges', 'badges.key', 'complementary-certification-course-results.partnerKey') - .innerJoin('complementary-certification-badges', 'complementary-certification-badges.badgeId', 'badges.id') + .innerJoin( + 'complementary-certification-badges', + 'complementary-certification-badges.id', + 'complementary-certification-course-results.complementaryCertificationBadgeId', + ) + .innerJoin('badges', 'badges.id', 'complementary-certification-badges.badgeId') .innerJoin( 'complementary-certifications', 'complementary-certifications.id', 'complementary-certification-badges.complementaryCertificationId', ) .where({ certificationCourseId }) - .orderBy('partnerKey'); + .orderBy('badges.key'); return CertifiedBadge.fromComplementaryCertificationCourseResults(complementaryCertificationCourseResults); } diff --git a/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js b/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js index 44d1769bbe5..0317d525553 100644 --- a/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js +++ b/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js @@ -6,7 +6,7 @@ const findByUserId = async function ({ userId }) { .select({ id: 'complementary-certification-courses.id', hasExternalJury: 'complementary-certifications.hasExternalJury', - complementaryCertificationBadgeId: 'complementaryCertificationBadgeId', + complementaryCertificationBadgeId: 'complementary-certification-badges.id', results: knex.raw( `array_agg(json_build_object( 'id', "complementary-certification-course-results".id, @@ -37,7 +37,8 @@ const findByUserId = async function ({ userId }) { 'complementary-certification-courses.certificationCourseId', ) .where({ userId }) - .groupBy('hasExternalJury', 'complementaryCertificationBadgeId', 'complementary-certification-courses.id'); + .groupBy('hasExternalJury', 'complementary-certification-badges.id', 'complementary-certification-courses.id') + .orderBy('complementary-certification-badges.id'); if (!results.length) return []; diff --git a/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js b/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js index 7e12d1817e4..2679c2e2f7b 100644 --- a/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js +++ b/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js @@ -5,11 +5,15 @@ import { learningContentBuilder, mockLearningContent, insertUserWithRoleSuperAdmin, + nock, } from '../../../../test-helper.js'; import { createServer } from '../../../../../server.js'; import { Assessment } from '../../../../../src/shared/domain/models/Assessment.js'; import { generateCertificateVerificationCode } from '../../../../../lib/domain/services/verify-certificate-code-service.js'; import { AssessmentResult, Membership } from '../../../../../lib/domain/models/index.js'; +import { readFile } from 'node:fs/promises'; +import * as url from 'url'; + describe('Acceptance | Route | certification-attestation', function () { beforeEach(async function () { const learningContent = [ @@ -98,6 +102,11 @@ describe('Acceptance | Route | certification-attestation', function () { const learningContentObjects = learningContentBuilder.fromAreas(learningContent); mockLearningContent(learningContentObjects); + + const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); + nock('http://tarte.fr') + .get('/sticker.pdf') + .reply(200, () => readFile(`${__dirname}/sticker.pdf`)); }); describe('GET /api/attestation/', function () { @@ -105,7 +114,7 @@ describe('Acceptance | Route | certification-attestation', function () { it('should return 200 HTTP status code and the certification', async function () { // given const userId = databaseBuilder.factory.buildUser().id; - await _buildDatabaseForV2Certification({ userId }); + await _buildDatabaseForV2Certification({ userId, certificationCourseId: 1234 }); await databaseBuilder.commit(); const server = await createServer(); @@ -130,7 +139,7 @@ describe('Acceptance | Route | certification-attestation', function () { it('should return 200 HTTP status code and the certification', async function () { // given const superAdmin = await insertUserWithRoleSuperAdmin(); - await _buildDatabaseForV2Certification({ userId: superAdmin.id }); + await _buildDatabaseForV2Certification({ userId: superAdmin.id, sessionId: 4567 }); await databaseBuilder.commit(); const server = await createServer(); @@ -221,39 +230,55 @@ describe('Acceptance | Route | certification-attestation', function () { expect(response.statusCode).to.equal(200); }); }); - - async function _buildDatabaseForV2Certification({ userId }) { - const session = databaseBuilder.factory.buildSession({ id: 4567, publishedAt: new Date('2018-12-01T01:02:03Z') }); - const badge = databaseBuilder.factory.buildBadge({ key: 'charlotte_aux_fraises' }); - const certificationCourse = databaseBuilder.factory.buildCertificationCourse({ - id: 1234, - sessionId: session.id, - userId, - isPublished: true, - maxReachableLevelOnCertificationDate: 3, - verificationCode: await generateCertificateVerificationCode(), - }); - const assessment = databaseBuilder.factory.buildAssessment({ - userId, - certificationCourseId: certificationCourse.id, - type: Assessment.types.CERTIFICATION, - state: 'completed', - }); - databaseBuilder.factory.buildAssessmentResult.last({ - certificationCourseId: certificationCourse.id, - assessmentId: assessment.id, - level: 1, - pixScore: 23, - emitter: 'PIX-ALGO', - status: 'validated', - }); - const { id } = databaseBuilder.factory.buildComplementaryCertificationCourse({ - certificationCourseId: certificationCourse.id, - name: 'patisseries au fruits', - }); - databaseBuilder.factory.buildComplementaryCertificationCourseResult({ - complementaryCertificationCourseId: id, - partnerKey: badge.key, - }); - } }); + +async function _buildDatabaseForV2Certification({ userId, certificationCourseId = 10, sessionId = 12 }) { + const session = databaseBuilder.factory.buildSession({ + id: sessionId, + publishedAt: new Date('2018-12-01T01:02:03Z'), + }); + const badge = databaseBuilder.factory.buildBadge({ key: 'charlotte_aux_fraises' }); + const cc = databaseBuilder.factory.buildComplementaryCertification(); + const ccBadge = databaseBuilder.factory.buildComplementaryCertificationBadge({ + complementaryCertificationId: cc.id, + partnerKey: 'charlotte_aux_fraises', + badgeId: badge.id, + imageUrl: 'http://tarte.fr/mirabelle.png', + isTemporaryBadge: false, + label: 'tarte à la mirabelle', + certificateMessage: 'Miam', + stickerUrl: 'http://tarte.fr/sticker.pdf', + }); + const certificationCourse = databaseBuilder.factory.buildCertificationCourse({ + sessionId: session.id, + id: certificationCourseId, + userId, + isPublished: true, + maxReachableLevelOnCertificationDate: 3, + verificationCode: await generateCertificateVerificationCode(), + }); + const assessment = databaseBuilder.factory.buildAssessment({ + userId, + certificationCourseId: certificationCourse.id, + type: Assessment.types.CERTIFICATION, + state: 'completed', + }); + const assessmentResult = databaseBuilder.factory.buildAssessmentResult.last({ + certificationCourseId: certificationCourse.id, + assessmentId: assessment.id, + level: 1, + pixScore: 23, + emitter: 'PIX-ALGO', + status: 'validated', + }); + const { id } = databaseBuilder.factory.buildComplementaryCertificationCourse({ + certificationCourseId: certificationCourse.id, + name: 'patisseries au fruits', + }); + databaseBuilder.factory.buildComplementaryCertificationCourseResult({ + complementaryCertificationCourseId: id, + complementaryCertificationBadgeId: ccBadge.id, + partnerKey: badge.key, + }); + return { userId, session, badge, certificationCourse, assessment, assessmentResult }; +} diff --git a/api/tests/certification/course/integration/infrastructure/repositories/certificate-repository_test.js b/api/tests/certification/course/integration/infrastructure/repositories/certificate-repository_test.js index a6ed53b5c51..3a55b2de32f 100644 --- a/api/tests/certification/course/integration/infrastructure/repositories/certificate-repository_test.js +++ b/api/tests/certification/course/integration/infrastructure/repositories/certificate-repository_test.js @@ -25,6 +25,456 @@ describe('Integration | Infrastructure | Repository | Certification', function ( }, ]; + describe('#getCertificationAttestation', function () { + it('should throw a NotFoundError when certification attestation does not exist', async function () { + // when + const error = await catchErr(certificationRepository.getCertificationAttestation)(123); + + // then + expect(error).to.be.instanceOf(NotFoundError); + expect(error.message).to.equal('There is no certification course with id "123"'); + }); + + it('should throw a NotFoundError when certification has no assessment-result', async function () { + // given + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: true, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [], + sessionId: 789, + }; + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + _buildIncomplete(certificationAttestationData); + await databaseBuilder.commit(); + + // when + const error = await catchErr(certificationRepository.getCertificationAttestation)( + certificationAttestationData.id, + ); + + // then + expect(error).to.be.instanceOf(NotFoundError); + expect(error.message).to.equal('There is no certification course with id "123"'); + }); + + it('should throw a NotFoundError when certification is cancelled', async function () { + // given + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: true, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [], + sessionId: 789, + }; + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + _buildCancelled(certificationAttestationData); + await databaseBuilder.commit(); + + // when + const error = await catchErr(certificationRepository.getCertificationAttestation)( + certificationAttestationData.id, + ); + + // then + expect(error).to.be.instanceOf(NotFoundError); + expect(error.message).to.equal('There is no certification course with id "123"'); + }); + + it('should throw a NotFoundError when certification is not published', async function () { + // given + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: false, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [], + sessionId: 789, + }; + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + _buildValidCertificationAttestation(certificationAttestationData); + await databaseBuilder.commit(); + + // when + const error = await catchErr(certificationRepository.getCertificationAttestation)( + certificationAttestationData.id, + ); + + // then + expect(error).to.be.instanceOf(NotFoundError); + expect(error.message).to.equal('There is no certification course with id "123"'); + }); + + it('should throw a NotFoundError when certification is rejected', async function () { + // given + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: true, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [], + sessionId: 789, + }; + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + _buildRejected(certificationAttestationData); + await databaseBuilder.commit(); + + // when + const error = await catchErr(certificationRepository.getCertificationAttestation)( + certificationAttestationData.id, + ); + + // then + expect(error).to.be.instanceOf(NotFoundError); + expect(error.message).to.equal('There is no certification course with id "123"'); + }); + + it('should return a CertificationAttestation', async function () { + // given + const learningContentObjects = learningContentBuilder.fromAreas(minimalLearningContent); + mockLearningContent(learningContentObjects); + + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: true, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [], + sessionId: 789, + }; + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + _buildValidCertificationAttestation(certificationAttestationData); + await databaseBuilder.commit(); + + // when + const certificationAttestation = await certificationRepository.getCertificationAttestation(123); + + // then + const expectedCertificationAttestation = + domainBuilder.buildCertificationAttestation(certificationAttestationData); + expect(certificationAttestation).to.deepEqualInstanceOmitting(expectedCertificationAttestation, [ + 'resultCompetenceTree', + ]); + }); + + it('should return a CertificationAttestation with appropriate result competence tree', async function () { + // given + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: true, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [], + sessionId: 789, + }; + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + const assessmentResultId = _buildValidCertificationAttestation(certificationAttestationData, false); + + const competenceMarks1 = domainBuilder.buildCompetenceMark({ + id: 1234, + level: 4, + score: 32, + area_code: '1', + competence_code: '1.1', + competenceId: 'recComp1', + assessmentResultId, + }); + databaseBuilder.factory.buildCompetenceMark(competenceMarks1); + + const competenceMarks2 = domainBuilder.buildCompetenceMark({ + id: 4567, + level: 5, + score: 40, + area_code: '1', + competence_code: '1.2', + competenceId: 'recComp2', + assessmentResultId, + }); + databaseBuilder.factory.buildCompetenceMark(competenceMarks2); + + await databaseBuilder.commit(); + + const competence1 = domainBuilder.buildCompetence({ + id: 'recComp1', + index: '1.1', + name: 'Traiter des données', + }); + const competence2 = domainBuilder.buildCompetence({ + id: 'recComp2', + index: '1.2', + name: 'Traiter des choux', + }); + const area1 = domainBuilder.buildArea({ + id: 'recArea1', + code: '1', + competences: [ + { ...competence1, name_i18n: { fr: competence1.name } }, + { ...competence2, name_i18n: { fr: competence2.name } }, + ], + title: 'titre test', + framework: null, + }); + + const learningContentObjects = learningContentBuilder.fromAreas([{ ...area1, title_i18n: { fr: area1.title } }]); + mockLearningContent(learningContentObjects); + + // when + const certificationAttestation = await certificationRepository.getCertificationAttestation(123); + + // then + const expectedResultCompetenceTree = domainBuilder.buildResultCompetenceTree({ + id: `123-${assessmentResultId}`, + competenceMarks: [competenceMarks1, competenceMarks2], + competenceTree: domainBuilder.buildCompetenceTree({ areas: [area1] }), + }); + expect(certificationAttestation.resultCompetenceTree).to.deepEqualInstance(expectedResultCompetenceTree); + }); + + it('should take into account the latest validated assessment result of a student', async function () { + // given + const learningContentObjects = learningContentBuilder.fromAreas(minimalLearningContent); + mockLearningContent(learningContentObjects); + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: true, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [], + sessionId: 789, + }; + + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + _buildCertificationAttestationWithSeveralResults(certificationAttestationData); + await databaseBuilder.commit(); + + // when + const certificationAttestation = await certificationRepository.getCertificationAttestation( + certificationAttestationData.id, + ); + + // then + const expectedCertificationAttestation = + domainBuilder.buildCertificationAttestation(certificationAttestationData); + expect(certificationAttestation).to.deepEqualInstanceOmitting(expectedCertificationAttestation, [ + 'resultCompetenceTree', + ]); + }); + + context('acquired certifiable badges', function () { + it(`should get the certified badge images when the certifications were acquired`, async function () { + // given + const learningContentObjects = learningContentBuilder.fromAreas(minimalLearningContent); + mockLearningContent(learningContentObjects); + const certificationAttestationData = { + id: 123, + firstName: 'Sarah Michelle', + lastName: 'Gellar', + birthdate: '1977-04-14', + birthplace: 'Saint-Ouen', + isPublished: true, + userId: 456, + date: new Date('2020-01-01'), + verificationCode: 'P-SOMECODE', + maxReachableLevelOnCertificationDate: 5, + deliveredAt: new Date('2021-05-05'), + certificationCenter: 'Centre des poules bien dodues', + pixScore: 51, + certifiedBadges: [ + { + isTemporaryBadge: false, + label: 'Pix+ Test 1', + partnerKey: 'PIX_TEST_1', + imageUrl: 'https://images.pix.fr/badge1.svg', + stickerUrl: 'https://images.pix.fr/skicker1.pdf', + message: 'Pix+ Test 1 certificate message', + }, + { + isTemporaryBadge: true, + label: 'Pix+ Test 2', + partnerKey: 'PIX_TEST_2', + imageUrl: 'https://images.pix.fr/badge2.svg', + stickerUrl: 'https://images.pix.fr/skicker2.pdf', + message: 'Pix+ Test 2 temporary certificate message', + }, + ], + sessionId: 789, + }; + + _buildSession({ + userId: certificationAttestationData.userId, + sessionId: certificationAttestationData.sessionId, + publishedAt: certificationAttestationData.deliveredAt, + certificationCenter: certificationAttestationData.certificationCenter, + }); + _buildValidCertificationAttestation(certificationAttestationData); + const badge1Id = databaseBuilder.factory.buildBadge({ key: 'PIX_TEST_1' }).id; + const badge2Id = databaseBuilder.factory.buildBadge({ key: 'PIX_TEST_2' }).id; + const complementaryCertification1Id = databaseBuilder.factory.buildComplementaryCertification({ + label: 'Pix+ Test 1', + hasExternalJury: false, + }).id; + const complementaryCertification2Id = databaseBuilder.factory.buildComplementaryCertification({ + label: 'Pix+ Test 2', + hasExternalJury: true, + }).id; + const complementaryCertificationBadge1Id = databaseBuilder.factory.buildComplementaryCertificationBadge({ + label: 'Pix+ Test 1', + badgeId: badge1Id, + complementaryCertificationId: complementaryCertification1Id, + imageUrl: 'https://images.pix.fr/badge1.svg', + stickerUrl: 'https://images.pix.fr/skicker1.pdf', + certificateMessage: 'Pix+ Test 1 certificate message', + temporaryCertificateMessage: '', + }).id; + const complementaryCertificationBadge2Id = databaseBuilder.factory.buildComplementaryCertificationBadge({ + label: 'Pix+ Test 2', + badgeId: badge2Id, + complementaryCertificationId: complementaryCertification2Id, + imageUrl: 'https://images.pix.fr/badge2.svg', + stickerUrl: 'https://images.pix.fr/skicker2.pdf', + certificateMessage: 'Pix+ Test 2 certificate message', + temporaryCertificateMessage: 'Pix+ Test 2 temporary certificate message', + }).id; + + databaseBuilder.factory.buildComplementaryCertificationCourse({ + id: 998, + certificationCourseId: 123, + complementaryCertificationId: complementaryCertification1Id, + complementaryCertificationBadgeId: complementaryCertificationBadge1Id, + }); + databaseBuilder.factory.buildComplementaryCertificationCourse({ + id: 999, + certificationCourseId: 123, + complementaryCertificationId: complementaryCertification2Id, + complementaryCertificationBadgeId: complementaryCertificationBadge2Id, + }); + databaseBuilder.factory.buildComplementaryCertificationCourseResult({ + complementaryCertificationCourseId: 998, + complementaryCertificationBadgeId: complementaryCertificationBadge1Id, + partnerKey: 'PIX_TEST_1', + acquired: true, + }); + databaseBuilder.factory.buildComplementaryCertificationCourseResult({ + complementaryCertificationCourseId: 999, + complementaryCertificationBadgeId: complementaryCertificationBadge2Id, + partnerKey: 'PIX_TEST_2', + acquired: true, + }); + await databaseBuilder.commit(); + + // when + const certificationAttestation = await certificationRepository.getCertificationAttestation(123); + + // then + const expectedCertificationAttestation = + domainBuilder.buildCertificationAttestation(certificationAttestationData); + expect(certificationAttestation).deepEqualInstanceOmitting(expectedCertificationAttestation, [ + 'resultCompetenceTree', + ]); + }); + }); + }); + describe('#findByDivisionForScoIsManagingStudentsOrganization', function () { it('should return an empty array when there are no certification attestations for given organization', async function () { // given diff --git a/api/tests/integration/infrastructure/repositories/certificate-repository_private_test.js b/api/tests/integration/infrastructure/repositories/certificate-repository_private_test.js index 56921226b72..1bd7c9e8563 100644 --- a/api/tests/integration/infrastructure/repositories/certificate-repository_private_test.js +++ b/api/tests/integration/infrastructure/repositories/certificate-repository_private_test.js @@ -1004,6 +1004,7 @@ async function _buildValidPrivateCertificateWithAcquiredAndNotAcquiredBadges({ }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId, + complementaryCertificationBadgeId: acquiredComplementaryBadgeId, partnerKey: key, acquired: true, }); diff --git a/api/tests/integration/infrastructure/repositories/certificate-repository_shareable_test.js b/api/tests/integration/infrastructure/repositories/certificate-repository_shareable_test.js index b6702abc606..c1135b3470f 100644 --- a/api/tests/integration/infrastructure/repositories/certificate-repository_shareable_test.js +++ b/api/tests/integration/infrastructure/repositories/certificate-repository_shareable_test.js @@ -659,6 +659,7 @@ async function _buildValidShareableCertificateWithAcquiredBadges({ shareableCert }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId, + complementaryCertificationBadgeId, partnerKey: key, acquired: true, }); diff --git a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js index 359f635a3bb..ef2f6b15da7 100644 --- a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js @@ -258,15 +258,20 @@ describe('Integration | Repository | complementary-certification-courses-result- }); databaseBuilder.factory.buildBadge({ + id: 11, key: 'PIX_TEST_1', }); - databaseBuilder.factory.buildBadge({ - key: 'PIX_EDU_1', + + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 301, + complementaryCertificationId: 1, + badgeId: 11, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ id: 10, acquired: true, + complementaryCertificationBadgeId: 301, complementaryCertificationCourseId: 999, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, @@ -286,6 +291,7 @@ describe('Integration | Repository | complementary-certification-courses-result- { id: 10, acquired: true, + complementaryCertificationBadgeId: 301, complementaryCertificationCourseId: 999, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, @@ -309,15 +315,28 @@ describe('Integration | Repository | complementary-certification-courses-result- }); databaseBuilder.factory.buildBadge({ + id: 11, key: 'PIX_TEST_1', }); databaseBuilder.factory.buildBadge({ + id: 12, key: 'PIX_EDU_1', }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 301, + complementaryCertificationId: 1, + badgeId: 12, + }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 302, + complementaryCertificationId: 1, + badgeId: 12, + }); - databaseBuilder.factory.buildComplementaryCertificationCourseResult({ + const pixResult = databaseBuilder.factory.buildComplementaryCertificationCourseResult({ id: 10, acquired: true, + complementaryCertificationBadgeId: 301, complementaryCertificationCourseId: 999, partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, @@ -325,6 +344,7 @@ describe('Integration | Repository | complementary-certification-courses-result- databaseBuilder.factory.buildComplementaryCertificationCourseResult({ acquired: true, + complementaryCertificationBadgeId: 302, complementaryCertificationCourseId: 999, partnerKey: 'PIX_EDU_1', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, @@ -343,13 +363,7 @@ describe('Integration | Repository | complementary-certification-courses-result- }); expect(courseResult).to.have.lengthOf(1); - expect(courseResult[0]).to.deep.equal({ - id: 10, - acquired: true, - complementaryCertificationCourseId: 999, - partnerKey: 'PIX_TEST_1', - source: ComplementaryCertificationCourseResult.sources.PIX, - }); + expect(courseResult[0]).to.deep.equal(pixResult); }); }); }); From 0785cf8e56d16b03a55fb81d277da5af7e2a0f7e Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Mon, 13 Nov 2023 09:28:30 +0100 Subject: [PATCH 06/11] :recycle: api: Update accpetance tests with complementary data --- .../application/certifications/index_test.js | 36 +++++++++++++++++- .../application/certifications/sticker.pdf | Bin 0 -> 113216 bytes 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 api/tests/acceptance/application/certifications/sticker.pdf diff --git a/api/tests/acceptance/application/certifications/index_test.js b/api/tests/acceptance/application/certifications/index_test.js index 4922294654e..d94b1d42ea4 100644 --- a/api/tests/acceptance/application/certifications/index_test.js +++ b/api/tests/acceptance/application/certifications/index_test.js @@ -116,6 +116,7 @@ describe('Acceptance | API | Certifications', function () { await databaseBuilder.commit(); }); + it('should return 200 HTTP status code', async function () { options = { method: 'GET', @@ -204,6 +205,7 @@ describe('Acceptance | API | Certifications', function () { await databaseBuilder.commit(); }); + it('should return 200 HTTP status code', async function () { options = { method: 'GET', @@ -287,7 +289,16 @@ describe('Acceptance | API | Certifications', function () { 'last-name': certificationCourse.lastName, 'pix-score': assessmentResult.pixScore, status: assessmentResult.status, - 'certified-badge-images': [], + 'certified-badge-images': [ + { + imageUrl: 'http://tarte.fr/mirabelle.png', + isTemporaryBadge: false, + label: 'tarte à la mirabelle', + partnerKey: 'charlotte_aux_fraises', + stickerUrl: 'http://tarte.fr/sticker.png', + message: 'Miam', + }, + ], 'verification-code': certificationCourse.verificationCode, 'max-reachable-level-on-certification-date': certificationCourse.maxReachableLevelOnCertificationDate, }, @@ -442,7 +453,16 @@ describe('Acceptance | API | Certifications', function () { 'is-published': certificationCourse.isPublished, 'last-name': certificationCourse.lastName, 'pix-score': assessmentResult.pixScore, - 'certified-badge-images': [], + 'certified-badge-images': [ + { + imageUrl: 'http://tarte.fr/mirabelle.png', + isTemporaryBadge: false, + label: 'tarte à la mirabelle', + partnerKey: 'charlotte_aux_fraises', + stickerUrl: 'http://tarte.fr/sticker.png', + message: 'Miam', + }, + ], 'max-reachable-level-on-certification-date': certificationCourse.maxReachableLevelOnCertificationDate, }, id: `${certificationCourse.id}`, @@ -638,6 +658,17 @@ async function _buildDatabaseForV2Certification() { const userId = databaseBuilder.factory.buildUser().id; const session = databaseBuilder.factory.buildSession({ publishedAt: new Date('2018-12-01T01:02:03Z') }); const badge = databaseBuilder.factory.buildBadge({ key: 'charlotte_aux_fraises' }); + const cc = databaseBuilder.factory.buildComplementaryCertification(); + const ccBadge = databaseBuilder.factory.buildComplementaryCertificationBadge({ + complementaryCertificationId: cc.id, + partnerKey: 'charlotte_aux_fraises', + badgeId: badge.id, + imageUrl: 'http://tarte.fr/mirabelle.png', + isTemporaryBadge: false, + label: 'tarte à la mirabelle', + certificateMessage: 'Miam', + stickerUrl: 'http://tarte.fr/sticker.png', + }); const certificationCourse = databaseBuilder.factory.buildCertificationCourse({ sessionId: session.id, userId, @@ -665,6 +696,7 @@ async function _buildDatabaseForV2Certification() { }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: id, + complementaryCertificationBadgeId: ccBadge.id, partnerKey: badge.key, }); return { userId, session, badge, certificationCourse, assessment, assessmentResult }; diff --git a/api/tests/acceptance/application/certifications/sticker.pdf b/api/tests/acceptance/application/certifications/sticker.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f7c613f259e6e3c51f55ba7660c0a127895463e2 GIT binary patch literal 113216 zcmd?R2UL?=w=NtDpopSKu^=E)6ok-=ASg-`5Gm3jw19M^_aLZ969FM0O;L~zQl&;v zdX?T0r6|2f354*kH-P%>z0Y^fJ^#4pjyvui8JfIhm06zo%sJPV`OZx#4sK3s{8dLF!M{8J0?EV(nEjeu?u2W^F+|A0kkZw(PXgcB_ZLNz<-n?lt zEW*V%)P+!EqYMy#q-rL}lq*WbkR;wvKE{QMIBy!v-=@7C&ES znvYd3+bQ4sEbx@tS>saV)cNR_dS~h0bw~2 zadS@m`ihk2*=BuJ&vHsmH3I+SySBwkhMDc5mMz8g&E?f1%(r0s?MbyY@%fRpH*DtH ziQesZp6N7U4+uXsz8Z4(gn=|&!=OL*1hL%X(+x2?pbz$#}-b=u)Kg1)1_jc=SOzUCf9*_YNw7DJZ$#8=|~V9BWv_tKkB}3`E3Vpk#E<8vv;9HjJ>ERp1e|f-z&iO z5T0KA)r?bG^8bbWaUkyc z(ZYS15e42?F8XZ+Ymzn|MC&vw)AnCR#kCO!e@*65TOWyv)SJ*#WUIb>_f9vdoVPaH zQ69l#TBMF=Z!Xt&@yZ06ww+}^O~GiOB}i=?>8dN*b+p{qpr;F|o*W?AOwH?hoEvcq zz3L)SF6NZ;g3;)>$O-a{n9IU8Qhvn-b1pENk0s|xOG$n{^$+Tdjb&eG4tD=kdNy^S zh^o9&fviHED>CCMImZW~#$a8fx)|Th*tTX zshJN{+BeB+oYJ=eyXPr@C<{%!J|KkoXxCzWS84&vWEnFsOC+kFtan>~EJ;rJx=2Kp zYf!#VXk;Gkp`zFN)zkj`M}7JnuITeOnB3?la!h_YtRd4>=2K^KU%Bm7TBmdqN5q{hhQZ43ZuBGqQ*RVe zJDnFIry4BL;Lk-(LZU$%@N-b$Y-}#w!n2QaqLp89MU48&g8jK0M@DKkN2oiQjf z=*M^cY9>m+tAk0;ysr9)LYFJ^A>^0K0o3kaRz1X{W<6d}CB(}t|0rrRPC13rY2orh z4CXst)qE@aeMnLi?&I+Eo>SeS&sBoK#S;#1j!?NFo4vVcWv!IH28uQO#Aaj)k__5p zf4E_z-a+-mtDkgD&-lS4?P<$6wPO3obE7hqFOmE)#P9tx=cX&oKMwaC7`K$kW%_Bx zi2S@Sf>$~6v3Wqfj6uAN)*;e1zHWJ8mEbksRAZSXqMDmZuhY(XXRlnb;Jug-Rdb_> zW}LW1`s{rdt!89y6}i2pIHF5QC*G{kRir3cto^EexNA*xBW;@D;>&wzd!=|fvCk3m znfjQK9j<@=CWo$Wp|#+hwmf_I>pk&H)fMe*5E+G_uNXOY6IWX({^X&SNIJM z@E05Sz>2(|JtFoRZi~8L(l)bxfV$GMbYsEpb!rwk8>BT>GMyEkQOl z+fQM`GcPi-NKfw3%)-`fc~Vzyt_yeCg1AQnS1MWJhr06+vrL5-T>Ig}xZ(MNeS>~_ zO)nYM;FX0jSr4rLqB-!dohpb3$<(`Wn_0DRSM{$O*N=TQ`nlA=S z+hH$CSFq7}lX$GHD|EYz%mh1&=F|)@Yg(M^xH&R}F6m8QBu*D!YB?J<`@E)QlV!Rr z?sXTPru(9T=u4Mx&92e`H6jenl56(qStdkx$7Lo4PLmYY;(&eja7ZE@Y23R11h-#vQ*zB%&SY{ zEsH8{+((j_AA9VVenwc}AQSscSpoy{XdyAjsEeU9O5=EQ70Dfcau=(v({qe7!W*|w z*iT(Kb(zD8^|OY+EB(0I@FDV(i`N@`l4GxF1z3=gz(D#$ynXOt{W zn4D8n^dmZsdS6**h#YiQ*DqkvJuQ*{CH$CV(K9Bi?9{0_#dl#%YSj%dtOL?H^zvt0 zKN$92=+uAkYNPVV3VEK4r_gMAx!Ax`;ZKv294XJGf!S1q>$|J5tG1ZhzR54|FHFkm zlvjL;t4@g<)6Z_Mux@>pxjdRL^7X2q=-KairpcC(GFzkkr3;JcmypaTZ=>q5V5F8Da^^_@pffLg96N`OY5H!WyKQ|c>nT3p9_;U`vB?~;qBBbrx#aK!2^z`8N{Y`NPT9Lb?NNeL=b`8Ek zyc{Cq(BFWL^9TtGLk~bdd4JJ%0#TQAagbJV03@A3R1~@Z{(~EEfgcR;8`l}QIQbdi zMh1kx0e#=Q&%Jk_n{bz#;rBgz0S1A+EMVsgo9Fcp! zZh)t5BAv{Pkjl~$(8b+NDkJS}9PNyd_6(4KmH*hB5cmxV9un!!th)k%-rmcc1b7I) z{wZyOo>Fl%bRft@%Er!$@TA1f_PKc(cmx=Pg&4TGxpp`IKekh_Bf?Vty$SuRiTsmE z2r~#nBHJ|y@Wihyk$o(euZQ<87feF;#sN8o4%OCA?Vp<3lq=iXPSx=V^5v?^w2Zea z^4*bvtF>k4sM^*~l8)_%-RdSI`@26tcK4s^fmiVVT|EE`0-yg#6FjOw4Tqv*8LhZSNTnen>MwKJur} zKoIh;Z~ii}o!6m(fXx$LgB%24)DBhv(AxQOT~>v`#K6*?P4PSR6w|Sxd^{xkY=X$0A%qA5PsgUv9z&Mu{AIP(6)mf z(sl*}gn`QfsIs%Qf$Ai`fCesM=3svZX?MfM%GSmj!akvYsh-Tn=*p-VobnC{I*rd) zlKN@-87}(}Db<#6Q(4NlMDt+foR;GWy=qS9QfnWg>!p^0l}{L0WD}nohf$fS;#D7O zYGKvY*xwdsdr}Eu>SqAz90$PT49}7^w5C0R)ma^ zZ&e5%PMKoh9pqG>g9j(euylN`zP0tJlK$bi%C*sNUp!wgtGI~<;37C4d)@lTLWD<# z{>a$(Q2y~y*cP_nn=tPAXDz=bm}0hnLVJb!3rq&xm$#qPeJtq+%+UTjhyxH-_kevt{=zCIhpIHTwWLe6x2fff1nI;@l;J@6O}E4oMavavu}flmazNFJSA|?=RVTnt(+fWFC9fvZ^>O)Q0F4EupcnH z;_G_qL910O`(dw!Z&w+%YhuE1&)3;hwtW_!e}YL=_yyQ+oWq|_^=c^9fln>5wr@R8 z&fr~sg12}=`V|Mau%@nEa` zLb?Mlkx|1Dt!N*oaU%6-AGwAjEj&adsw8iEb=CadQ*oUt;)_%XIFjZ-)N*WD@$gs) z=d}(t7w7A)rwmb>+rf&u*LWJ;dd=>NqZDcRkG18$xX!#Uj;e1zU-P-JZ7wExwI|rf z*Q3|EQ_>S;$y^R;F*|GNg!vsnye+c32&?Uv-M6l>|F%akkvNe9*Z#N5FC{TjAtn`v zmY3su#>8lScu#A78h@)geDb*dauZwQzSFTZdfIqCNn&XS+fT6r5mK&OUvD2S15k(N$5-BqQ}K>Gn|yVqL}Bw@5hX?n&d#9S<(sJ3 zZ=z&)rHJ(-r@vQ2_eAWz#e%Rc%GT)&eAW$8oEho<8hh;}|LMz-XLVu}`l2dDEp3qd z&&=w!Eq-xN7)6=66EAgYCOeHrH@$x)6Pj*&l+-Zg3pH&G?Rm}fJ`(adZ*)-!YV*x! z8^`D7m+_x@K0fB6ORL%3oS#aTf0Xuuf5fCT<4tf3ac%9qQJix(hGXp8;#<7QL6i@} z(@&izo8wnZ?-(aaZ)sj`w>=|H+>IG`%baVg6S9i`!FW&c&cLj1n$ZP=KGEgpZv;p z%wVE~)R|w@^XUoKl`zv2r&~Y!l^OQs^BV(FBZ zTg@0RsW`i~{X$~Q)0r2ice#Q#3s3f-$2X;(|9W4Xq(}^v>wFFJsnJE(w>9^eJ|h-2 zh#nJ%?st{0;XL8u;wX8LxF(`xe)aC~df3cI63@INc_X&Z1WQFfmT4cHxt;f>?VM{& zV>l%((|(fD_{;TYb>6)B!f;7QhV)pNkMIAnuevHi%?0T1_Ku^1MP=e;j%YTc{+d$|QmQ{Qir={n_fCiBUpYFPY8zaDPo2QQx$^JW)~g>HgJ}5e5TdDn z*w!Am1KZ-|`L(OPZ1vyd6!zSL4B)fN*8VAz;0CKEP|1HyFS-8&>c5k@UFa6z{kJ6W z7v=k_xPGNzfE5lPe{TH z_L1rRgZBSR*)WgL|GTQ-X5b;DHUFrJU%4(9&t9_2<$N&e?{en(Gt=2I`Tr>QB6!E2 z$rr)L_7G<0-G9oy2vh7NPXD#+i)W8T{n0Vz`9H&#{{LlPedkFfl#W0Cd}MCa!1+Md zr>~^L$1DXlr-^L}2J}APjXG*l%5dl_)h8nPXvRrPeX;d#&$EWL9`_)xUOF+vKaTG? zuX<;Bzm~QXsU6FDpET)kG*PY0b(3o%m+rj0_Fz8|Ddok#eZiSl^5*FWQO5Efc`S21 z;nYHuBx%F5RjX3C#J;2^!O|D{r7wsN{EjQ$l=Lo#G8_NFGX4lF+=9QjEW!Wxl)j6I zJpaC*50S{fqvW!(86|ARXFONfVAH1TR8B_pfI1N)n$=_Jbm}J3f)TEb`317?KO49KgeaTZV2Qe{ELMV zR<CFuOw7U|80Q$U+_vEZo!=r)o#T~ zh-;@3{r8I1|EgR5NfKvr##upN@xjhD|iN-i%&9 zkao~YG4B=K1KXQYCOpxV=t+NckPDAEz39c16jh$D^>uZ1lV>rL#j9KNhz>pl=<~vQ zAYp6aAj}QE`C$roLuL2!&dvP;kvJl6$~wf(&z(Q>ANIGHis*)j>;pZz$&8uZ!Tz4?gS>m~5{WsvYZ>{KxYzl8Fkfz^zMM+fjte{Z0LGZ?jQ9 zyIT1PtIJWJYBzU|uQ*nZ$dz{TTp@uyi?Uy$S%Jge z=x>&7ptA%?VGP{jSLR#f!SL5SmuO%V%&2{^6h-`gSX5LK7|c8z1B2z? z#P7eB*a^OMvV!OC`D1HgGS6{Du$N-c0CM2Yvx|E}c}J$8_Aw}FPb-eWVKDxBFo;$n zp2+)V>aQs%z_b3!+xua65}-ktC!53z@o%Ps_j{-R<)QvCXi^qv(t6kBkXeUvC73Ng z*a>WfFtFY2t-uuoOr{bD9c;t<1vDoI_x356f4>qmK@4Gn3Hk$nS&<>08%B|Y|Fwmd zbW|wVg*PPdYgW)uJKF$n{vns{jc`bKStj6Z?~Om^`n^p54IoYA78qdXzOd;Q4Hy$B z@zjo}cLyFoRa}4sB1@3D{S+c&d;c}k-!hiPzA%>tVkd=W+1V^XidKz4pu1WD*5pr^ zc}FWiX*aF1=Lu?qF7K!fjJqdiNYTjOOTUCr*|PO~OOQJx%_)Me-*_k#WCDEw!`{0z z7K=k}$@ZKjf~gN^p%{NHn*u0;=@wW8!G^pe!5e>?MMIA&q!i^Gf^jK88zpGIzYzaR z$WbV=zgb2<(8R9N36|Db2q{qj(#&5?EmnOyw_`;LINNVADFdef#&wPKkDYg;fJs7H z-BAjp>OTw?T6gE_#z|-f1%mGXzLSB39pfvoS`OLw%OqFekWBS|t9!@vewzbiwLrEq z-oL#Ctdyw%XSAnPf+ZGkKtkOS(O-m0@TRF>x*)jX@68h4{zIq2tSC(Y2#`en)t2NS zTl#&;e+AMNu+={#lEU%Z_+#_%fe;9Q*>~-qpt@X0a}Z$cDjNX7uGxIr0NXNm-|~kx z%0kc;(7(9O$Pc&IurKDn{fJ;R=Gwp-cAb-;n?E-A7psQ+={Gw4<0}3`*+QlOaP=<+ zvg1d8Itpa-z#AEe{*GGFD6&16gFH0xFQWg;%K9TQzf70lj*wsNcq>6be>ltEQ4(4$ zc>kGCT&oi_%^%qL$0{K6fskQOe2@YD0c3>RK-qh@$1T!kS6FpzC}BDZzdiMNc4{Gn z+WV)W_5K0szb!)86weLRc&Qg_Nti7hk}?F62fPW&%13KIYCWqcyMZSkjh zLXv6#R=bO?fAyrlYztBjaO-D2vn%I*;{-v8z<~FB1lmJN<_at1Av>{%FpQHJF9iR( z1jiw~^T)<+LI(ebV?nyV@~;V3p71HDP~+f)8(Q5xI-vgNVZ4Yc^*-S01A z{xc9Z29=B8pE*u!nMxN49?M((JOqcE3(}X>(b-P^n4Ve89xj`A`?$~h9Zy4 ze~F|B0x+K)&zDn~6BlVPJr$!lqik)l*(XpYf^TzFgu!k=&avyF${nTO$g!eEd6!*^ z(`Z=+=q{1LeqP6h_d}L(9deiiTvD#VQf}^m=LF-UecpO(D5-R?qQeAc(LV<78bmpD zzJ7rYREe?_DV<}3Y3PhAnnG~B`#N*?^2cvadtCjkphY+jS;2CiwA3SZmB~*C9IPn+ zFh!wm*LdYrr1r4mk`;FO`O@2kJt|m8U~vj~<+{nGwD7WwnPJzBk^x;8XWV&P_!*rLV%YneSQQs- z2m|lzP_c2Q_a`~c!OTltF>AW>pW1-cG@2?+i?Z(bu09X#XV=Ha8Y?5U0A->+APN@xKDD!|2&Zv1&#f2p_ zXiUaTh1+t)bS80>X)Afiytqh7`Tcd^0RV|e;$!njMm!c6H`Qey^7J`97HkY|_FKAd z0l7^M!5#~Vnh*eI0yw<5mrq?gt5@!t5EpJUD3Zd(I)=5f+6K}u*G-?E@THmJq*1^| z3_-DcKOvxT=;Oo4e@X^VWX?I3*AH2k=;V)6pA9FX!{q_^tB%8gY%R-v#C?7>UAfeF zOxOee;Yz)kp3i4TR-N`U;uLJos{BAl#d{rcrLq34;yF#)NVJMw&hIajy*5m=}^HzGx=tQKS^_RB$TAPmbc)fhz zCA3BAI6RIO9zF}i3nY9?Gf4tnSb66gw_vp&b?jhsxAy8876oT2kzfZ@G333qo zLft+gf~p^Au$Ezn8up6o8H1yXp6PzSfe(vww7Kfh=jyCb6fq>Ch$zZ$d@w|DcYjA` z_bM*sm}R~0v**qRghY%$o{R2a8((h~Dfx;z65CA5gY{aK>G@Aks?V7uZt{aHgfOls zs$Q{}`_jiCZ5IX2vtMrk2RfVD_v6>@LEQ3Y0?{0nV)F`UX&`4aNBNo}VV%@WsA7Sr zYi68l7P*j)&X=}X;oEC-(RSK>o?b44>lJ=aI3Addd3pcVI$_@gQ9f!we0?0>y;?dv zO!vd%1IHVV+jzU#cI=a|;q3e27@c7Wr&sAXW<~1mRgw9`XHMUSultJ7v2V_v&>pyX zSiUu6>O_z#shHaMkK==9~;6dqs6UVo9ko>O_1NOVW?GEhK{y z{fXmnnj!K8oY&D8xbda#^%Hux z^mW#wPz6sp4d4z&ktc+etDtab;!^J!7=Xu-zkihfs1Z@I{Y{%3(K7C(3&hZMM!bsM zxl==HRIcc-`?%nbaSL(X)JbVaeYrWGH)Th(2>=F?frbP_2w|@oQnR8C)8~~t{pg)H zoFWJoxOBnN*23~T1SExT(k|mq*~Tm?GUgnIIUdXcHuPdsTP5yw`t3i(3Z)PfguHa* z>lhrwPcr)9RPknfEKpWvu)39)U4Y%;CXX5PFB#kpSV-#QN2sk?4~o?3RY0M(JZb~R z=CuR`Pj8RIn{|V%?k!hDv=PnD_u?p`ew)RNN8wIx{A)s`Z@1${&J76?gi_svXs{n| zuJzi1f>8hk_P}MOoTh5ZeSs^er_9&k>j4+Un&|NF(`v?+o>9WwNm&{*`;84CqFti^ zwgPeJ2}e{6)vzOgGS0c%37&!H3WLwYS-WGbZD=Q6*G3SVy!~1Qc*mK+oUD@XVJ9Xk zfJf6V2h&3nTbpIt4@~K;-s;lP^qRrvy$;kX6?+Kpo9LR&I>T#hVw0Zy^utUJ`N$U& za{LTs&rD!@p`D*)w)jqVN#LRTNxtv*+uXR!hWg=VihPl#2h#)x52SA~rj= zw@Z}9N2d&ERya^UX1T?Eam)#>GihPrjr~)4a(NG~ii^f)HE0gZe|+z$l0N3()!EDf zsK@v*h@1V^nUe2AT^q}JCO)=PSqa$2dVP5j>lZHzwpIm_{dAd7hxvR@EM4IZ8FE|C zoxR31{a|$ZeMyhs0&|+l*pgcD!Q@LzXxs5)Pyyl~lty?Sd5-x+X60D^uKeV%x}DLL5QXLwyl-SZ_rGuS_l064~~uJ>2{CWK55rRTLxY zaOcz^*N6xNHNS_j|7Fv_dqWW){uqH!6Bqz~aYAK03YEd+5zF!FMiT2j=~lsP@&PVM zu!ZsyOX@bme&WkHnKPW0>l8h@7Mn`v2JRjX!ZGU<-+<`L!*ja@8g30r#WIQy-%qcUOj_;7g*gX+`>-K$&=scet!v_ zA`*ZocO6+-U6pg`VRlIQDU5Q>-xe_UC@sHGNHCes_Cu~e$Bd4MF89iIG4SIU=F|Mf z;iC=bpxkVSv)e!ejb;ePbI1RS|h+=6& z@6I{Jd@%q)x&?m_3K`~eIql__mX01>8yn0+XI%-uTja}VAkO%3{2q`FWu1Y%2m#^E$f70_jbgyxAag*BOeDvyv7C z6)*cZ&2LIz<>HJgyfV6hAUYrPn+x^Y^NTwyQABl>UukViD32L`ymF`b1_3-lnh(1J zfhQ~Xh58;S()D^Bo-awgF%BU2O1(t!3LC24E6S=BWC=&-^dEIk4dk)LaHmdU4BY zyt!z1BK%pmD}HHi{Ud-x*NOI@S$8ed-4z_`irE~L1~#Ih7-+p3HE)U!n>FdwGY-iv zrtuF5xXpP<_lMQ4eHQlY=Jh+3wj&Xkn;hC8rBMQr>cZ%j6A+zwy>D-~0iK5}LTjL) zMPy7zNw;$5&&U#Z5PtISG*eO?o z(Fi0Hmp2&iDdJg?D-6PoJ25-_O|D%iCo+ z?EhDoi@?O*9F{4oW0zJ;_i*1g+ z{>#<~IV(hF->-;h>Gg@8cYGWgW3_}fus}_`lUQe|N4x;PDEakp%HIukiCcJ|>h)e{U2S&=2(y#$KeYFhH4ZgQCiBkGPlH+< z7%0sDy)bt3mj2@@vI~L(WAHpO#O*Qo5I`KR}lrC%iaVif|l?b6S+|S zF+TE#o323`=}(-5TWo@2icK1c#6z1HTvDF^30BT2H?#PNng51us;%v+IbI$s_ukI$ z4@&z1H;x^>KX~o?)UYLW|M$EGqq^WC$ri+wRF*>{qFA}S%mkom%BOjnhbmaLIJ{Dq z*tIHLul*9_O9J%*NeoYq+FGL2J5ZtNTVZj|=!e;zwc=hzgY2^D{S5N5$Axitb0IL1 z`|~d(UtqYWyi;R?e!l&~{c~PJLlpu70p&yIj>P5BJ-KTID-JHahn4fImwV+me*!D_ zQb4NCNREs8jf<_xJ5|f=B6U7MqCm9St4B8P@JkbdQx!xEy&rA+X?i}LHwasWAv&mp-n<5&Ah$gDJ%Z z$RND#?wde}758ZC>e2~k&Z^TL$>doQJ%ef~DM_eR+`=WD(rR^ESvr^TY;DDKG-$yp z5<@mHH4Fycv~4Piz2Q`p;R@)nfHTJpH@rqh(yC4j5(XgGeDYXD9Re68M=l$P< zPi39S$<@yEVsbm=hAdk4&&yI3My1|~RW@iSji)^J&?X1REHW`olgXC?5V?0FaMdY? zHIbS!T@$DLq!jfsj^Y%-PG+Fu-)`b(0QPZ9?UA)A*IRT#n2DA|^P{=1ica|mDrG<>9=j_q)RHJ$N_bUs?3mE4xf;AUH zzOYkX3XH{!-O%7(<6ylKt3gMX!9`6oEDVT?(lzpGl7&qq;35`y}q*s zl44gV=d_I-T?7r@WnL%ayGoW8s|Vx=z6%vHb_<=2vX9)0SqDIEkoTrSy`R*_n#C&V zw;x5A08a|XG1~*f9pnZDFoW4^c!$I+b<=4@N&SN(l3lru@z@I zme@hnPU3{w+`e;iRysJlGo_!LbuVOAvN8Q0c(*=#e$`~czP6x*<6*s*dxSxGj^pX7 z1TL`XblmtG<@&qo2YRl0{1}Hn=%+iMO|3lwfOzq0x$@#u_x$a`P}9ITFAn$fp!hq< z8`NCbpVam7Ewr_-H8)>KPDIchUCF?S>?L`MBw-x1AG&sAS{ z-Zle#SZYr94c0%W@@Q#l5#|+9G{Jo5i_+_}Vq(VFIcD-&Sz**XksX<-yUqoHa8)E! zO9EBk@TL0FDRNst$S*&-WReFeOX~J9*|FjrxKzM3OPUdEPj$0rTi^S+ueUcM_BZQ4 zEZb1kN_Xe|UbN&9w9?_%`xQ-BC^h9xuuQ1Sad-eS9x$}ZS8L0PH<>dgid{1S2f{&x z3GsFUS&})6z)x2tumO1u;KrHk4*RF}Oaqw9`!Tg+sJy4nXXiwHHZHwC|1!z!Y@R3P zJ5@%pj(T>0CR<@wxs==c+1D1<&qW9ggFRl`a3)YcK;RUcjd{?!h;e`t*%{r3QH2kW zMSxhmk$&tbD7iyOYk8CywWAypUI{$|?$@c^ma1F#wr3- zLR~`LR4br9Ehq*fSUDQo&a^gjBOhm+CsY#$6lX9d9a{`xgm zcXFO>1q__^(*#B%Fg4e@!b zS|J5-7hcN$ip@DzPzxED06M)w!VNP3$g9!?n>ZU=RXKK)u>WC76HLL?ABnO6m3ZmS z+X(uYz0RC_lt2t>m{VT9Bv$6{-lkHos5%Za5SG|SXd~_=LdD9c$3rj7saS@8c*MNc zvct*^e>{471UL+!F6)gwbvE*xKE?Ov8Ekt@)Nwo)SL<8a`#x(o$($}=xvkS@61?jM z1f1I~O+Tz7p_(+Bh>jQnjGq1HwKZ+J4&^4ql9A;E(4)969}57u`a`aEC*RPO2T>BN*nI3-KwN9ik~_#VKtciGTUFt%cRhge1a;Rrx0IZTi-=Co&hV0v^{7Zg zyj}bEQ`xvAP;y#8XIGBIsOiU*x1r~6J1yCWDm8eSDGj&l7ll+ueo)=ic}B2BDBk95 zO^S$h?}M3#VQ$t-*M*l9xDQT|-At%Mn5A07wUg?eF!j2@7tB|p5RbLPKj{AN~ruYUS_UhN-j1(HPI093cx3iQo^*tQtL99v) zD_N58f_=DLk4UTE%-lXXlQGBdnC-Z>NzoD3g1G(SiT)T|5+tP<4-TsvVl;jIUaXT3 z8bNbOpTQ1nE&yYQ6m-Af>}dI^MvCf|{)VkXQsUrwsQPty37u>_(y*M~AdnS6!hU#l zb@LF+mJyXY2{P&UWUh4gzC!-@Z-?fUG*y*syn4K@{I0aAa+~ax18)VmnXg6_e2o;&^>it@n9r zETK0xBC7b}$|y@lvZDY$Zj>!Q!@lQP180{(slWD8Q-gKkKBadu4$N92Z%51x<@9q8 zh)rSL9UWFcIRG5Mf>?_jKj7M}@C&6SbGk!~oNgS{2Vh(0QDzMb6Op$$-w|_c&dv_a zwU2t;y|)~Si4F40_@)9_6~Tu`ECo_yC|Sf@h5g}PsSE7=PM{OI+H&@)Xo*|;;Q3#t zLI`*uUC;Tf3Uf1m$Xd+h{G)x|V$U#q?4?!peq|=b!RlRa3lz`74l9u3~a|0>xA%3;M1R53sUUE-&J zYqX{EDcJXCI3#hvi5!ohceiz$`n^JnT{yh4_HOYRIWwO@>5klj#TpzQI{`{cdj~1d zP#4Ut_`|UQ{aeFXn`AKRP#khKVB^vK#VUYsQ1305>VSW>Raf7Iyjth-dKmBBSXOwd zL<7FzM<^522=B?<-hXX>Cq{=c!O^3};*3Ch_o}Y=fe%4YYhUYM&v#6^Ipbb!pM0u2 zOj$+niJd2)*7{DriHDh9T=v~eQYY}qanKFcbq38mLK#jDGDY)&4Y=vhu+WUCpjmF_ z%!@!Wn8`KJTH8IfB(U9E2VMds*C8P)zYU~5Qzgzjebb$lKt;X%Sh-OU=PWEX<4Bli z3qTpwd8ih~)e6RkaX=5GoWnLYRiO*EpbAL=h)$p#dbuA+qng|_=W7#aa+l1R!J@Pw zN)QPo03BR&RNTnoy)cSxJrV3^9TFQfwuAp)T+Ft0O+s5QkeX#1Fh_xQ*^pF8a zTyP~xCQu8$-j0n*IVzIW9_9_5LTP&sDpbRJNZQ~Z}s>|~egu|DYCvZkk@9cEN zAmEGx+P9Xj$IH#RN8gKkcoUX$4=nVWZyFQaRC~2`GMPHL%$X2u_D{e;|IgwP$ilB) zVy(}54INg%b^}e!xbg1OKM(kxck)=^hpd@+dyaRx?toUWDr^H1V>phUA@AEZ5ik3` z*mt00IEEOahMk~yfz5>*ATOj2UJVObjdhRU@9objItHVD1cZC= zzdZ|rplROt?4|im!ZAzBOmR)nQfcrBmD93eU23?IRIv5qsNTT5TPaNc0ko7CEn7|? z7rx$U$l2$+Y!`(s5m$lEjA*57pg%kaweN4RR-B+M{S;lQ)4jT>_Vxg589GtZdH?o$ zZmu>~stttbw-XIAS9}NtI4jwJU~3VLlvr0jCB^l_k_*;!6o~jv?axkw0!;s8`bhja zVp(TK9&piL3mhT=oND=81!Zn}Dxtya3-**U146or(TQMrb0xh4YYmdd8>t=&(6Ps` z90shMSN;7;#+7Bt3K&cm%n9oPRU~}VGfA_{Ux5_Cc)(zBzyQZ1x4MA&(v0)FQZ@Cl zD&8-#gG#UF*C*&3vy|B=F{<&VR4@%b;0!Nc0EcmP^}qFnp$0ffOS9Tifj$dePW~}t9;7q{kT88ZS z)uFw!VbrhiNmUJS07$u@I}B5XPAaiQv3Us#o{lcjbja1!9!RJd))-X9mu#g2SB6?sz-kdzOoh zfFqO+n|ouSvmpTH=?Xrve4Bj4)2FiHbWuccTYU|*R^FiPA-MWfvix&p%&|&x%|}nz zFkII}QEXyxDKa%M5o70<@dmhoM4Ff9lL)oXxUvW0qf_ z2-bpHd<K2F0KE)cnV@s7_aMo%a2v#B~+6{F@J(H$JP2x7rj1)?Z-kZ89F+g1*Lt+$Zt9-nS{R^5+(M1JJdJM6v0SlsI)6RHU~N1CqwpWsXx z*kz`8^fw%G>e%tuj1ROHdtWpMOlFyPSZw1o1dI6P0@lCjP-ue7^vir4>ANbyHM1iX zFE4hc<#g&Vk_RF)b4?rLJHGnDj@+C2IvN)EaAzfrm_YO4w_ZjEHo4fxTRXtXF~Wfk ztBM|!fBBF8XELvr_Z2-80ENBJYHL*%2Fm@}x3ZIQ%*BNE_OWnu>ebngnpda7Qz|tX zKt+-j6)g^+El+W5H0^HQrE%>63|NN7;4Q|FgYQ!~FQ;8%G~!ekO1H1SFNV#rr$2sb ze9VF}*;P{+-}W4;BF|zoQPEmjb5kbc=+La1zQlgfNw_am5v%KI8e3&w1tNG8ff*w) zp7H#`Q8bg;IeTgXYKWg5?Cfqpm?+|TNCK3;8hmN@%j=ucbE04;H7=Puo5G1>tF8~< z5?`NF^qLeA$9K;ox3@mqY|klHthj@pMI@;8)escBObz<*eH*MrD+{mi4XR6=F67oa-;ZQ5x~z_mU>^fe_V85L%(C3Ign=1qE?&W1Y$JAl~G>YwHj?1#>y&JU_@e@hV4+b~|9@0iNj6X8S+~;@Jf;V9=ST7vx-?iu zj`d;A6xmy!(ip(tq$=uLOCJtvj(1cMnt7Jl^UuYrONLWSj1fw&Ie$dthd7E?9dDy% zL}9(edhM`Wa%r1XII#GzI)vf!T$WpI`m)E&dzA~m={t*N7C8@&sy#<=uD)tvE+nMj zJRXJ0-y?C4Y%v=f&D|>_r)n({m-bOp35trcpkp9~%qS zpv7szYCuADl@)cqVR(K!FEJikpP519iW0SoY5^IZ>-2leAbi>V!hw@pMt*ah7aX%``=)0gZb59Rlg1oo)zdG=n2aK{R)?LnPk3HHo$p4i55*)Pwq3XeG)^R64q-ih zwwAM+hx^;Zq(g|usm;oILV4U?O(~qoQQ4N;b9XkolbykeXcl-Qp*VKPvpkp438sNpz&%>yV?_WYbXrPqAHsZstMB zn)Sb+XHA-y(I#)0UFK{};oyE{<75P@le~x4=_(M?gBY zK?*;D5@LIHy7jgbi@|BtPh=>e7Gw=Gm-yCeaNuD3wgTNGje?u4-Cew$-D>}Q*b#gg z3*Q%mtEyrggc=F7HvYLz?020J2Qjtd?bKYTVy9u6R%|aE+sK^hhm~6%1)QPqZXddG zMzTq(5H1N7I@jv~u}Mohj2_$+Jd0(p4USJ;UfhxCBvcj|UI|0ng;%wK<{=gB@K)Yp zzR~M0?MRZ@@D2ePjhbnUF>3fdKDgN*#q-4GaYyJ;7S6^5Tcd>$1RXA8T9@{UN?LJ= z^XHEMekR2jwhxv~akH9ckHP)j-BLWX2*cDuCWo@-wZ240DqeTh!^@*Sc|MK*O1K{pFAS^ zsCnqrN7;0Mc87@BJ49uoU4U8<4n%b9`+Hs7iK)edd0zKy2TG_;Kc0Lu++@g0uy0eDdZiOK)L*q);LAf484HkajBca>F&*R+h)O3jffJ% z!gnbwj|@P(S6%*!$b5QBH&VKsBM;%wT~@GXts~)6rF0R2F<4GjrGmZR3U*nD^-V zQC529^l`nRX%9qIYl7#K@+-iA%F9}r;Zz`otyaw82CKe4!`x}!#NekhG0V-Z+1noz z%aG4vVl!P&-xet>6OPieG~|dH5B`iZsumyAU<nBqPMq!TM|;M)v8JO5it#itcBreH@OQxxlotJ5HdzJ`Py-Bigpjk z;KrKFmIqBTis+t5&Og}H{`T9g==+t8HSIYw$Zs39XHSmv4)kEK>DeO= z<$=Yi#$$s=(&|Zceu{#B!DpORc)7SZH%5mPA;tkU%)l8u&4tQD`$ubfJ=$kp6@ent zSnTagx_4J+7(w@>a2Y*yft9_K_w0zf7x;I`r)NIi>|U{Wl)RBM0IvhJSP;O3!i&rc zv{wsfZUyEwnU2`F7nc7YS#KE@_4d6F zdu&RiQ&5zSp+qGW1w=ZeVW^=I>1I?^q?@6UF6qt@kQ_oKe~==uK6|9a-l zd2_s2d+(L^y4U*Dbie|BBmA9M&SuUwp=Sw&uJ#srmltrw6mcwOA~4z1YG z^(=39!jpq7g%X#sKRHFcBc@E^tUs)s9=Pt^3wpSq)GK!P{_znIiXPi8LMS!`fC4h>r00O-d%sE00b zZdxSOL9Co21yMAZLqGW0gSu&C>dusiT03SAdPTg`ZDD2Y9i}_7XuYaVU>!(wj!De@ zKkp2pd*{Nw5nWaX^kPLkQk`CCxrvD@slzMKVt@95(3-le zO#;}IkGPfmAXHxf2eqPrgh~6~{rbX|;P?Or(Y*$n-$- z$dO!DM-Hm-;%T`tVz!!|fcJ3^ZW&sNpt=8nxBMwqQsL|{cvCbv*2)I6=p}}D!X$8v z-f0_nR?q*!sX$NtI zkxN+9ZPU3SlVlEZ=dTdsC}`HmB_b>RBF%x+>Wu)d$YrbGt7th;G0R0YbXUXd=01q| z4T2e``wg`xn?kG$H|dI|9fTWx05z9w6AG|G^k0UB%8(%_KJd0O3Q3169Q<5^L#Ixk zjMlm3d9f-TJknav%^LB_-BtHi{u_<{A7MO_k$WI-{c|Zm9*!U{o}KHm>f3T_+^70D zIu>{(TEt8RU72|Nh?ee{^XGxvXoCIItx+Fqy_M2OIt}wq%_S<${9oR6GS-OZMbQ+J z2pmrs568}L@9>Q=VvFX_@^D#M;%et`Lr!fpMnUuVf`YKuqen@T49!^&f2SgZ&RlWx|7gna-&x&*?sMvLu%r->tfE)K7i@RHW3)+ zFihr?1^QK4RPUoeFO6(=p!g>f>FFRgtg79Iq$rv4ND1zu4RaAaow=P?Lm8D6gpxyS zqK3TE+5FI*RgF{oJc@N+ls%jwbxU1^-`luF^r3FDHI*s)1Fj7Zh}bscaT77k2Z_BB zl=rWX{EZ-XyNI2LPnzZbM^GG6#X4XMT-WRIa|3Dh;uB@LT-2=&E-9b8Gifk6bJLk3 zyJC6)_A}&0C)DEG+3w6Kaj48NXZqHBITjX+BB$V!n>`adZ8-{0r|DL7OiYVfuy8C} z)fbiVq0y+Jb%H&p1HqV>Rt?ZM{w-($r#q*~g=$x=xvp@PJNSDahM93Pq(1T0{>I@S zqN5yIZjEh;X@*6=;3%^hndIGe2WOwT5G3mzhu){Qlsyi-LwDh+BuLStv4>tX8LX&; z^a?|vk7|(=yAj+eSW|vmnpVMC+Bl|nW7L_v636Q^>)?42`i6^8x^!{I(F4$XiZA2b z-Ffv2H`jAsB0QTa6T7CCD#Tg8$^%aZ10G{knQIknR%v$PZrflqAYI4zP`46sPJ^20 z$%(n;fbQmq{ury%@F6#oC+}_nCi5Y6>F9%l*djQk6+7I&>sp=8tw!Rs7<+bC^*%Y! ze|b9xxj%o)q6xw9fT~-a1)jWe{Jqy2+wXxZV^ByRvh|I6`9@2H*?X*ilU8 zjwo;95OrX0;a=(Hf;L>5+nhIHQ#%;6fD)ZRw?~wGY3b^e#^QzC8L8k^cdY6c`$wYp ztm?n)XRlk`g?CQ(t9Wh$vQ$sQbIl>VUz3nlZ(O(aas+aSgu>WLzrsDf{iAjS@_SSY zrq@+5n?zTKp(PmzGOz5zc z{!kvEBT20d{8Z@&isMpl+MD(8x@Or?W|&eP`QSuZJ&8Up+^K^3FEt&%9$PtpHK&r; zG1$FIPBORA>T~dRPt21qyy2*GTcATjQPYox=jLuFZzBz&g)n&zaZO^?L*@|_YhJ755QU-~^MNXnEqvtxxAX`FS9MXJZas!m4^yt7sG1?fOK{IT6@P+)FP0Pasm zqCZhoL*U=lOF_aK5IrTQnpZuzOt?uOhPyrW!IclsR&AY&Vx>!CT}mxN4`vj5M1;{* zt+OXCZ%Kxq@qPoB`UwC8hXKYhX%U&vwF3BK)>y}&!I}=8J`ZC}ZE8SxqwY%Np%)0U zZ2Q35Qp^IqZ|1&9jGWjHacBg^<@siT*jWto%4+>GIg2y4p)q(sQROSdE)n$Qg zYf?6IBT2@?#oHc6Nt=dlWKB0gLhmiLjan#nf@y8>CyLoDmq*NR4_IP%+>DJ&I>VSl z^>-u^c}`SC4fUSv1{QfV;CDB3{7oDdoKF#vKm5N@?f0%bI%6}>1vSYywGnuZ*YY51 zottJ=A3LC!;ETz`@?@|<7GR~Fn!IT zlAVDy5kvtWjP49iT1=lzO{XOv$}pm2n+X!iy1@A zsP`t+rJ*h{{V#45JG&1xw!3%0?eJySZqL@M?}ZpVt|!Or=%Cxs1Rs}d%|QU62V#aD zaC=5^(s@7O6$1Cl&z2pJ8~|etAhjY${LnavR+DxtaYT)KMj@+EMJB`> zzVK$PMd-@JQF62kwzO0vLFMz7LVQyjIq)ySeYp84KpyPuEQ^p1gyP$?<{xA;2aN+F z&T|Njr0BBsxnuxo7jRQ*?dlbB*LJmg7u!lWnR}k~{BpGmS)`{Mtp|22oc!9Rx))`E zUn+0~;``9-Xr@UJHjCH(Q7w*}$OO@(6ce}-n{v9l>Dn+T&-o`iHEP^NfKHaQ-f*8O zIVWBcyvT0WQ==Kj)$7g@!<)$)v+484Fl#NMQ5Qo3o)O*lLw>V&jPEhM4Q5LKGkpo< zzey?1eIQEtwDG`j-C9pO=c1MkhZa%_aDB5>Q1AP_%5C%9p`g-g`(|S@8`myv)Dzcg zydwc2vNmE!5g-}cnNt!%h@WolH&5veIfAtCJRW*2;(9&lg)39NnoC=9-kvjMsy$FM z>c+$lXsxwl7?MlpRBrN_Q8?OX=CAhO`5m)pT~V+$?X_hb!&pkx6_Vx^96RP3e}ya; zKtZ=W0qSBuuKJCW&Ro;2bG&WeE@;$|-O6+)B`Ax0g1VduF-9#86u%7PrcU4(4;IVp z4o|^H_X{1R(dD2Cb{lUu32FNK6^Z&qf$!UH&qZd+BD%qZhnXl3d84hs1bDX3LV)j0 z+AM`CN)Lbrb-ycz3+Ap>Te`R0_N=DM@)Y&6hy||tOhC#O)pqoc94eAoz}K=}oMG-^dR$i_ zyma>vYd`AQNAowKw(0W(x!1}o)en&jK~h!QVw?bU#w9bM4)5)zyM<%{9X}Y8x!pRy zC_VXbV8!4Al*F^k%GsX{m(Zf-ck<@qn6FSF85iw-9j#g2fk#$ORh4mfzK_%tc=8zoS*gcb0jjYs7;zr7O5wGP2m**k2g3jCi3o2op`IHSOTsx1DJzniuS0WyN!*7!zUI~Y z3KM+vW8s;1*KFo5J=2y+)8+C7m}7l2Du;4mPxh)@gS@DN`<_iUF?Gx^7x$SmXw`!P z;2aJ;+zo}LEw&pRjZxInGgz*BYS5xlhIl`-Tx#BJDI}dsRuPW;)f z8&71v0gieMUYT_4nx5a{4`5R~9hv$r44>j}@fG(#`)>+>^Z_&=(0w~g8EkIav>za& zN>o;3^f1o!Z*%hU0@`!@D(n36wgE6>A-~r9s01|%!E~9%+7sWxy0@wS10fr+^BDtI z;0Iz2s|6dXCeR<6E)syIG6HxD_MWrFlGd*umW-n1>|!q5m|2LkRW(M-4VyG#!zAfI zlGf??`Q_%&P7U+L)ohHi4bN8mX6+p_aF17zM=SaLzqwKp zd+!NFXOl~Dj<(y&lYHX}HK(4S{&GuAz1>t|MoXATBS09jX6I*#GW3tze#Z}xA;!~# zO_wP$!hhd6FbojLZy+delU0RPwY6Z9s8qjDlu~Cg0XGl~n!p>s4%4shAez2=9Ei5w zFk7FzlY}2gaJX1Guw|P(#siXX{-b3n84|yhzT;yNYy$Pw#v&{nX}3`KjcvO zqoul}vb63xYo?rOio$;O-7;RLV1Sl1DZ$U`64FZ%{Z2+L%=$t~qqb-_y+sEW;ToS> z`R+D1p3)|AQ(up`Uwg*cWbq0Mkd|y69{!}YZGd~Gt@hs56z9fmI=ROMl9c&)iFufG zAe{ndsPD@o$*9vpFw}MIb?CW|fjMvDT2R-X?nSwB)`>0G{*n9voJO8MQ-gcVkT2TyIiyS+HyHJCaPW#gzGQR zTiy-crwIw!t&rJw2$eVd{Q>M9LW-Jh+R2#wU=?A6N{T#U0?B1^EsPUPJ?;kN^h*GjhtloM0>ku#43puv-b=s#5XMawHrtkMw~D z554tjr8CO)7oBLC!io`gpwFZ+6HuA?(}m3&7t#K;!N}ZuOfKDd!M(r&bO-@2cSMSzZ`Q!+mz$%2s)BF*L&p49^50PIIS@Gyz{(xm5r$3*bPO2BMDH!QWyO2lb6;qMe8@SuHvataxvx+&zBG9i zaHkl>X6%3b(#xmKs^Pc^1IPhCrl$sFL zKZ7wPX$z}~sC_|4fRvM=(*Be#{nNp+0D* zO3%!z`5X2kABbQNTDN*FEoH`UQhXBJp);{NUT>*}5krpwj!CqkQ-&)&zu(zOd{o5D zTSD{77b)!WBX4^jbuM-!EzPT7+!}}H^Y04Cv=_;D2Zdh%pq@ab@pu|J85_&@r31=9 z>=4t_UkZ_;L*ys&5Je(NyhLwUw3WqBpqdD&K4{anb7n7h=XfnR?UeKjP(?<2V^Arl z?S&#{{lG%cp}$_Hc?gBs2ii*VSK0NZ#)&_qXqr8fP-!N7O0@O;KVGJvYs-y&fsmG%tObSZC>6G&XgfSp~I@1P~8yPWyaq#mYCy6g9#8A=9zue!}2s`wrF9CC;k1uP^)B*ghr0Q+;B@B z=gckm^;!`z776^#Fb&rN?IgfHJnV+%Q!x#Vwjcn8k^>wEnf{|6n_q=BY}QTP->|I) zug#_pe}&cF*TGE@-Gj4t1A)=P6W#KS^xf{+6^{&B0$+7PuSXSOc3iRjv-VS6V~@VI z*_tdnr4{#IHb!NhEr3?Zw)z-jjCfpPkCxCvhZwd|Ll;0q6 z(=|CFtE@*TtZ)Dlq_V^C)Em{m)O(=x(R#NSI?*&fu&P{@Z=$84V_=EOSW)j{?HZd) z+}c@|Fc@;MLyb10vH%U>#?B?EwUX2XpCN_y*%|?AAKf4FVZr|PAiJR|S?k#1+u6dq0jwY2? z|E;;T%J-pdX;cdj?V@t?Ay|+|YA}ou$c8kh*j^Qxh`Oywezl*Tz#L{n{t6&_2iGQ$p(F|86_B?Bo7^A+%)AAcJzpZBqHmPjy!Lnt07So3f z6sbbA<8waHWDEG7O*LW_n0fWt|JI9?W!Ow!u~o@T7r+|>ajnlLp=|S(v*t~}hI8<3 zNwF33ocaW$|1l%(lWB&WR{nGU0K5-i=Jj;}zz5BwM$+Neu`-WS*YEO(5{vZv>VI@T z7w#zc^DQ@*g|bUb?5s&dGTV)^cm@BF$&+6*XFZ*g(YWr&uW?TE1xj6pGk0UWSOK{z zn~-dBU66UD)0;ao6~)=m%}N=?D4NqH#J;m}r#>ZQaQ}Qc>+ZbVMI6wfHHVo`^e@V< z1T3U+?1~3q^Qc)rfIcG7K2ocwMfI$JQBLAYD`vTTPmsbSYq$Ni*L5%$vCtiy@3~IV zY-$&_E8~6X_X6s^^T>MfeN~+BGX?AvYLuvXoHX~(eUsl+_!%VMQV zzW|F3vOL9F4qVtXZyherhp zHnWr)IsT9*4B)Eg^Ymt{Db#sQ{`_G6+>TyIM12~w)AP?Is$YF0Ka(p9z&Jui?ES)N zfSueIe2#l@Fvnn-$Za~G;95S?A@s&QP zS)^vK_2xfZ)r{EF8Tu~`JKJsh^MiOz(yL{n3`*iIwLz%Y+_9bLCUHj0Zs!LR7Bg;C zWKyvIa_iOp?guQ02pV%?aQz?q!ek`PRg+sJPN20L#Zu~x97n(VTx+Mw><7oPEt zyuPdks!NbiYw$$&VTS=@g{Yk;{cUwvopeh|@$<{Iwr?B8-LVlk)=qF!&ef{ozq1Gm z*n#ffH!9Ls*6!hGkU#12cHZ(`YlTSFl`d?*0B``9hw5f2BGhiV@n#NmWe-8&RZR|4 zFXZPCW!JFOKHl42o-<6>?uZdT1ujU63-md*tI|d1cUMsf*@K^wK0+e#P;ONjH`CHhL?n@9)oEjN2w=&8Fo{_R@PIP_iM9wQHx~n}xxIq$5))kgHoW zr$zd%6V2>UI-4RK@cK_+Jr>Sx5>DZ_e%DS?l4y5hI(cD(v-t=Bd!?m>p zPyqXwQTSDmV^O!iH9xoKfm<&W`Zm64Z^gjozVYt7Us7fKrBU#|5+oqI(~dIR^xj?A z8$-cQ%%v(&b3sPg1dTy4_U@t_ievP{W#nh3r9&`Lg|V%a&{Y_VWK2)zPTXorVyAX5 zcGxmYU^4HEwH^=L2(~C;4TjHQ-Lc+-(6BQm`b5*3S5FD{StcVXl+L2{Zxg~Vo$I7<_} z@M3>KI8>vJ=>ja<+f+~wscB^-TPYi{GuXpI5j(Kt=jOj#!@!~`1we&1!ZO8zna*c3 zM^pPkG8JEwR+O+2tls*2k3S_l_2WPWnnegJo-{Aeq>pQw*eARbV8CW8JLj$u} zl&+A>U?9aEIqXprJW$!}=*a)2e%mjUTVrWck-;>z(i0&au7zFu3e-0{2=*o4=2)oD ziHQE;j4?YDN4o`sZkb%rBcG3V?FuS!J1Cak_C65F1KI%xiFt6`{K!FeU>okHQ;>=G zsG7d4eSF`~-Bai?K5|gzgHr4rlcS^yQSvVs%oSY&$pa8t?gMjNnvVK5?FCCTC}tMr z+xR+UuF`b1&pPXtGKOgC)4-TgZ1f}wj5@Kr+nOk;IQebjcFW$j{lT~{A!)MkV8%xV zdS-$$V>{Iyu%lQN?(Wx=rA-3n#GdCRhTiS@z{_CeK-hc0-kVn{DSVLUiM}|P3%N#C z_7fGg@jx7>+(jU(kv6|$?T~*wBy|PcJub}|y12@Tc;e5^foH{82ht(ac5}M)4V6sc z==0${kSKu>u$hesA)jrQ3#!`-+Y7H1iw7;g^>$wpDyc+0W8v?G6{b+4ynIdt-?4M( zC1b|zLI_NSWcKg3l*acvnbXOs1vYajX^aF_nd-5Wsc=yW1elb<_akS}tonz)FS0{f zZO&UE6Dv@jEx?N%Sro7#9jZ%aMVtCvN!Ik!PN21qZe>PGiS zw61-5KWw^<0%Hv}&Kml7>zLSrJfR)jrN2STO-Q}o9n!oYkKV3oY;bq!DiV`amP9+xQbT5XU_xss`B& z?)+&+KSoXm&$w)+(KU((WW{X`ns)#JQ_)1vep2Ibg;>K0NyW$gI{P>VnMh91Bxvt+ zzgpINH4PVMKyDEE+i>IXxAq-1>Ur0lQ6IVKYFFGQF|N%Yp}GZTw#eopkhJTRV=|*X zO8i}5O|%!5k3m;@wnF`5?u*7ZatvPx2g2qDyJ2bg3%koNbw0xQ6Vh!mJ1lTlZif6# z1wCEc&dD+}7DM!N@D9fH?!2Zg{}p7~!i4xG%s(q?v0Sd2<<>pjbC$K*s7F38cLk?0 zmsgQ`%V1hSaO9!ZSb+dmH21{Y-}0@&IuL%lXA z+HDRW`~fpDCM~w_ln?)DGgtPW8`o41nAsVeIg9KUG|I1K6U#k-j1S?KVG|(3T*f6SfaZvs|C31 zXwC+z^R}R(jrk_GhOGD_ZUjlC#kuPYVgFqj7!}#N$H~MSn1d#cXLJElO}S(#K$nf_ey- zua4J?=e6Sx&s-anPKSR_xJ>JgLZ<3z3cLA9ih@wGz;S-ERs2$}YRXKP-X6MYte9f7 zJQ(a1Q;aF{IcShb!svrmg;HQU+@M_t+U}a zi@Rkhn4O>bg~E>ZQ8TgU#d!>dc^ZuCZkXTGowBxDD%yoSwDqD}am1?TByQZVCi+8o zF_?#ZC{&FFxvFt{mC0`4nx-Iw*C@H}MY8>EfHIXUqvi5SE^Fw3xR+RWFZ^A$Ev@-_?r#M zha<0Nd_9Il;}!t|Qs5ZmkvFIbzE%)&tYQch>1IA2ry$9Fz=G^uCy3clZPU#3AI$^% zG^UG%=k-Z!VTT|x4;zQ)Y^9~#Z7Cs99XbJfWh}rJu7iQ$D-r>eVNWe$bUY?_Bv+3! z6F5)cTT{8}^G+WSDzL8EUy{72loTE>q^JSA*$!jKY((1nX%iy?KHG;yBOd~Hul_|K z+192$q&tVf+_C&y_nav#d+;+i#Y^6q4hH0D%2^vwNr97IJ+}D=S-1(E&!GPWYML3a zG;qqjQ^N(co$Y~RvRSC@JYY@*Nrv_&80ISI3dzTf4}i2!Dxn8AW(P<#G}W@s(ygA= zvn2r^G$nOLGRtP_*CwMI9v58Z^BpuvQWy3t`oMT$Y}2Yxla>-|)~R!#^IpR72wfJL zo-YBGw{&XqSg*&M-#GST3uct`#ujhr1tK9UswmE&N6Q$JDlc z|5l9{mK9}b`)&Z#&9`R{B7y_7L0a3YDZzO*a0ovg)s;hvb5+swhzk7(n*XInWX8+M zLNFE^QdX)w#1V+4vB(0sv3gWbphYvxDMz z_g^tK+C=wxA0&=ZdHfP!->2V`wn){zb`(q6O;3%JJ#b=eb(D|96 z;<;yDH^^HbuR*ZFgOy`0yM0M^F5)mu-m#ii|%(H4^Eui9VpP)$zjM{CcJPV+QA$aEY2F)_w`00 znV0ChY4g{6h?VAc4}lqC^%u*UBzuS()zQ55uL_~H;zqXHg_&={)<0M?5ut{gcblI7 zld8il3g^0UfQ47RYFJW|u&v6XJ(+9Kz7uPdcjal5@x4o(cJEa`l@s}S@r`qt;CEea zag`8OTSU_f2H43TL4k;FO@in5ci~MT&COF-&1rcag<>r9YQzBK9=Mv`oE?FSAeitOI?< zSf|@53N?n$G+aMsq{Ze+<`PI>MOd;bj=vjSc@*g@yB&;odga1}H&lr%7oHpN+5=b$cbm<1$spLECz(0!|ACb5RA{KqcLz~2W{euhQTQ^BMM@{VIV&zd6 zn%^ryw@9)~<6X$@JP1b{ktxY{vKEOYUr4ISWFo_*jP@R+ez{Fn77tNpm##I8m_W3* zSwD>Xj|8{D4leNf?0NMW{E=IUcK^Z5D-%>J7AczZy1N*IH9Oj@avQ?IV^0O#yLd(5Q8D>U?BI9YI~-lWfFVQhn-aKvjFK~7%+s0>bt_q} zHUCPK36=W}75=H$x>Z%8%{Lk?*3w0XSE1nVqc`q#R z$E@#|yo+}Aca}QAgGJKtzoN>rfBx&K**k8yP-Vd^_!`8i#!+k`f6Mx#xJEM7l32Wg zj-@01%$lW3BlcE0QaZsboWgOw^4r1wxP?O(>MtDs-z})BY$MVUn>8CWucem}WWW|5 zYVu)h3!%bDm1yYmRKp6*oSXpihe7Y33~TYPKfRoVB|toiV9chyJ0X@lUqa9Z8!T@9 z+)aD;{_W+ExSs~pEEhUwcCM}D@hzX)HEwHAm^*xBZ{!OHOR+9|CCDn&Yu_Tpo&U4k z)hF=WoFjtf_N#FN5Z3?LA;T_LK8C54AZaAis_wZFvt?w{bDMhe1ktMBACmHtkPJ^@I}^9+k3Ex z0T|(DFlh^Zj}P}6u%(ke0Us2Uh5z5H7hTv;Qj*c(^zX1FeSa*iE%_niX5{l2Ob4oH zD~R7xRJJkBYR$8VA;qe9`@6K|9GH9&KWNu2C&kx{N=N@Peih| zQp(OJU)X|F#0Ko1=i=?!n>KCDwzl44oUjtK4B@6`sUsRRj=gL$AC&9c4}B>`y{qf} z9@?<$xPb5sehGUVP+L_4I-4UHC$g-x&RD-ITNb#9|Q23Ki)Xy zH|NqsCco=2=C1x;RKAk>^y|f>Rf?R8oRLkRypAwRwJ#r-5#N&SdH9xZTsb2a!{O9K z8y6;>7smcFJ?PQXh1=HQi%?@WG;{jsg8$l`Y6JO)(#&jTMr;kkygWUbK0 z%TyHqF**p&noX(sgvj*oe5g^}3x3PNr<2YCZBl4lLqO+&v+weOkxs{}A z!Rv;6q}tC0)-Da|chpkOVMG>w%?NF$6w-sxxAe!@(O^X?_)QJY9HSggEGgvTC8#(H zJ+IQM9ZOv~&Ain6k`SJ-3O>xzNJEDx{m{9FFKO^Y>R!*t!!@VUz23AEzl@tGLsG)C zvQIA_^$xQo6H5PlO1>MWLuLA zHwRtvzg=R`0&bV){0_VfSGt@DA?M+@HUqhjkgL*p$z>_7qw6{)@H6)Rl_9x=-^Uj_M2^OSMqtEV`o4-t5<&gY`JIh9$7r%GZrb)$o z7v@zXVHwJ_m4}q8KGjIt(*!PxXZy{ynBR1cK!DL}zyn>dxv}!s1eD;b5bzT75X+F> zx(&+LO3S1F)r7w4gQCOTT5kCu!);@t0d2R2=&&K8dEx#Wv=p!r>c$dJ`0_PT_}V4y z2HaSj`sZ&ZwDA^OI`z@n zVT=A)uHj#wpQ07aq7Y71D04Y4Osr^&bXD>|I^1Y@-Tg9+yOKs*AHt`5Mkt|~-e34C z`-RrF>tw2KrgP(g4W9#Q9DZLO<|)BwZ}6FVDRfE%dHIy^ndsHH5a? z%-E9g#eOW-m+__3k6j7(6emh|!xt0-P;|KRj@Gxm_)jl$Qpx$zHnRE6l>!#z5+D9J zoUMO`{w7}xOOOd$oc`BcnY?tGsklXX_2pm)>+Dr_@|GAklAN7A(K0!|AR3Chy%BZB zwhZB+FE&_^MTKEqvkOexn(t*L)#mk&?7Qv>I?B1Y)gv|7v<=I%qf2&@L@r5ft6j&Q zr|G;zXMe)X=wtZUtKB{IHx6&Hs;u9kkB7S((I&HGMjI3ysidnBdnCC6wJ{CsuNiY< zizdCz4AA{$!+yiJFzc=}O8Sq!2nD~8LcRF<+T@16jE)OlG=4f`PTug6F<&WtcQ+%$ zmyMs--u}-1Uco6)NQ@7(>j(WD`1A|-IT7zsEkO*gJ%;C!EkQo3YNU5ovo%_rwz?oJ zQa)kBYfOA{y!e{Jq9k&>PZ`hZ2WFvAZ;OF}@)RlRlpCY4p>0m7&W$xw-n5F?uy0hB zIp~G5?H6_5XTY6?{R!ot|D(s5kO^|s{JIL|+hjbVe| zqWo3x40pu&Z?4HYmaOmyN{A|K(4}7dE~y$KUmUx=5VmH_(Mr83Z#c#3eiY0|pHoZV zEx)a1fq$++jBmi~IQV@R`wp#~&^yH2yZjxxa`~7bC&WcGPKg0^nV|oNiDqHd#?xWd zFPNc z5a4`2ak}#JN)A|s|M!o5M!vaq0cZ>O?@HJLqB_<1TiR{GD{2{vsq1&f=}7b3_|&M{ zViksV!3shx-MmQ*leVa{4&CdZJW}&4Y7*WQMMD~+j$17hak1W+GCd5!dP*|K#OKx_ zdgWYaF1*1RWP_0KX06g^);E8&MM~5_hreGp_g-pV+EN71Q{hxj@~oRdJC&xH1_$`u zO3$tRy*__;HfnshfaExk3X4 zX^)egh)e=339HhpdpEd}F|n4iZM96u0C_~e3xnD%7PHzQa^pSjRCfBvDSpqT=Sfq` zbO9-n0_W-R)<1Kh`~RTUvmYRQQo7%S7&IcfIAKaA3UowL1+66EoYt)pxnVPwb?)9Lv*;9T9EJnG{g{X+XsBU#avV_$02ydMKC z{68n&yB8hz8_ow@y3nuw@#EnSegnfh17kG-mdu7vWt&3?;-fA z-mHJA*|?nJ3-Y_x$P{oNZrgLq3x14{q~i><(Xqn8X~ z@rHqokH_pk&0TLoF?wH|&Uju=;VF}WN_lQ2U)m=9uI<9y_D?3m!#*L|E?sq7?WCb_ zQLE+jl$s5w<#SJ#iaEx()w_9=z%g5C-MdB#^h$Lj_apWqX)@PzPwH1`A%Wu#fSA?v-PLAHrvQDCmH= zv=Ggxlu2$c*G$9MbrX)eoUnL9H-73=JQ1g?a1=)Ruz)Vd@_v5L3tA$Iw9KBw67HT3 zSz!vh(}m`l>W!I7?N<^CFodm?aY!%RWA=$eobj<9oZVCYos`3EcJD2Jx%^A#7WH$& zOrY{G?fUH5aF(H%aN02>>nQMeSlN^ce&-Bz-*thibARe&-mPzm{fWgh8tV*mIROV{ zsdza$S~hd-EU7zcT`i zWWpGRdDmgpiZ3?R>mu3}wi|Y$7uCSbtF~{m)A#^knB~hg;CC<5d-QbT>I?Vf-^iys zp?%J*U8x|i8z`s-Dikr$o>*NjtbDFTjUJ2Mc>~5*1)2={c|}wmp58j{k2Tif_Ea^> zkRHTrnN;1jBm-Ne4nPK%MaX8KJ4qq-MGD-B`XXodMU0WT<9egFSMAfQv z=iiJ*F7&oF93g&by>-NuF8K-k!s!*8Kw#Xto9lsVV}c4*vmalY3Q?(1md$N-aEU0( zd(85?riwaV%}!_H=?<07Izi{GdD>rbm%XyoX7j=)*VJ@*5 zCxy^|E|7nRes$3n-uE9;2+A8M9-*{2WLZBG%)cogPcqi!Yj~)%v0Eka@MkSNVavky z^k=KtwtlhYg6YmzkXh4R=QCf7n6MVXa=gL5>yt0O^a5G<8R_(SQjO>o_ElThUFX%a z@Dn9@Sr3+q*kHCtB0(BV&--t_&1}t>4vQ4~MWjt5k>acz7k_1U?KLaG`q%zbm>J{x zXBBrCzk@ge*#(m(mIODi43t4PE0 z{ZrfbsvhRi2K8IpVGQc7J zNpBnG+l{AuUfVL0;a#<4D(ef}wFO5Eq?f2xW<4i?lOVV#f#~{LjI_Q5L;?)zylYfg(I;%hW zl_Y}r1hs;*S6&CWFIKyN#T)MrKY!Prpu1%PrkuOlsXdVf0WI#H+8wO7&1&>^}no{ z{j?U~5^r%)4e^#)09h|BA`jr$R2bEhS^7L>yZKkSDr6_L_zUiLf=vK)ylS8WE}o0l zvm^U%yCvUd5)4g4+C6%A?NrEWDVB(Qz6d~fo3(J9{O|rCLbu^x7(UnAG~5X%eE1KL zng%2Wftt?0QfJ8DGnk_Y9Flp4#QThNlYVvnYP&7qx%6ojJKs6+vX>hG@tau7%`acH zl{{T~+bjKGDJflHLvTn(6rK##{keKBaP=8aZ#4|xqnmzjddRjWr&6b! zF?o}!>pLopXEq7%;~BOfy|6CU{C<3S<(SsO{Ojf+M-`FRr6R>Y$JAq9z@?;2W4&2! z@e~F@Ur$L9v-#lnoI}b#F9H1Btmf3I1;5oINtfUCzK&HL+|3Rtd zL_e0S_t+03J(U*m_35nu2K<2Hj*agjo*fq>N2z*y?YEiH;x#SV4;b!kW&u2qnQ^v0 z=?LSqlP|()4e{6+x?*xhm6NFi8vrN*rDs(^j2 zNH4=FB7aVDmWln_RyR+s`18YSoi_n!LvtUN{&{-uY}#vX#(K3?%d_0JV(xNGX&LPs z1rYLFx`U@q+>>kanyi=_!-%y9nt;^p%uBlFC|f3E{Ie z=DJAoJ_K3FdmiBqod3nk4pC6o~89BjT=#tx#!pDx5d z7zl-me3{IIk%-9R6e;}{39#>WjLVvrP{DM5_=P49oK6wE4^EQ5b5gHpk`pt$ zCs#YLb?kJeQggsY{Vtt2z>q+$^OW67?nmo*XwU+k6KxlMlyg;2^H1Ky9}5uiq;{)$ z{E2YnQ>lR5Za#`~DG^nRSktNG(-n~W)ouJHh!b@3nOnQO%%0J^lqM8+ zGu?@10>6O4K>%}Dcrv>Bf7Q_77BZ^G;e`D~L)7$5nX7qMj|rBpYJZ5K@ap%Z)fTLM zxsn+UE&423K6&>pWA)Z6B`rfP*f&Ln$hjfEIWZ~jo?l#RvJXAb2HQn*$=@KS4QKlZ zM*`+ExCFqzmA;C-B&5|p%ynfyRDTMmdF&(NVsLpROgi=}AE$3%!jq$~(y)>hqX-6q z+=n9!MbFOMHZ&Z%SB~}#Xs7JSSo5vFg%y(Hn|bvHAHE;4kD&7ua4T>H7Se>P;l2`> z{e-hetH!+U7kXvBc$1ts?-G#yW2;)WiH7gXrRR>;O?P-TsK$?<=Pr+Ve2B}Iv5_;~ zu~CW-d?gZ_-_ZCgURTkZZTb$Hu&)6oL_UIMxQZN6IWd6e8hhdiX}$pf5>otuRo0}M zehfCk>&89CAlW*ne5q#dk`OKJ9taFMTHWZ0WT*mPOH*FUV>dwryFW7_rg<;0-K=Gj zBTN_^71PdBz44m``1kw@@g7wC zfBISCZ!jgL)n%m`*hb}YNnety>X-MZz7x$<=;&~cwQ%OIeA*1lniC|;{`#8pJ>W-*&b-6!)ZcEhslFaVAiTRIfZW_ia0tZ#PqtL6`<3ry z5XIkz*?bI_6(5Ln73<_w{2gyJF~*mR&QvZBVYTr8YHFoj2^Gl-df7f;@*5%qohW2j6=@BmIXnl_%o&1kHJI4*@kV{_;E0 zyoRBX-d>}ct5G+=S(vM4#FBeT;m;E!Ejbi83Cq_bi}aUz!q#qxhBBS_|(c8uj2MP(IomX1SfA38QMlkVO({`?dNKw6_W3^>E ziVh^-;$DGx;cZ9-8e$k%1Lu+%GQjgpMk~K)QGN7P3Rm;4+>n&su@4XV;=xaU;TY9h zeKzJ^So!)=$NOCIqLtX|2G>7oiW$nJhWM)jOa{S&A+gR2Vks25M@d8a~#QR z3IhjLRLh904{X}0ETY7ds3fMiK5I*G%&fjhu|Q6 zzXkXD`lpwI%UP)SBOW+x-V3rlEpDRJegtn)cj#~Z?S7M{^BN2M<$P$S!eyHjf0v^d zGuP%z3yUiC4;|>d>fAJ~IJ;WEB%)f>>mm6U1)jx~hv!|ts}v%eJ}-62J)VY(mrm77 zMCr*nOa+3!%h&V@=X_GW_|=b==5noleI#74v%hILEg` z9zFi}^x^Q2#SAr3qAxAY3t4Hhg&I13&5WQ9z~p*u%Ye>kM7>q!8QCG5@eTDS%bMh% zJ)zEbQAsSK!PaeTtGKB$R?O-BKwFN7e)mV=8V|M|Iv8H6@t((R)B3L-TeIo2v%M+U zIQzP#?jMQytsI-p1D!#XCiH{S!7sRZ+e7L$pb#Mc-9J64+i;sYTq4!OHv5kq=m>dh zf4^H`DBW?scd1b(Jiv43b`evBfRSi%H$HNHN2>P5l@`l6lMkzH0f3ql=HK96wt zIK?J(lXefW?ywtN_59&yQs=Z1b4hkcR`D$&x2Ue~pZ5yxn7Hu`Z$xD|g64>xtGypr z((%24ek%uZ)-MJ3U&N&`J$&(Yz*l(P9nbQ6$GSidv4qPMGCBRoCHYThlo&k3YDVt< zO{hnGaIXo`wKYPZNL*$v4LsS?%TBs1qY)-lf~X@|&A(_ITTNdLZU$~n7e zy`fTbjA{z(DezL>^*j3W0LrZ{hc}FzK5w$0I^c5nfxgO6B2eCf4#UfcJbQQEcA1r_ z>y{VDR=fF{GAOOL(pNLSb?t?VPgs%HZOq?57Y@e*pg(-x@8Y-a5L&Z<@Rnw+>=Mfv z<>$oyHT)FsBa4DWZBGMNc@8LnzdG-#Y+XEpRz7EbOK0)2{QqO>tOBZBx-hI#0s;b3 zA|l;g(n?8pcXvoPNF&|d-5t^;-AJb(-QDpI>N)4X-Z%T3ShHrW=Y6NrI!v<_5rkaf z4I;QK!P6N{9Jem5j>CUx?mBbA5^S$|9 zFE)dUyfleu+IQC?L_~)}Lv^4-^S41B^{{?c_R)O?U1<%2T&x{mj)9?c{sVnF{NPSi zxg?@<6k$624|sjF^bC?p7^H@MZ3W6?4zp-W*r0f#F=vacaeT#iZnj8eYrM>tP}SKQ zi6GwZYgkCRu=D<@Jj=8sklkZgyGn9?a`&gHdKFWJn!2>nd)phDjNaF10>0}Uq`bmf z=YzFIKQaTNd+3{fWc!*{dwBL(d7#O^QF6-c6~RDYK#j!~#=SkZN=~j=#Gz3q8O}$# zRS2D&Jh3PbA>wA^kfQ+@h6uk`Mj`z3dR$52mYBy#9Kcd)4<>3-h@vIyR~TM$2oq&( z_~%^Td21eQeuuKSYcm~*?*jB$Vm$|QiwPQpAlr}$My7}9EyrCl>S!4yU_)g>SJ9R? zRCw|NY@mn@`-RT88DOD-njT2TU~-n-XM&{V^-Rn*&_V5x*w58=d3i8<>EIh-UvhNy z>T>F$Ll}o#LH}iR6ErNe>0|%Gc~+{(J6f|;7#rTkNV$nRUHS)vh+X)_GW**)GGF2O zv96v(i$*zv7OR~-5Xi6>&13*R?$>@WqSdrezftEi<|IuAk~3JI3QbG36Ki>Ydnn31;;aeJ7^9_4P5yT;qwVh2eHO9%CV zL}lPwP(c+LOte&$%&Jc!86A{VAK4hCUmJ4*Qf8RBvsDF<=&%D|ew)@@eq{i%w7~8hTX`tpoEl6W20+ zocGvF@I;r2F?7t~7Zhvsa$~q9lRTI+H_pk6QmFPFNFM+C*-5-Wbp@j!Ok=G1;WXAe zps3=qlr!!R)&ru4>SOevI;IUFe(emo^r79(>($N=%Nd{jcU2p)2ef#>Io#iU3<~$+ z72Np7zG}`{kOoZ7YIbaI)J1{+!H zE1%1GjOjQN70xrzc%CqWj?8An_^md~r?{5~zVwZ+B8jGFJ81{M%YCl5>0>1_}t z%={8K!&|+cZN0F{@7W^{{$5PPvindb(n;H>26+Xvj+6~8*>5lk%uwATlgqeicxlM} z@O)DQf@W5>y~O}?u;d{7*6heHbU7#q#v)g;jCAsOqL!wb2KOkRwc)O)XW0}BpR(JH3tKjvC{?Uj?uW@5xWcuqc%I1Se(Qo>R&Ql-a)PKdYk0v;1KSN(yg}>^0n! zR1U1LQJ6(*=0TcpvsAzAT7zjn>GdMyKBAH@8l<;-?-|i9MYb?d9?#a}p|fHTPT))i ziGKAWh}vGN3KvLf72x3xKVO?hF#6#iq5n;W7_<2TBeTR=iI|~5o#jlmY8Ma*druN* z7tz^mKF>;ahU>Kto-M$r*_SAgHYfjw+|c}vCj0q$HM>J9>vmgAGub_v)n_(l zgVh9GnPsm>*g+*>Aq+ilEgD$z+SO=89k=pQm+JC}3RYS*s8h*kO$Y17r~8EX*oRI{ zwk1`REw4;~DVuj?9+!9co~^Ny=atz)*vQDH(dquVC=l^owg$hOtNFMnnuW^2Yc7Kt z9{R2ky_I6Rpi%A9cb`Hp5E0L&6}crPzeZ|0T(7wmQqoY-vRc4X1ZGC+&!kan zPdt(9!9Hm(Gpx{3)FcEk{!h^oG*?ADQcyG^$g)0-QhWy?!={oEJ}kd9S*3MfsSE<7 za^`WaCwsRclKLLu6Z#W`Y$}5zPxW|FgE^HI>CjA#E(qL*T(l;OIL)aiCqJ1qxe}zd zgRfEDItTSwcJ8kl4^Lr)-C2*W?u75Zc06Nnh;l=FJcWrYRF=R#5_4>ve#NFDDwhu* zMaCN)G{uXN-QHsv!hHVv>ciXzZSxns$|vJkQ%#hq@oJ@oA91pKppC6V81KIiJj+}w zF8c=IXY(easyUCvw#I<0Ir$GF0gf&!S+xA}yLF%CY=%BWnL+fK;JHnXNweP<2fKlT z=G%zv!fhklxH%|c72Lp1FX9x#0<@>@tR;^2 zUi@Jk2U;K61u;;Q6q$GJKtCdMWxtlE-dMp4&f0Fsz7y^}hz!$owM*LO;UkeoOTGp6P3sECYD5kNKss%+HGgZWlXia4_w-FJv?O-Y~2p& z&wN^)USHX-x!Mk=9*%}hc1+bZ%BFg2^IBG2PzePHQt! zbf>#pB6K!ylM_q}iKxMPC{8UjA>%VCX!LA?F#j~vDxO*Yh(ZsPN)BozDE;5XPWjHh zOGh{@R1Y~i5`Fp2IECxXr@9}!c&*P3-^6j$P~n5g5e1X;n?|7daEIZEaa-I%ODDTnR(_l81HU+XIyDq@KDmBSgj!KzBuBl+zR%1kGQd=<@%dJ2{NqV#52^_?~ zS-(PK^hpI-nd{Vb?Cf=7>9v$}Z*k+p>26Z!11k6&A@$7dE`BccCz=Mbba{4bl&dS}N7RYy=rkn|>LLVS-%y^)?a{e2?zertYH+I*om|K8r|iH^ zA|lo_QV3U*rhWc!-@{(Qz7hBD4erT9U{(Qrg^E$;iEHJnO8&RS#j(!qq3X?#l_clK zX8mDS9iPe@Ydn;!x~#DmG%|(m{P8GO7Fq8U0@a-47CpY>FoYuo6;(PM|6+4aUMl%) zp5DLPEn8D&>nL#-W_2i!gNk zYd^nkGS;ooyHQ^Sk#~iPo<#AH_105!nlbsyBZ6XajAZ43i`KI*|tPNsH z`4a`aOFnYntj~c12j*ezr`DRrkgpk=TQGQesA;(U?YL5T)zethG2D&N`|_Qkx~zW> z(lG9rVUGM_+a|4V+?&iHMOmBJPr!`q9Xl8am0^`9eS|OIFJuWkR;J|k(Aca5X9s}I{WuxRpjmGCb-+_Fz8 z0JZ>A;8g200nTmz2HfPYuVVoU1Wl#avkPvLzXHcxAIx{;~0`belRHl|4s^M2)u%a_PtvOgYG%lm22&LK?+C> z&4vM7cCJy{BV;E}(<+o0jPfbCSXvb!-UP`L$%?pQhcFj{77{7n7fh(}O6ve#WU}YJ zy5t0uwF?Fzi(L&BAyT7FVf9Dzi5b??&e#pFf|jV`<*N+nz@2xJ*Lr|Of|zJh;WUo( zHv_ZiTu>egO$g9C$Y&`91=|suV#MvlPy;7^-Vv-OS!v9CEPd8g7RwqpAH+0YCt zrvHbpkA1ZjA!{j;P@maCZTx1Xd*6Gh7!%m`xWQ5*-b|{=IMaZyU2n;ByEn@6jVD;1cRYh~|sR=ZgTyodi z>#?*J)1I+UsfU(<$Ey|XMDo=Q1NnG`O`^~QHa*!934y#^-kG0dD($>#npt_pwwZ zkx?@*V86;OWy;!8$i9KOG%DrQ{lCT@=a0 z^W+6~JbYOCVX#I%gjf2Y1RyTnX?3#&s)Tm;U99XEpeHUK(r5!M<1Vzh{cO4&t)f_! z{U=Vg2E$|Za#_VT@nC0FkIkW+Y~l*ETYF(LO&wa;mjN(dPuI>IOlsx>7c0qt67;5H z6EC>Rl$646F`36o;5ZKl+jdS{TW{CpWIVow`*>(Koj~-dmRE)^7S2{jn0zuG&KKfO zhTKWCq4b~f2nh=+rpi)kHZ@`hIH~$DDXPlLb-g|m-x$t`XqwXS9JG!~h%m>m#^4^` zv7%lZ)#2zLW&T)Gf6^>@OZ(^WgwKJNa~G;Lk&jhumSP9><{fe8R`kjqm*KppoXn~LU>SsHBFNs$lK zY&!-Ik>IZ1RZ(eap2eQJ+h=>_S;Ln!yjL7=?94uee_T5Su7!3k7$gq_k1~q~t|vEM z0HR?#G!|sH^Rq=}Lv!w|_U#cTBq)R@gPO(D_9b01b@V@OplP`+b-zjl&NR0ce)oc0 zi6i{dJzsUOB}9wk=Pe_!NXD+zcoZfX-~#t`k`6+oGQX|r%IRyhY3n-&(e=E*K=Ora zE|aFQBE+Ik74MnA`qezz9iyzNTo|p6(j;_L3H7Hstf_fN!$cDLk!G3=+{M`svV0<- z3ILKn%_frTmXkZDkCBadT63;-e2CCiIHRnOj&&4%FTZXZRud7az{i{{5sPf|1sax7 zI?7VdV;Pj`=j!y{7m9K{#SUBj5=b-}=F$f;;@-Q(Bgfm)Dc7>!=Cg3*=&{Nruo znv2TqCrPYK>ejAnuYK|400nmjo?n?P#F-qc@UFL9HQx6al1l;Pbcz|G;g&@@`W<^~ z>xVP`g%wd~TLy-j< zHbZ|pnowR!580Ee<(1ZgeX~z#lwWiwy?$(Ml<`k0;0`q&zZSb1loxfD!q;w~b4>FS zdfR{cx{dhPt)q8|5yfTdbJE0iLq?Yiqhnj91RGc17hs&pq;qJXV&8ZbwaA)#w8_UP z3yRYtXqc!l(A-J4DV9UXwMc9cwPH^a$am4K`clkJo1pO`ub$|&2lW0LBlnVo)fBiF z+%I!5{?%AyVKKOH^@FUh!N z5YDAG+&T8%`)%>)=fJn^g&kp1q?5`MkUl|Dib@3I72=q51Plm!MTix@Fltix-7Sx! z!xKs))zdDGyl&|L;igUt!lP@iP9E|Sls@v8*KnI(`ar@~)3eemaXGFcF~`g7C2#CM z)SxMjr3?h^@siMS@XT~9tk@jiM2W@AlH*<4v@*1q{Xy5&Y|6m!GnVe%n&TOIT$Yxo z99i$yAXWO*iM>b*pc`${W?8mo>c9o~(^HItZ{^P12uAdBM5{V4UVBTDq4r5evm6({ zJIzc>EABcRzi>OPGTX8YO$;2AF#vsaiA13Qx0m_af=dWE>Z)NprlkRLEdshVD~+5= zpTd-cQ5$&8pLFr|8gd5-h3MI%3N(xkVIDOzNJ3(TW@PL5Lx}h<%fI8w5_M|? zZ48B}Ac_E7_Wu(QK$qQ%kQElGBzbA3r-@L)LZdl+j0THF<)gftR0G9cS1@gPik^%o zTd{K7R$bBezK#2$3<@MV85N3&1mob#qlPFjJsa2=AZ z{m}rFiA)? z_mV1n0R1bYLqSVGx{4#QzS;k}$6X8+d;2ZWgh+Od--y?P>^NdUoas{V!?MO8Ua!ti z5LaF6zRgRGG5MySa@S~&YXkJ$bhUk0GN+1yQc1GC3Ss-cwF##x^5Uq6`84pqh9XO+ zbGcdg1+cBT1DxS(D}Dge$2%4~Q8&I1d~W3=DNY4fyhmXd6i2OIYFr^Hv31x}OfWr( zF?-aGI1DEgxF=27wU`l|ALlbD4XO0qbZDCcklXLSe1q$#%Jq+iUuCz39(*PP5S+ia zRDEbNchf+4f(2%Op!paH&4Mzv&3Hdyre&*sxi4md^^pqaFgeV$UQEV8Mtg$%!Psq; z{cYu5vVH8q+LnP#1gajhF*P=`e&b`gjM84#$yzt%jBFqBbZ;p1&%zkJBZ{;N-6rDO#Y= z-{sTOuyIIOTtON8VKu=EkDFdnk`cPJpP6=q_Cp4eVSL1qz@lO%DKhY#PPXyDm#_;8 zTIkfT(AZ~3)+YF79X;rcZ*Pw)c6Rc(0EzlrN9fRY`fiJ+*QUsq>FIkWpe6e)o54?X zjU4uY@?5j?@HHLd9LODJw2wJ4j654;=Imn+^sWA&Wkx6G_{$W@;AI8o$*BO=#Zy3! z_gmMcXpj;}3wc5QU{XQW&@HTbDY`WU{pZ8eTO0F}vK zF)Y9Ehww9P9@k7)Sz#C0_$87oHz`(3lsfP8p|UZCYZ_>I1FAV>8C?K$*X)So|KA7y zTn$wNY*B~t2TmVhz^9Lk8oQNmv{XgB8=zvmV0pS=MNBO-VjHehnHyzcNT}q8ra5t; z`xU+F^}A=YuEuUeRrA(20Pu1hf=1k=L6Q4b>17AZ8UUL?g!Xx0?`>C+}BM!?? zT`TFHl#z!PHZLQ*YGAKEmG1*Da1-#6wqRNv2vJ z%pnz7mYlyPm$6^DYeD%|`dgq|>KiyocB(KB#ySO4i*~G#c`lQPDQX%6tXC@Z*L%=$ zczIn9QA5(aDWeuXD|apT^!IiK*!caiM65f12NLnqxI+qa+huuG^S-pXy3ZeK390&p z7h0D^e3TWhq9v!-5qD_ldkNqKqh|WyR=1iD!z0$j5C@TGVT5q10vh8CFayjW5E1sO z5LjO!I6HI&v9`16$He4>lBWg>u)93lG2CmUCvMiQb+`OkwgX>43>Nhty4n*6gSV#x z9L?KoAGJ!d?JPRKVvKymQhmfH!Wc}7>lEcwS^RgI(R5_81y<#;J#Vd^YOM57>Gd?_ zXSE#ac{gx#?p+TcppR(yubuzs>sA|=w6{nk%r%)?5t$^PHI%(3`tN(`!R>x^BKvt! z&-0gf*bd&FXf9Ib8fI_t=sdT`wji?6_~&%tj)V6c@lX*SGO@43##4uGDc8;G6&KZw zJkr}KR+E?W%Q)_$?=!@qb=;#KjMxQ4Lr z^lsA7`}n*Se1_0M=jd@y6riRdWBbVA{jRjZ15I-N{H>LpdEMRk-|r7D`cNDI^}`a? zg|(f&l5w|}UPYZ$K!8tGqC5&Ca;Oa#We2N1?F=ra2YC(|_Xo{^bItY0Suggxw3g40 z`bHe5nGRanr8)fk%Q1eB6!IbKttp6qx3w^PK%@tx5L%UUmgHx?d~E!N%7OQKDag)G zI*PD#k+jyM+B1vd08aYT zz=x6n73*=yE21mZ5@%Qt%TR#MN~HpRt{zL}#E@X*GfTiL;ZYPQ> z)D1oziK3le{T4Wa>L=O-KLAk~qaU+hJCLG#1S-!P7GJ*HN9Y(32jbL%WX^u!mwGNF zw*wS=h;R$a&e1gT@c?+iN69ETzAbI!0a86)@SdXA$GU%Lz8 zGRq95c=i1EviqFEYr1qn9bJHKv^%Wus{K1W)utkoA*$iFek3$$Wdg1yjuRds55FzL!W9M8*S3I#he}Q^^WrUQg zj`|$DAsS~<_hJB!_qa9Q{cP5r>hhWKvulx%abu~qC#D@f7Xijmr%!tws*_Gs?N-du zVn5b>nwIztXI}OgGsaVJlXNrFhX(RtjlGw(z1bZC_*tk=&_V}&N}PZbOzrgZYvCKk zXC;k-ndRZk+Ur=oXuk+#g2uI@4#Um)V4==@w(Asy{e;vqb;4M>KK7i9;G0ASeZ>j1 zF^oeb)lsUxe!lg68;8Dwhg*yJ^?WAMbP$@3-9VOqd>Bjd)X4ptD-vj+RR@Rq-#+Oz zmwVB1-NPn_{xvw$ORF;D_F~5Mq>EWA@@I?-37vdPev$EMWp2spe4iyTa-kE5#f$y~ zPZ87H!J^eikd^(ysr}(x;bG=X>CLOOmRI>qKB6_1))2P7wLHopxw16Ubt<94Eztii zhfn6sN9e^Hkz%}#rtsMU15bEeGY^s?)#4EGMQt%DLMd_=_?$F5L>9YO>1KMqFr@FY zrqdBr4w7?Ni^3%PoF-Yd>ZfoU#P=eV@Yn^2wie+aqraX)!bXRqf2r4P? z-0O*~{LK9|R)pUQGS1}^6knJW>e5mUAP$_GP=u83ElSYoy{gbMt9wzT2Un@Bi7sR_ z5|2nVOhu-;)U@T-p=Tkd<%YGaJ46O^}xQPztkXvg1~hm>7BbvFzd$83=>P>71jmer}b`_$TYm zRL>(zb76B&)Q!I5cDv5BdVW2@e$deT{c$K5Pj;~WN=C(xkqR$02F^Z4cY&^*u)zgT zomemMg9A}+G{k3i#gM0lmmI!dxYk>Wx{+tEX*|+|xs3#=KUfr9=`D8dVA7=YhnHi? zMth#;5?!W?gXl07#`I3H#GK@HL{}6g!)qU&-0AU3pYTKNJS5gy!+Y5kSQB>q^ukD# zOI|#NYm0I-{R7VV-ml zQ1baNT{x|MSOc0K@}#NMWZq-UCr?i4DlP8ghKmMe>+xjTdQURerzWBKYsy-^QDkQ9 zt}5xS6wGgbBFN9kAFzvw4DK)gu%3lwO}|i#$mz(vb(!&yhXC};{<$dP^AQboKI67L zbB@V!07c(xhD?%IG#$#v42)L7Ove6+A)T{h8G1Xk`!CLd3uLuJNkxquIDP4`^aSAO z^bsP*jioC^H_`WKm-AI8BMm7&4P{4AbY^UC8f6Ahr_&S0rN`YF!9fyy?dHJVNn^#d z`Y{qL-4_2sz-FtdNtI>Pi+X3i4?B3clQ2$ceRGzo41&ToMj;QSR+BysDz?KA8> z(wMpTWUrH%iQxK!c_y?Xa4!T*aat+G4jt{8WtzL_+hV%A2+jxek#egU!$U98oyvKp zQAEvevYSsv`lcQINUT460sbX0KAL&S$=G@B4FJI?os7Pc?>lzh`sCy8kF(hZ|75)D zR$_6gBRVWpZpHJHmjF%Ud-&P_%rzp+b^7mo9Whcp9vRgTZ30X#-y;y+3~>WpQd*U-ULOT6(F!9 z&m_q?#*XZ^8Yzk$WT$`3(fgdE%TU5=#7`_sgFX}M8D^pzS-R^yyFKUb2;l7B+Tfp1 zuDkAa0Hs}yvbI^YefS_|YRQT4V?sLb-*Kw+L#*thti!s4riX#DxBf`DA&?%N@NgOH zKKL*Ra%Ol6&yFJIyzil{!X=WCu9d+*#Ge@*LP4TMpVvCy7Z)Ns98p00;vmBPXx0M)wa$kfDjq8QbYXEm&Oqp4?Sm!S$IXrE8te4ft1$xOs- zun|ukO9=063HH358A?uCy)kkB=6(X-E&($4->wEfp=7|=$g;q8R+*>$k?z(Tm1s0I zYoCuG&qn1LG!xT#Ss0Et(D5>h<&;%~1ymInS=rTgB1vF8tPHX&MwrLKrl^;oQUPs? z+DCy>k1|*XE?E~|qZ~R8Aco)Jj)Z^$+<{P2uQ=ng1|Z8VO_hxUwf|`1ee+qsP}y1E zX5%73Z~q+}c)(s;O`?4CLMO1xEB6k0Uispkjk7*B-PfYJ-0C;fvQ6R}zDKxXN~r1{ zoiVIcZ%}6;uqmhqNQDtjrfgl}?J8(3p6``NRq$)peAE5(%`YWjU?|^9z~l|#%JX9c zDe~8mBGWI!^Od)|IC*eY>@d@}WUoKRD8wjefqtqPJzVdZXuLj%;#oB6o-RMiYAcEC?S9)WK4SO(O@*y;s@Y>&>Yz1SZW+0<+L0 zjAIAirvDfiu<~o#8a2(*yVD~W|dRV zvz0+B4^XK2ell+WqBLFFnNMj5jsiCG<@A>+gi#%|g7Wt77-l?LZ?N#B5RO1kYyUXoi_ONzkHZE)=!%sC&? z;it|~DmrVX`&nr*GDD1!^^<6GdkBhCk*X3^Hz@&9GTnL8>;$ho#$Fk-k1c-A!lP-? zpRvLFE!aQwbQ{Diq;`2>UH=i{S|s^&s6Hul@T_o(d*1EBl_9qVf{1u}Aa?l3n&JuL zQv+-FGgA#5_WO($X8?rav-^5d(&~Gb#(o-K@m(}Bs$i#N6)5H2wEY4aU)vm!Uf^y| z6>-g^>aLl<%$E8_A{JJesdC=UY31am=_g@^9RTn`8f329G>aA=xn<}o^}}y zFy}sJ6WDXuIbtwSj~Vy8gngq++q=crNQ;YbE?BfY8%8u4W|H%BXZaX)${Z3&NJpp6 zpMK_)YCaYx9N`l#IaYZr5JNPd_T^rZLH;2ZHWdK8!fM2VD*BV)x8h%f<#BLGT;^2C zeL24KQxVfXLnwucoIC2tc`&X|=6DFax1k8oagG?mz`86oEG2u$q%$GD2qMZ5(HMwa z*G}HB1{<})c1)Y7gqxw*&TfBm&6)Fl>=DniDLex$%6*C0g^0}U91XCs{#+Ts>hYXA zaQ&b{Z)H0dtsC|gxsQ;CX;)OpUSvGYhF-*T6ipG5ArbQ{A6g|mRgbPJM+P&l~DfHF=`t;C=hqq6qcHO=Cz4gBb+e zg@&2gFLxDm9eQWnzxnu`Z-lwze1B&Kl_AD{EVn5F|9_?kn<95aVi=`{A*{eVz&g$R z|8|wcx4Zo#K@)T<{33|q!GS5!MO)KvX_QB%5!AYLO3lTLDr}EvLGr^j2tqlZZB0OB zY|Kt>r+M0f11wB zEi^e=V?B*^0`eW54|6vzQ^rR)pt-oL!be%?Gh@I5fk$rN4lOwE{t<%YIL`E?Ud{qb z`?N%mdZaa0`RGv%WBDUe`V&xV~1;7yE_x{7VC(}9(v&2NYH*pE2h6-U--7DIiM-SRu-8zM5SRzSLunGfQwMgC_ut~KGgnkm+xRnp{J>O?*V$B5XD?3y=NnL`P6r&Eo|r2Ck(#| zqK~b{TUYWK!l~gSW_0#U%fCP)5!a0unDRi0T?+EDnq5f3VqST7X=k>8(47X+-BS@R zml11+{foM{i&^8%`&%TxB_PaQU(^WV3d+=sym0{QM^FDY^Tdg3IdTO^Rfj`wpm?>5^E8C=q7L-GohH=&IpLsA8;i z95a;=L%Qr;StuCZc=8PBV=nR9%FaQk0{W)ZTM7yFKJ!=ctL426gu{_Z9&d}b$LgXD zVeJ<&XaE|gZt^%Bl>klXAsqSAR1jI%2m!x{DW3f{8!bB zQc*A%|M)HC9Gu1imXhMNzD}DizyTh;Zyr-3&V#GLa-<5fy|AG18a;T_-BzBifeh~v zCW-82qLqt*7)qdAX(=XHW|!5oQ5?9JA3)*2kLi0`{^Y!e`PVcBRR{1?D%lrBqy)<$ z$R0(sya{XpQ|SaUX0nQ&^;^RR)3x;@wu2B1fSi?9ORI+nD`~jCKRfnsBJnmNeWgjn zgpBT3Akz~yl)-FOHvS`y>9qnOL^SMiP1$z_aw6+Jq>Bgchq=^;3yu4}v#(pMDK#oT zf{_c3Fy?3=uS(ZaaNG1~&<_OW+*Vn05%_yUTN=(ZRPDs-J%IpFn^GpD z`IJqW!bT-l+J{$oTb7O`4p|iOTt>QQg8~L^CR6u5nv6e}vfk-Slo=P@oVZQ1()Hco zVd(vw#{I-WOXJ$L%PHelU}sU^z~@)52SrkK+qCB)Q8kE<@e`a<<_?;HbUItgg@os? zPQf*|MOOB9?fb@s+Qo}+PeI}WE;u=LZfCdpTbU9;Zd!1i>k)4ZA9AG`DNSJc?GD%c zO^sb#*@0sOD1$PFu45VMIwJ>j@(fq&%wpz0BR!XNQ0AL^7tcsgjR}1F0Ob{{T0dcR z|1n@PKRLND<5|d!+to-~%c(NO+FD)+Al7>o)PL#2y$v+(`&L$ucYPzt{#mRsI!bxG zqUD9Wp2*!~Ma$%~h?e_AZq_3}c-XiS-~JS`9&Kc-K_|BPqH;utjZ;iFpiX0ALteWB z@(HVjPAk%8RH;8bh*OPwMSh*#0*VsP6sZ3`kAs-0rIDk(gK-6VuB%s-1ofO!adoBV zS*F+F^og_kmBJin7Rg|?;#5fZ4q-XxuXSh18G*?%$x~()PTWbn!;T*`EipEY0Vk|w zV8GrA(l*TrY*4&@(QuG}^v}ZGR^+htZLr_l!|zitA^#qZ(S@n*%b)I@gk$|0j(KSL zdI-Z0ktQ^_f$QnLCFnL1dK}$x>sCx^Oq5^~w|TE8zFX~s-+OhvJ#9I4N8z`^j{HYv zxyf*sUC^oG9g0Bv-yya^2liEr&}$c(JaR1y64OMKyf)flo(-9walN>f^-wlTL%Jb0 z#3i?sIp4^MzM{zxdnZ#wocjli8s_`2?^d)T$P4y?0V11MQgYtfO*)Z~Wh_iqaj2?M zAEnrDw&!Bpq-mNIc4RI1HG6T9xE5Xwhj=5omoJ`kOh)G| z<_j6>9Z)RrTh{B&LdPtuLCmMZ!*)lMflHcw^}V18z(_IE->%QIXeenr?N(YlZ`FMRa5 zkHvD?L$0pvj!I@C3S6mK8Q^I|QT5t>sy>N0v5Dl*Q4lfww1((8`T-EZQT*g>vZy41 zHD2;(w9}3-O@<>HPQ~C1mi^9>8rKSTJiIVAjABk&gzp ztEKw4uO;kqGFFNeFy7V!yUzdrv_5e7+aX|xXwl^(=6~!`cN{@Vm!wC{D{*I^zy$5@ z#iR1OCHHFs8`GFQqOyB73GqNha%a&%k|MSN$bM-Nx0^Zm7SbbK#P=f_!mGg1s4Y-z zBel>l)?6+{db2T9{h#?GUjb-iU0cu@g8gLykyBkkYCgk^p&x=RPr9@muUS_1`=V2G zkWkD6Fr0HAbykY9iN9bR@vDZ_^I6IADyt8qn`@OY<+mP$RYWY}EsebFb`l_r_qL1D%Vl4Rd)r@-gi0mj+oiW~m#v&rT zDBu{$l2@0qPeLc#%%ag;dY7AX|68e=fRwY$6f}SGa%(i?P02`@z;wg=%^_vG@|B(( zb*zs8Mq7Z%0QOQlc=z>+O5gNN2G!4w^TV~8zlUNq8{=Mt;LJ4f9wk5jbBBrBFTE`n zhMwL)SG9-#2KiqwY19Q~NNY9EE54eQ2e|ey##)Et3VU*g&e#= zp-;UncgYp_qbNvXl{CHITKRv7{`%Fk5!QbH(A{BH-F9J$i?7c>wqK&zW!0E2^T!t< zQ(=OB#@M1YUq`|uv4=L39ksX$XRshGU;pu9+swVD0V%3nP>F<#+mh|@7m@!i9>XF4 z2eR0M())-2B}bGc<~wuU5k_RqSd?aaH4Sdy2lvk-$fF1liv#PL%GZBu+;T~j4cHF(>dZkC!T?b@h%6Q0Z^NH*0RT9$iHuRN2buDT@ zK_E>}>_>Q2jz-Yf1ew+%+XqZHTA$090eJZoxUJIWv90M=ICuIXO15Qnb`?YsfU2Yyyx|A)< zW@V#ob;ssB4f-w;$yCj#25!N>{eL;qQcr(dU8J= zT?@6;TL=EEd0vC;W0&|mQt7q+ z#ZqLB0+T><6BB6l-$}4--z+w(da9n+--Z=YXSMgB(6-OM=L^@g}EELv{pyu>oDr8=L=Ihzv2@*!^a&vy%K&f5xd+_vl$pYa<{jOB{q%S+V$2 zr`!^f-}9r>Y54@jKW9o73x^er!V-t4RP8qEoF1K}0w?5Cfw-|m)H(R&#+!e)Ro|M$ z#qFHx^JvC)NyzxM_`zWO@jJQpIND#3(dKMteS%6Q>Sbgn-61E%^nL9`FwUzXQp^U# zRhIS)p}kD(pFKxRzzsh6`5)2Dsm$G@1#>i4l{j%0FjaZ;XiN#(az9jCoKn&UXo8cx zkp=4UsfrauP@uhC@$SFwe&{^$IfBLAdRD37iuh)-mr z+1MRLSLYi}Ie#btXAwlSO+5SiRqnd|rPeko+M+{^#RkG%k0tM;9&dp(Et-0l&Ih|; zT<|j@Su<+x@*my(T@<&x&%LLu)gNQX-?PPa1YEH`jLtL$2-ncaqQ%Q?2Q6x0{@9b< zO~K$`T7J76jN2$Rsx`bjYhjQ~M%M|4Ig0u@T`!?mlP^X+d%LhbB@~{M_rcHp;adg) z?#r~6_fIW#*$qKlcg}52KdD^i|7WTwXQfm>??zy*`MTKf8zJaHS-D2Q{_ z35NumH*##4d9gjM2jns$W;~6Wh!hv7`Z81L^RltbA=0d9(SpRbVY>=TLD3d&;edb( z^$p(}tXcgSBT$XFe#c8QK2NB!k~B>*TYjgUX&(W)4N z<9)$IA??I^<-ad_q!B#fRXnD6U&V-a+Qgd{3XnHHFW5+7jxHwu1TR!*E(^hS(&PN=wKpqFNyzf}(^UX`evj^=*_b&gyZ!kfZo~ePIEdf5rGH4`r>@y8vb0srk0#T= z0_1nb3!mqaXc2csxn|RQ=4V6S-#?RhRWwPqJD6?h^*upy(X5{CxN4zb|7Yo*Q?hn9 zzFK`J=|}q61^P2@V!^C(zu{&_dLb*8nTD&>MvaBG3*bgN|9I~YkpANO!%5OH{5?0r zzo9}{iz#6>kvfn34MpeM*=n_-YA3B&#II**f?VWSw!nt(X9e&ZhAe2+80dRAf@CGv zFl2tVB{1=XEHJ1pW6Y%E(_^G~@AJDc&h_N3Gd7*+GiU~YtoB)fK2ri=oi~Z;VI!he z_j|&nuyy=_0hXXW*izHSd-CZecmlv^Wx$NRY5+f(z5SIx7IUX|)f?WAx{R^8f`_X& z!e^QHU^mi~&AY+Th?blc9phWNxi$Wi&_=cICR!A}pj)3{s*~$`i7a3mmIlqdHQTHj zkK{wuH+~F=P=DBV7qD40?h$NhzDZ+^ zb83IVg@QCryRAe4aHLy0-{h>xaec0Zpp z%db8g^k(E^@x^7q0AbG=?3TZ;7_`0s72&;k&mFG++-`O0WI%~CDv-yi23-#_Ub~2m zW|kk{tN(Pivt1%&Y9tVxScFBGNFqaH?Tu717`}UML3?~m^$_HH`WdCN_+rc%CeVEJ9K=K|(V!aK&xaQGX~cw$2f zA$nu0g_e(x?sI448HzE4$a-nd3lAp*#k%QdRqZ z3Rl7R5d(B0xC)O2#sBy4ZGQjxeXOTB2Z6qbmUtHJ93okk$fCLM%r-VI&4g?t*WPpM zrA>8G5s{?qj4iPUBOd$tQ5addd+XZ_eKN4V7FMG!u8|mE#vMkyy zaEAe3F4X**5ka}l@+7$-i4couG2+!1&Sp0(Y^gr`Fi=pESxnF3!l$==ju(f7Rxj5G6=r`j%3Ee+V4Ju?wRsW=y+ z&y}d5)R9*nM^r4BrZ702*&dYD(J*t}A^{}`E}#wv9};tKvEa&HdkADZ&6w&|-?*K* z{<$pGud;M={mjHTS>k?v0;}T@ZnUe7zv@1dd9Jw$bxSgsP!sj#QM0N~6DAxPNt61f zU9Tita=&UBLRQCR(yJ9#Z>2`S_qe-sG2h~Hmb23y{}1_{X0hD@!W-J(Ewud&Y#}F$ zjj(9eoWNmeMK)ILRfREPLnSY(w}O{~Znq1u8k2L$wX#R5aMJJtuLC8kj ze>;B70-SQsSg7jQLn0dHpsxdIKo8^q6|c)+2E1SV+V!7MVAD}Mm~-82mK%%(O2Rmi z(js{{&FS|A0qvL!rZN0>H)^Sqt2Zg7i#Hm!Y^v80dJ`^H-AetWsPUu(e&2ZD7!fMl z1PZ9KdYJ0_o*kZKn=~}$z2E0;Yrg>TCg>|;NFwh!L|D9Bn}N65Cp*=K zDuw5b*RkTqSKE9?<;5JU@6-*nOSTt+eDkZ)(>KPD#M?y!gy9Pq^Ful)(>$|6&U-gE zz?1|RgiH3Um%r2)mFPc+A}_XBv|+dO)OoA0mv8;BYRfvZ1Rsf(S+?o2P* zA+b)Mfg~}piaOHHoRtq>9*O?|JL|Aq@)L`0EsXP^{-`N|Ha$T}iwr;yR7RI5!-J+4 zC-&d|Fd4<{V;-dULh4t~tgNCx54>hyV^X<=?AVaD0MD8{dfQ6Nh)NjKfpa$b#oNzE zpoD;_81V4sPwoQ6(30}UV1ymG7qg-Fenk6o$4-L-6-ZloX0#+G%Qqb9BKPu&w%feX z#l;k3V+b|i>=}!vqq=N~GBGQW{e5nKteWlMC$McP?ByrH6C9%V_P9+h zG;+G+v^|6p&WG9zSEyh^fA#_Y>Ir|q1Lr?OHKI`_h{y(#=Eu`&%(1a5OSl&Ej4;Ya z9CE@P_WU)*iz{ti{_?g3FcXVPy$u@QCm7Krb67iL$~bS6fHwENOC(eNi4S~1mo z`ZKeL)Up*~*O=_@SH57Igyw#5#{&3`gdUdvsykb?LU;522(VX9-FLKI);gXB=8Gja zkB07-e{B0xo;`^8Ju2&`T%Q5I@ZXm5TcAbk@G4G|dLNz6Nw~xRm9953ePcglxRTyJ zLpFgNOY@cKyd})iO)FdD2O<8(DzSM@VVTzY)|-?qCZ$r|lfte;W5Rj65rV&t?y`KA zqD=qMXNb2nIJ1R18V&oXpFu11G4beI6SD3_QK~xOmv;^)@6e@f56I1{qEkZQzn`4u zwK0vAC5^a|U^=hc*H&Ke-EItXCX!b&-cI}qU0d7ZU9LqivWc6YJA>LMgZ$gWRP7WL z${z>*37XU!nZKre*TU{(>>=3K@c*m^E1^M}J`{SsY?Y{A1ezr=AB4zPjgbeK@re>e zdoJ-xHBvb?RzBsb+0(5R{*SG`IZ-iH-M1Vp5f?uMHNX_0P_?hfge7M1QU>5}d) zrMo*-I+bpC_C|ew{66RRm*;qZ-Pi8U%r)0sJ8O`iB;Mj+tp4T@Wwx`mh^bH{cr=Mw zCz!^C)biyhEg8N*563Q*(}FNRxJm^bSPrFwY2qIx>L%Z)&S>Cg(CcjGk8KF0uwoUJ zQy}fWffr0i{Ms!+m9HD9s10)*B0-QzbK;G$662nHu2ND2ZREvFUUkkIyh}dmqstsY z6`b(!)zdi6GzHGi3-{%+tsY3HWn?h*k`3Ki4UesD%E@nW7 zl=`1bB#|V%mQv(Gu94LB;{4v6Ga4@#>7<#^J3Cq%sH$n3R&DJ^93e>HT?pF`)N}`> zAwA?PFi}Tmps3N+hwMX*R@5ckW z{;V|}cz`NERXHAAIt!1~p)|hqiby#b9phz=d6yWu;KNRou@A28V6Q>5=Ewy7U3a~& z=Jjhqd!D0g`3{+tG#;a)3d}PhWyOyVv*M0&7D3yI|1&JqreU~#r`him&OoOr1nn{9 zI679T$!L=c7&ZR4X4u;6UY=No9pkI3nrQhP zB#7JUtN!Z;<)crQJWJNeaEsbJy(-WDKy|D`_mX#n5*WHd2l! zP5`J5UK?d(f3~cr7x__9$w4Y16yH1?RWFa_;D#QhZf~_St24DztX(lT9(1d&AQO~t zRZU;+Wgx4R7Flct;A={22fP9R{m6TFzQXapHq4+A@C3(2-(koNsq>6B{^cw z80EN&QF>hkHNxR6coqYEVZ%RPi13>kf5|`LCH{7EN3%zAHS@6e^5GqQ;;I7`^TqPy zS^%dW8CM?YyE=n^SUHjCYSfOcjZ@wpfP$jnK|Ia<_06zk(M#iq2%Q|$N8;PvhO$+5 z-?56_W&-+QD#Ai_82iHZ)KzyfoxW~JRez!o7Pop?do(uo|C`I-)gA`D+<>c$YgW7g z>;nKaP*PMpYy;BMz~}M}+FiF~9P~*$`e5YHeAIVOMGS+^i(TZaK3WJN&9P8S400XZ zM;_+#N%2o=1`qr2(45ZVqs@wc@@>=yC4pw;%l)0Gk#T;^U1IM?TG5`n7hXVOokf-? zC)Wo+i$n0>$K9U%>qGNyBwIoFjcb19>Udv`_5pbNfy*|#1zc23O|M>m!t zNZt_6nt5q1Bly92>9YzbVgipJ1|T+c9%&?j2Ki=pb8=4`x`Kd>kkEO|yMOL6wE)8a z4PI>95Kzm=$=C(`xpee3iDguu^UKEk$SxOv)v+L^P;}N!44O0-RA%l}6%;xliz;bc zs}IsxO&Vgb86clTF=Q5&HYumZk}5pHvJBLVu^oqJf>Cyr`Q74~7vaxCH&D;|=e8i< zVT)p!;>~Cs?B6LIZ$PI&02Fmt?hLhpy++1UH<~?X@!~Z3!|uu(A0OguA~C(xKuYfI zph*e0ffmpKbAk&b<*DnrMl_=-D@i@NA1_Q-gUu4elS#Cha`gtKocR1e;;nUBf^~O4 z?{{#)IOqb4^7duwCS-`?B6r^76@2_lC=;fb-$GZDnqw755UXY5PJSb$pd%#G66YTp6QBD<^=Exo94vR~pJDtgm1{x4>ASkm|A z6EZ&gb7x>qwl^t2$lmK9ywInc4aHTbdKkI&)Y1XI<^{^^-~@j@}#EV5EY7q>&x3 zHvu}EOZij11grH-NVYsvn9)w~=XQRNm%|pk)kBzvq9iwVPLhqnN|yh^jb}4IK{r}3 z0|!`KSudAc&CKJ~VUEE#UOG9PVVzlVV){?@Hea8pK`O#{@oQ;5Sh1FbFc#(uNrfw7 zCl#LgvU~N%dsJ>k3q`?G)BG|eUZ5dLl9KrH!-QdPbJDPy|rXP^VZ9qbPK>k&-}0sqgO}KWO2ds`^uLLVuNl&WWKh-bHmQTv-vu6++xzDI z^Kz%y*6s_o+Vdcbvy4>5^7pQbhCyZ+6sU$cl(i$f_CV1YGP!^F-P^I)eOR~>8hoJN7O*1FMc)@2yyi>NtxC@d8W~Sbs|qs z51SXxR1PaFk*e=Q{;LpQ#GI8mPs+Di+RN*3m%LK!!=I!E9eA0F-)`msu98=P%SKU< z@VKzEtR}siVOr6D+up5XUd3pz`YrSqF&ov0FV=U<{i2Rnv}`|Ix@Nv3DRzZMgH5-u z(2mQO;1P*gT%%p+#nz|{W)s&Ymce~J^d!*3%DGngEBoSJ@4pNGch{R1g=R4e^3JyT$Mb7+&A&5!C~LAzb3kNY>y^jm4( zO4_0rZorJa{_k^JbqbWmGC!(!n(L7hK}6C#@FX+IDsO5iN4#k+#=r)rv$g zzGLgdJqw_F^K-_K{V*%D1G^~jRZnnw=i#=2A8>6w|JWDJowKQbzZp`{($2%>S(mm& z6-e0x?wj8EQbT&+sJlX+U`ppSN|!@;YJzhyDLsgVE%M;_oLQJ=L*&tdMeLzxH(Gdq zCdI>*^5Lh^wV8Vyv6WJ(aqYA#6$xUyOv&=bO1N*TSZpr#Esc>HV{tab70DG%m_myXvSnIAgVk!VIY!8 z{WJCi-QC~|U+xBFoO@xf$xw?J{Wp)$HVJq*>g0~* z6x7N}SYJilR3%)?Co^Bxum)Nev3d~N11$MC-Y1Yf56Y%pOywG-M}-(;SoF@J^Rrw| zkAV;WKZEz@js4HU)V%^`cx|gb<80!tx6}ij@4k2I-{8U%uW>v}wyKjoPpgieHc3XI zM0d!%WE>h{F4BiU$Lh{?VKI|Q_etaU%?fWvonE2RrY}UnUmP~NW9Xqm^giBpIA$qk z*Aj3q+5g5g(9jm7?ZZeGJ6C1#l0h|r1rb(mCCVpf0^k3hID-DxMUP2;;`*BKC?Dpo z?iKy|HtZRnhghynZ+$h|o&cu;d<(u}*V8ij?#E2jO2VuzC~p1hj2xkK%col=e0yFw zJ6~fS{o_#uZ=p{OYJJ@inbOfGz;DsiOaSqlqnCh?3cRKD+rl2474OH*{N(MZ7Z(8d zrDWrZHY#xyY3mVFC!k3KBMhjjq;kzRjmk%{T0E_`ZDukp(46DqB9k|fYzDD^(q?AK zMJ0TjpOl5d?)ZUnHm%y4kq^ zmH~ure>j}YYSk+g=RnFO1j&1}N1<5Pt25;h4j<*q-yb{2D4g^>n}zKV^<9@(K@!x52~YO4pN04fM-+H6ivw;k|zZ4=Qm_y=P@$ z7A(s8ZZr*MgmOnd0=WIrq*ZvJpR!Xk8~6(WHbBnEwOtiU>cFYMvLn-c*J=mz|8aZ; zAZK7Hx$5OKv(c#yadE*iW-TAZaSZZvdA`urzWH`|_IY#l+CJnhH=KVt6N4^>orD-1)T9q27lLwF8I-2hdG^mw8{9ae7AZy?B z(AsN=dUBsRXE{%AupnHP&&qwvzNQF$n$d~szs4rQmHrLFZF9-M(wR1DG9`kdci-cq z#R95P)QrNpg|daBcPs_s%D-Q>m@e|!Hw}|mHr|x` zO>0zEa3O(M8(rAa=xBS|K5@1oaDP+aje%&EjexZFg=MwsXkxn<;|1(IjtDJV1P8X< zLp7q-@@a{Rw>xLEHS61mSj7a_tH6FaTMVWUjPacO6+aEL&?biKw$JfUFOTz_#t1gU zn^t*;$gwPE`_l0t2d2VvvPwxXTJd9@tEF3J8fdhAe#e};Lcss|2;Mn(=Dd(<067Gc z<2e^NuXl$Diqn1eXR{4|CG;pRLfc>o1477KW;8J%hQ|P> z!{6YMYex9$5#AhuaA9oCYfwu0%v#Z{%X&kZyhpw?C#^YH!#itelv>ZatZA8IOw(`;T2F^8J@vN4 zX?h&4(|R5yuVzCQUz}|IN=Xp)#I9dM$%8YqN4hkhMrK~M?B&v@=%GQCgHioLn!(64 z;@8p0EHADollV;8J#W*dSM7?~%rPSIF|wMtV6$ho6k}gE7vFJcS$V`5uc%>Y#|I@+ z=BJtbJg53e>*AaL2DCfz#s)%;Z1Nt;bv6=ayi+2xYZP#BD=*!Ffg=$jfD={u(#yr+ z>c9^RL}|Iwr_JW9IfOG7(J-;>LTxCMMwQQqU=aro6zbpEe#JijB8J@KW-BNV@gYS$3YS&k9) zHCnqZY@jn&p#_F{Gs`t^qlgU^)iLV(XQs`ro)h~r5#s7Le?GQq;ZUp4%%qXwpzH*( zk{Mg#0G;!g$?8l`t?7o6_JlFdf4mdMMGN?d4FuP&TRT||-B1RB_nRV-2B(u97*1yz zSV1?Y(8&{l$kh@tdGP^NM@J8U4!UzlH{WX z{YUQb-RMyY8gIW{q<+$-U$aeBH%UeDpwUOVqTW(*TIFnxN2vOM;u`D8RMio|9DZxr zO&qB-`Vt77>;WN0QR)O}Pzx~r6_d05RAt{AR$B0~2z$PA6 zy4y7S;nXR8mj~disMw#D<1ZSL!*2R8>3PFpNlDSd+jXyrvRdPv7)P)5d@u$F4?v_l ztG#{x91GJ>2r0eDR9uk5Yrn^$wMc=AOnOc`t3+s%PMu7jJ8Y+bWS3=9`4#T7+8>CQ z9{rs*LC@{RrZE|r(}II}s9#WL@NpjLH$+*IBs^=$+VnlfBJFzY74G?S=QO20YO|<9 z{x1Oqr5pYxqPx7nnvt4`zgC8WOju=U&iWH%aQSx{d_U6sXK;m2yFX6r(RVSL=q_Oh zi>>NtS?ink7W&NB-NsL;qSm72c%J)H8sSEVW5A6(@|k+3Y9BbX4KLK^W_7=9##FE4 z{78{b^_x}QxVZvJ(0DG%n@}`~HblP)c$BU;44vbz_Kh$O8BiNPmXfb9ny6GLWZvX) zIc4qSjRUB9x6)8RJMp{M=7Iq77UXpDTkgAuW5k8x?jw?VA-ca2t-!$ua&@=UFKS|6 zf=ijX=s+8jaD?!;O2N`?p9xMvLQQ{2UXy$qq$|G+sHzux6%r%1vaV2oqeB^7Mry1e zCQO|aa9lV2efp@hu#$=T;47zhh%iZYHm4`%)K#S2=`U@&UE%%1Yp2`VWZSdX?*kh& zOsmG0mI}FdEZ+Vz(cjFT18hZ0%I?eS)!ETqzeUa^0(fH(XUqNOVt8uTCAo!>euFYy z%&=QhKL=Z^NX4i)f4k0LqqZvVhdXflg`MecannyJ3YBYZ^}J~tO8GLPqxqW1)o{hq zDhW2-N1P(RlvNJLj`e1D3E{pGBNwkPcv$>R&@O=RaYYzwdidnBQ22h%qwyZ$>JQ&# zSnZJGzq($*EHy?~b0qcA1Ut7G=VqnT1lO>r zQcvytAsrZ1zgPL)`9=VJp?-HWC7sg-iPy#(G_#J6s}HsjY>dn!lTV{_`**sK|@vL@{RWa^`~(1@eSNx=K^0hSkJSStm; z)-@OF&p@(a6jsY!TltpuHr-{}z3-IecgAIk8HVpnQt=U|l~?Ygz&Gv){grMpN3rj! z^sy}6*I)TP%-G>MH~J@>gWJQeqSTT+`=V=lsx?svX$ZLAL`3k@c#`pOT4J6zKCcKV*$es8e3?V*$(-zn+99V+4}zAv z`cjmA?j%ou{ee_AlCD_$+}!5?)K}QM+jnj5uM~l-ZRQ4l{7&0{M+>bXvXHTYJb0F%i*9Lbx0-=8hwf z(qx^=UZM`1qBYe5|LyX!{EyXc8+-IA6%Y5SrF+9j^QiK}HI^3=ew$H_w6C^OqBZW{jnoWjR|&gSG5+#5RjUf>Z)RD(OPLZySow;#x!1@ka> zP}RNJpAIOeR4My=)S)Dp*_{-_;Q?=P%e=WgLV~)JtfnxgTHsqh3%8QV*5rA?0+io# zpMSlErOZ*7D)>=TT(9-2gVhaCcSfH4HJ(qd(uX@HSq&0x`iHKQ9h?5_(?|j!9`h#NicFe?BfW< zvrWA$@`(Hlq10|OCKe)La{Px~a}eH_E&E=ICo44nB4f6@!z92l+;^?RDvqFL@2%nz zTf;yCnm!;Xi(;ua`h_u6O00k(m3)rvT3-mIuV@uN+lNY88e-Tbg_?5zm6CPnu z7}OOgEpv!<{G{ex03mHhm+33-<=*GI;{F5N7M>_0ZyxA#_PjaX&L%feP<4yV$ScoP z_5Nqw|B@sBG0qLp6mb>S3H|$&m@SIU++snMAtJ!VfTz&4GM(P9^Y^7zZ}nGpjYNVJ z(oOfam?TrsNCj&WjAQuVch_7{w%C!*?))`lql@1#y5flITl>#3u^(qI~6%SyWFtqQ5UZN!lHL=c#HrNR$ zurYj1k#$9FMzUov{|3|cqg*bXQXgd>#p9!z$m6t$5JX1;G*f+kw$yC3h?{fIN* zyhB60^mA6_J<(*nUDEO#)TJ$0g^tnyLlM{TeH{gd$8X66_ryQp3vPUW%JwpQ=uA{- zFkS}LV9wvtgwzLqMAD~;p8ZBLUQQ@bCC57m;b9a77JVLPh+R8h`W4gM8U?z3>>=h9 zV%rZD9&Iy;a(2QltYyjjROfQDgGd(M|4#hQcRIj=dAkF$`x5u-uY&`))Kf*#__w9| z;d2$%Nkc9`Q4G$@5$7Rj-$sctWb*Q5>_t_j-mQ4dQPupHR&S{WI|sO_8vPU$$74utO3d^Ig~UDs%%KsY)}_0@T8RxhiBT2Z&rxWnHL^T>mB?_!blv99 zK1p^y*H1TPHAtpQaeUx2%CvG$lGnfE_XR&xF%+gO`G`6TE@D12Z2bJYNneVo@Eywf z<21whAKj7XJ~x8VV~>47E?(1KIC4&AX1zN<1TsN|x!>pgh`CRX%>1|*i*#(l$MCT- zp%6m?ge&H3K7+g)fuugBaf&donvR+f0TIE_ZA2EqIlvz!lYpt3JTS&ghyvThANKj{ zCw;u5qz0?lLqit%B}EKZ)YLs9&GDzWUZ3Yx=q86v)Z8Mfrfc!-gZ-m0Q`{#cL^z_$ zNN|veJ(maGRPAgkKmI3Fy}7$3^Ug{ayi5EI&@!{Rh=sLyT5rLr-&v4D7uN$X)Q{{1 z6bx876>z{`^j!mvtnW(iXHfMeyc^fY;b&VpH1*$pBIO%1C=8}CkSXhJ#_cL9Ttd&AuJiAuoXd~!=7o++8u*m zkMaLt@bBG92L71+T4FF`L(@NV!2#v+ZR6N0_@j(_rx!BgbL2gU3&{UYpQ$$k{q`PcbDFYR!3(k3 ziybDi=zH5$5)nvo=$`jDTyr$nC$i^gTRKgdXtoy-F@O|P2%UnWJtFu)_798nf%}J_ znUIiQZs0I8^gm|GqTSC$Wcygi`=(+bap5gfZrGDu+ChYMMH?_1D#>_KPCH{~V5`Ga` zI{9T+#o|$%oT}hRk@n+zF_=tNDr6x1b;wEFN zN5N;*w$Op(a8m5&uk+U#D_S2K^A%fF$!3Q)baES|z5NkM%R1Nbtt$E=rMIFsExqFw zSQ>9V(0nO&SoWjTPe-<|ibsx5v9tayiH+sT#QiBneYq_s(OIaUdx;vXUfrb9S430^ zr5K=O8$i)I7X(p=2VM0bKW`G(_p~i9x>9U&tyUKjjE2`lX+Po`Y4v_aZTP@7iFhp= z2_L0t+BY%}V~c@Bnc(2ji;Ljd+Kr(qc>_G69FoCDZy(!5aXzA7AnO=2*omr+7NCNt z+n{`i4AC0xL?gbK*1{=Q&el$NB1!{UxhIhu<>I;Fke$xSd@Sp2Pow}7tCn6qan#o) zzu;B6lQ*URPX;hScURnZc<`%bKJ00!%Q<7DPWr>o8S_IWD2-`&L$^D1;E#f&SJS{=|gW~L&m^6hFr3=1I zG`;K@6VG(aSevV2Kg;qoc&t;tYFP6VS@-o__w!xWS+9QD6~;fqj><2SvXscf$x%t` z$ylPDNf10}DVxGC$-nHJ2gkkcD{>rlI2Wn3$yVSG*2E{ zkpJWO{$liBEj;hQK5`9o>0zPj11hg(Uh8N~w~WOItEt^BuZIaUy`^0Pqr~Y0J$*tT zsJ8>)sC|(_Z4@vxu!bz^raIj)RBeuxu8IdsVNh5oN@`SVgHy8crTFF3LA(0txyfIx ztHcZBqm268Bh$#ElVM?=rAD4z#c!8EY3BBL{r0CFXBD~#hdFL(?iDC1IL2f5Da}ry z{d{HL9o^U}D!LXU&M&hOZ`+>_m}XGRp;cSP*!f+@xcAO662RR<^P@Xr9~Lx5X2b_w z?Lg3v<3DS(RF`U$1wr3q&aG;2iDe)NavC_I!@e>a8Ax*CR1gcwE*xH*vtI^W7q*D3 zDk9}rbDSTdqxfMwhhG!#a{tP^T2Uk}#2%-egv3;!iXjsCn9Iogq>LU!U@#R>Pgi}K z+q-CKxoTV_u|}H8Jo!nH?@Q@+ISRi0ufma(qud=It^zl-`m;;Hl!}#d=MPG8~0bqRW5Eyp8_2ug}jtfT-#6?|nw7_NuR{=t{W?Omp}S8u_p{1vT2B`j|)#y873 z5I~Sr#ovIumtxZm_+}@rmDdnC!2zSAE659}zl0B>oVWi- z0nXObw038|3bb_jUts=|klni`ho-&St{xbO5P*knwk8+ehq9r2voU~vc5gOquCuwE z{0sICbAG)r={NRq!6RBT&+f#$;(h%cndfN-bI3Q!-9*cQ+x{hs7S96}qp*gCx(auL zoXDeLCWW9EQhb+cN8XP=L&C<|yDHAD*WN1aG*m|0lOpsH=vVxkW&LIB(_2pUI!KB5iYSb$qy>uPP(0iNi4pd%J8?p}cCrM4X3Rfm*XH-F@c)nTOmPh82@w8|$Uj%WaH1UWIT(phUY z0yPGbAND^hb1;IYQR^^dIsd|jF8rkuMk+bRZuR5tIn>7(B5={RRIyJroS-74vos&F>?=MYmZ0lPFcmR$c?7QAHSky+ z^vvF=BK~u{k_ENZ4wobQec4!A9H5StJ`h9u?<_?5r%~%il!%|}bxY}~2ZkHy6Y16< zUec-Np@=K@;GnAbgjqp^S@>D;5;B+Jw!Zj?m^}AyPbUr-IA?qI!cJW$(@amm;H{|dVv~H%vWNyV0p;o^VicR=e20!@?*x6xO;tB z(3biZpK67!@Vrz|DtoA2*Fgn^d@So@{8>-QP%6YN8aH*Dk}?`|?a+!6b|9CNd5NtK z8=u~VT-?mtYU$OehP!_iCK~@aC=G!{`6~(kN0OL!E7|9bk`tc2TaESTzax=z!+bfd zOq%zW-HQ&@|ef=vs zAUlvy$|cD`;1Y3JrC?=mbdBm7?$E)-d2P@4dNkhP`o(1C$56%l4w|iC7LX5{N4myjYQ0cs%Z2H z{z1}zR4n(8;xb-n+&;23gi>5iVm=7}?MLFx!D{=*yh@c%R*;+mM0)RV^=>3R(tL|B z?6Aw$v;7Z1D0W^vLgnVqD|-D!Z_uA*_pRo#0#z`5z%98;LimJO#P?S`)E3e#x95I9 z?9fZ_qaJ!bse%CR09h5EZXLARZ29osyBr`D6}$nhgx?kGVqQpUoc;O{;up#gtsS`= zy1F5FrPEy=77zQ+%*z``iDW*nW%tb3**_)$ zgOV^;Zx}>4Y@v9#?08cc4Y2R+Z+ug*PZd}GAy$>Q$FF(vYzhhZQvqro;5myg^@06v zle-z+>AIj>H7c{nKlKjbFfF?+8Vl*aQ1h9&&koy$B4!XN3V+U$0t24b!jp;3eeAo@ z@Cj!6sK?b|KiXU$fzHqJQfHkkIx*@h;a;lweLun^2L9GDvLm+3-c3Iaa7Z1Yv2ECM zJe$*!pLIhrKV?C(* zK2ogB4uaR`X8}On%LhFnv;9BHmKE&v@HK;$dtjBd0&maOAiawU_wJd-0%mvfb#C7x z6<8--$DH(*XrYYOLSkCoWr(%bj_RuLga6L3sF&K)Tvd~i-Ps32bOGZTY*+|I2#ty; z;&9PwpoGkDv41se3MS{c=Dahy6%ZO}fF+d{%0|dMaob^{BSm!36}iYHqI?*_8%WU! zO2ELuA43_dVVP#WTk(8hbpem6l?B8WuM?m08ld}A7XYQKqY{(9ll$gH9anK|4Xlf{ zNl~L06}gf2jog1GZ-0cfa4tMGL0R|Wu{mwZm;hPU0rygS-&Z4%9C@x#PwPfHKRxK| zVq5*C#l20)s=Eo8QG{aEU<&eV-y~RD$SJZnvU?Wp5EnlHP~}~>e0- zFOhmdo4|8pG1UBfa<71J>`#5!*o!ymE-9aNf^h=7yc{hrey+-Rt`>H zLxqAitFUah!{OKW4|{$Y&%+Ho4hlu=p*prO>$_@HWk}d%D$^;MVy$%|f;YMs!_e7K zbW;LQRxyL>$uP7(kbnxHpG5b87#w8RvD$U?hM?lZQ~CCt(a&Ya|8235R{|l9(>yJS z23!IE;wnqRi*z%vHyQ{%21r zQU{?v?AwbT=($tAJ9llXaoVvp9N6nYCfDiv{2ueS{mH9)mp_p1-8&@(P;?FbclLju z1;D*95WzfdH4WNUB0C-g_@N=^?_4LM{TEpQ)mKwh-$Xs7rTXSOT)k*1!#9mFp$-~I zJz}tvcsoD8+f7G#7aW}l!BWO?)ff7ETO$0Q0^7PczJ0=TJFFf^tZ;FVNKTmJ(mXkv zgx+uvz*WWbQh75O>Jy%N`sk;i`~7_uq@(kl!4|t!iN)e_G2*vlh0@xOe6Yv zue)vFOh{0GO!UB=6r#JHCs-!`V`8x1&Cvio6a_61K$! zW=4D>*|1jaz1IMEy$Agf^j@$Gj$))WVwr1@=E2=ynwu);&b~#dd-te50;iMkKc^GU zKe-FiqT*NsD2{2ze{HHTGXk4zj_!rmD+>PHJ1F8v<78+$k{I0*=IB>Q42lwr6=Qs$ zgXdPWGXTO(hU!bNyEa$?9MFFJcGWi_m`31Jx5k;9pkY91OVC>|=)6+l<%UTLyl=XZh@R!HfEL4ddRuM5snK?Qt`om*-Z* z0FAbkhSnp8S!|O~Y2}MM@{t8BkLv#n?%mDrglFI`AlQLr1zJQA4)+mng9wh#ULiEi zPR;K1_MmGFc)P?W3eYAH?7#5`ggf`B}(q4ba*dSySA*R|)lP_wLoB z|H;OFPfoD;Q~a* z4zd6xt23aG>s=H8KS6uf6MQ(k|E^0S_z#>vvxLgq*bx7$4)h<8A-Vj;J`aEU5bC>M z{xc5rCt%A2Z>IkzaQ#CJB>so#3%_}t>zMTZjeK6bcLRXGEdKW|w} zqpEi}hjc1nd%R$h$LChWzG^5f7fF>hG6bE+@5!J>9=K ztK`ZUB6SoDX@}!#<|IRQ+PP?3#M<=l;*WY=axxonrgib-*{$pKicn+zTeHvh^xF0_ zA2euxXC#s@p-2yy{ozsVO*x(nJR9mXvD&q27HE6jdQlD!B#a=yjF^w*F6iHp4@9x$0q6dfh1ZF4+p^l7{ zCbp-t<@9F;k8O#Y`QZ(p?+XLtE6Yu;;1*hcxSvQ}B>~~ZIXjNl(>2V7@N!6lof%X>t9q#pY_+v%Rd;@gW3xsh<6yL1cOMEI<;{ltJm;}TXX7Q7*gQi2loi7yUD_U=pp{M)l`e;w(I_gh~!$H@5RFABppK1mc~Z>bh|9UMRJT2JQVcyKK1 z>Pv?8G4Q-6_xllP(hpfWSIvQNV11X{Gk;m5JHH70VQw7Ave15R36>Q#Cnho~h2_EU zyq-A%@yN(>{c4yBqcT|{y4a{A$+<#%sBCs?nmFGj@#~|MqEB74sUACD2trmm1*7N6x`P%_UqcBtuP4*{}~p%<36Dn&97j-k0r2^I6#;z)>I&--slcs#sG5^o{77+BDcswOBqBNC9N3Q25XX~3?HCI+c zUM-$>8I~qv1L`q8rC$heid86a6MkllJPGOw_2O!zBqL`?+r-J+g+JREUKN|1p3wGS zMnxmNIR|XWmO;r<<3Y2# znvH#>ubNb2xcelhI>S2L1X4OqOh;{5`O(Nb))UNbcLEQ2v&=oI$Z)mcsVh!;NFGhl zD39uV`^~d|f!>q?k$?vXeB7r@`*|8)XsEGqlogB;PBAX8Mppsk^vAv+c?zPSonm6l`agI3<}_C?L?PGnt)1((Q(Ngs-n197V4 zm>ll^8lqgZJl{Cj2kNn`BgBGawSeAW^Ak5&4EaMHgv_NgFG+H_L`Fevy*jyAZPM8? zo`%{r&4$Y>^V>{sR4nQe<$UBB0LT9G#=!8rns%UU&1SE0mC3~ed;jZq zYf^S^3X&;SpD;^2`9)l+Vlb3Izt7xapyC#}6f8tb!9K&~-mD+(HUK zmv+7ZJ>QY@@+%%ELyCkeKDYLbCkLyx!D3SDE~Zau18m)S;Wv&8Eqlzxwld;ACB=A8 zKFzQC6W0D+ax!wY2wzreck+x!eHl^;Hnat$IoA2>G?gOn_8z}!ESAzo}N(aY4h84Rq#6Id)TFH&sTFOS#0&C({L1How*(5Ahumax(6?tt`f| z;|Y0CJv8hmqrTboL{w$!0d4Ix7M!uU%L6JODT>I=#8N5UWw+ywVt=neOMEpRrwpGi za$%1h#+VWhHuCZdmF=KkYQQB;bcXg{CIpB<7dbm_p{|+%0QTf?b?Wk%l%oV$<{$^J)I?yLZFc|E@6W5&b?vNBr_7L*Y5*Qh5viekt4jIZECZyO)v$LueH$$z-eSu)lC zV4aIU20{8pHH)@ji~Rs-HEZ18ZIh>Xhq46q-mpG*C@Rg(MB1}N<&YZfEa zr$8q!el3IeoG0ka_-&KQSZ)l4tvkaP3F6@LzN;_F=239lIB_Q}8|~F!CWk^opFcK} z{W(o-J(d+*_nKU%TK~m*CbmyAyiU{ft}HPt^Qcg?xq)dVk$4T46IpbZSgr8e$H$Y> z!5HT?QD_xQZZ!GbTy@EmgTYl7$rEwM8nsx#=sT)7lAA>8;Z3zBOJ1xuO=&M*L@B!$ z<7v%I^~>85?31^O#}}?%xJ9fE9*ly}!i`F^^d)Uj-x4lrY>4Kige_vbPu$I?kpu>q zC#!p_EgqppB2DGwA87vQ7ymS@IyS&@gSx?<~jlSZy0e<%bvX>w8NqvSwy{gtnwsf%;W$7r3In z^sQ0iGh_jVS=gm(&qGw|TsBd3&3JEvJ-E)TrL&5lNBu%k0Up8V3Ps8G z@K^v|m?4DW+iT}1smW~Fr_7}>yh_PYP3jJ6keVBPWR8WvAts0VZ;&=ihq|9Z`v&xc z%d-LN1g9^>CJyUteV4iNOjE@W_Z(Z;JzMEhC{rQ?jMI8#X7bqwaYCdcG7v0@%5OFX zU6zW~#x075MfBk~ka{BcF>TIb#DmJ=c+mr#9PD0jA*w9Qyfa>vbd-~7K!SK6MWDuL z6%oXtaxHUs&fiLzYh+AyKJAeW*m)$ksJ=Mds&u{<&*C60=!(E2#cj@d@T3Fdl@2eb2E=eKamwfd@vgsDJ=&4Ejh=?xkGAx=7}+KNX8H3EBL@m*B2kr^aPJdn>pbZ)A_g zV7`ufIX|fMiB5u1ng+yv72 z9C-!!g_{YJ>1PTF^RCiHJJBRg^W>#C1fHa%Uc3+|nFW_Ij@&N+DAM2-aQk*7#i*~G zIzHRGm%!HAW8gp+tUwqMzHr5+=h?+@?iV??R9Ys?kES?ELa`vJYpHe+jWYJiS1OUe z_XL6txSyo5y_raSRj3Faa5Mm7I6Pp2VMbS|Ez<|@#xDORYN1S#>gmq$ zOUzpzr{(85vQ)ETJwA@F==W@tHH30YC(oK+I2R{;=oo)WkRv^c6~dP{zxiN1@)=DJ z2iBl8Z@Jo@0C?C_tfHdMW#ug@cRoJ}9PB?%!%h7Y$2xm-sJXCU~-+3SWpxMC@Pr zBa9?GBu*@I*YIRF>aH-;b-E@&3eL{n>|INm*y3_Z*_QH9rkjwq!^TV;l*D~99 zGyKUDn2tviWsKZ@MYIPOXg%-(VU#Sb2@|r}T%GK+IF~JrM-m0T;u)xFUp~5v5TNm= zSQK5<;(c0Zj)I`4*G89l;unr_$Nngp*a9PEA@q~r1X-=%?ncEG5^)pP?Z>*Y?h+3s zUJ4~)(?bs4@Z)<_`!tmO1s#Cb`VFO70hA`IX;11-*(Ll=EB7#tOia|9@P4Wk8f&x3-0dAP5R5E!{(dq>6NRgLHT2 zh=_EjbazO1Nq6@EN;eV%%)rciH$Kn%o^!sR{2TVY_u6Y+wRL3DG+@TmoUr#i?SsUv zP815rh1*EI?4r0~;$A7Q7l_H+(+I{+F0(_IO!m`vPz%qvm5+Qrd2`D?ca^eg57W8fnf zZ19?V&10C@(8b-;63UHk=o8s<0-sJ5iBjlRkjO;Ht#R5c7{2X{(cARBxp-$?cG}{% zjLH?TzJ{Qy0J4LOUP0#t>BJVtsn8x`<`#|+q86&RRUeI*j#fJlsYHOTxq_U}=WhNV z?U%u^k)iq%!)IRSMtkS&s(T?d#rLwj zF)TXNob@=lGlft&Y28{cwB}IUyj)&@>;D=+AcC)CsZwHt8r|Rh=8C79t;4 zeDUV%)=I0LQPi`BN!P-}T5ba>|3-GrRl$*E_2GGx*i6t{uVGv&!44Btm{W0Z;_kz2 zKCY5(N@+bq1htPNGLN8Vm+=#-&J(MK`U%dDY?*|AtwQuPQ_+fVp!99e!e zy#hPpaD~+S_N{5`4bO8P=vSi-vXlGHqH@DeXXb-o*}s95h0(AO;)nmjmYn~=!&2^w zbUe~%JvJUi^IWJ)6mSL)=1p-ocxVkDowE3cp<){MmR2{?Yp2Fk6gJqM?2ji-e_kYk zLGH04wEGRZ`&{3ZfY#19lb9%9`N!V(4``k|Pj)FtD|Y!?+S03M0I2J-!VeFd^0(^L zae1`%g7blu9>pjrCsZ#t8=XwC@iqDvtrmZ6Vc?|M?tBoI{hZn_(9fV~4K0TiY%)2O zYTp1_fMAmHGKub|qG=l!qk#oPT{C@=$!YAreSRxhHx;hxzIYDPx?guBc_D4Dh{9s1 z9zQm5CJ<306>ur6f{X;Q22I+dqgZQy`wJVqb}`F}FLd=Ae+TAu~z8jqKE4CqhQ zn|tN-3Q7gm`8#<_fvaufmkIu?Y?s6|kSh_#_`pBp9I8GpG6*4_p~_u_^v4um!Oumh zSjZ1U@3J!VaRuHVpv6^l)|nR+jXat@8S<^XS-(Dw*4E}rA66=|A%KD|} zFwyRMU32vdt=o)tqgq=v8(^Obt9cewD{p-`^}@+dLgmHbkaf`RLKB7_K9%w#xtG6b zcuQv|E<~&P(8nIX4488IECL{xqjS9qdH{S_2v||IKNd$}eq{)#v?;ie%CDUJc%1l6 zj=X|2)=WvlVvfq+`hAWj+p;80gXehz0z9lihBV&fk&xMTYx2(G{EG>GW<~p(rFQT0 z-BG3qp*~A9Vh++}5D2%or6@oQ-P18{Y@d%cTP*$Q^?bhNfg`Us9!>l_iqe;>`I}`6 zW6_!R;FS|jM8VQfswk~X+&)qeJ`9<|wIdjxwJeNzis6tm-wG1svz0cxFvd^{>f=>g&=YqMdR_?Yt=3?iFjf4qxBNNzf$FnB9s) z0}bD^JeQ}OGO+0`(}5PukgJnRO5`5%xM?J1F7E11ND{pOZ{PYTdGq_o>I3q_^+6I; zS^LruNyoE?|3cQBe*shgx4FA)Ai*EBgyO^)O=)(WpURai*}y-mHdbsI_*9S*yrT0S zVkwA0(I^ZKLu@LsNJ$i4MnB27MsCgF0U}nFw!B5|PTqzcrqq7x?N*A=iM^<=O(8f! zqtA)UIg5qeE3R4DP)aB&y1di211q|VK$jQo({&O8xbZFTHF`JDn~V$0kmor0RNdP4 zJy>@%D6p%7J&0=j^5}dh)|jY zsCuMj1$(sk_ZoOCNP}rf_@<@l6ekxPP zj~UHyIH<;L%x*{jvF`f0X##iC3V#QxcSZ)52ux^pz=HZ>g=QUUO^3Jeeg&9G^MYzb_PB>9_>&% zc-Vm|rT=Vr1I&*XFsV%SZLXtNvjp=xz`(7L5%lsHfGbg4v~!`b+|m4KMt*FE$5~l^*EaJVaF+0h0l+GS*hMAI0s~3cO}u$X$$8>-2Z28eECG)k(mYFn{E^{I?stG6t#ZdM8uTb$(}$> zbx^w=c^ZoVG181g$u5(o=5sQXy#@xGa{;%ikxiHNtX%SrU+WpZ`_t&QQ}Vr%INPwH zcX3D5WSu7&hz?2R(_-ee@fFPr$W6U^e$)Q&gLdY_opW)u{>TYE#FE#>z#};GK%q~< z7wMt>s2&DU@yBa?e64A6@lJP%F3LLzTaK}Wh&zhMhbaojpvTthtM$lf2N&saQW?(}^kU)}aNV3LU$#L6 z7U#M|w~4P^BiJ*avNgE&sA3l@N)_NuJ0cG+W9+S=QdFLe$9S$PZL1n+TAz?5>m5D5 zHxDh-gN2ur)&-YGgHB{Bch1E3?lI|U^6=iKrkqnSMESLICw<{*aZE{zFQT-HnDil= zZ@vrP`ECalANb`_x+lT2qvPp{dhh#wn&<_NZVLBVNPWu3>*>`_llEs{KOFpiF5>$E zKYcO@z-oiz)A)YGJfnacI9uC7TvnSYOxNh^YCQ)SRO!>mr`Bi*LK+PtrVN}eF*oKu zoqeEAJ*@TNBmM@E-2db=H{btXs6>)D7rxgXae(O-o9c=JD`Im?a{Hxc0JR#QUA(yJ zr%|~Sm+MQ9n5sBVeko1Ak0-?A;Y#xdL*+DM45%;`y5^@bb~`clhd|(&==kMnOa-N0 zb%x4sZOoE~n59l}GX!*aEI3YUGN^mVA6H_+yE99vU(NLv_WAAVc8Xs<4G_93HCX4b&j^?EUA!Vo28 z;qn^X1hL+pi;IlSFG=F_jyUrgYGLb;aT+Th%DuZWQ*X@{A_KZIW5f?7OhVdYpWn|~X} znCHjDb0(Q=aP0Vkd~WQp{`=5*!b8vPZ*F90s`5J&9H9m>m$y!uW;`p+*O=c|Y|4{~ z1t%lRcR0|vx(jp1PicCaGA6J!dT#~kvg$#BIa(pw&LRgEO(3F_J2Fd$I9QmfdZ4mW zMocoUG?oka)pNo?9=IXc_xFd$1azOguPO{qAZ%0Xxm-wZI2ydOs&sRZI^3PmTc1`e zo;>Kfx1*0Rf>OMe{oP+(9V^Q|%R<$CpU(pGVXBenxFoZTC@L_;#Gw!FaR~lfg^%hxa5?F($euToSe#Wil zpQ`W4HVI9(UhH*NyXKmVdM((5KU2L>36+hsE{HtXJou9rn&Et5VgD=6 zmP#z#!WfA21t{vIFaJ?xOn1X&DE6J!g6^NaoHVb@m_CRbV%HtxzxMO*xJ3Dumxuok%+ zxpR3Dr(iOg?nwLj!>U?C;VVFT<#S-40r+_W^%w;$1B2KXf301M3{{y4mrE`i4d-p7 z*uaA4BFD%#4yaqWO`G+w+w}K9!a91pl)nyCq+|pE@6o+~6c5UOnZ>&s5jc$neBC0? zn?Y>Vlxe((OFDWjxACmpc9i4_*agrEE&Zc>{XRJ#>3NzR_wZLi6j2(wTNfk)j&i{Y zZo+fQgo)|`yC9ram0CZI*SBOK~V>o-^ z2p|F5CRBm0KE56(DQL>S{e%etV#X59$YI%3oFM}O%S8^L{hN)Of7Xea@RRA9d;`5haFXGn#E1=n58V<`pdI1*&zGdpPWeusHQ+xL_rb zQWG-CFm8HRsg%RMvvJen%j_J8yKlZz$`vhI%dJdkFOF$V_Xt;gh&+|WI~Qo!KhJ*u zcBe%IWHgpu>R?!yEk;*pz_>c5UaL02em5Wwb3Gb^pbk&K&K2_p9xt{jS0 zR>#sSU6|3HXenU!U^C34IC!~6j;J>~n-)?pV#o6DlsAU?As@NMRHWem&?iOGA{BJuogZ%WixsWyz8AqrK>Mq;C=* zl~(ncSm86t{G%0AV0!G~7Yxa#QJLYO`M-tgufvNK?QhiVKkb#w6yBHPbvGaKRf?o` z6?cwIPotK$xHL>fA9u&zJpUD~_Q@r=EkSTov3mUA@OEIuJM6^0>)5M1s0)T-nLx)0)~X-3qObZ~e5{gt3)$^2`K z_^Wte7V|ILbauz7_=NtQ8U@^SmVq=XyRB*4%#xoki|KOJF57*47_H5mbHiC>G{>Ynu_isAd^KJrK3DOX0zHctJ z&OpIfg$)}x;nZL5XLmk*BrGG(!DsQ-nJPhHu&0LzvU}pv%q)lDns(oFL8%9w!ynTtG!2>C?iR!+cU`q{`T}X=Xnidlg zSzD+>^*-fSy)ffu)VY%9DBINAQYWt}QAE_GO)Vegf8*(Ja9l{1Q&m)UK`9H~=xU;y z2t>^lo>@8re9y&1(c<$J5fJ}2SFMf&Ox*RUmj-dg zE5jYD(=C0@aXa$gOeWM2sShk?m1QW7{PTW`Yp)+hIqK@|W)Em@()TY={3@5?fQ0g1 z>0d|Moxl&MOqLERuDBW94Z#OH#NP$JCw0?1d!^=Yfq(_it`NK$!p&?Y9C4C;k^)P*ONRVk2O9AVnCH>$i|EkOFQ=!%D(8^96ak%N48#ed) zCA$?3xLjSQuDGJkP*KBrF)HgKDGg>UFSY`iJ6_h=8m_2fYU?%^4O$xXqjvN5Np{-D zl$MhE9wOPHnT-lFv*eH*Wc`4}k+ABcy#VO1_F}YMqckd-oJzmiHXbTZD$Zlnn@_GN`xAFG+ zHl`8?#+|t)!IQ&5`1G}Xenyj@6J>vjPNZOuvI@2ZP7%{By!xj{>x}!Vexyf3nZ~pw z%V2^Wvh9<1F1#C;=wQ0JQfZ0RDz?8T#g(jJX;xwT+zR?na)k@Se~}h|clMPLxs;+L!D8r()9JqSu0}pFqJosWbFA9T) zH7Dt;cBCKL)^+EGPKH=D!Bgpd{W(S<^|Wx6<9QkQMmtJ8Hu{LpXicZ7Qt|9wJKv?< zzMm75^Mu@?L3rgSzotSlE_GYMHT>rZJV~YHw*c!PM@cZs4z%QorKi9~l96}gZ}cBA zGD1ue7j5C4r5K)Ghe+~E&^yYLb@?z#*&a&nV$9=#LoxWzoiq8ecRYmF(VHLGE96(F zinHgu2o;Hz^qKVrH;cmq%FtHB`5SVv+Ya`T*= zYA*a|D;TsMrzd$Hza;UcGDB~|fb8Bs)%yRyDj2BWsok#Sv8B!l?Z1%6QK?Y=7JSVX zb|$4~xT-MgKR?${jWnr5PaXSd|HSQ34wZL!%ne7km#=WHbPl&Cc^Ce1U*Ngw^U1Rg zd3!UA6ZVerjg3--qhJ3J5a-Pau9;ocsEqt_gr2>MoWwb1N-}A>9BZ6T4wx60c#DJr zlGU3wJvWWQdQgs#T!sGfRqa`?oak5a&S?;1%UCPx#mdcnv@4AH5c@}b_$c|nnJW!%bsk@DnZIncS9;0=o+q${%ixLPbh!WFTzLLnx z?;S6@+JC9^DkXg|H3I)IE~ zKjxHSGQggz~e~I@~~?>cdodEJB}P z$`gk1P%;AvDlku!Ts2J&Rz{N`5fJq^7j^_A1f@GIFf(_5S$19LUw<**gtGcFJ!_xwf>@}DCXs?h6*_z zW_Xd%<8?a+o#bY4_-r#<-C|GnMi0^fm)RUes1iJfBoz6xps}7h&Zq-c!uXJr-MULN z>fMs409Jy3acIB3m@eb->w37FLH*5AByg8h)b_JFp{e7lWJoP&p|=Fs~%1kHjZTdU5v7$YeEin{%%Nk16hR zfWjVaIJ?e5zxF)P$OB@%by&uNpO`R;z+`M~1ofBjq=qR)^JK7e?tY zqZH|56kui?iylhV_??5uPR$Q9gGh?i;+}i^u9pV!tWtE z;#`&R&SbT`S@`9xruIkKoD~q-02+7~ z4-xx;&+S=RwydN~a(YjN$Y1VFZrs*=_5h}zdpgQtSbO_$o{Odf-v6 zGe%t8`;y(-XCf!0rDn1Et{(s z)pKH}DpauMnQYn_@zrIy63e_6n++<|v9DZ^$&4k#bBy&|y2b>X#8GK9?dIIF>j~9Qr$)x88Mb!8k(W?#(`}r_MRpQ# zuGvqDgObxTz1$dk#FB1z&C(@)i6*fmHiAZ~eO6NbhoPa$RU_-^$@D!9_Gicv1rAtg2tZ2%G-8_btd#dzw@9oq-Y1W3?cks`S z6r3$Bz;VGj@-L?9PT~H7+-`fV(NS8_Da(3p!ifDfU#5pcjBM-{bs z1)CSN7wln@ibd08ri#`^?8L4(ZB0E@CIk}k?GE51G`)s+s}@)Cp>tw_Rr4)%cB)y< zZ#cRfO$|7Ag2XkQqyzKNOuh~MPSQ=m@(`Dz3Cq3xvN=HoK5YL>x&!R--`L83HIf}) zfm33votdf=xYaiLo2WSx@IkR7{?ICk&3_5D?JrxVDYFXnU~6_Mz^s`N=rXee?|4j!=?x^n9PB!Z`iL%LcTb7b+O z;8lL9$W8tNUQO(0r%o5lsGFlskB1j4XHJiwiqW!n0AhS?S7M8T@8i!ld(KmKQPr-R z8i!5&5z_*qO>L~li|$*UF&;?3{uC~f&vWVJdZ>6SD@bfzNmkbG^#kx3__g~{c%OX={EL3SY>c(5IJO)ZGUevQEy?6V?AX zv4&bHUrY1TeJyE5MX#2&_7K~UzSEaSg|#oCQ_qNRvi>H}1}4az_?mI%Ap*iGxHx$gelPmA{v?>vsZ$?#`ZO12H>zr>5qlsO4CiS>R6JUuiq(yw+!o(RVr?mE;`q#J7v z`vau@FSfrG7bJ=&93hE}T?c}t-Iy(_y9xIUa2$%tC8gI)bRt&Y#0}+W)|&|#mHQ0A zasppD{(4+eS>av6JM+3{ox>ZPH%!Y$OVO;>xsk!=>oYWMP$B%uQTrQW^-5=6uf$JMKpQAz)?JEi|a;sNOH9nTp;9qVd*mf-N#B0`EIv%^`C>uPEl z?)htObHm`yRexq%GCkRSE~<5P=@PHViV6!Rl395(o_{7@ti>OK86Zoe+lVP>s8fWc z`!*V6V7>72;-l}lI7T@z{?hIRzmI)3PTp_v3%01m)nWYA;FI&_$K~|B6MOyKUSs9U zUq6;VnM=0UpE-->>b9sub(+B$+|u&`1fkD)j{D0$MEmeF6tOYQ1#C2?t)4_RNyH7+NUtA@T0f>ZC0*LEWn|+NYeN;IN2l84!`4bTGW+8#ckDm`Ufv@J4{9dX5;e?m> zG?Ym1_Q1uoTnzg1m~>v)-BQCH7pDjb0vfTiZHGl@U%_0wFUWlzjGM2=x+UkCs>w6t z2)Yv50vj59W@bQFjW#d_1+)zF761|GR;=6De}pUO#a}b*q+;$!%2b;gvc~P-SHB|H z-FR1x#4T}4@hH`(Z;ExI3M=iS?i%BUWhRFNQB*dMlgt3wK7r4@r=h$uT1zr zB<_UqO^AD>^OGsoSLM9rH$5Rcv3jXvD;&z)-;s07y7m4kdnigE+G!Grx;#D+2)!9O zzXnepk;mRgRmaYNwGe@?|wS&Z1#b5-PvuU?H;w?wUsU1_qHW&5wmTl zF5(I`HLF1<8*1aX3$MpXn!OJXt3r-!WX7H^AAZI$FWtXE2(J}ta}<>}&%{Ih()mf= zWVMvGl=Q&-_C3j*ztMtPG*MNJZ;#2|F!d|W8d8lL$eOM#JtY;2Eqiy7cuNtyLNjbm z4})pJcY;0jr4)Qdl{hnhOTUls<|+oqFb#&^JaTP#XMk&r(uVVvdWZxCB8~5TPRT(5 z9@2N7I&Z--G}7~D1C>lEu6YrKIP>GE0Bl_oZU(0Nuj26cE?u&-KH6c}4SCIDYn3Y4 zn~Laf&iU3|*b;W#=6PQo?4E4dZ6+a7{T4@ktkCB0P8wT!R+LNZgJ>4#`ua6p3P;PF z=ecF(Ik;#?s?8NfmBCjb9U&LC=XUQoTpEHKQd)Rcb!DPZk$Qw#3m^@Gj3{T#%qM5c z-Qsrl)oDLp3oH{?G=Dr}US^O-W)pc$MRU?TS@9wFY;H$-C^s0bR`_2sd7@}kBOp$- z;J!nuM)*if0-Llv>^=J0+A-A25fZkJqkpS*b6vbyZtc5cn7*@Qu3D#Lg;)%-(^7=~ z$xN!my4EV+SgMLc8tUbg4DZHPV!nK@n<`A&^fU$1Quv|vh5_u^MQUt(W_R$kjEi_h zL=$Pm`F;gZ693?A+YnFFI!PVT4`;k}EL2m!O=v$Ho4j8wLp5wLmug4*3 z7&ZJnRR49PonN+tRU3P`y|7`mxA5%OK!drZ(2oOk`_E1I-^N3#PoK9}hUq-ACI`)c zN6KKO%gI!{&*)srZzPt1ziGtfI;IX<)*5;1JGIx-nZc)2Vlz9eZLOH$MDLPSlNyBGGrjpU<(YlD z4g=JcV0-kU{5B6yV)X^-oMc4!*o&KH!9}t8Z*Q-Ugt6mW%Ee<^??Lawuq4;1MM^0< z9*`7MDPoJ%s2`d`Bw0SgKaj~qSJ(w+XWa)y%$by=}yg>Xo(NsL_aRyA)v< z*-`sMJ0tySaF5}=EIl*;c2Hv_usvhgsx}cTYv0#)4XhG-Hw{^y5XQ}RT=&{)1tl#|!kRU~<(o0kL%g<4yL$h` zm}M0@Q`NS3o`Kj(*tIOpq&;2c)JYQ&-sUleefZj;3omj84o#&?VZYjG*&4F$f4Z&0 z=!tQ`ScGF_0!Gl`4a5}pr#FgeySI3Ew?E`Z{$Z+`Dx?>Y>L(O><~LILHD*}4&-6i9 zRFG5X?Nl9sr&0Ym_Et}`(^>!X95Dp z=*K3Y+;<&ywKKyCL{6B$_CG7MH#M4zM%?hcci+zgc}zNLc59Tq95DD8`6iiH-t6d*w{U(fst2os`@OZXOEHyfN-Jn8<;oa#n!qo%SZsv>cD~hgLB6 zcxy^1Uw`DrZ5fgfneRiyF|Ljq8X|!MP;mDN?hP$h!-lQuv#85&@)g>TAydQ-G%V#r z(cjD}nyEWvc~`wY?If*SzQeJ$TbNt8BoaZD~Z+AZB~Ds#eo|>_t6aBg6W7BDS_3 zkWcekl8fX|m4@)4zNHdnj{Q0}hz)wSQuvFh{BYT#GXF{eA+xg^0!b}96BvX(_N{9U z(3;bkqPxzPaT5m)x5-@l*B-KQ0aY($2V@wwijkVq)(e7!{ALm!Fw}uPS|Id+FCHxR z`!|tZh^U<8+RurkaI~0EKT3Gi{ds}VGHyuKKX4`ot&YP*`G)%5t zIw1$blJai|Mqk=<-Y$`k_{V4N>M2$QlL%gdlD+4RHB{Ge^hhCGUm&S*>Aq9BvQ|=g zW?eoGBpA~^A?WG-+>r~44vUoC54{6%2K@#??(L?$p z#KuwJMbfNwOR8oSI^Wq?Z+(QTCVk_fZ2DXvKJDk|k0ZCuwI_bXU`z8#o+0w@{62&7 z=l9PEL++_2u@vucNDLS4bdYd>oW$#T*OdFt{*7zLt_u$`IR?^vNXCfzfp(q*kwcbDBW;We!y{hVg3`Ib zmO~tksmm4Tfj^<3#eQm0rkwUft%%d!`~AT{6>*knkwf`yC(@f9hE;rXAm8#HN4iXq z#(W_aNut(>$9*Zl6YH)|Zpm2xWUi00sofmeew76V&qsZNe13(mdHsU|b+tuyJy)-1 zP>)VesT1*`Zt9>9aiP(8ZE6Mn$zzhf*nThl;gG8+HnmpDu>j&mJ$B2UAi1Xsccqx{ zBA7sn#T{LWVC0$KCl~qFEbBU-CtDn4Y%C-#;IpK-ed9Bwct#wI$K~J{cZF}fVs@&D zg2O|S){j9aLKIcOX{K-!S!9v7&o9}z3-Y)bQLh6iH%VN*pLKNtwBj)YCNsDb+WBILre?bWe*=+m;imBU&Zf|yge2dWe4sX>9g1B%UhJ4nuy;eTA$wVv2Nn zk-5dS!+uYp{=I#;LhIE`%tSukaR86~cJhTqX#T*@gpa*;Y9cU=hjM=GoBEC`c+%?0 zHO9FEH#N1WPj(o*H&0|8C3Z8`M?=V2j#71(-05Mij^ppo4~#CCFH&!CSYeeopaO$D zowM&L9dSM}h!;0W(WPBB@47v*)yFhzWv{meHk4G= z{+>mNRcs$5J++q?@nG~7{809y6mqP>wiAPlpOvmgAZN|};HKt3gufEcr~AFJi!9BR zf0pCaZys|?Bf~|B-!-7KGuD4ojNH1$nAKI%|0AlLhL0^=-gk9nEltAQ6cB+WZh)H7 zsMw;7k!oFi%kN-jyKit2mE9i0I!IoZ(o(JB;WU|syg6bxl;6BymLG>Bf+`OM(ptw3 zt1YKM7J@W8Q65ZCI>30w{I2$6jBcQbfOkPv4u?PXieXXfN+JGncK>p!Mi&^ zdZ`%}mWslIsadN%pJZMgM6J+)DX%ajO0LEFAgO3opo-SIF7%5AYz?mDjg9oocmmgBedn7$lp)b9PR+4}tM)oRiPD$^rv4cl_BT-e;#hwU^pNH)*I z%1Q?Cg}*YY#xlG)!Cpuhn>fg{KEa6!Ro7%b5=r)^$M=((`@uhDV@EMKc+B*^dXJ9Z zVX|B6XTi&j&Fz3f^o(($FA$w;`!?A%7y)ece-2^FI;Co0M9i!3QCOKs>S!MtIa4U1c`7 z))egJySY9p4P{kk@E~v}GEg_!Y1Fe%cEV)VSLl!c_PaPd{m=mNC-AggDE8KLVh|+0 z3#S+)gg=yP*ZNcrjcf}O*5H~%>2rS}+5(xe#n~Ip4S)$M?x%#ipVob1WjOy`Jk;dF z$;!JV#jT79Uu_ufit0Wuan&=-HFQHRXW)}!s%ox6bkXEMhz&jL9+cax zO{QTE#}wOR&ay-^LUd*Xl(RIs@7FvuaGsQpK(}S0`~Is;CHasWxC$4+3%u!^)+dXS zyAy5W0X(!Ec5X{v{=6GLi^|BeoJn5#PZO- z*|EL%er=xEAf{xPBayMyeD%+wjesli;j^TL%AMgX60jWsCTr&Ae6 zUxCL69_5M>Tsr?bGBSm65}qnT6J+P#Dhzv1;d`0)MAGPVGwiVcH59aFkq!d^TkJD` z@;|F(%d`W6b<(iC;NP_S9qWGG5S1A#rZ>oUafnxw))Ld(jC7MKsqTY>>3@p9BvnDh z+FKXwEG6xA@-SNxGABJ7v~j`69${jxKash?DK!exC4X~Hi^c!y1JF9nSskCroVx8T zudu8=akBTmgwE#lg4`j`$=?c|#mTu*w>TeZ#@ue5tp@PRMZcO%&_$yX;dwkIbFzx_ zp~dA;#E$!wX=|IlQs=0=z~ZP&>i0b{!!t1_xgTpG>k6#8k|MyH!pJALmdB~jA{*+D z;9jnr{86u0_6VtYoVpg1Sy!bn`yY!qHuuYavq}k%s(@_K6F+e3c=_I?P#*SFf_uH+ zj9$x)%>l&@+xz7`A?3|`gTOVP^QNUzi`=}@-v2z6(&b-?ap@L#h+9pq_zUw4F1KzCn z_1p$Tt)6G=7W!UMSh+N6-7qG16fskci8Pj#MBM5hL%i($N+R2mZZxZ4v~e-1R8&GK zF7@0N#=s}n7#qliO}=6D#XRayIvxXB#5Kk$jy1v&nB_(UMvb-Dd2s*|8+8#;-L~iA zqM3^{-W$v)rKo93{(wOeM(~uz-rj(XQN`>7G&{+XUE^~E*+rNVngDIkJ}@B_=ts-@ z#xSPG1kE$KD#ORIik`-&vH973z;ge?+RMuQ) zXWY`qUpH2gsd#A+jFmM!*aix`RVYx#?v^C2qbJ5dHqLeX-PIr{t^*=Pcu!%-%lpty zAZbflN}(cC0DRT2glS=SS*0yHc9sN;Sq2p`SSu2;Y*<~!}Gw}Mszf81m=bL+Z z`T}O5mXZh4PPn}2B#ZzMnFOm}1;i<^rOf>P<0Q)9v8w58c^#q+yzP`$3eUwse6y_n zaLDVGs{kq&B~^ZI=5WTP+z83a?~GWnD@jA`MT+Bnhi-usa}sr7wId%n_YIEi(g8o@ z-4Uetr(VhIfXuWwcoPKN<9r73x0l;it=E=W!c^U8y@6n~v(dn#s1=0cp>pBDZxGY( zW8A(hsH@Y@}vrvsbTZihI?_<3qP?_(oYTmZ!;9f4BK0Y6t z*J)WB--6`KH;^S4C^sir9!UC&)_i&+R{8<9#F}sQnh#L@)#3W-aOj(D|+o> zS?!&^h>5*|?r!@hrE4=b|Ac&i`>V?+1PCYlQ$YpHeedMh@5I4>chtzf=d*gTC?ZI- zPZeI09J#QtbGwcLGD#w@>JWV%=-)WdaE)!rU%lcV+UR`QF5-ny~v{ufY-*l%WB{1fwv&f zuMSdcqcZ*k8drZ{JtC;lSV$+k?$L@^Z)caSevoOZu-9#um%YBt1`Bhsz94VFT&cF? zez*fNbPggB5gvEa8>pTUF0+W@Ss!Zej^*mDn9a^(JI*D)PKh1`p9+oMwmsU943rBa zJos-l&-$khm8W$`dJ_oA@VnJ9IvN|%&yrVy!3xMRXYc98LB$cL-GgQKHEqOgDp^-U z#Snp#D6f`)OC3hKcq941d)>N>;uR`Cd%ID2dWkSSEjKljV zBb-!YnXA!Pr!APKwu`d@J?&o@5VDo!yz0n|ymOt)Uj?=?C+_Bn`V!0LmK58^z6jQ) zoO|To+zLZUB>P6?TcGKB%`6!tMT<4zf%S+_6IRE~D+kXU=jUeGRV-Dfs6BNx*L&D) zAv1@r21~i)LyVV2pN}sX_LAv}#in3NSWL@PIs)T$t$W6-mf8#~M!2hI&{ zf@-f=>3aYvozlfbdjqmS^ciTL8hl!ZZ|A9nLlP3&E)sTwy)`xXtplvTT1o^Z*BOLI z?d|iRSWQ)wq~|)QE97K0e-B&b2_?;F(wr>cj0=gBA>g#>#e^dXUGj3uRn;c!XlDfp zY@w!71`!WpevFg6Qae<-KMaO$KwL$k`p(dRhv3~Ns(uAn)p?DqM9)pG*~CfNNP`ou zvtk*tmvr`hGC7#a*fbgCM0i2|@PGb&9&nXRbxByHPSSa4Shu#6&H^%7yR0TaW@hDj z?`%cSE?k3Cnz{?)meGQWUL9p*MWQPdiQ_`)KJCG^|N3C>C7$bW^NdkXy|r%hz7F$W zG-WP<+o}t4c)i{(D!P`=SdH%l^PT$#?;^jdqHF@*J+l$xl+fTLqk;a2)Lj=Lxn<*P7yrBfMWU$68@&+>(Pu}l`+gIS@e zq0hYXK$~q$Q=fYj%LRiIUeSVPsln{ziJRXFq{ZCP|6gU_0o7FY#LeouC@P4GC|!CH zLPtPAr4ytVkrL_Br39qLhOE*Aqzg#zU3!UtfOP3aIw3#^0qLP6-%DU!UH{+t&X;p? zAUE&bd*965nYlB+xj|iShg{iF?as=cEjQFRH!CmJev@YV4xTm=x8 z+d`$Y*r7)`t|G`PX)-#KM||F&6cQ|B^OwH5yj!)R*AZ02wkWdX%W&IB%A@Jr4K2zf%)&kyad$ zGg%hgQOCbznB{fu$|65Do*v(JShVJ^mRudq3JLYXbv#3q#hTGYg=_`%0{zm`FXfjS0p_cO ze2_SuT~Q!_1L0TZPlUc7!i{)HPrYf^WgTRLy&kx?ROR!z>m0Yc{qCe2Hf$dGM5yOU z(mQc`xhVLb)tjmzoYzii-S~{&kk6DF9?xE8De=4}X<_eM{&zxsL>t6V%%1)YW+2;V zLtvOwHFD;1?vBB@hZ6gJd6dIPZ)=Gvs+_!Q1wcT8x9WDD^yb$jyp_NTVw#K_NRHhOucQUn zoy=iAW!vc}NY*<3NoMT8(sUnF<|?syHL!8QZ~Wa$pEX45R1j#$f>%>ovQ5QD@k54< zt-eL9uMPfALY17>SJUBd{mln}VqG3B68W;8Du6E=Ygj*Q4MI3G>*7QE+L>vpT-`~61s5`=AAZPH$A30rYdk2s`&R=i{2qB?@@nq~=|c#0>4&du z5S$7RrTbpj9Y#@J^Ule-ulKt9OslcoMdWE_ZqQJ=lfE*K^!~zEm0wh-it?>8;}gUC z??pQ)5ETkRYL1cuNmhmztw*Tjss4^xe>PLVKlNn)O&zJ^Sq(&pTb1@iZK6lgf%25h zW}poKM$GxBvP{S8(&Raorj(L&@c3Fkn8Op{L4Hdh$QYzl_!vc#uCe97qvv0|Ir0n_ zE4W@|?Idq(b?wXa>v5Axw%MXb@!7(#w*1LZa2!(yS|kT1(Dhe3)ku(cjoW@6W-X;< zn88P<@CkSxY*F)Ybd^JW&$31;k7Ka9KO8>7a)J;0?uwqq^lg^qTh>n@x&ZW`a&8FWF9J0mD;m?Zrg7}XHzbKTqyfRH1=9s zyShIq8jIrD-hRIKDg%2XqwOHN#$X?oa|N)zAIN_RDVxxN7w9BSZW+qF+ZwsKf{;N- z9!&Wn|2Da(1CB1w`*h>sc7AA8`l?Y|&^Yu~zfaakK6+&-VtqhR(l9EpYYx6O^2O0o zeezGKhh_=fLFoMoG7)E$eLHMBCG}#Kv^>-R95f*?NB&Bnj(YUZB(u6D@?Bb_@tu3` zVNuCR^n(J2tri+jM%GBfw!tkQI*-NY|D!bY0I0SMg!@u zTG2~yTBo0nw73#E+FB;>6MsV-Hs8Ek=YFqG_4(R2S#|QgqKrx|m#`>RKv_Iusde*_ zmMjeAn<~}RZxDWdB6K{^Oh{2-7_8D8F?z+$rHk$i(Yx&>0N;sQNMK*%ckX2elN+;k zW%(>$J=%SXes^?z;-!iY+6(zDLS_ixK52;2tF{`ieIoXPY4JU$OKX|Ul9Oz321Vjs zX!Mh1$(mOsbEooNB?tAq?NJ)K*tHx)y2@mcRyRGMdJXSf*{Y6feC(0Qym!To5rO^d z&Z&5#*d0YP>DYn-p}-mUh1>z#%l>>aB{r$_Rx`s9f#=(?>~N`rcRrk6!4JmVl?Ojr zQ=8DwZqTe`_lKX}oxTBwlFi?%(9`eC1f6=+SFf&q7rTp|I`5k)im}^ZE(O~ywtNPc z-cg<+3ks?B+cruwqgNKQc%!i%1Z?4Si?wOL0}6OO+)d;?yj|q^FwVu}nWE^51azB~ zf)8HDxYs>Vu?YNNX%^S<%4)}g@xcu!QD~{oU|;|&0KWV%qJ_+bx){FgmO{mR!Erzp+*`ICCxQ%hj|Kvbri7HNb}{*5G~C24Sax z$F1%@z4b$5<=QD)*(zUS3q6(35*=)xztw#?mM^j|6ut&Q|FR&DR7*a|@?BxA>5D&& zpXXFS`BLF3ou5~wxA0rQqAfqY-Ny{Ir0r>^C6#_Up&op@x$F+Cub1;GS#o^;3)IU&mY^$Mmzmu{qiW5BKeoC?wgJwrk^T;l?DLSvL zkIt)7``XHHp&#>0W3n`s-gDu5htt1O3>Ej>{5B#!-e5{w@eyBC^OUr&tj{uIdt)zi z0SA7jh-r3qr+7MX57IL41y$(EiCglO{0gi)4Q+yJ48#lp-^T!7jO%T~ZSQigt~oKv zu%%v`#ci$Jj@q)IRKN1oCy8;`^F~ds_#kTISgjkIHwuB>S+V3HbVuAcb+ebp zn(S6dHq(apgd2xie@{a)e#H2QX0F5-bvhwNt+lK)O6KF}DQX*}??$^ZoXg`n<;%Jm z_;OUx-rPQ;ip8x#C5@(8>E0UyWue2$&Xq3YvQ|Zj*oh@}3&$_R`ESCf>bIv;o4#!Y zS3A$=*XuJo1x}KYS^#U1e@U0`R75lCK^N@xAj)HC*ikjqcl8QQX?^9w2X9r#G!V8% z%q<2lvg1$mnThG1Wzto1UmB^`Y(mRe<-OE|nI`yd-_FtOe2#q_T44b}Ypx_ZwlFXt zC6{bHXAbZ2K}==q0>Wt)ToJ=B7|%I}iej3g@p{(T;gL#zM0<7(_q^xX-qkOyt{tfh z=KC};bE%+ZcNzcv`wV74!9w2TSx{9NVr-y6na;ITLq=KhMyH5jgx&Mk+nvMUefEHQ z?I{{Nyt)VhB$IXWU+tA8aRPg;abh6BN7I-#7TDVw=1fbxZX1u)pqe(4+1c?x)70?# z^@hD1?=pG9wIvigjCOo9tV>a1v16GjHBbi^6FO*4S94eLTGXTL3JK_<_h*E04MRHk z9>if!vs35Z!=)(1DqRsu9oi1Gblp4ro^Hy0Cu{g1TA0*kq?an$Ksai8(D;u6DzY5R z(n=d+M?5=6UMR0ma0~xNo?UkZLsf7W}U>SNtijrjZE6l6j&0FyF1#d+}eoDc=0g^ z9+zlFk$7rsCj88{av-whE)Yl>+y6z9Jllq;>v?8 z%Zr7kOjP3>(@U#XzXVZ;Ot5SZN-RP6YTxZ`p%zLKGqhv!n7#yP<3BF(enB$&2-_pg zPkBgIjCnKA8; zUH_OTl{aL$az%_`sHzr#znt5t$vjKmSsgL)7n7tT>`}E{2&<9oc!JY4;x=UPXouzi zXsL4ieOb``c}!e^SK}_=tj9t-zJj8I!J*VbicHw}8As2A%&1Kx4TVihcHlOMY8cIK^Co5ldB2i<$jkc4dcdpf0>``wrjVv2A+I z%Yt}$a^qNqnN`i6?ZvTT=Hk2iyJudX=wj*EB)yidS-CucT$V3>S02lKRf;}UHTkQ; z$IzrVI^e9Y(uGM75bOa`Zpi*HFyy_FFw?wfo1zhtt4||4c~Vw?r`LnG4%d!NM)xX= zKQIO|MyQMV_Y-Km7S&xec{k*o{*?K65>wi%X)ud@>$WH8)NBV6|0wLhf_scK!t^EB zl`MpQREPMMt!eL~I$i;e&tN+UyK-S?(eM=(2&?`;3`!FM9kzPU;Seu2avtCFg$`3J zEXiB#Rr?NmV)b5z+%rGt?=#l!@|2O$D=|@trineM?z@>6ITL&z0E+09 zQW@bce&J3s*sLk5*4pI)vSK4U1|4~qHoB@i!^?ScgI|B0x5V*x*JJ2-)*ILuD|$|^ zFUUU{e*a=etd2XU2q70Tkw)d}$L4K{>P~x}>Ra0wyHt2M?>vp=n+D|^K{!u8ko0UK^_)bR&DK_e1?)QxaU#@jaLEZ0S zm1--)gFS8swOrbc4%zW+r%S(hp^5O_tu}kEwuuF&)PNkx8^$u;YEkW$hqpE>Y;#St z_8hl;Kna}m^d>$_yZFOmj@1{&>aMYYc&l>v3UN06;hu!hfzu}qCg06#Z!}8=B=K$B z0PmCg`#M=k&s3KbGGGIP1baQ-Lu~mZ^rXcleGr*6JlTp2qffU^sLDsG8h^)TkmYc9 zLBgq-PO11h{B`KjR|>kH=#z5L4-HM)q#_%A^dvzzUFvJxTr*kf=jfI4_I>u0`m^tN z0+q_Af*e|$*Bb)iL7VbTb?35M!`SWTQxWkA?&wJ^@f5Rtn=!0l+Yo@s%AZ7OY-2Fe zyH22v03MKX^*3d9@2%L`uGWh{3UFTQLN(Y8T_A3?BPQgMLrnl7{m5&?!}Bb7U;CU< ztu7IcisyPi1lN(485^&#|0bb4kP^4>r7)xAPN-#=2qHW`!f$7j(m--cS+424q4T_G zJbyS2SbsYcisSYBk)7A-hDXI@s-6^gUaG>bKl`S7UWcg$enwLAaI&CS%`tebBy*ya9@~o(=U8kK~qVbI(DM70$PDSlq9&xL?decY5&F&bk9d z>9+GTqjBc!9M|QAw&7uw)zupHgJ21i4{wl!6?&LWAGf-+$FneJg}Xa5`$eP73+(40 zRpiPk?jmtp#p4C~EKlieZu`IVlW@w|h3Xn0ULdveiH|yPH~C?GT#xUCT+M8kZbW7f z*X6eXlij%uv# zfE&+)@=>s(Xz)RpTRfML%6^Dr^U!@K-eNASPJ@*m?Ixh$om3Av!`GTcEL_j}in4&M zd3*}d_Wm8-6qRSj{9bEO%o>b|iN8z8c`%hyvD$;I*RIT)6Puc15gXhYBvan62^W$R z4Hj=SZkee$7!_5CF9AWyzqw9%-oJQZ_+xwPpDq~q_9`O})(A+f&bM2+_pf9pjSB<` zzIDv40$(}MrBcT{5?EcUiInHhc%vcqR>#R{I$q2QCY;A5vqYH=j-{yre;kltgKZKV zF3%R|1Q1~FDsF``uU>T359%+{wrPiam9Bd%5WSJ}wM${dW^MDavEb;v|3nPHJ*OC} zdO2(_gKq`V_KM8{k-Mtq`xAzp9;+04XDv#gYC>%(2U@lgF7o*^;JLWU8Svdsms!T{ zB*qrCoqaFM`L1Hd7I*t+abM=hV4EG4e&zPj@tabs;5eFR^~SfbXJtnVb4DdX@}6+O zNb6TZb#Cfa?ro~G#%m0oe-;#>l=yLQd<*)T>sCCwco_6BF!YvDG~CUye*D(EV5Nmu zh)nXOSH{S#v?bdm1_OG@`c^i|suWtu8-AQy(m>ADZIW_NS*u#1hp!hx?&nt8&xcRL zQa@JFZ&?cBa=Vm}Rf6(@GE`usC%w<#? zn`BwxEp_ zKj}-QN3UIFSN=J{Vbx6Enpk+)D zXUy4SIdeLVEkRtzPiXrEkYxI^KhS)q4fD1suaN3~nBnE?z)3^hLB6UF;BVMfCF00&08ig9Tn-4ke^$Br6IJzp^Jj_N=?FsFNt{*1*aF!!EAv70qF2+|6z~yoTIrZ5P(w_nBAAV@LtbU_t zGqi+Ky-9Z{t;f=}T8zW5NL<;{l*iO!?rDo0`c<=anYkaU%8Y`;N#&-YmY33D`=}zb z9nGj~^ezs2AEkXa)1HWjz8VAC&gA}RCCW8k+<;IQPsrx2se0naO z0bRFC9z;Lgon^=7O^I(JG`97@3_??@9r8BYClbLCFo1iehqrCilj5efmC!Bbqhb&g zbG%D3xvKbJaK#iu`-TZ%{`gL7owxSWtKPqY=7ZzMH-wq{kKxxpg2Q^V1_lxD3_l+R ze;$=EaVlMp28?SM*sz33NJPDXH!Z%b3!yGsfnwXa<&hCkL~vZ+#3C=`h#b^6Fk-r{ zm7e$CX4u_qY&KK(0Pwyt?`81utE=C2soYIvH0*HC{I)Bv0XV+L8A>-+;y)U{9$&Xv zEQyIl%4+%|;fuJ3wjOClSp%N}-6AE2Z-6K4;a2HV;#Uij_V@Q)d!cMW_DNajTPPq` zsW`21w=)>9r&?5&e-@B|{*LntsWPYoy|;z3!rB7LFJjWDgJ-r3!8LEw)j!HIQaJYm zA#%FUau3@)koegdgd1*iP~U0Ci`Fwr#<+@LYrR-_-bGMqI%BT+&PK@Gd@)?UIyqf> z4q=s%p@8x}BEGp(QVD z|4tWcllON2rWUatqjk^oTCx|ui+bZ5gCC#+@Wa>TWuO(?c%6-(v*&17fC^@eGlYHpc!f&nUF=bBwI$Q?-(IPAR*CyN`t&g zgY?4kt-xiIk*iL=*28cU1De@x+azNjU1>q_^Y`x4~r3Rc`GRBv(gUbninq|Xo+rw(!CCr4;3O&zU!`{gb1AdL*c_qN@ zeDK*+KTQ&naoUZe;Cdq#yeMghecTX0?ks_~j6gvyLdpu~F&B3PUfysxRg%RCcCd;p z?41R#yTPQBj&-vD?M?C^wr3`==V zvoK1aWBOjrqidoqhfaeoDQ@O=A|4K`Q==c!eMxm~rvnw!FjhAB8B;0!bRT)G`VUQM z=h_My z2Wsct9OEd#)#C9FC@Mf1lC=?wiDTNCrP@sTujVFaZoiobU%}TNbUY6QbxdPf#;H7m z&wo#G=Ex7auN?3q3#pG;`$Dy>{QSb6`V>7w)$X8LQ(OC?i+IR@dv;SN^;TtFMAh2T z{P(?4+t#pyxgNjCo$Dyj<~Sd9UMQI6QN0XcqL5lum8)Op>qx^v;ZuAWB3_s`h=Pc) z^Yt&Bo;U$0W7a8q5syjq$AFsB3a{M!Rdjf6T>=eKGJJ{WY0|4pq8^=D-*{RER9bzd+~dDUrqhkTw^oL1@Y?F=Sac0mV+&l-&mDQRX5+R zxEgP`sHdIN^(MvmvEBwmHMFcAqui9xZ0^V&YLr@4Kh9gAei+C@db;zR+m`5ElKp4dMw`H@$0YsIu>-bL~ci5L|JwLQwAvKc(f$N2e6weKE!IOIJ4 zu$9Q0v}(B_RJwh`t%u%M?;VIb;u;j$lK&pxD}C^wLQ)#O5>`kGFUA9PS$<4PLT9~7 zJf)KJo`|)!3uhSWyJXdhe`nb#-lI7$bB&c+tCDk+H)na?OSgaKTS=F&M@-pX{Tw%D zf6SPn>)S5iKsHTuLF;zV>cb3m;atn@ES?RbHtigGRUH|*I&ab{;*H+w&Vd-+Emo5) z=<-QlUAu3WWK^5ggQ@?M*L(ZSnfTf9Ondkxl>lY>{G5ucRhkq(;ROYh5~q0quPz#0 zbnHVBAzB{+vqW9b?sNkaSDvU#VW1IZ$i`zQOL)Y2 zoJJ|_o}P+j1MkAtu1_(na?;!`u!c={LvK8M^ICt8v5tht*x*P>ymox`hAl53r* zh_1H6;QdHjgV}l@Z@MK7_R@W05Er2 z6Ws(nJDQFS+Ko!O{5{UNR5E2u9|dE`{bamXzD4y|zRPIQ+)Uf}lFkG!szMFdctDW* zk3VAa9k#-`)cAdxv3O|my>1A`BKQTSZv@uTsp`B%UUJ#MFW5Jq5cO_64X-V?x_85! z{-Mr7>?huaQN6o4!*8qSB@J(M<#714u+l><>Wa%8tf@SlK`WAoNv)a~8*YzIojAmD zvlM9nE=_k$o-PL|9};U?7Q43yOAH(;Ky=h$96fKju<$o#oFASQAq|E7Ipxgv_j>N; zHj@W9&yQ+YJuYVPoJ(in*a2Jgg;9n3HUdi34^5ht-8}Ruq?fv*5hCa{@FwcBnA4XJ z)Xe=bRr0@fdjAaL*;ScDq1H1Heb=c4ASY5KdM%Tj3L;Yqqr>df%#A2YAhf!RAI4wO zILne=ki?g@lb9L9z4w$5xzHASiP0S!E2u4SPzKFgA0Y+fgTWoE?Lk(QElp{ct{yhU z*cYk9XKY)oC0OB6GMw{~V=c2bUy(Mc*xKyE!X&4*QI#x4>U4a0eGmM)9eHAoN7XrE zOd|}~Fp#{f0PyBp3YszTA6MQ?J@z(n_`G6LI5?hOv!gV{I&}^{L%SQ}XiUXR4|ADm z96wpJc{hpSI;Dv33wi^jNExr+lVC%;1m`)W0mJ+qJEOZ77z0FFr>=S*0Lz8j-2pHJ z2l2NG;yu{le3)$TVPJK3{V3c%KG=ZqgZi+sj~nvf0CMXF<0HrR7j(gbXE+T#hu9wQ zsea#}@!4c=4UIQ0z}36G+z?2@_CG^GIuEFm4zq2~(sGNii&o*-zRPC{0sDbH!Dyqf zFrKqsuIPZ1QZ6oc)|@gVSzYGt(#%cFQFYf~0jF#sPV(L<$ds~!!ZW!pqdHSezs(nH z7V>EpS~Y)#wEl`hUtNow%er6Po+s4XgPIZUB3frved_^VsE}ip9-o%xoZsdjAWxM% zwUztyRMjO4W{w*{KpbAx?{E9=ZWECN?s*|yf3S1Ciz4jrJ*I(7*c7#Nh|}(XiM6^! zpum2;xVP~+LXtNl=7*Xu!O2v4=UBdbZ8&*GR0dWRu|XE5;d3Gd|O@9%%jJp zR;tm>LmFSEmd*(lq*J%9m-tS|$zFNVrL1Gh@cmx3cIM`+kBeYShLdrq87c zSwI0fkWb%-szacX00q~eK6cC}OVg?8w7({+k#bR-agY{4sxuF=2<$wDbgeI8$Bz*y`8&Lte4z+f$c$g=of^bKE`%o(*4>f+7OdD*B`zjCt_Wr>3^5W?903*h@O- z;eF<*8Q!l>**7LA#PljBmSZRNe8Ka66ZdtQ7bUTSkZ$@xFNs%4=fj-pqahw;u3CTP zz<{zJUn)|UtKk0Hu0ogqkU;46&zE?rA^$iZ*&RGA;ue~q)2r}MP{UKdRD)%aE}wtu za&9lWuUQRZ{8e^18M1M^2wFjYy|~g54S@`Cu}QiZvyT;;OQ{ye!X17ZhzS73K4im zHem!cWD0nRt0?|6tWQ}zfqtDrv1Fh2Bj9%O5t->hWIc8LdV72~4wDXjE}1BJR>`ZC$#FUDXHxT#A1X)mEPANZFF~$)M4lEL zyi1MTHJ9y*rbzdQg+x%KnaJX*gdO|ps{Y~*tuC(3i!PBhRGjYFL?_~agI!j;7C}gLh1O86^vwlKW$o;lch!@eKg=%Tk{)AL2EcvnV^rm19MUc^D zhCSIv>eWtR#=eeqP4kp82Yrt{|9uY6-hw2l9iXJ!YZwS0+C9mV@ldwi%%V=XmlZUq zmZTmLz5OUW`XDh8#XSEyUHJYbN8()8GY6aq(&K&4r>U>*8v^sX9@-W2?Ik_v(${; zEqw*t`zr_Kia@Y9OdU7ue{VAIM1AGDyGtUU{S>cw8Qsg#;uUM!s#nISC=%)m;I<%a zHxOE{_rt6zKKWX(-11F-GCGMItLnZ|#2~(J@mEA)o{=P~`IN*(bdH_ckeB1PZq3!h z38M4&21vRQ3XDb%=G8<`WJp0vTsJND7hhur&H|W@l{t>iLfB1L$ALn=1eUe^Xun?Z zxZ1P`Aih`KpE=f_84(>_Kd^+bOxj&bhfJXIL(ubOg}!b`vX&h}XGrKR{=(7ixP=sFiNRcN7q zSSGR-7cr5oJu%nzhY$B?*%bVsB3|hA{t#*bP+c+aMSu<4#M<}UImY(f9LuOB=XH(6 z{h(8}%_3{qzY9peTSA_-(E+iZ%?~wE2ZsoIl}Sce97m?s`Vt3R4Zf!9@i}Q%vG9GH z$w>C5s*GQs&xf{Q;NVT$Th8dtEYS0vWsC~t5ui9p%fkk8thFzQ<%niUdXm` zs}FowerSazZM!CnN~ov()tR9_*Siwgqn|n&$%#>`ds{$jO?jJ!)B?Vunoth_Y}PY* z;tlg(gjE*TzW4ds_8s6MUep*X<0sf5R(jRB-7kUuI>$YlaO4T?Ole$_bthb)veU<1_*jYi+)zlyO5@nuj<^^3jj3}n)` z#?rBxa842uOfb>#-2%w+DGR_%RU0QK?Wew)&U=XfWeE^-HMW?(6GXJPGON)_vHT|dF%w- z0w^AW+E>Vuf=W2tiE=0wX14n?<4Q}=Q)S#E(|vOwG88!Y$bF!Y`f8^HwjH>QbmE(v+}FY8%z|)H^ywHkFucgOjXB%XMrlx0@3gt$+gQ>w z3U%to4}BEkCoYr2>AhH2JMBn>-KFf2H&?f)H;4J$i@RDohf^o7GszR7tN*Tr9YQKW z1V8O)_^I!l1!oZEuGOV$K;8A}7y#A4KG70`f4)z8*h;`hd)Pc9E=E@1?oWqv?uEm# z5~E|~9Td-tp?uGrxIY@q% zI9XvLSKl1rwvOtrkLDLf&=eemfg66*u@fIX>&qBkn+PI=-X+($=35a>^$ zL0}s2aKtPKL3n<&0Oh#0A;7>Iq(`_q!eRl)xQqZrBNY!*NJuKh{uMJ4?h+GR$uVrP z3-5I_V}c|B4{}bwnltuGo_xO!S@q;Su3cZQl@6g}==IXlwd^ zo&WDoL~LmWqBzHP0+{rV&B1Y4=l{wefRJ4Tb%%P08z$r|NJ!X-POr{OqEa#gj*$W2 z^GA@l6U{gkS*~6KR|!d0|58AJTEeaiY8tRNg=9;HVn)xaEHc74bHpg#2GM2Hcr< zD5Jal&w^I?Wfc%fdIZ`2rPE*J9hl?~7}C$Y{8)x{h1j6 zzLzK;e-OpvSYnP2RUmdxG^&6asypx}NEXc_BF*0sY(I$xC1HUaA2V`HP{Jz6%050+ zhalnqaF;Oj4@E`PIKR*s@WKYG{{b2w9b9tUBVpku5`Z7aG5&v=|C#vD+VV*Hf6=o< zl{zqh}6;Y`CTY})kEC})CAVj6Qr8t!!OeK%dJHI|G?uMJoij`n4$%$koQ8`vU5?e5 z|HHx%F8!x0i{MWE|Ck|wTmJwbexcTp2srS=ut2$N)WJQ`<5zjG)6q01aON!dhW3`D zmtNrttQI$_uulEs!gX4L@9-d|<|F?)W1RhOFWPaUBhXmA1^Vqf`<(^iiF#Lt67Y@3 zL;U;49p>l?*v`6?zW{8%=IElN>=B|8BF4kOJN8JLGb#roIx}_kmpuo>BEY8m_ zDi6Hx^7D%c0e=Z^0pNdr;I9Zjzaa2e2>2`rTq6X0ApRwM2HFL00@nawT7Zl_d7Zel_5Ec`- z$-^(m!Oze2Gi`pSuB@q>slAgmu{$EAnF!Zt{dCh47fUNLekLJ7GGOM%FD6kD5g`#K zD<)!_fPf(XO;IM|cTA2)Z3Kdd0-Peeern?v5dLo)Kfi#;f7=Ab#er%3datk`ztDf% zgv7*tyH`l)=5N=D2#NpJCMqKI+gyYNgnw@n6bD4=*SUxYiT&0lDkAXPoJGZiZ~pgQ zQ8B^aXeA~p@_U=0&~I&`H-9Qp=l?H{2=XHOpX-1&0g?Ya8_*^UNZAkCySbR!*ju`g5p;vLjh7`s yCJA~$%gM=&NeH+ZkXyoMWk)L~CZZl7%CW1Psf!y?iU}Rwyh+B!Ca Date: Mon, 13 Nov 2023 10:24:12 +0100 Subject: [PATCH 07/11] :recycle: api: Remove readSync --- .../pdf/certification-attestation-pdf_test.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/api/tests/certification/course/integration/infrastructure/utils/pdf/certification-attestation-pdf_test.js b/api/tests/certification/course/integration/infrastructure/utils/pdf/certification-attestation-pdf_test.js index a74c02e716f..0e6892e7839 100644 --- a/api/tests/certification/course/integration/infrastructure/utils/pdf/certification-attestation-pdf_test.js +++ b/api/tests/certification/course/integration/infrastructure/utils/pdf/certification-attestation-pdf_test.js @@ -3,9 +3,8 @@ import dayjs from 'dayjs'; import { isSameBinary } from '../../../../../../tooling/binary-comparator.js'; import { getCertificationAttestationsPdfBuffer } from '../../../../../../../src/certification/course/infrastructure/utils/pdf/certification-attestation-pdf.js'; import { CertificationAttestationGenerationError } from '../../../../../../../src/shared/domain/errors.js'; -import fs from 'fs'; import pdfLibUtils from 'pdf-lib/cjs/utils/index.js'; -import { writeFile } from 'fs/promises'; +import { readFile, writeFile } from 'node:fs/promises'; import * as url from 'url'; import { getI18n } from '../../../../../../tooling/i18n/i18n.js'; @@ -17,17 +16,13 @@ describe('Integration | Infrastructure | Utils | Pdf | Certification Attestation nock('https://images.pix.fr') .get('/stickers/macaron_clea.pdf') - // eslint-disable-next-line no-sync - .reply(200, () => fs.readFileSync(`${__dirname}/stickers/macaron_clea.pdf`)) + .reply(200, async () => readFile(`${__dirname}/stickers/macaron_clea.pdf`)) .get('/stickers/macaron_droit_avance.pdf') - // eslint-disable-next-line no-sync - .reply(200, () => fs.readFileSync(`${__dirname}/stickers/macaron_droit_avance.pdf`)) + .reply(200, async () => readFile(`${__dirname}/stickers/macaron_droit_avance.pdf`)) .get('/stickers/macaron_edu_2nd_initie.pdf') - // eslint-disable-next-line no-sync - .reply(200, () => fs.readFileSync(`${__dirname}/stickers/macaron_edu_2nd_initie.pdf`)) + .reply(200, async () => readFile(`${__dirname}/stickers/macaron_edu_2nd_initie.pdf`)) .get('/stickers/macaron_droit_expert.pdf') - // eslint-disable-next-line no-sync - .reply(200, () => fs.readFileSync(`${__dirname}/stickers/macaron_droit_expert.pdf`)); + .reply(200, async () => readFile(`${__dirname}/stickers/macaron_droit_expert.pdf`)); }); it('should generate full attestation (non-regression test)', async function () { From ab718c272d74097fde34cfe988f0d66d7b250b78 Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Tue, 14 Nov 2023 23:05:03 +0100 Subject: [PATCH 08/11] :recycle: api: Replace partnerKey by complementaryCertificationBadgeId --- ...mplementary-certification-course-result.js | 3 +- .../ComplementaryCertificationCourseResult.js | 31 +- ...nScoringWithoutComplementaryReferential.js | 2 - .../models/PartnerCertificationScoring.js | 9 +- ...icationCourseResultForJuryCertification.js | 13 +- ...eResultForJuryCertificationWithExternal.js | 24 +- ...mplementary-certification-course-result.js | 12 +- .../certification-result-repository.js | 9 +- ...-certification-course-result-repository.js | 3 +- ...rtification-scoring-criteria-repository.js | 2 - .../jury-certification-repository.js | 29 +- .../jury-certification-summary-repository.js | 7 +- ...mentary-certification-course-repository.js | 2 +- .../repositories/certificate-repository.js | 12 +- .../certification-course-controller_test.js | 16 +- ...fication-course-results-controller_test.js | 22 +- ...r-get-jury-certification-summaries_test.js | 1 + ...ry-certification-course-repository_test.js | 12 +- .../certification-attestation-route_test.js | 1 + .../course/acceptance/application/sticker.pdf | Bin 0 -> 113216 bytes .../certificate-repository_test.js | 498 +----------------- ...mplementary-certifications-scoring_test.js | 3 +- .../certification-result-repository_test.js | 20 +- ...ification-course-result-repository_test.js | 46 +- .../jury-certification-repository_test.js | 27 +- ...y-certification-summary-repository_test.js | 33 +- ...-result-for-certification-with-external.js | 8 +- ...lementaryCertificationCourseResult_test.js | 15 +- ...coringWithComplementaryReferential_test.js | 1 - ...ltForJuryCertificationWithExternal_test.js | 50 +- ...entary-certification-course-result_test.js | 24 +- 31 files changed, 230 insertions(+), 705 deletions(-) create mode 100644 api/tests/certification/course/acceptance/application/sticker.pdf diff --git a/api/db/database-builder/factory/build-complementary-certification-course-result.js b/api/db/database-builder/factory/build-complementary-certification-course-result.js index 42159e321ca..6f7fcd4db2a 100644 --- a/api/db/database-builder/factory/build-complementary-certification-course-result.js +++ b/api/db/database-builder/factory/build-complementary-certification-course-result.js @@ -11,7 +11,6 @@ const buildComplementaryCertificationCourseResult = function ({ id, complementaryCertificationCourseId, complementaryCertificationBadgeId, - partnerKey, source = ComplementaryCertificationCourseResult.sources.PIX, acquired = true, }) { @@ -24,7 +23,7 @@ const buildComplementaryCertificationCourseResult = function ({ : complementaryCertificationBadgeId; return databaseBuffer.pushInsertable({ tableName: 'complementary-certification-course-results', - values: { id, complementaryCertificationCourseId, partnerKey, complementaryCertificationBadgeId, source, acquired }, + values: { id, complementaryCertificationCourseId, complementaryCertificationBadgeId, source, acquired }, }); }; diff --git a/api/lib/domain/models/ComplementaryCertificationCourseResult.js b/api/lib/domain/models/ComplementaryCertificationCourseResult.js index 73aab6e5822..97e5af3b3d5 100644 --- a/api/lib/domain/models/ComplementaryCertificationCourseResult.js +++ b/api/lib/domain/models/ComplementaryCertificationCourseResult.js @@ -9,51 +9,29 @@ const juryOptions = { }; class ComplementaryCertificationCourseResult { - constructor({ - complementaryCertificationCourseId, - complementaryCertificationBadgeId, - partnerKey, - source, - acquired, - label, - } = {}) { + constructor({ complementaryCertificationCourseId, complementaryCertificationBadgeId, source, acquired, label } = {}) { this.complementaryCertificationCourseId = complementaryCertificationCourseId; this.complementaryCertificationBadgeId = complementaryCertificationBadgeId; - this.partnerKey = partnerKey; this.acquired = acquired; this.source = source; this.label = label; } - static from({ - complementaryCertificationCourseId, - complementaryCertificationBadgeId, - partnerKey, - acquired, - source, - label, - }) { + static from({ complementaryCertificationCourseId, complementaryCertificationBadgeId, acquired, source, label }) { return new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId, complementaryCertificationBadgeId, - partnerKey, acquired, source, label, }); } - static buildFromJuryLevel({ - complementaryCertificationCourseId, - complementaryCertificationBadgeId, - juryLevel, - pixPartnerKey, - }) { + static buildFromJuryLevel({ complementaryCertificationCourseId, complementaryCertificationBadgeId, juryLevel }) { if (juryLevel === 'REJECTED') { return new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId, complementaryCertificationBadgeId, - partnerKey: pixPartnerKey, acquired: false, source: sources.EXTERNAL, }); @@ -61,8 +39,7 @@ class ComplementaryCertificationCourseResult { return new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId, - complementaryCertificationBadgeId, - partnerKey: juryLevel, + complementaryCertificationBadgeId: juryLevel, acquired: true, source: sources.EXTERNAL, }); diff --git a/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js b/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js index f649258fa6d..655ab6d9b75 100644 --- a/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js +++ b/api/lib/domain/models/ComplementaryCertificationScoringWithoutComplementaryReferential.js @@ -4,7 +4,6 @@ class ComplementaryCertificationScoringWithoutComplementaryReferential extends P constructor({ complementaryCertificationCourseId, complementaryCertificationBadgeId, - complementaryCertificationBadgeKey, reproducibilityRate, pixScore, minimumEarnedPix, @@ -13,7 +12,6 @@ class ComplementaryCertificationScoringWithoutComplementaryReferential extends P super({ complementaryCertificationCourseId, complementaryCertificationBadgeId, - partnerKey: complementaryCertificationBadgeKey, }); this.reproducibilityRate = reproducibilityRate; diff --git a/api/lib/domain/models/PartnerCertificationScoring.js b/api/lib/domain/models/PartnerCertificationScoring.js index 1ef760c17a0..4aeb016fafe 100644 --- a/api/lib/domain/models/PartnerCertificationScoring.js +++ b/api/lib/domain/models/PartnerCertificationScoring.js @@ -10,20 +10,13 @@ const SOURCES = { }; class PartnerCertificationScoring { - constructor({ - complementaryCertificationCourseId, - complementaryCertificationBadgeId, - partnerKey, - source = SOURCES.PIX, - } = {}) { + constructor({ complementaryCertificationCourseId, complementaryCertificationBadgeId, source = SOURCES.PIX } = {}) { this.complementaryCertificationCourseId = complementaryCertificationCourseId; this.complementaryCertificationBadgeId = complementaryCertificationBadgeId; - this.partnerKey = partnerKey; this.source = source; const schema = Joi.object({ complementaryCertificationCourseId: Joi.number().integer().required(), complementaryCertificationBadgeId: Joi.number().integer().required(), - partnerKey: Joi.string().allow(null).required(), source: Joi.string() .required() .valid(...Object.values(SOURCES)), diff --git a/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertification.js b/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertification.js index 5593662f904..336a08121b7 100644 --- a/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertification.js +++ b/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertification.js @@ -4,15 +4,20 @@ const complementaryCertificationStatus = { }; class ComplementaryCertificationCourseResultForJuryCertification { - constructor({ id, partnerKey, acquired, label }) { + constructor({ id, complementaryCertificationBadgeId, acquired, label }) { this.id = id; - this.partnerKey = partnerKey; + this.complementaryCertificationBadgeId = complementaryCertificationBadgeId; this.acquired = acquired; this.label = label; } - static from({ id, partnerKey, acquired, label }) { - return new ComplementaryCertificationCourseResultForJuryCertification({ id, partnerKey, acquired, label }); + static from({ id, complementaryCertificationBadgeId, acquired, label }) { + return new ComplementaryCertificationCourseResultForJuryCertification({ + id, + complementaryCertificationBadgeId, + acquired, + label, + }); } get status() { diff --git a/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal.js b/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal.js index bf29b44357f..66dff9883f8 100644 --- a/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal.js +++ b/api/lib/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal.js @@ -9,11 +9,11 @@ const { EXTERNAL, PIX } = sources; class ComplementaryCertificationCourseResultForJuryCertificationWithExternal { constructor({ complementaryCertificationCourseId, - pixPartnerKey, + pixComplementaryCertificationBadgeId, pixLabel, pixAcquired, pixLevel, - externalPartnerKey, + externalComplementaryCertificationBadgeId, externalLabel, externalAcquired, externalLevel, @@ -21,13 +21,13 @@ class ComplementaryCertificationCourseResultForJuryCertificationWithExternal { }) { this.complementaryCertificationCourseId = complementaryCertificationCourseId; this.pixSection = new Section({ - partnerKey: pixPartnerKey, + complementaryCertificationBadgeId: pixComplementaryCertificationBadgeId, label: pixLabel, acquired: pixAcquired, level: pixLevel, }); this.externalSection = new Section({ - partnerKey: externalPartnerKey, + complementaryCertificationBadgeId: externalComplementaryCertificationBadgeId, label: externalLabel, acquired: externalAcquired, level: externalLevel, @@ -36,7 +36,7 @@ class ComplementaryCertificationCourseResultForJuryCertificationWithExternal { this.defaultJuryOptions = Object.values(juryOptions); } - static from(complementaryCertificationCourseResultWithExternal, badgesKeyAndLabel) { + static from(complementaryCertificationCourseResultWithExternal, badgesIdAndLabels) { if (!complementaryCertificationCourseResultWithExternal.length) { return; } @@ -47,16 +47,18 @@ class ComplementaryCertificationCourseResultForJuryCertificationWithExternal { ({ source }) => source === EXTERNAL, ); - const allowedExternalLevels = badgesKeyAndLabel.map(({ key, label }) => ({ label, value: key })); + const allowedExternalLevels = badgesIdAndLabels.map(({ id, label }) => ({ label, value: id })); return new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ complementaryCertificationCourseId: complementaryCertificationCourseResultWithExternal[0].complementaryCertificationCourseId, - pixPartnerKey: pixComplementaryCertificationCourseResult?.partnerKey, + pixComplementaryCertificationBadgeId: + pixComplementaryCertificationCourseResult?.complementaryCertificationBadgeId, pixLabel: pixComplementaryCertificationCourseResult?.label, pixAcquired: pixComplementaryCertificationCourseResult?.acquired, pixLevel: pixComplementaryCertificationCourseResult?.level, - externalPartnerKey: externalComplementaryCertificationCourseResult?.partnerKey, + externalComplementaryCertificationBadgeId: + externalComplementaryCertificationCourseResult?.complementaryCertificationBadgeId, externalLabel: externalComplementaryCertificationCourseResult?.label, externalAcquired: externalComplementaryCertificationCourseResult?.acquired, externalLevel: externalComplementaryCertificationCourseResult?.level, @@ -86,15 +88,15 @@ class ComplementaryCertificationCourseResultForJuryCertificationWithExternal { } class Section { - constructor({ partnerKey, label, acquired, level }) { - this.partnerKey = partnerKey; + constructor({ complementaryCertificationBadgeId, label, acquired, level }) { + this.complementaryCertificationBadgeId = complementaryCertificationBadgeId; this.label = label; this.acquired = acquired ?? false; this.level = level; } get isEvaluated() { - return Boolean(this.partnerKey); + return Boolean(this.complementaryCertificationBadgeId); } } diff --git a/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js b/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js index d004bb51f88..f127d093487 100644 --- a/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js +++ b/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js @@ -17,8 +17,6 @@ const saveJuryComplementaryCertificationCourseResult = async function ({ ); } - const { partnerKey: pixPartnerKey } = pixSourceComplementaryCertificationCourseResult; - if (juryLevel === ComplementaryCertificationCourseResult.juryOptions.UNSET) { await complementaryCertificationCourseResultRepository.removeExternalJuryResult({ complementaryCertificationCourseId, @@ -26,9 +24,11 @@ const saveJuryComplementaryCertificationCourseResult = async function ({ return; } - const allowedJuryLevels = await complementaryCertificationCourseResultRepository.getAllowedJuryLevelByBadgeKey({ - key: pixPartnerKey, - }); + const { complementaryCertificationBadgeId } = pixSourceComplementaryCertificationCourseResult; + const allowedJuryLevels = + await complementaryCertificationCourseResultRepository.getAllowedJuryLevelIdByComplementaryCertificationBadgeId( + complementaryCertificationBadgeId, + ); if (![...allowedJuryLevels, ComplementaryCertificationCourseResult.juryOptions.REJECTED].includes(juryLevel)) { throw new InvalidJuryLevelError(); @@ -36,7 +36,7 @@ const saveJuryComplementaryCertificationCourseResult = async function ({ const externalComplementaryCertificationCourseResult = ComplementaryCertificationCourseResult.buildFromJuryLevel({ juryLevel, - pixPartnerKey, + complementaryCertificationBadgeId, complementaryCertificationCourseId, }); diff --git a/api/lib/infrastructure/repositories/certification-result-repository.js b/api/lib/infrastructure/repositories/certification-result-repository.js index fe3e8bff127..a79ca930bd4 100644 --- a/api/lib/infrastructure/repositories/certification-result-repository.js +++ b/api/lib/infrastructure/repositories/certification-result-repository.js @@ -90,7 +90,6 @@ function _selectComplementaryCertificationCourseResultsBySessionId({ sessionId } 'complementary-certification-course-results.complementaryCertificationCourseId', id: 'complementary-certification-course-results.id', complementaryCertificationBadgeId: 'complementary-certification-course-results.complementaryCertificationBadgeId', - partnerKey: 'complementary-certification-course-results.partnerKey', acquired: 'complementary-certification-course-results.acquired', source: 'complementary-certification-course-results.source', label: 'complementary-certification-badges.label', @@ -100,9 +99,11 @@ function _selectComplementaryCertificationCourseResultsBySessionId({ sessionId } 'complementary-certification-courses.id', 'complementary-certification-course-results.complementaryCertificationCourseId', ) - .join('badges', 'badges.key', 'complementary-certification-course-results.partnerKey') - .join('complementary-certification-badges', 'complementary-certification-badges.badgeId', 'badges.id') - + .join( + 'complementary-certification-badges', + 'complementary-certification-badges.id', + 'complementary-certification-course-results.complementaryCertificationBadgeId', + ) .join( 'complementary-certifications', 'complementary-certifications.id', diff --git a/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js b/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js index 56e2be992a1..6a2a26fde12 100644 --- a/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js +++ b/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js @@ -22,12 +22,11 @@ const getAllowedJuryLevelByBadgeKey = async function ({ key }) { const save = async function ({ complementaryCertificationCourseId, complementaryCertificationBadgeId, - partnerKey, acquired, source, }) { return knex('complementary-certification-course-results') - .insert({ partnerKey, complementaryCertificationBadgeId, acquired, complementaryCertificationCourseId, source }) + .insert({ complementaryCertificationBadgeId, acquired, complementaryCertificationCourseId, source }) .onConflict(['complementaryCertificationCourseId', 'source']) .merge(); }; diff --git a/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js b/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js index 50cf5df9701..ed1b14b6b5f 100644 --- a/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js +++ b/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js @@ -29,7 +29,6 @@ const findByCertificationCourseId = async function ({ certificationCourseId }) { complementaryCertificationCourseId, complementaryCertificationBadgeId, minimumReproducibilityRate, - complementaryCertificationBadgeKey, hasComplementaryReferential, minimumEarnedPix, }) => @@ -37,7 +36,6 @@ const findByCertificationCourseId = async function ({ certificationCourseId }) { complementaryCertificationCourseId, complementaryCertificationBadgeId, minimumReproducibilityRate: Number(minimumReproducibilityRate), - complementaryCertificationBadgeKey, hasComplementaryReferential, minimumEarnedPix, }), diff --git a/api/lib/infrastructure/repositories/jury-certification-repository.js b/api/lib/infrastructure/repositories/jury-certification-repository.js index cc8d9c6d32d..eb0554f8fa2 100644 --- a/api/lib/infrastructure/repositories/jury-certification-repository.js +++ b/api/lib/infrastructure/repositories/jury-certification-repository.js @@ -23,7 +23,10 @@ const get = async function (certificationCourseId) { const complementaryCertificationCourseResultDTOs = await knex('complementary-certification-course-results') .select( - 'complementary-certification-course-results.*', + 'complementary-certification-course-results.complementaryCertificationBadgeId', + 'complementary-certification-course-results.complementaryCertificationCourseId', + 'complementary-certification-course-results.acquired', + 'complementary-certification-course-results.source', 'complementary-certification-courses.id', 'complementary-certification-badges.label', 'complementary-certification-badges.level', @@ -34,8 +37,12 @@ const get = async function (certificationCourseId) { 'complementary-certification-course-results.complementaryCertificationCourseId', 'complementary-certification-courses.id', ) - .leftJoin('badges', 'badges.key', 'complementary-certification-course-results.partnerKey') - .leftJoin('complementary-certification-badges', 'complementary-certification-badges.badgeId', 'badges.id') + .leftJoin( + 'complementary-certification-badges', + 'complementary-certification-badges.id', + 'complementary-certification-course-results.complementaryCertificationBadgeId', + ) + .leftJoin('badges', 'complementary-certification-badges.badgeId', 'badges.id') .leftJoin( 'complementary-certifications', 'complementary-certifications.id', @@ -45,7 +52,7 @@ const get = async function (certificationCourseId) { certificationCourseId: juryCertificationDTO.certificationCourseId, }); - const badgesKeyAndLabel = await _getComplementaryBadgesKeyAndLabel({ certificationCourseId }); + const badgeIdAndLabels = await _getComplementaryBadgeIdAndLabels({ certificationCourseId }); const certificationIssueReportDTOs = await knex('certification-issue-reports') .where({ certificationCourseId }) @@ -56,7 +63,7 @@ const get = async function (certificationCourseId) { certificationIssueReportDTOs, competenceMarkDTOs, complementaryCertificationCourseResultDTOs, - badgesKeyAndLabel, + badgeIdAndLabels, }); }; @@ -110,7 +117,7 @@ async function _toDomainWithComplementaryCertifications({ certificationIssueReportDTOs, competenceMarkDTOs, complementaryCertificationCourseResultDTOs, - badgesKeyAndLabel, + badgeIdAndLabels, }) { const certificationIssueReports = certificationIssueReportDTOs.map( (certificationIssueReport) => new CertificationIssueReport({ ...certificationIssueReport }), @@ -119,7 +126,7 @@ async function _toDomainWithComplementaryCertifications({ const { complementaryCertificationCourseResultWithExternal, commonComplementaryCertificationCourseResult } = _toComplementaryCertificationCourseResultForJuryCertification( complementaryCertificationCourseResultDTOs, - badgesKeyAndLabel, + badgeIdAndLabels, ); return JuryCertification.from({ @@ -133,7 +140,7 @@ async function _toDomainWithComplementaryCertifications({ function _toComplementaryCertificationCourseResultForJuryCertification( complementaryCertificationCourseResults, - badgesKeyAndLabel, + badgeIdAndLabels, ) { const [complementaryCertificationCourseResultWithExternal, commonComplementaryCertificationCourseResult] = _.partition(complementaryCertificationCourseResults, 'hasExternalJury'); @@ -141,7 +148,7 @@ function _toComplementaryCertificationCourseResultForJuryCertification( const complementaryCertificationCourseResultsForJuryCertificationWithExternal = ComplementaryCertificationCourseResultForJuryCertificationWithExternal.from( complementaryCertificationCourseResultWithExternal, - badgesKeyAndLabel, + badgeIdAndLabels, ); if (commonComplementaryCertificationCourseResult.length > 1) { @@ -157,9 +164,9 @@ function _toComplementaryCertificationCourseResultForJuryCertification( }; } -async function _getComplementaryBadgesKeyAndLabel({ certificationCourseId }) { +async function _getComplementaryBadgeIdAndLabels({ certificationCourseId }) { return knex - .select('badges.key', 'complementary-certification-badges.label') + .select('complementary-certification-badges.id', 'complementary-certification-badges.label') .from('badges') .innerJoin('complementary-certification-badges', 'badges.id', 'complementary-certification-badges.badgeId') .where( diff --git a/api/lib/infrastructure/repositories/jury-certification-summary-repository.js b/api/lib/infrastructure/repositories/jury-certification-summary-repository.js index d79f83913ed..7492ec5977c 100644 --- a/api/lib/infrastructure/repositories/jury-certification-summary-repository.js +++ b/api/lib/infrastructure/repositories/jury-certification-summary-repository.js @@ -80,8 +80,11 @@ async function _getByCertificationCourseIds(orderedCertificationCourseIds) { ComplementaryCertificationCourseResult.sources.PIX, ); }) - .leftJoin('badges', 'badges.key', 'complementary-certification-course-results.partnerKey') - .leftJoin('complementary-certification-badges', 'complementary-certification-badges.badgeId', 'badges.id') + .leftJoin( + 'complementary-certification-badges', + 'complementary-certification-badges.id', + 'complementary-certification-course-results.complementaryCertificationBadgeId', + ) .whereIn('certification-courses.id', orderedCertificationCourseIds); return orderedCertificationCourseIds.map((orderedId) => results.find(({ id }) => id === orderedId)); diff --git a/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js b/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js index 0317d525553..905070edc28 100644 --- a/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js +++ b/api/src/certification/complementary-certification/infrastructure/repositories/complementary-certification-course-repository.js @@ -11,7 +11,7 @@ const findByUserId = async function ({ userId }) { `array_agg(json_build_object( 'id', "complementary-certification-course-results".id, 'acquired', "complementary-certification-course-results".acquired, - 'partnerKey', "complementary-certification-course-results"."partnerKey", + 'complementaryCertificationBadgeId', "complementary-certification-course-results"."complementaryCertificationBadgeId", 'source', "complementary-certification-course-results".source))`, ), }) diff --git a/api/src/certification/course/infrastructure/repositories/certificate-repository.js b/api/src/certification/course/infrastructure/repositories/certificate-repository.js index 46162380813..444bc167007 100644 --- a/api/src/certification/course/infrastructure/repositories/certificate-repository.js +++ b/api/src/certification/course/infrastructure/repositories/certificate-repository.js @@ -142,7 +142,7 @@ function _toDomainForCertificationAttestation({ certificationCourseDTO, competen async function _getCertifiedBadges(certificationCourseId) { const complementaryCertificationCourseResults = await knex .select( - 'complementary-certification-course-results.partnerKey', + 'badges.key as partnerKey', 'complementary-certification-course-results.source', 'complementary-certification-course-results.acquired', 'complementary-certification-course-results.complementaryCertificationCourseId', @@ -160,15 +160,19 @@ async function _getCertifiedBadges(certificationCourseId) { 'complementary-certification-courses.id', 'complementary-certification-course-results.complementaryCertificationCourseId', ) - .innerJoin('badges', 'badges.key', 'complementary-certification-course-results.partnerKey') - .innerJoin('complementary-certification-badges', 'complementary-certification-badges.badgeId', 'badges.id') + .innerJoin( + 'complementary-certification-badges', + 'complementary-certification-badges.id', + 'complementary-certification-course-results.complementaryCertificationBadgeId', + ) + .innerJoin('badges', 'badges.id', 'complementary-certification-badges.badgeId') .innerJoin( 'complementary-certifications', 'complementary-certifications.id', 'complementary-certification-badges.complementaryCertificationId', ) .where({ certificationCourseId }) - .orderBy('partnerKey'); + .orderBy('badges.key'); return CertifiedBadge.fromComplementaryCertificationCourseResults(complementaryCertificationCourseResults); } diff --git a/api/tests/acceptance/application/certification-courses/certification-course-controller_test.js b/api/tests/acceptance/application/certification-courses/certification-course-controller_test.js index 83b379b0037..609d38fa75e 100644 --- a/api/tests/acceptance/application/certification-courses/certification-course-controller_test.js +++ b/api/tests/acceptance/application/certification-courses/certification-course-controller_test.js @@ -220,17 +220,19 @@ describe('Acceptance | API | Certification Course', function () { }); databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 777, badgeId: 456, complementaryCertificationId: pixBoxeComplementaryCertificationId, label: 'Pix Boxe 1', }); databaseBuilder.factory.buildComplementaryCertificationBadge({ - id: 789, + id: 778, badgeId: 457, complementaryCertificationId: pixBoxeComplementaryCertificationId, label: 'Pix Boxe 2', }); databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 779, badgeId: 458, complementaryCertificationId: pixBoxeComplementaryCertificationId, label: 'Pix Boxe 3', @@ -239,20 +241,20 @@ describe('Acceptance | API | Certification Course', function () { id: 654, certificationCourseId: 123, complementaryCertificationId: pixBoxeComplementaryCertificationId, - complementaryCertificationBadgeId: 789, + complementaryCertificationBadgeId: 778, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ id: 987, - partnerKey: 'PIX_BOXE_2', acquired: true, complementaryCertificationCourseId: 654, + complementaryCertificationBadgeId: 778, source: ComplementaryCertificationCourseResult.sources.PIX, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ id: 986, - partnerKey: 'PIX_BOXE_2', acquired: false, complementaryCertificationCourseId: 654, + complementaryCertificationBadgeId: 778, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); databaseBuilder.factory.buildAssessment({ id: 159, certificationCourseId: 123 }); @@ -353,15 +355,15 @@ describe('Acceptance | API | Certification Course', function () { 'allowed-external-levels': [ { label: 'Pix Boxe 1', - value: 'PIX_BOXE_1', + value: 777, }, { label: 'Pix Boxe 2', - value: 'PIX_BOXE_2', + value: 778, }, { label: 'Pix Boxe 3', - value: 'PIX_BOXE_3', + value: 779, }, ], 'default-jury-options': ['REJECTED', 'UNSET'], diff --git a/api/tests/acceptance/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js b/api/tests/acceptance/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js index d1c664965a7..e223a9745a4 100644 --- a/api/tests/acceptance/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js +++ b/api/tests/acceptance/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js @@ -13,13 +13,31 @@ describe('Acceptance | API | Certifications', function () { it('should return 201 HTTP status code', async function () { // given const server = await createServer(); + databaseBuilder.factory.buildTargetProfile({ id: 99 }); const badge = databaseBuilder.factory.buildBadge({ key: 'BADGE_KEY', + targetProfileId: 99, + }); + const badge2 = databaseBuilder.factory.buildBadge({ + key: 'BADGE_KEY_2', + targetProfileId: 99, }); databaseBuilder.factory.buildComplementaryCertification({ id: 1, name: 'Pix+ Test', }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 24, + complementaryCertificationId: 1, + badgeId: badge.id, + level: 1, + }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 25, + complementaryCertificationId: 1, + badgeId: badge2.id, + level: 2, + }); databaseBuilder.factory.buildCertificationCourse({ id: 456, }); @@ -27,10 +45,12 @@ describe('Acceptance | API | Certifications', function () { id: 1234, certificationCourseId: 456, complementaryCertificationId: 1, + complementaryCertificationBadgeId: 24, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 1234, + complementaryCertificationBadgeId: 24, partnerKey: badge.key, source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -45,7 +65,7 @@ describe('Acceptance | API | Certifications', function () { payload: { data: { attributes: { - juryLevel: badge.key, + juryLevel: 25, complementaryCertificationCourseId: 1234, }, }, diff --git a/api/tests/acceptance/application/session/session-controller-get-jury-certification-summaries_test.js b/api/tests/acceptance/application/session/session-controller-get-jury-certification-summaries_test.js index a079d7139fe..d28bd488749 100644 --- a/api/tests/acceptance/application/session/session-controller-get-jury-certification-summaries_test.js +++ b/api/tests/acceptance/application/session/session-controller-get-jury-certification-summaries_test.js @@ -74,6 +74,7 @@ describe('Acceptance | Controller | session-controller-get-jury-certification-su }); dbf.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: id, + complementaryCertificationBadgeId, partnerKey: badge.key, acquired: true, }); diff --git a/api/tests/certification/complementary-certification/integration/infrastucture/repositories/complementary-certification-course-repository_test.js b/api/tests/certification/complementary-certification/integration/infrastucture/repositories/complementary-certification-course-repository_test.js index 02d2144a624..a9bbad03091 100644 --- a/api/tests/certification/complementary-certification/integration/infrastucture/repositories/complementary-certification-course-repository_test.js +++ b/api/tests/certification/complementary-certification/integration/infrastucture/repositories/complementary-certification-course-repository_test.js @@ -66,21 +66,21 @@ describe('Integration | Repository | complementary-certification-course-reposito databaseBuilder.factory.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 999, - partnerKey: 'PIX_TEST_1', + complementaryCertificationBadgeId: 123, source: ComplementaryCertificationCourseResult.sources.PIX, }).id; const complementaryCertificationCourseResultId2 = databaseBuilder.factory.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 1000, - partnerKey: 'PIX_TEST_2', + complementaryCertificationBadgeId: 456, source: ComplementaryCertificationCourseResult.sources.PIX, }).id; const complementaryCertificationCourseResultId3 = databaseBuilder.factory.buildComplementaryCertificationCourseResult({ acquired: true, complementaryCertificationCourseId: 1000, - partnerKey: 'PIX_TEST_2', + complementaryCertificationBadgeId: 456, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }).id; @@ -100,7 +100,7 @@ describe('Integration | Repository | complementary-certification-course-reposito { id: complementaryCertificationCourseResultId1, acquired: true, - partnerKey: 'PIX_TEST_1', + complementaryCertificationBadgeId: 123, source: ComplementaryCertificationCourseResult.sources.PIX, }, ], @@ -114,13 +114,13 @@ describe('Integration | Repository | complementary-certification-course-reposito { id: complementaryCertificationCourseResultId3, acquired: true, - partnerKey: 'PIX_TEST_2', + complementaryCertificationBadgeId: 456, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }, { id: complementaryCertificationCourseResultId2, acquired: true, - partnerKey: 'PIX_TEST_2', + complementaryCertificationBadgeId: 456, source: ComplementaryCertificationCourseResult.sources.PIX, }, ], diff --git a/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js b/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js index 2679c2e2f7b..72414d481f5 100644 --- a/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js +++ b/api/tests/certification/course/acceptance/application/certification-attestation-route_test.js @@ -273,6 +273,7 @@ async function _buildDatabaseForV2Certification({ userId, certificationCourseId }); const { id } = databaseBuilder.factory.buildComplementaryCertificationCourse({ certificationCourseId: certificationCourse.id, + complementaryCertificationBadgeId: ccBadge.id, name: 'patisseries au fruits', }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ diff --git a/api/tests/certification/course/acceptance/application/sticker.pdf b/api/tests/certification/course/acceptance/application/sticker.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f7c613f259e6e3c51f55ba7660c0a127895463e2 GIT binary patch literal 113216 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 { - const { id: badgeId } = databaseBuilder.factory.buildBadge({ key: partnerKey }); + const { id: badgeId } = databaseBuilder.factory.buildBadge(); databaseBuilder.factory.buildComplementaryCertificationBadge({ id: complementaryCertificationBadgeId, badgeId, @@ -433,7 +425,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId, complementaryCertificationBadgeId, - partnerKey, acquired, source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -444,7 +435,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun complementaryCertificationBadgeId: 101, complementaryCertificationCourseId: 998, label: 'First badge expert', - partnerKey: 'EXPERT', }); buildComplementaryResult({ @@ -453,7 +443,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun complementaryCertificationBadgeId: 102, complementaryCertificationCourseId: 999, label: 'First badge avance', - partnerKey: 'AVANCE', acquired: false, }); @@ -471,7 +460,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun acquired: true, complementaryCertificationCourseId: 998, complementaryCertificationBadgeId: 101, - partnerKey: 'EXPERT', label: 'First badge expert', source: ComplementaryCertificationCourseResult.sources.PIX, }), @@ -481,7 +469,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun acquired: false, complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 102, - partnerKey: 'AVANCE', label: 'First badge avance', source: ComplementaryCertificationCourseResult.sources.PIX, }), @@ -717,14 +704,12 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 997, complementaryCertificationBadgeId: 789, - partnerKey: 'PARTNER_KEY', acquired: true, source: ComplementaryCertificationCourseResult.sources.PIX, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 997, complementaryCertificationBadgeId: 789, - partnerKey: 'PARTNER_KEY', acquired: true, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -742,7 +727,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun acquired: true, complementaryCertificationCourseId: 997, complementaryCertificationBadgeId: 789, - partnerKey: 'PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'PARTNER_LABEL', }), @@ -776,7 +760,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 998, complementaryCertificationBadgeId: 100, - partnerKey: 'PARTNER_KEY', acquired: true, source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -818,7 +801,6 @@ describe('Integration | Infrastructure | Repository | Certification Result', fun acquired: true, complementaryCertificationCourseId: 998, complementaryCertificationBadgeId: 100, - partnerKey: 'PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.PIX, label: 'PARTNER_LABEL', }), diff --git a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js index ef2f6b15da7..c28425d2517 100644 --- a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js @@ -30,7 +30,6 @@ describe('Integration | Repository | complementary-certification-courses-result- databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 42, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, acquired: true, }); @@ -50,7 +49,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 42, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }), ); @@ -76,7 +74,6 @@ describe('Integration | Repository | complementary-certification-courses-result- databaseBuilder.factory.buildBadge({ key: 'PIX_TEST_1' }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 999, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, acquired: true, }); @@ -153,7 +150,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 60, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -161,15 +157,14 @@ describe('Integration | Repository | complementary-certification-courses-result- await complementaryCertificationCourseResultRepository.save(complementaryCertificationCourseResult); // then - const savedComplementaryCertificationCourseResult = await knex( - 'complementary-certification-course-results', - ).first(); + const savedComplementaryCertificationCourseResult = await knex('complementary-certification-course-results') + .select('id', 'acquired', 'complementaryCertificationBadgeId', 'complementaryCertificationCourseId', 'source') + .first(); expect(_.omit(savedComplementaryCertificationCourseResult, 'id')).to.deep.equal({ acquired: true, complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 60, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }); }); @@ -206,7 +201,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 61, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -216,7 +210,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 61, - partnerKey: 'PIX_TEST_2', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -224,18 +217,19 @@ describe('Integration | Repository | complementary-certification-courses-result- await complementaryCertificationCourseResultRepository.save(complementaryCertificationCourseResult); // then - const results = await knex('complementary-certification-course-results').where({ - acquired: true, - complementaryCertificationCourseId: 999, - source: ComplementaryCertificationCourseResult.sources.EXTERNAL, - }); + const results = await knex('complementary-certification-course-results') + .select('id', 'acquired', 'complementaryCertificationBadgeId', 'complementaryCertificationCourseId', 'source') + .where({ + acquired: true, + complementaryCertificationCourseId: 999, + source: ComplementaryCertificationCourseResult.sources.EXTERNAL, + }); expect(results).to.have.lengthOf(1); expect(_.omit(results[0], 'id')).to.deep.equal({ acquired: true, complementaryCertificationCourseId: 999, complementaryCertificationBadgeId: 61, - partnerKey: 'PIX_TEST_2', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); }); @@ -273,7 +267,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationBadgeId: 301, complementaryCertificationCourseId: 999, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -285,7 +278,13 @@ describe('Integration | Repository | complementary-certification-courses-result- }); // then - const courseResults = await knex('complementary-certification-course-results'); + const courseResults = await knex('complementary-certification-course-results').select( + 'id', + 'acquired', + 'complementaryCertificationBadgeId', + 'complementaryCertificationCourseId', + 'source', + ); expect(courseResults).to.deep.equal([ { @@ -293,7 +292,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationBadgeId: 301, complementaryCertificationCourseId: 999, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }, ]); @@ -338,7 +336,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationBadgeId: 301, complementaryCertificationCourseId: 999, - partnerKey: 'PIX_TEST_1', source: ComplementaryCertificationCourseResult.sources.PIX, }); @@ -346,7 +343,6 @@ describe('Integration | Repository | complementary-certification-courses-result- acquired: true, complementaryCertificationBadgeId: 302, complementaryCertificationCourseId: 999, - partnerKey: 'PIX_EDU_1', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -358,9 +354,11 @@ describe('Integration | Repository | complementary-certification-courses-result- }); // then - const courseResult = await knex('complementary-certification-course-results').where({ - complementaryCertificationCourseId: 999, - }); + const courseResult = await knex('complementary-certification-course-results') + .select('id', 'acquired', 'complementaryCertificationBadgeId', 'complementaryCertificationCourseId', 'source') + .where({ + complementaryCertificationCourseId: 999, + }); expect(courseResult).to.have.lengthOf(1); expect(courseResult[0]).to.deep.equal(pixResult); diff --git a/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js b/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js index f0c40b5c4de..ea2e0e0b9b4 100644 --- a/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/jury-certification-repository_test.js @@ -72,7 +72,6 @@ describe('Integration | Infrastructure | Repository | Jury Certification', funct complementaryCertificationId: 23, certificationCourseId: 1, complementaryCertificationBadgeId: 3453, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_WITHOUT_EXTERNAL_JURY', }); databaseBuilder.factory.buildUser({ id: 22, firstName: 'Jury' }); @@ -138,8 +137,8 @@ describe('Integration | Infrastructure | Repository | Jury Certification', funct version: 2, commonComplementaryCertificationCourseResult: { acquired: true, + complementaryCertificationBadgeId: 3453, id: 456, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_WITHOUT_EXTERNAL_JURY', label: 'Badge for complementary certification without external jury', }, complementaryCertificationCourseResultWithExternal: undefined, @@ -206,15 +205,13 @@ describe('Integration | Infrastructure | Repository | Jury Certification', funct complementaryCertificationCourseId: 456, complementaryCertificationId: 24, certificationCourseId: 1, - complementaryCertificationBadgeId: 3454, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_1', + complementaryCertificationBadgeId: 3453, }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 456, complementaryCertificationId: 24, certificationCourseId: 1, - complementaryCertificationBadgeId: 3455, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_2', + complementaryCertificationBadgeId: 3454, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -232,28 +229,28 @@ describe('Integration | Infrastructure | Repository | Jury Certification', funct const expectedJuryCertification = { complementaryCertificationCourseId: 456, externalSection: { + complementaryCertificationBadgeId: 3454, acquired: true, label: 'Badge for complementary certification with external jury level 2', level: 2, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_2', }, pixSection: { + complementaryCertificationBadgeId: 3453, acquired: true, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_1', label: 'Badge for complementary certification with external jury level 1', level: 1, }, allowedExternalLevels: [ { - value: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_1', + value: 3453, label: 'Badge for complementary certification with external jury level 1', }, { - value: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_2', + value: 3454, label: 'Badge for complementary certification with external jury level 2', }, { - value: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_3', + value: 3455, label: 'Badge for complementary certification with external jury level 3', }, ], @@ -336,14 +333,12 @@ describe('Integration | Infrastructure | Repository | Jury Certification', funct complementaryCertificationId: 24, certificationCourseId: 1, complementaryCertificationBadgeId: 3454, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_1', }); databaseBuilder.factory.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 456, complementaryCertificationId: 24, certificationCourseId: 1, complementaryCertificationBadgeId: 3455, - partnerKey: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_2', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }); @@ -363,15 +358,15 @@ describe('Integration | Infrastructure | Repository | Jury Certification', funct // then expect(complementaryCertificationCourseResultWithExternal.allowedExternalLevels).to.deep.equals([ { - value: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_1', + value: 3453, label: 'Badge for complementary certification with external jury level 1', }, { - value: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_2', + value: 3454, label: 'Badge for complementary certification with external jury level 2', }, { - value: 'BADGE_FOR_COMPLEMENTARY_CERTIFICATION_1_WITH_EXTERNAL_JURY_LEVEL_3', + value: 3455, label: 'Badge for complementary certification with external jury level 3', }, ]); diff --git a/api/tests/integration/infrastructure/repositories/jury-certification-summary-repository_test.js b/api/tests/integration/infrastructure/repositories/jury-certification-summary-repository_test.js index 44fcdc9c770..2616cfd344f 100644 --- a/api/tests/integration/infrastructure/repositories/jury-certification-summary-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/jury-certification-summary-repository_test.js @@ -254,16 +254,23 @@ describe('Integration | Repository | JuryCertificationSummary', function () { const sessionId = dbf.buildSession().id; const certificationCourseId = dbf.buildCertificationCourse({ sessionId }).id; const badgeId = dbf.buildBadge({ key: 'PARTNER_KEY' }).id; - dbf.buildComplementaryCertificationCourse({ id: 998, certificationCourseId }); - dbf.buildComplementaryCertificationCourseResult({ - complementaryCertificationCourseId: 998, - partnerKey: 'PARTNER_KEY', - acquired: true, - }); + databaseBuilder.factory.buildComplementaryCertification({ id: 101 }); databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 11, label: 'PARTNER_LABEL', badgeId, - complementaryCertificationId: databaseBuilder.factory.buildComplementaryCertification().id, + complementaryCertificationId: 101, + }); + dbf.buildComplementaryCertificationCourse({ + id: 998, + complementaryCertificationId: 101, + certificationCourseId, + complementaryCertificationBadgeId: 11, + }); + dbf.buildComplementaryCertificationCourseResult({ + complementaryCertificationCourseId: 998, + complementaryCertificationBadgeId: 11, + acquired: true, }); await databaseBuilder.commit(); @@ -298,7 +305,6 @@ describe('Integration | Repository | JuryCertificationSummary', function () { it('should return JuryCertificationSummary based on their latest assessment result', async function () { // given const page = { size: 2, number: 1 }; - const partnerKey = 'partnerKey'; const label = 'label'; const dbf = databaseBuilder.factory; const sessionId = dbf.buildSession().id; @@ -341,18 +347,15 @@ describe('Integration | Repository | JuryCertificationSummary', function () { description: 'second certification issue report', hasBeenAutomaticallyResolved: false, }); - const badgeId = dbf.buildBadge({ key: partnerKey }).id; + const badgeId = dbf.buildBadge({ key: 'PARTNER_KEY' }).id; + dbf.buildComplementaryCertification({ id: 1 }); + dbf.buildComplementaryCertificationBadge({ id: 11, label, badgeId, complementaryCertificationId: 1 }); dbf.buildComplementaryCertificationCourse({ id: 998, certificationCourseId: manyAsrCertification.id }); dbf.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 998, - partnerKey, + complementaryCertificationBadgeId: 11, acquired: true, }); - databaseBuilder.factory.buildComplementaryCertificationBadge({ - label, - badgeId, - complementaryCertificationId: databaseBuilder.factory.buildComplementaryCertification().id, - }); await databaseBuilder.commit(); diff --git a/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result-for-certification-with-external.js b/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result-for-certification-with-external.js index ee0d147cf85..2fe6f18de22 100644 --- a/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result-for-certification-with-external.js +++ b/api/tests/tooling/domain-builder/factory/build-complementary-certification-course-result-for-certification-with-external.js @@ -2,11 +2,11 @@ import { ComplementaryCertificationCourseResultForJuryCertificationWithExternal const buildComplementaryCertificationCourseResultForJuryCertificationWithExternal = function ({ complementaryCertificationCourseId = 456, - pixPartnerKey = 'PIX_PARTNER_KEY', + pixComplementaryCertificationBadgeId = 12, pixLabel = 'Pix+ Édu 1er degré Avancé', pixAcquired = true, pixLevel = 2, - externalPartnerKey = 'PIX_EXTERNAL_PARTNER_KEY', + externalComplementaryCertificationBadgeId = 13, externalLabel = 'Pix+ Édu 1er degré Expert', externalAcquired = true, externalLevel = 1, @@ -14,11 +14,11 @@ const buildComplementaryCertificationCourseResultForJuryCertificationWithExterna } = {}) { return new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ complementaryCertificationCourseId, - pixPartnerKey, + pixComplementaryCertificationBadgeId, pixLabel, pixAcquired, pixLevel, - externalPartnerKey, + externalComplementaryCertificationBadgeId, externalLabel, externalAcquired, externalLevel, diff --git a/api/tests/unit/domain/models/ComplementaryCertificationCourseResult_test.js b/api/tests/unit/domain/models/ComplementaryCertificationCourseResult_test.js index af7a289c246..72185719d4e 100644 --- a/api/tests/unit/domain/models/ComplementaryCertificationCourseResult_test.js +++ b/api/tests/unit/domain/models/ComplementaryCertificationCourseResult_test.js @@ -7,7 +7,6 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResult', func // given const complementaryCertificationCourseResult = new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 'complementaryCertificationCourseId', - partnerKey: 'partnerKey', acquired: true, source: 'source', }); @@ -23,7 +22,6 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResult', func // given const complementaryCertificationCourseResult = new ComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 'complementaryCertificationCourseId', - partnerKey: 'partnerKey', acquired: false, source: 'source', }); @@ -50,14 +48,12 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResult', func const result = ComplementaryCertificationCourseResult.buildFromJuryLevel({ juryLevel: complementaryCertificationCourseResult.partnerKey, complementaryCertificationCourseId: 12, - pixPartnerKey: 'PARTNER_KEY', }); // then expect(result).to.deepEqualInstance( new ComplementaryCertificationCourseResult({ acquired: true, - partnerKey: 'PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, complementaryCertificationCourseId: 12, }), @@ -68,25 +64,16 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResult', func describe('when the jury level is "REJECTED"', function () { it('should return an acquired ComplementaryCertificationCourseResult with an external source', function () { // given - const complementaryCertificationCourseResult = domainBuilder.buildComplementaryCertificationCourseResult({ - partnerKey: 'REJECTED', - source: ComplementaryCertificationCourseResult.sources.PIX, - acquired: true, - complementaryCertificationCourseId: 12, - }); - // when const result = ComplementaryCertificationCourseResult.buildFromJuryLevel({ - juryLevel: complementaryCertificationCourseResult.partnerKey, + juryLevel: 'REJECTED', complementaryCertificationCourseId: 12, - pixPartnerKey: 'PARTNER_KEY', }); // then expect(result).to.deepEqualInstance( new ComplementaryCertificationCourseResult({ acquired: false, - partnerKey: 'PARTNER_KEY', source: ComplementaryCertificationCourseResult.sources.EXTERNAL, complementaryCertificationCourseId: 12, }), diff --git a/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js b/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js index 02ec77969db..0e0df93d1cc 100644 --- a/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js +++ b/api/tests/unit/domain/models/ComplementaryCertificationScoringWithComplementaryReferential_test.js @@ -23,7 +23,6 @@ describe('Unit | Domain | Models | ComplementaryCertificationScoringWithCompleme reproducibilityRate: 71, hasAcquiredPixCertification: false, minimumReproducibilityRate: undefined, - partnerKey: 'BADGE', source: 'PIX', }), ); diff --git a/api/tests/unit/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal_test.js b/api/tests/unit/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal_test.js index 1d1f8e09ff0..9c4ffc7093b 100644 --- a/api/tests/unit/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal_test.js +++ b/api/tests/unit/domain/read-models/ComplementaryCertificationCourseResultForJuryCertificationWithExternal_test.js @@ -9,9 +9,9 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = domainBuilder.buildComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY', + pixComplementaryCertificationBadgeId: 99, pixAcquired: true, - externalPartnerKey: null, + externalComplementaryCertificationBadgeId: null, }); // when @@ -26,7 +26,7 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = domainBuilder.buildComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY', + pixComplementaryCertificationBadgeId: 99, pixAcquired: false, }); @@ -42,9 +42,9 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = domainBuilder.buildComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY_1', + pixComplementaryCertificationBadgeId: 99, pixAcquired: true, - externalPartnerKey: 'KEY_2', + externalComplementaryCertificationBadgeId: 99, externalAcquired: false, }); @@ -59,11 +59,11 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = domainBuilder.buildComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'BADGE_KEY_1', + pixComplementaryCertificationBadgeId: 98, pixLabel: 'Badge Key 1', pixAcquired: true, pixLevel: 4, - externalPartnerKey: 'BADGE_KEY_2', + externalComplementaryCertificationBadgeId: 99, externalLabel: 'Badge Key 2', externalAcquired: true, externalLevel: 2, @@ -79,30 +79,30 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury }); describe('#from', function () { - it('should return a PixEduComplementaryCertificationCourseResultForJuryCertification', function () { + it('should return a ComplementaryCertificationCourseResultForJuryCertificationWithExternal', function () { // given const complementaryCertificationCourseResultWithExternal = [ domainBuilder.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 1234, - partnerKey: 'KEY_1', + complementaryCertificationBadgeId: 456, acquired: true, source: ComplementaryCertificationCourseResult.sources.PIX, }), domainBuilder.buildComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 1234, - partnerKey: 'KEY_2', + complementaryCertificationBadgeId: 457, acquired: false, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, }), ]; - const badgesKeyAndLabel = [ - { key: 'KEY_1', label: 'Key 1' }, - { key: 'KEY_2', label: 'Key 2' }, + const badgesIdAndLabels = [ + { id: 456, label: 'Key 1' }, + { id: 457, label: 'Key 2' }, ]; // when const result = ComplementaryCertificationCourseResultForJuryCertificationWithExternal.from( complementaryCertificationCourseResultWithExternal, - badgesKeyAndLabel, + badgesIdAndLabels, ); // then @@ -110,12 +110,12 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ pixAcquired: true, externalAcquired: false, - pixPartnerKey: 'KEY_1', - externalPartnerKey: 'KEY_2', + pixComplementaryCertificationBadgeId: 456, + externalComplementaryCertificationBadgeId: 457, complementaryCertificationCourseId: 1234, allowedExternalLevels: [ - { value: 'KEY_1', label: 'Key 1' }, - { value: 'KEY_2', label: 'Key 2' }, + { value: 456, label: 'Key 1' }, + { value: 457, label: 'Key 2' }, ], defaultJuryOptions: ['REJECTED', 'UNSET'], }), @@ -143,7 +143,7 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY', + pixComplementaryCertificationBadgeId: 98, pixLabel: 'Key label', pixAcquired: true, }); @@ -163,7 +163,7 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY', + pixComplementaryCertificationBadgeId: 99, pixAcquired: false, }); @@ -180,7 +180,7 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY', + pixComplementaryCertificationBadgeId: 99, pixAcquired: true, }); @@ -198,9 +198,9 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY_1', + pixComplementaryCertificationBadgeId: 98, pixAcquired: true, - externalPartnerKey: 'KEY_2', + externalComplementaryCertificationBadgeId: 99, externalLabel: 'Key 2 label', externalAcquired: true, }); @@ -218,9 +218,9 @@ describe('Unit | Domain | Models | ComplementaryCertificationCourseResultForJury // given const complementaryCertificationCourseResultForJuryCertificationWithExternal = new ComplementaryCertificationCourseResultForJuryCertificationWithExternal({ - pixPartnerKey: 'KEY_1', + pixComplementaryCertificationBadgeId: 98, pixAcquired: true, - externalPartnerKey: 'KEY_2', + externalComplementaryCertificationBadgeId: 98, externalAcquired: false, }); diff --git a/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js b/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js index 1683ddffe0c..72310eddaad 100644 --- a/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js +++ b/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js @@ -11,7 +11,7 @@ describe('Unit | UseCase | save-jury-complementary-certification-course-results' complementaryCertificationCourseResultRepository = { save: sinon.stub(), getPixSourceResultByComplementaryCertificationCourseId: sinon.stub(), - getAllowedJuryLevelByBadgeKey: sinon.stub(), + getAllowedJuryLevelIdsByComplementaryCertificationBadgeId: sinon.stub(), removeExternalJuryResult: sinon.stub(), }; }); @@ -46,20 +46,19 @@ describe('Unit | UseCase | save-jury-complementary-certification-course-results' .withArgs({ complementaryCertificationCourseId: 1234 }) .resolves( domainBuilder.buildComplementaryCertificationCourseResult({ - partnerKey: 'KEY_1', + complementaryCertificationBadgeId: 99, complementaryCertificationCourseId: 1234, source: ComplementaryCertificationCourseResult.sources.PIX, }), ); - - complementaryCertificationCourseResultRepository.getAllowedJuryLevelByBadgeKey - .withArgs({ key: 'KEY_1' }) - .resolves(['KEY_1', 'KEY_2']); + complementaryCertificationCourseResultRepository.getAllowedJuryLevelIdsByComplementaryCertificationBadgeId + .withArgs(99) + .resolves([98, 99, 100]); // when const error = await catchErr(saveJuryComplementaryCertificationCourseResult)({ complementaryCertificationCourseId: 1234, - juryLevel: 'KEY_3', + juryLevel: 101, complementaryCertificationCourseResultRepository, }); @@ -102,20 +101,20 @@ describe('Unit | UseCase | save-jury-complementary-certification-course-results' .withArgs({ complementaryCertificationCourseId: 1234 }) .resolves( domainBuilder.buildComplementaryCertificationCourseResult({ - partnerKey: 'KEY_1', + complementaryCertificationBadgeId: 99, complementaryCertificationCourseId: 1234, source: ComplementaryCertificationCourseResult.sources.PIX, }), ); - complementaryCertificationCourseResultRepository.getAllowedJuryLevelByBadgeKey - .withArgs({ key: 'KEY_1' }) - .resolves(['KEY_1', 'KEY_2']); + complementaryCertificationCourseResultRepository.getAllowedJuryLevelIdsByComplementaryCertificationBadgeId + .withArgs(99) + .resolves([98, 99, 100]); // when await saveJuryComplementaryCertificationCourseResult({ complementaryCertificationCourseId: 1234, - juryLevel: 'KEY_2', + juryLevel: 99, complementaryCertificationCourseResultRepository, }); @@ -123,6 +122,7 @@ describe('Unit | UseCase | save-jury-complementary-certification-course-results' expect(complementaryCertificationCourseResultRepository.save).to.have.been.calledWithExactly( new ComplementaryCertificationCourseResult({ partnerKey: 'KEY_2', + complementaryCertificationBadgeId: 99, source: ComplementaryCertificationCourseResult.sources.EXTERNAL, acquired: true, complementaryCertificationCourseId: 1234, From b6728f7d34ba20e425702278ed68485a7f7fff35 Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Fri, 17 Nov 2023 01:08:51 +0100 Subject: [PATCH 09/11] :recycle: api: JuryLevel is now either an id or a string --- .../complementary-certification-course-results/index.js | 2 +- ...plementary-certification-course-results-controller_test.js | 4 ++-- ...ave-jury-complementary-certification-course-result_test.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/lib/application/complementary-certification-course-results/index.js b/api/lib/application/complementary-certification-course-results/index.js index 90a2a3d73b7..9cc4ebfaf26 100644 --- a/api/lib/application/complementary-certification-course-results/index.js +++ b/api/lib/application/complementary-certification-course-results/index.js @@ -13,7 +13,7 @@ const register = async function (server) { payload: Joi.object({ data: { attributes: { - juryLevel: Joi.string().required(), + juryLevel: Joi.required(), complementaryCertificationCourseId: identifiersType.complementaryCertificationCourseId, }, }, diff --git a/api/tests/integration/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js b/api/tests/integration/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js index 98f94f217c5..8c4b887b298 100644 --- a/api/tests/integration/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js +++ b/api/tests/integration/application/complementary-certification-course-results/complementary-certification-course-results-controller_test.js @@ -29,7 +29,7 @@ describe('Integration | Application | complementary-certification-course-results const payload = { data: { attributes: { - juryLevel: 'JURY_LEVEL', + juryLevel: 1, complementaryCertificationCourseId: 123456, }, }, @@ -56,7 +56,7 @@ describe('Integration | Application | complementary-certification-course-results const payload = { data: { attributes: { - juryLevel: 'PIX_EDU_FORMATION_INITIALE_2ND_DEGRE_INITIE', + juryLevel: 1, complementaryCertificationCourseId: 123456, }, }, diff --git a/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js b/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js index 72310eddaad..d8e9545f1b0 100644 --- a/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js +++ b/api/tests/unit/domain/usecases/save-jury-complementary-certification-course-result_test.js @@ -26,7 +26,7 @@ describe('Unit | UseCase | save-jury-complementary-certification-course-results' // when const error = await catchErr(saveJuryComplementaryCertificationCourseResult)({ complementaryCertificationCourseId: 12345, - juryLevel: 'JURY_LEVEL', + juryLevel: 1, complementaryCertificationCourseResultRepository, }); From d2e14ab83f93dcf731ca265bd1cd39c05fb278c4 Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Fri, 17 Nov 2023 01:09:34 +0100 Subject: [PATCH 10/11] :recycle: api: Get jury level by complementary certification badge id --- ...mplementary-certification-course-result.js | 2 +- ...-certification-course-result-repository.js | 21 +++-- ...rtification-scoring-criteria-repository.js | 2 + ...ification-course-result-repository_test.js | 88 ++++++++++++++++--- 4 files changed, 93 insertions(+), 20 deletions(-) diff --git a/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js b/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js index f127d093487..4b67e781584 100644 --- a/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js +++ b/api/lib/domain/usecases/save-jury-complementary-certification-course-result.js @@ -26,7 +26,7 @@ const saveJuryComplementaryCertificationCourseResult = async function ({ const { complementaryCertificationBadgeId } = pixSourceComplementaryCertificationCourseResult; const allowedJuryLevels = - await complementaryCertificationCourseResultRepository.getAllowedJuryLevelIdByComplementaryCertificationBadgeId( + await complementaryCertificationCourseResultRepository.getAllowedJuryLevelIdsByComplementaryCertificationBadgeId( complementaryCertificationBadgeId, ); diff --git a/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js b/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js index 6a2a26fde12..bcf319677a9 100644 --- a/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js +++ b/api/lib/infrastructure/repositories/complementary-certification-course-result-repository.js @@ -13,10 +13,21 @@ const getPixSourceResultByComplementaryCertificationCourseId = async function ({ return ComplementaryCertificationCourseResult.from(result); }; -const getAllowedJuryLevelByBadgeKey = async function ({ key }) { - return knex('badges') - .pluck('key') - .where('targetProfileId', '=', knex('badges').select('targetProfileId').where({ key })); +const getAllowedJuryLevelIdsByComplementaryCertificationBadgeId = async function (complementaryCertificationBadgeId) { + return knex + .pluck('complementary-certification-badges.id') + .from('badges') + .innerJoin('complementary-certification-badges', 'badges.id', 'complementary-certification-badges.badgeId') + .where( + 'targetProfileId', + '=', + knex('badges') + .select('targetProfileId') + .innerJoin('complementary-certification-badges', 'badges.id', 'complementary-certification-badges.badgeId') + .where({ 'complementary-certification-badges.id': complementaryCertificationBadgeId }) + .first(), + ) + .orderBy('complementary-certification-badges.level', 'asc'); }; const save = async function ({ @@ -39,7 +50,7 @@ const removeExternalJuryResult = async function ({ complementaryCertificationCou export { getPixSourceResultByComplementaryCertificationCourseId, - getAllowedJuryLevelByBadgeKey, + getAllowedJuryLevelIdsByComplementaryCertificationBadgeId, save, removeExternalJuryResult, }; diff --git a/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js b/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js index ed1b14b6b5f..ccb210747f7 100644 --- a/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js +++ b/api/lib/infrastructure/repositories/complementary-certification-scoring-criteria-repository.js @@ -28,6 +28,7 @@ const findByCertificationCourseId = async function ({ certificationCourseId }) { ({ complementaryCertificationCourseId, complementaryCertificationBadgeId, + complementaryCertificationBadgeKey, minimumReproducibilityRate, hasComplementaryReferential, minimumEarnedPix, @@ -35,6 +36,7 @@ const findByCertificationCourseId = async function ({ certificationCourseId }) { new ComplementaryCertificationScoringCriteria({ complementaryCertificationCourseId, complementaryCertificationBadgeId, + complementaryCertificationBadgeKey, minimumReproducibilityRate: Number(minimumReproducibilityRate), hasComplementaryReferential, minimumEarnedPix, diff --git a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js index c28425d2517..552a5f83cfc 100644 --- a/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js +++ b/api/tests/integration/infrastructure/repositories/complementary-certification-course-result-repository_test.js @@ -93,25 +93,85 @@ describe('Integration | Repository | complementary-certification-courses-result- ); }); - describe('#getAllowedJuryLevelByBadgeKey', function () { - it('should return the allowed jury level for a partner key', async function () { - // given - databaseBuilder.factory.buildTargetProfile({ id: 123 }); - databaseBuilder.factory.buildBadge({ id: 1212, key: 'KEY_1', targetProfileId: 123 }); + describe('#getAllowedJuryLevelIdsByComplementaryCertificationBadgeId', function () { + context('when there is one target profile for a complementary certification', function () { + it('should return the allowed jury level for that complementary certification', async function () { + // given + databaseBuilder.factory.buildTargetProfile({ id: 123 }); + databaseBuilder.factory.buildComplementaryCertification({ id: 1 }); + databaseBuilder.factory.buildBadge({ + id: 1212, + targetProfileId: 123, + }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 97, + badgeId: 1212, + complementaryCertificationId: 1, + }); + + databaseBuilder.factory.buildTargetProfile({ id: 456 }); + databaseBuilder.factory.buildComplementaryCertification({ id: 2 }); + databaseBuilder.factory.buildBadge({ id: 1213, targetProfileId: 456 }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 98, + badgeId: 1213, + complementaryCertificationId: 2, + }); - databaseBuilder.factory.buildTargetProfile({ id: 456 }); - databaseBuilder.factory.buildBadge({ id: 1213, key: 'KEY_2', targetProfileId: 456 }); - databaseBuilder.factory.buildBadge({ id: 1214, key: 'KEY_3', targetProfileId: 456 }); + await databaseBuilder.commit(); - await databaseBuilder.commit(); + // when + const allowedBadgeKeys = + await complementaryCertificationCourseResultRepository.getAllowedJuryLevelIdsByComplementaryCertificationBadgeId( + 97, + ); - // when - const allowedBadgeKeys = await complementaryCertificationCourseResultRepository.getAllowedJuryLevelByBadgeKey({ - key: 'KEY_2', + // then + expect(allowedBadgeKeys).to.deep.equal([97]); }); + }); - // then - expect(allowedBadgeKeys).to.deep.equal(['KEY_2', 'KEY_3']); + context('when there are two target profiles for a complementary certification', function () { + it('should return the allowed jury level for that target profile', async function () { + // given + databaseBuilder.factory.buildTargetProfile({ id: 123 }); + databaseBuilder.factory.buildComplementaryCertification({ id: 1 }); + databaseBuilder.factory.buildBadge({ + id: 1212, + key: 'KEY_1', + targetProfileId: 123, + }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 97, + badgeId: 1212, + complementaryCertificationId: 1, + }); + + databaseBuilder.factory.buildTargetProfile({ id: 456 }); + databaseBuilder.factory.buildBadge({ id: 1213, key: 'KEY_2', targetProfileId: 456 }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 98, + badgeId: 1213, + complementaryCertificationId: 1, + }); + databaseBuilder.factory.buildBadge({ id: 1214, key: 'KEY_3', targetProfileId: 456 }); + databaseBuilder.factory.buildComplementaryCertificationBadge({ + id: 99, + badgeId: 1214, + complementaryCertificationId: 1, + }); + + await databaseBuilder.commit(); + + // when + const allowedBadgeKeys = + await complementaryCertificationCourseResultRepository.getAllowedJuryLevelIdsByComplementaryCertificationBadgeId( + 98, + ); + + // then + expect(allowedBadgeKeys).to.deep.equal([98, 99]); + }); }); }); From caafd8957c9917f43ba70f4664087199d939df44 Mon Sep 17 00:00:00 2001 From: Antoine Ceol Date: Fri, 24 Nov 2023 13:37:44 +0100 Subject: [PATCH 11/11] :sparkles: api: Limit juryLevel to id or specific states --- .../index.js | 8 +- api/lib/domain/types/identifiers-type.js | 1 + .../index_test.js | 81 ++++++++++++++++++- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/api/lib/application/complementary-certification-course-results/index.js b/api/lib/application/complementary-certification-course-results/index.js index 9cc4ebfaf26..f039539f582 100644 --- a/api/lib/application/complementary-certification-course-results/index.js +++ b/api/lib/application/complementary-certification-course-results/index.js @@ -2,6 +2,7 @@ import Joi from 'joi'; import { identifiersType } from '../../domain/types/identifiers-type.js'; import { securityPreHandlers } from '../security-pre-handlers.js'; import { complementaryCertificationCourseResultsController } from './complementary-certification-course-results-controller.js'; +import { juryOptions } from '../../domain/models/ComplementaryCertificationCourseResult.js'; const register = async function (server) { server.route([ @@ -13,7 +14,12 @@ const register = async function (server) { payload: Joi.object({ data: { attributes: { - juryLevel: Joi.required(), + juryLevel: Joi.alternatives() + .try( + identifiersType.complementaryCertificationBadgeId, + Joi.string().valid(juryOptions.REJECTED).valid(juryOptions.UNSET), + ) + .required(), complementaryCertificationCourseId: identifiersType.complementaryCertificationCourseId, }, }, diff --git a/api/lib/domain/types/identifiers-type.js b/api/lib/domain/types/identifiers-type.js index fecdb86ab98..c36b2e8f766 100644 --- a/api/lib/domain/types/identifiers-type.js +++ b/api/lib/domain/types/identifiers-type.js @@ -39,6 +39,7 @@ const typesPositiveInteger32bits = [ 'certificationIssueReportId', 'complementaryCertificationId', 'complementaryCertificationCourseId', + 'complementaryCertificationBadgeId', 'certificationCenterInvitationId', 'membershipId', 'organizationId', diff --git a/api/tests/unit/application/complementary-certification-course-results/index_test.js b/api/tests/unit/application/complementary-certification-course-results/index_test.js index b27b0add460..e94dd2291ac 100644 --- a/api/tests/unit/application/complementary-certification-course-results/index_test.js +++ b/api/tests/unit/application/complementary-certification-course-results/index_test.js @@ -1,13 +1,15 @@ import { expect, HttpTestServer, sinon } from '../../../test-helper.js'; import { securityPreHandlers } from '../../../../lib/application/security-pre-handlers.js'; import * as moduleUnderTest from '../../../../lib/application/complementary-certification-course-results/index.js'; +import { juryOptions } from '../../../../lib/domain/models/ComplementaryCertificationCourseResult.js'; +import { complementaryCertificationCourseResultsController } from '../../../../lib/application/complementary-certification-course-results/complementary-certification-course-results-controller.js'; describe('Unit | Application | Complementary Certification Course Results | Route', function () { describe('POST /api/admin/complementary-certification-course-results', function () { it('return forbidden access if user has METIER role', async function () { // given - sinon - .stub(securityPreHandlers, 'adminMemberHasAtLeastOneAccessOf') + sinon.stub(securityPreHandlers, 'adminMemberHasAtLeastOneAccessOf'); + securityPreHandlers.adminMemberHasAtLeastOneAccessOf .withArgs([ securityPreHandlers.checkAdminMemberHasRoleSuperAdmin, securityPreHandlers.checkAdminMemberHasRoleCertif, @@ -27,7 +29,7 @@ describe('Unit | Application | Complementary Certification Course Results | Rout const response = await httpTestServer.request('POST', '/api/admin/complementary-certification-course-results', { data: { attributes: { - juryLevel: 'JURY_LEVEL_KEY', + juryLevel: 52, complementaryCertificationCourseId: 1234, }, }, @@ -36,5 +38,78 @@ describe('Unit | Application | Complementary Certification Course Results | Rout // then expect(response.statusCode).to.equal(403); }); + + // Rule disabled to allow dynamic generated tests. See https://github.com/lo1tuma/eslint-plugin-mocha/blob/master/docs/rules/no-setup-in-describe.md#disallow-setup-in-describe-blocks-mochano-setup-in-describe + // eslint-disable-next-line mocha/no-setup-in-describe + [juryOptions.REJECTED, juryOptions.UNSET, 1].forEach((juryOption) => + it(`should accept juryLevel with value: ${juryOption}`, async function () { + // given + sinon.stub(complementaryCertificationCourseResultsController, 'saveJuryComplementaryCertificationCourseResult'); + complementaryCertificationCourseResultsController.saveJuryComplementaryCertificationCourseResult.resolves(); + sinon.stub(securityPreHandlers, 'adminMemberHasAtLeastOneAccessOf'); + securityPreHandlers.adminMemberHasAtLeastOneAccessOf + .withArgs([ + securityPreHandlers.checkAdminMemberHasRoleSuperAdmin, + securityPreHandlers.checkAdminMemberHasRoleCertif, + securityPreHandlers.checkAdminMemberHasRoleSupport, + ]) + .callsFake(() => (request, h) => h.response('ok').code(200).takeover()); + + const httpTestServer = new HttpTestServer(); + await httpTestServer.register(moduleUnderTest); + + // when + const response = await httpTestServer.request('POST', '/api/admin/complementary-certification-course-results', { + data: { + attributes: { + juryLevel: juryOption, + complementaryCertificationCourseId: 1234, + }, + }, + }); + + // then + expect(response.statusCode).to.equal(200); + }), + ); + + // Rule disabled to allow dynamic generated tests. See https://github.com/lo1tuma/eslint-plugin-mocha/blob/master/docs/rules/no-setup-in-describe.md#disallow-setup-in-describe-blocks-mochano-setup-in-describe + // eslint-disable-next-line mocha/no-setup-in-describe + [ + { value: 'CACA_VALUE' }, + { value: [], label: '[]' }, + { value: undefined, label: 'undefined' }, + { value: -1 }, + ].forEach(({ value: juryOption, label }) => + it(`should not accept juryLevel with value: ${label ?? juryOption}"`, async function () { + // given + sinon.stub(complementaryCertificationCourseResultsController, 'saveJuryComplementaryCertificationCourseResult'); + complementaryCertificationCourseResultsController.saveJuryComplementaryCertificationCourseResult.resolves(); + sinon.stub(securityPreHandlers, 'adminMemberHasAtLeastOneAccessOf'); + securityPreHandlers.adminMemberHasAtLeastOneAccessOf + .withArgs([ + securityPreHandlers.checkAdminMemberHasRoleSuperAdmin, + securityPreHandlers.checkAdminMemberHasRoleCertif, + securityPreHandlers.checkAdminMemberHasRoleSupport, + ]) + .callsFake(() => (_, h) => h.response('ok').code(200).takeover()); + + const httpTestServer = new HttpTestServer(); + await httpTestServer.register(moduleUnderTest); + + // when + const response = await httpTestServer.request('POST', '/api/admin/complementary-certification-course-results', { + data: { + attributes: { + juryLevel: juryOption, + complementaryCertificationCourseId: 1234, + }, + }, + }); + + // then + expect(response.statusCode).to.equal(400); + }), + ); }); });