Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🏗️ Major Refactor to Remove Label Button Services #1086

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3b5e169
Merge branch 'master' of https://github.com/e-mission/e-mission-phone…
JGreenlee Oct 9, 2023
dc28f99
Merge branch 'input-matcher-rewrite' of https://github.com/jiji14/e-m…
JGreenlee Oct 25, 2023
8f76ecf
Merge branch 'input-matcher-rewrite' of https://github.com/jiji14/e-m…
JGreenlee Oct 25, 2023
8712380
use section summary for getDetectedModes
JGreenlee Oct 25, 2023
f7716cf
refactor filters: prep to remove buttons services
JGreenlee Oct 29, 2023
4d48aee
map user inputs instead of populating tlEntry objs
JGreenlee Oct 30, 2023
3855a03
remove btn services and survey.ts
JGreenlee Oct 30, 2023
40d3f75
add back verifiability with verifiabilityForTrip()
JGreenlee Oct 30, 2023
49ff385
remove populateCompositeTrips; update types
JGreenlee Oct 30, 2023
a34c219
TripCard: don't show footer if no notes
JGreenlee Oct 30, 2023
c262f6a
remove unused vars in LabelTab
JGreenlee Oct 30, 2023
9009517
fix label button showing before appConfig loaded
JGreenlee Oct 31, 2023
fb46586
combine processed+unprocessed survey responses
JGreenlee Oct 31, 2023
af5948b
move LabelTabContext to own file to expand types
JGreenlee Oct 31, 2023
d96ff8d
expand types
JGreenlee Oct 31, 2023
22b80f9
fix diaryHelper.test.ts
JGreenlee Oct 31, 2023
081b142
Merge branch 'input-matcher-rewrite' of https://github.com/jiji14/e-m…
JGreenlee Nov 1, 2023
956f2d9
Merge remote-tracking branch 'upstream/service_rewrite_2023' into rew…
JGreenlee Nov 2, 2023
5110845
apply prettier to PR #1086
JGreenlee Nov 2, 2023
1f952f7
remove 'locales' submodule
JGreenlee Nov 3, 2023
1b01692
fix syntax errors and apply prettier
JGreenlee Nov 3, 2023
5eb8d8f
Merge branch 'service_rewrite_2023' of https://github.com/e-mission/e…
JGreenlee Nov 3, 2023
24b34ee
clean up confirmHelper
JGreenlee Nov 3, 2023
55c313d
add tests for confirmHelper
JGreenlee Nov 3, 2023
a60b1e2
Merge branch 'service_rewrite_2023' of https://github.com/e-mission/e…
JGreenlee Nov 10, 2023
d3e6af2
fix en.json -> "questions"
JGreenlee Nov 10, 2023
69b18cb
remove duplicate 'verifiability' check
JGreenlee Nov 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 170 additions & 0 deletions www/__tests__/confirmHelper.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { mockLogger } from '../__mocks__/globalMocks';
import * as CommHelper from '../js/commHelper';
import {
baseLabelInputDetails,
getLabelInputDetails,
getLabelOptions,
inferFinalLabels,
labelInputDetailsForTrip,
labelKeyToReadable,
labelKeyToRichMode,
labelOptionByValue,
readableLabelToKey,
verifiabilityForTrip,
} from '../js/survey/multilabel/confirmHelper';

import initializedI18next from '../js/i18nextInit';
window['i18next'] = initializedI18next;
mockLogger();

const fakeAppConfig = {
label_options: 'json/label-options.json.sample',
};
const fakeAppConfigWithModeOfStudy = {
...fakeAppConfig,
intro: {
mode_studied: 'walk',
},
};
const fakeDefaultLabelOptions = {
MODE: [
{ value: 'walk', baseMode: 'WALKING', met_equivalent: 'WALKING', kgCo2PerKm: 0 },
{ value: 'bike', baseMode: 'BICYCLING', met_equivalent: 'BICYCLING', kgCo2PerKm: 0 },
],
PURPOSE: [{ value: 'home' }, { value: 'work' }],
REPLACED_MODE: [{ value: 'no_travel' }, { value: 'walk' }, { value: 'bike' }],
translations: {
en: {
walk: 'Walk',
bike: 'Regular Bike',
no_travel: 'No travel',
home: 'Home',
work: 'To Work',
},
},
};

CommHelper.fetchUrlCached = jest
.fn()
.mockImplementation(() => JSON.stringify(fakeDefaultLabelOptions));

