feat(deltagare): Added functionality to sort work experiences and educations on from/to dates. (TV-270)

Squashed commit of the following:

commit 44f2896a04aca23cf7f6aea0ab84ab041d52535c
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 4 12:46:01 2021 +0200

    Adjusted sorting functionality to accept Javascript Date objects and added testing

commit 735627f65b40f339cef782cbad1ecdd4f75dc71e
Merge: 61be403 5d1e357
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 4 08:30:20 2021 +0200

    Merge branch 'develop' into feature/TV-270-sorting-experiences

commit 61be4038cb9616d3d9d6b28ed517bd8602a79cfe
Merge: adc7ae5 3709987
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 4 08:01:53 2021 +0200

    Merge branch 'develop' into feature/TV-270-sorting-experiences

commit adc7ae53411a426a1686c7234a49bd537f317184
Merge: dbdfde7 7ab8320
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Wed Aug 4 07:46:12 2021 +0200

    Merge branch 'develop' into feature/TV-270-sorting-experiences

commit dbdfde7b7d32f08f83af2ac93d942f8ba9c55142
Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se>
Date:   Tue Aug 3 15:34:01 2021 +0200

    Added sorting of educations and workexperiences to deltagare service
This commit is contained in:
Erik Tiekstra
2021-08-04 13:20:31 +02:00
parent 5d1e357e83
commit 517d75daf6
4 changed files with 132 additions and 11 deletions

View File

@@ -17,6 +17,7 @@ import { DriversLicense, mapResponseToDriversLicense } from '@dafa-models/driver
import { Education, mapResponseToEducation } from '@dafa-models/education.model'; import { Education, mapResponseToEducation } from '@dafa-models/education.model';
import { HighestEducation, mapResponseToHighestEducation } from '@dafa-models/highest-education.model'; import { HighestEducation, mapResponseToHighestEducation } from '@dafa-models/highest-education.model';
import { mapResponseToWorkExperience, WorkExperience } from '@dafa-models/work-experience.model'; import { mapResponseToWorkExperience, WorkExperience } from '@dafa-models/work-experience.model';
import { sortFromToDates } from '@dafa-utils/sort.util';
import { combineLatest, Observable } from 'rxjs'; import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
@@ -64,11 +65,15 @@ export class DeltagareService {
return this.httpClient return this.httpClient
.get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/education/${id}`, { ...API_HEADERS }) .get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/education/${id}`, { ...API_HEADERS })
.pipe( .pipe(
map(response => map(response => {
response.data.utbildningar if (response.data.utbildningar) {
? response.data.utbildningar.map(utbildning => mapResponseToEducation(utbildning)) return response.data.utbildningar.sort((a, b) =>
: [] sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
) );
}
return [];
}),
map(educations => educations.map(utbildning => mapResponseToEducation(utbildning)))
); );
} }
@@ -112,11 +117,15 @@ export class DeltagareService {
return this.httpClient return this.httpClient
.get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/work/experience/${id}`, { ...API_HEADERS }) .get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/work/experience/${id}`, { ...API_HEADERS })
.pipe( .pipe(
map(response => map(response => {
response.data.arbetslivserfarenheter if (response.data.arbetslivserfarenheter) {
? response.data.arbetslivserfarenheter.map(erfarenhet => mapResponseToWorkExperience(erfarenhet)) return response.data.arbetslivserfarenheter.sort((a, b) =>
: [] sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
) );
}
return [];
}),
map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet)))
); );
} }

View File