describe('confirmHelper', () => {
it('returns labelOptions given an appConfig', async () => {
const labelOptions = await getLabelOptions(fakeAppConfig);
expect(labelOptions).toBeTruthy();
expect(labelOptions.MODE[0].text).toEqual('Walk'); // translation is filled in
});

it('returns base labelInputDetails for a labelUserInput which does not have mode of study', () => {
const fakeLabelUserInput = {
MODE: fakeDefaultLabelOptions.MODE[1],
PURPOSE: fakeDefaultLabelOptions.PURPOSE[0],
};
const labelInputDetails = labelInputDetailsForTrip(
fakeLabelUserInput,
fakeAppConfigWithModeOfStudy,
);
expect(labelInputDetails).toEqual(baseLabelInputDetails);
});

it('returns full labelInputDetails for a labelUserInput which has the mode of study', () => {
const fakeLabelUserInput = {
MODE: fakeDefaultLabelOptions.MODE[0], // 'walk' is mode of study
PURPOSE: fakeDefaultLabelOptions.PURPOSE[0],
};
const labelInputDetails = labelInputDetailsForTrip(
fakeLabelUserInput,
fakeAppConfigWithModeOfStudy,
);
const fullLabelInputDetails = getLabelInputDetails(fakeAppConfigWithModeOfStudy);
expect(labelInputDetails).toEqual(fullLabelInputDetails);
});

it(`converts 'other' text to a label key`, () => {
const mode1 = readableLabelToKey(`Scooby Doo Mystery Machine `);
expect(mode1).toEqual('scooby_doo_mystery_machine'); // trailing space is trimmed
const mode2 = readableLabelToKey(`My niece's tricycle . `);
expect(mode2).toEqual(`my_niece's_tricycle_.`); // apostrophe and period are preserved
const purpose1 = readableLabelToKey(`Going to the store to buy 12 eggs.`);
expect(purpose1).toEqual('going_to_the_store_to_buy_12_eggs.'); // numbers are preserved
});

it(`converts keys to readable labels`, () => {
const mode1 = labelKeyToReadable(`scooby_doo_mystery_machine`);
expect(mode1).toEqual(`Scooby Doo Mystery Machine`);
const mode2 = labelKeyToReadable(`my_niece's_tricycle_.`);
expect(mode2).toEqual(`My Niece's Tricycle .`);
const purpose1 = labelKeyToReadable(`going_to_the_store_to_buy_12_eggs.`);
expect(purpose1).toEqual(`Going To The Store To Buy 12 Eggs.`);
});

it('looks up a rich mode from a label key, or humanizes the label key if there is no rich mode', () => {
const key = 'walk';
const richMode = labelKeyToRichMode(key);
expect(richMode).toEqual('Walk');
const key2 = 'scooby_doo_mystery_machine';
const readableMode = labelKeyToRichMode(key2);
expect(readableMode).toEqual('Scooby Doo Mystery Machine');
});

/* BEGIN: tests for inferences, which are loosely based on the server-side tests from
e-mission-server -> emission/tests/storageTests/TestTripQueries.py -> testExpandFinalLabels() */

it('has no final label for a trip with no user labels or inferred labels', () => {
const fakeTrip = {};
const fakeUserInput = {};
expect(inferFinalLabels(fakeTrip, fakeUserInput)).toEqual({});
expect(verifiabilityForTrip(fakeTrip, fakeUserInput)).toEqual('cannot-verify');
});

it('returns a final inference for a trip no user labels and all high-confidence inferred labels', () => {
const fakeTrip = {
inferred_labels: [{ labels: { mode_confirm: 'walk', purpose_confirm: 'exercise' }, p: 0.9 }],
};
const fakeUserInput = {};
const final = inferFinalLabels(fakeTrip, fakeUserInput);
expect(final.MODE.value).toEqual('walk');
expect(final.PURPOSE.value).toEqual('exercise');
expect(verifiabilityForTrip(fakeTrip, fakeUserInput)).toEqual('can-verify');
});

it('gives no final inference when there are user labels and no inferred labels', () => {
const fakeTrip = {};
const fakeUserInput = {
MODE: labelOptionByValue('bike', 'MODE'),
PURPOSE: labelOptionByValue('shopping', 'PURPOSE'),
};
const final = inferFinalLabels(fakeTrip, fakeUserInput);
expect(final.MODE?.value).toBeUndefined();
expect(final.PURPOSE?.value).toBeUndefined();
expect(verifiabilityForTrip(fakeTrip, fakeUserInput)).toEqual('already-verified');
});

it('still gives no final inference when there are user labels and high-confidence inferred labels', () => {
const fakeTrip = {
inferred_labels: [{ labels: { mode_confirm: 'walk', purpose_confirm: 'exercise' }, p: 0.9 }],
};
const fakeUserInput = {
MODE: labelOptionByValue('bike', 'MODE'),
PURPOSE: labelOptionByValue('shopping', 'PURPOSE'),
};
const final = inferFinalLabels(fakeTrip, fakeUserInput);
expect(final.MODE?.value).toBeUndefined();
expect(final.PURPOSE?.value).toBeUndefined();
expect(verifiabilityForTrip(fakeTrip, fakeUserInput)).toEqual('already-verified');
});

it('mixes user input labels with mixed-confidence inferred labels', () => {
const fakeTrip = {
inferred_labels: [
{ labels: { mode_confirm: 'bike', purpose_confirm: 'shopping' }, p: 0.1 },
{ labels: { mode_confirm: 'walk', purpose_confirm: 'exercise' }, p: 0.9 },
],
};
const fakeUserInput = { MODE: labelOptionByValue('bike', 'MODE') };
const final = inferFinalLabels(fakeTrip, fakeUserInput);
expect(final.MODE.value).toEqual('bike');
expect(final.PURPOSE.value).toEqual('shopping');
expect(verifiabilityForTrip(fakeTrip, fakeUserInput)).toEqual('can-verify');
});
});
33 changes: 22 additions & 11 deletions www/__tests__/diaryHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,29 @@ it('returns true/false is multi day', () => {
expect(isMultiDay('', '2023-09-18T00:00:00-09:00')).toBeFalsy();
});