@@ -0,0 +1,54 @@
import { sortFromToDates } from './sort.util';
const A_LATEST = {
a: { from: '20210101', to: '20210801' },
b: { from: '20200101', to: '20200801' },
};
const A_LATEST_DAY_MISSING = {
a: { from: '202101', to: '202108' },
b: { from: '202001', to: '202008' },
};
const A_LATEST_DATE = {
a: { from: new Date('2021-01-01'), to: new Date('2021-08-01') },
b: { from: new Date('2020-01-01'), to: new Date('2020-08-01') },
};
const B_LATEST = {
a: { from: '20200101', to: '20200801' },
b: { from: '20210101', to: '20210801' },
};
const A_B_EQUAL = {
a: { from: '20210101', to: '20210801' },
b: { from: '20210101', to: '20210801' },
};
const MISSING_FROM_DATE = {
a: { from: undefined, to: '20210801' },
b: { from: '20200101', to: '20200801' },
};
describe('SortUtil', () => {
describe('Sort complete from/to date-strings from latest to oldest', () => {
it('should sort A dates before B dates when A dates are later', () => {
expect(sortFromToDates(A_LATEST.a, A_LATEST.b)).toBe(-1);
});
it('should sort A dates before B dates when DAY is missing and A dates are later', () => {
expect(sortFromToDates(A_LATEST_DAY_MISSING.a, A_LATEST_DAY_MISSING.b)).toBe(-1);
});
it('should sort A dates before B dates when provided JS Date and A dates are later', () => {
expect(sortFromToDates(A_LATEST_DATE.a, A_LATEST_DATE.b)).toBe(-1);
});
it('should sort B from dates before A dates when B dates are later', () => {
expect(sortFromToDates(B_LATEST.a, B_LATEST.b)).toBe(1);
});
it('should sort A dates before B dates when dates are equal', () => {
expect(sortFromToDates(A_B_EQUAL.a, A_B_EQUAL.b)).toBe(0);
});
it('should sort A dates before B dates when some from-date is missing', () => {
expect(sortFromToDates(MISSING_FROM_DATE.a, MISSING_FROM_DATE.b)).toBe(-1);
});
});
});

View File

@@ -1,4 +1,10 @@
import { Sort } from '@dafa-models/sort.model'; import { Sort } from '@dafa-models/sort.model';
const CURRENT_YEAR = new Date().getFullYear();
interface FromToDates {
from: string | Date;
to: string | Date;
}
export function sort<T>(data: T[], sort: Sort<keyof T>): T[] { export function sort<T>(data: T[], sort: Sort<keyof T>): T[] {
const reverse = sort.order === 'desc' ? -1 : 1; const reverse = sort.order === 'desc' ? -1 : 1;
@@ -9,3 +15,55 @@ export function sort<T>(data: T[], sort: Sort<keyof T>): T[] {
return reverse * (+(first > second) - +(second > first)); return reverse * (+(first > second) - +(second > first));
}); });
} }
export function sortFromToDates(a: FromToDates, b: FromToDates): number {
if (!a.from || !b.from) {
console.error('Some date is not set: ', { a, b });
return -1;
}
if (a.from instanceof Date) {
a.from = a.from.toISOString();
}
if (a.to instanceof Date) {
a.to = a.to.toISOString();
}
if (b.from instanceof Date) {
b.from = b.from.toISOString();
}
if (b.to instanceof Date) {
b.to = b.to.toISOString();
}
// Remove optional time and '-' characters whenever the ISO date string is given.
a.from = a.from.substring(0, 10).replaceAll('-', '');
b.from = b.from.substring(0, 10).replaceAll('-', '');
a.to = a.to.substring(0, 10).replaceAll('-', '');
b.to = b.to.substring(0, 10).replaceAll('-', '');
// If no complete dates are available we will add as high numbers as possible to make them sort on top.
// If no tillValue is available it means it is still ongoing and should sort on top
const aFrom = +a.from.padEnd(8, '9');
const bFrom = +b.from.padEnd(8, '9');
const aTo = !a.to ? +`${CURRENT_YEAR + 1}9999` : +a.to.padEnd(8, '9');
const bTo = !b.to ? +`${CURRENT_YEAR + 1}9999` : +b.to.padEnd(8, '9');
if (isNaN(aTo)) {
return 1;
}
if (isNaN(bTo)) {
return -1;
}
if (aTo === bTo) {
if (isNaN(aFrom)) {
return 1;
}
if (isNaN(bFrom)) {
return -1;
}
return aFrom === bFrom ? 0 : bFrom < aFrom ? -1 : 1;
}
return bTo < aTo ? -1 : 1;
}

View File

@@ -10,7 +10,7 @@
"importHelpers": true, "importHelpers": true,
"target": "es2015", "target": "es2015",
"module": "esnext", "module": "esnext",
"lib": ["es2017", "dom"], "lib": ["esnext", "dom"],
"skipLibCheck": true, "skipLibCheck": true,
"skipDefaultLibCheck": true, "skipDefaultLibCheck": true,
"baseUrl": ".", "baseUrl": ".",