//created a fake trip with relevant sections by examining log statements
/* fake trips with 'distance' in their section summaries
('count' and 'duration' are not used bygetDetectedModes) */
let myFakeTrip = {
sections: [
{ sensed_mode_str: 'BICYCLING', distance: 6013.73657416706 },
{ sensed_mode_str: 'WALKING', distance: 715.3078629361006 },
],
};
distance: 6729.0444371031606,
cleaned_section_summary: {
// count: {...}
// duration: {...}
distance: {
BICYCLING: 6013.73657416706,
WALKING: 715.3078629361006,
},
},
} as any;

let myFakeTrip2 = {
sections: [
{ sensed_mode_str: 'BICYCLING', distance: 6013.73657416706 },
{ sensed_mode_str: 'BICYCLING', distance: 715.3078629361006 },
],
...myFakeTrip,
inferred_section_summary: {
// count: {...}
// duration: {...}
distance: {
BICYCLING: 6729.0444371031606,
},
},
};

let myFakeDetectedModes = [
Expand All @@ -82,5 +93,5 @@ let myFakeDetectedModes2 = [{ mode: 'BICYCLING', icon: 'bike', color: modeColors
it('returns the detected modes, with percentages, for a trip', () => {
expect(getDetectedModes(myFakeTrip)).toEqual(myFakeDetectedModes);
expect(getDetectedModes(myFakeTrip2)).toEqual(myFakeDetectedModes2);
expect(getDetectedModes({})).toEqual([]); // empty trip, no sections, no modes
expect(getDetectedModes({} as any)).toEqual([]); // empty trip, no sections, no modes
});
51 changes: 34 additions & 17 deletions www/__tests__/inputMatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ import {
validUserInputForDraftTrip,
validUserInputForTimelineEntry,
getNotDeletedCandidates,
getUserInputForTrip,
getUserInputForTimelineEntry,
getAdditionsForTimelineEntry,
getUniqueEntries,
} from '../js/survey/inputMatcher';
import { TlEntry, UserInput } from '../js/types/diaryTypes';
import { CompositeTrip, TimelineEntry, UserInputEntry } from '../js/types/diaryTypes';

describe('input-matcher', () => {
let userTrip: UserInput;
let trip: TlEntry;
let userTrip: UserInputEntry;
let trip: TimelineEntry;
let nextTrip: TimelineEntry;

beforeEach(() => {
/*
Expand Down Expand Up @@ -46,9 +47,16 @@ describe('input-matcher', () => {
enter_ts: 1437605000,
exit_ts: 1437605000,
duration: 100,
getNextEntry: jest.fn(),
};

nextTrip = {
key: 'BAR',
origin_key: 'BAR',
start_ts: 1437606000,
end_ts: 1437607000,
enter_ts: 1437607000,
exit_ts: 1437607000,
duration: 100,
};
// mock Logger
window['Logger'] = { log: console.log };
});
Expand Down Expand Up @@ -78,7 +86,7 @@ describe('input-matcher', () => {
const validTrp = {
end_ts: 1437604764,
start_ts: 1437601247,
};
} as CompositeTrip;
const validUserInput = validUserInputForDraftTrip(validTrp, userTrip, false);
expect(validUserInput).toBeTruthy();
});
Expand All @@ -87,7 +95,7 @@ describe('input-matcher', () => {
const invalidTrip = {
end_ts: 0,
start_ts: 0,
};
} as CompositeTrip;
const invalidUserInput = validUserInputForDraftTrip(invalidTrip, userTrip, false);
expect(invalidUserInput).toBeFalsy();
});
Expand All @@ -96,28 +104,37 @@ describe('input-matcher', () => {
// we need valid key and origin_key for validUserInputForTimelineEntry test
trip['key'] = 'analysis/confirmed_place';
trip['origin_key'] = 'analysis/confirmed_place';
const validTimelineEntry = validUserInputForTimelineEntry(trip, userTrip, false);
const validTimelineEntry = validUserInputForTimelineEntry(trip, nextTrip, userTrip, false);
expect(validTimelineEntry).toBeTruthy();
});

it('tests validUserInputForTimelineEntry with tlEntry with invalid key and origin_key', () => {
const invalidTlEntry = trip;
const invalidTimelineEntry = validUserInputForTimelineEntry(invalidTlEntry, userTrip, false);
const invalidTimelineEntry = validUserInputForTimelineEntry(
invalidTlEntry,
null,
userTrip,
false,
);
expect(invalidTimelineEntry).toBeFalsy();
});

it('tests validUserInputForTimelineEntry with tlEntry with invalie start & end time', () => {
const invalidTlEntry: TlEntry = {
const invalidTlEntry: TimelineEntry = {
key: 'analysis/confirmed_place',
origin_key: 'analysis/confirmed_place',
start_ts: 1,
end_ts: 1,
enter_ts: 1,
exit_ts: 1,
duration: 1,
getNextEntry: jest.fn(),
};
const invalidTimelineEntry = validUserInputForTimelineEntry(invalidTlEntry, userTrip, false);
const invalidTimelineEntry = validUserInputForTimelineEntry(
invalidTlEntry,
null,
userTrip,
false,
);
expect(invalidTimelineEntry).toBeFalsy();
});

Expand Down Expand Up @@ -210,13 +227,13 @@ describe('input-matcher', () => {

// make the linst unsorted and then check if userInputWriteThird(latest one) is return output
const userInputList = [userInputWriteSecond, userInputWriteThird, userInputWriteFirst];
const mostRecentEntry = getUserInputForTrip(trip, {}, userInputList);
const mostRecentEntry = getUserInputForTimelineEntry(trip, nextTrip, userInputList);
expect(mostRecentEntry).toMatchObject(userInputWriteThird);
});

it('tests getUserInputForTrip with invalid userInputList', () => {
const userInputList = undefined;
const mostRecentEntry = getUserInputForTrip(trip, {}, userInputList);
const mostRecentEntry = getUserInputForTimelineEntry(trip, nextTrip, userInputList);
expect(mostRecentEntry).toBe(undefined);
});

Expand All @@ -226,13 +243,13 @@ describe('input-matcher', () => {
trip['origin_key'] = 'analysis/confirmed_place';

// check if the result keep the all valid userTrip items
const matchingAdditions = getAdditionsForTimelineEntry(trip, additionsList);
const matchingAdditions = getAdditionsForTimelineEntry(trip, nextTrip, additionsList);
JGreenlee marked this conversation as resolved.
Show resolved Hide resolved
expect(matchingAdditions).toHaveLength(5);
});

it('tests getAdditionsForTimelineEntry with invalid additionsList', () => {
const additionsList = undefined;
const matchingAdditions = getAdditionsForTimelineEntry(trip, additionsList);
const matchingAdditions = getAdditionsForTimelineEntry(trip, nextTrip, additionsList);
expect(matchingAdditions).toMatchObject([]);
});

Expand Down
1 change: 1 addition & 0 deletions www/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@
"registration-check-token": "User registration error. Please check your token and try again.",
"not-registered-cant-contact": "User is not registered, so the server cannot be contacted.",
"while-initializing-label": "While initializing Label tab: ",
"while-loading-pipeline-range": "Error while loading pipeline range",
"while-populating-composite": "Error while populating composite trips",
"while-loading-another-week": "Error while loading travel of {{when}} week",
"while-loading-specific-week": "Error while loading travel for the week of {{day}}",
Expand Down
3 changes: 0 additions & 3 deletions www/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@ import './js/controllers.js';
import './js/services.js';
import './js/i18n-utils.js';
import './js/main.js';
import './js/survey/multilabel/multi-label-ui.js';
import './js/diary.js';
import './js/diary/services.js';
import './js/survey/enketo/answer.js';
import './js/survey/enketo/enketo-trip-button.js';
import './js/survey/enketo/enketo-add-note-button.js';
import './js/control/emailService.js';
import './js/metrics-factory.js';
import './js/metrics-mappings.js';
Expand Down
4 changes: 1 addition & 3 deletions www/js/diary.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import LabelTab from './diary/LabelTab';
angular
.module('emission.main.diary', [
'emission.main.diary.services',
'emission.survey.multilabel.buttons',
'emission.survey.enketo.add-note-button',
'emission.survey.enketo.trip.button',
'emission.plugin.logger',
'emission.survey.enketo.answer',
])

.config(function ($stateProvider) {
Expand Down
Loading
Loading