From 24304f325afe51e724b7e3a8f594b27f607d2b59 Mon Sep 17 00:00:00 2001 From: Jijeong Lee Date: Mon, 8 Apr 2024 13:02:17 -0700 Subject: [PATCH 01/36] add collectCoverageForm to track all files for Jest coverage --- jest.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/jest.config.js b/jest.config.js index 6a1dcd42b..939834e51 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,4 +18,9 @@ module.exports = { moduleDirectories: ["node_modules", "src"], globals: {"__DEV__": false}, collectCoverage: true, + collectCoverageFrom: [ + "www/js/**/*.{ts,tsx,js,jsx}", + "!www/js/**/index.{ts,tsx,js,jsx}", + "!www/js/types/**/*.{ts,tsx,js,jsx}", + ], }; From 87370b6cabb263a21a7fa8d98dcd1ab0aaba0c09 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 14 Apr 2024 19:57:43 -0700 Subject: [PATCH 02/36] =?UTF-8?q?=F0=9F=94=A5=20Remove=20code=20to=20build?= =?UTF-8?q?=20JSON=20objects?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In an earlier version of this PR, before I added mocking to the plugin, we saved JSON objects directly. We created the objects to save by removing results from the in-memory objects using a destructuring assignment. However, after the mocking was added, we create objects in native code. So we don't need to create them here and can remove these unused variables. https://github.com/e-mission/e-mission-phone/pull/1144#discussion_r1564981905 --- www/js/bluetooth/BluetoothScanPage.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/www/js/bluetooth/BluetoothScanPage.tsx b/www/js/bluetooth/BluetoothScanPage.tsx index a4e5bda9e..b50bf5283 100644 --- a/www/js/bluetooth/BluetoothScanPage.tsx +++ b/www/js/bluetooth/BluetoothScanPage.tsx @@ -97,7 +97,6 @@ const BluetoothScanPage = ({ ...props }: any) => { in_range: status, }, })); - let { monitorResult: _, in_range: _, ...noResultDevice } = sampleBLEDevices[uuid]; window['cordova']?.plugins?.BEMDataCollection.mockBLEObjects( status ? 'REGION_ENTER' : 'REGION_EXIT', uuid, @@ -118,16 +117,6 @@ const BluetoothScanPage = ({ ...props }: any) => { rangeResult: result, }, })); - // we don't want to exclude monitorResult and rangeResult from the values - // that we save because they are the current or previous result, just - // in a different format - // https://stackoverflow.com/a/34710102 - let { - monitorResult: _, - rangeResult: _, - in_range: _, - ...noResultDevice - } = sampleBLEDevices[uuid]; let parsedResult = JSON.parse(result); parsedResult.beacons.forEach((beacon) => { window['cordova']?.plugins?.BEMDataCollection.mockBLEObjects( From 1fca7c58519c30823ab89f8654f3cfc47ed0392b Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Mon, 15 Apr 2024 00:00:39 -0700 Subject: [PATCH 03/36] =?UTF-8?q?=F0=9F=91=B7=F0=9F=94=A7=20=F0=9F=AA=A0?= =?UTF-8?q?=20Plumb=20through=20support=20for=20requesting=20bluetooth=20s?= =?UTF-8?q?can=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complementary to https://github.com/e-mission/e-mission-data-collection/pull/224 Very similar to the fitness checks, just calls through to the underlying native javascript Note that the bluetooth scan will currently only be used for fleet configs since we don't use bluetooth in the general case, and forcing people to enable it unnecessarily is bad. Testing done: https://github.com/e-mission/e-mission-data-collection/pull/224#issuecomment-2055712525 --- www/js/usePermissionStatus.ts | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/www/js/usePermissionStatus.ts b/www/js/usePermissionStatus.ts index 8c3128bea..43206973f 100644 --- a/www/js/usePermissionStatus.ts +++ b/www/js/usePermissionStatus.ts @@ -291,6 +291,41 @@ const usePermissionStatus = () => { setCheckList(tempChecks); } + function setupAndroidBluetoothChecks() { + if (window['device'].version.split('.')[0] >= 10) { + let fixPerms = () => { + logDebug('fix and refresh bluetooth permissions'); + return checkOrFix( + bluetoothPermissionsCheck, + window['cordova'].plugins.BEMDataCollection.fixBluetoothPermissions, + true, + ).then((error) => { + if (error) { + bluetoothPermissionsCheck.desc = error; + } + }); + }; + let checkPerms = () => { + logDebug('fix and refresh bluetooth permissions'); + return checkOrFix( + bluetoothPermissionsCheck, + window['cordova'].plugins.BEMDataCollection.isValidBluetoothPermissions, + false, + ); + }; + + let bluetoothPermissionsCheck = { + name: "Bluetooth scan permission", + desc: "Scan for BLE beacons to automatically match trips to vehicles", + fix: fixPerms, + refresh: checkPerms, + }; + let tempChecks = checkList; + tempChecks.push(bluetoothPermissionsCheck); + setCheckList(tempChecks); + } + } + function setupAndroidNotificationChecks() { let fixPerms = () => { logDebug('fix and refresh notification permissions'); @@ -409,6 +444,9 @@ const usePermissionStatus = () => { if (window['device'].platform.toLowerCase() == 'android') { setupAndroidLocChecks(); setupAndroidFitnessChecks(); + if (appConfig.tracking?.bluetooth_only) { + setupAndroidBluetoothChecks(); + } setupAndroidNotificationChecks(); setupAndroidBackgroundRestrictionChecks(); } else if (window['device'].platform.toLowerCase() == 'ios') { From 9d6e5292782ee9393e3e5f8e35255d785390ef48 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Mon, 15 Apr 2024 01:02:02 -0700 Subject: [PATCH 04/36] Prettier formatting fix --- www/js/usePermissionStatus.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/js/usePermissionStatus.ts b/www/js/usePermissionStatus.ts index 43206973f..f8fef085b 100644 --- a/www/js/usePermissionStatus.ts +++ b/www/js/usePermissionStatus.ts @@ -315,8 +315,8 @@ const usePermissionStatus = () => { }; let bluetoothPermissionsCheck = { - name: "Bluetooth scan permission", - desc: "Scan for BLE beacons to automatically match trips to vehicles", + name: 'Bluetooth scan permission', + desc: 'Scan for BLE beacons to automatically match trips to vehicles', fix: fixPerms, refresh: checkPerms, }; From 705413683b4591edef6493735725c68fc9f7a099 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Mon, 15 Apr 2024 01:03:47 -0700 Subject: [PATCH 05/36] Bump up the versions for the usercache and data collection plugins The data collection plugin may have other changes coming in, so we are installing it off a branch --- package.cordovabuild.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.cordovabuild.json b/package.cordovabuild.json index c3f7ec8d3..7ad923049 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -121,13 +121,13 @@ "cordova-plugin-app-version": "0.1.14", "cordova-plugin-customurlscheme": "5.0.2", "cordova-plugin-device": "2.1.0", - "cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#v1.8.3", + "cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#integrate_ble", "cordova-plugin-em-opcodeauth": "git+https://github.com/e-mission/cordova-jwt-auth.git#v1.7.2", "cordova-plugin-em-server-communication": "git+https://github.com/e-mission/cordova-server-communication.git#v1.2.6", "cordova-plugin-em-serversync": "git+https://github.com/e-mission/cordova-server-sync.git#v1.3.2", "cordova-plugin-em-settings": "git+https://github.com/e-mission/cordova-connection-settings.git#v1.2.3", "cordova-plugin-em-unifiedlogger": "git+https://github.com/e-mission/cordova-unified-logger.git#v1.3.6", - "cordova-plugin-em-usercache": "git+https://github.com/e-mission/cordova-usercache.git#v1.1.8", + "cordova-plugin-em-usercache": "git+https://github.com/e-mission/cordova-usercache.git#v1.1.9", "cordova-plugin-email-composer": "git+https://github.com/katzer/cordova-plugin-email-composer.git#0.10.1", "cordova-plugin-file": "8.0.0", "cordova-plugin-inappbrowser": "5.0.0", From 8cc6a7e3debf882e4a6861087c7568eb84097ff6 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Wed, 17 Apr 2024 00:58:06 -0400 Subject: [PATCH 06/36] UserInputButton: before appConfig is defined, don't show default survey --- www/js/survey/enketo/UserInputButton.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/www/js/survey/enketo/UserInputButton.tsx b/www/js/survey/enketo/UserInputButton.tsx index 118b687f8..e3e629bda 100644 --- a/www/js/survey/enketo/UserInputButton.tsx +++ b/www/js/survey/enketo/UserInputButton.tsx @@ -34,6 +34,7 @@ const UserInputButton = ({ timelineEntry }: Props) => { // which survey will this button launch? const [surveyName, notFilledInLabel] = useMemo(() => { + if (!appConfig) return []; // no config loaded yet; show blank for now const tripLabelConfig = appConfig?.survey_info?.buttons?.['trip-label']; if (!tripLabelConfig) { // config doesn't specify; use default From 03673a40a511136cc07d8092c701804f21bb0905 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Wed, 17 Apr 2024 01:02:10 -0400 Subject: [PATCH 07/36] in updateUnprocessedBleScans, await call to getUnifiedDataForInterval Without the 'await' keyword here, execution would continue without the request being completed. This would cause the BLE scans to sometimes not be considered while rendering and evaluating conditional surveys. If the BLE scans took longer to load than the trips took to render, there would be no surveys prompted. Thus, we must 'await' to make sure the BLE scans are loaded first. --- www/js/diary/timelineHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/js/diary/timelineHelper.ts b/www/js/diary/timelineHelper.ts index d82adb4fb..6e82c0fbf 100644 --- a/www/js/diary/timelineHelper.ts +++ b/www/js/diary/timelineHelper.ts @@ -182,7 +182,7 @@ export async function updateUnprocessedBleScans(queryRange: TimestampRange) { endTs: queryRange.end_ts, }; const getMethod = window['cordova'].plugins.BEMUserCache.getSensorDataForInterval; - getUnifiedDataForInterval('background/bluetooth_ble', tq, getMethod).then( + await getUnifiedDataForInterval('background/bluetooth_ble', tq, getMethod).then( (bleScans: BEMData[]) => { logDebug(`Read ${bleScans.length} BLE scans`); unprocessedBleScans = bleScans; From d83f4d4a28744dc33a71f86b94014e32cd2c1219 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Wed, 17 Apr 2024 01:21:17 -0400 Subject: [PATCH 08/36] cacheResourcesFromConfig: reload the cache when config is downloaded We recently added the ability to refresh the config from inside the app. But whenever the config is downloaded for the first time, we cache the resources referenced in it by URL. Subsequent config downloads would still use the resources from the first time. fetchUrlCached now accepts options to pass through to the fetch API; if cache is 'reload', we will skip checking our localStorage cache for a previously stored value. This option will also cause the fetch API will also skip its own internal cache The result of this is that when we refresh the config, URL-referenced resources inside it will also be refreshed. --- www/js/config/dynamicConfig.ts | 4 ++-- www/js/services/commHelper.ts | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/www/js/config/dynamicConfig.ts b/www/js/config/dynamicConfig.ts index d9b9f3235..5843af3d2 100644 --- a/www/js/config/dynamicConfig.ts +++ b/www/js/config/dynamicConfig.ts @@ -92,11 +92,11 @@ function cacheResourcesFromConfig(config: AppConfig) { if (config.survey_info?.surveys) { Object.values(config.survey_info.surveys).forEach((survey) => { if (!survey?.['formPath']) throw new Error(i18next.t('config.survey-missing-formpath')); - fetchUrlCached(survey['formPath']); + fetchUrlCached(survey['formPath'], { cache: 'reload' }); }); } if (config.label_options) { - fetchUrlCached(config.label_options); + fetchUrlCached(config.label_options, { cache: 'reload' }); } } diff --git a/www/js/services/commHelper.ts b/www/js/services/commHelper.ts index 26dce8056..ec2ee9d97 100644 --- a/www/js/services/commHelper.ts +++ b/www/js/services/commHelper.ts @@ -5,17 +5,18 @@ import { TimestampRange } from '../types/diaryTypes'; /** * @param url URL endpoint for the request + * @param fetchOpts (optional) options for the fetch request. If 'cache' is set to 'reload', the cache will be ignored * @returns Promise of the fetched response (as text) or cached text from local storage */ -export async function fetchUrlCached(url) { +export async function fetchUrlCached(url: string, fetchOpts?: RequestInit) { const stored = localStorage.getItem(url); - if (stored) { + if (stored && fetchOpts?.cache != 'reload') { logDebug(`fetchUrlCached: found cached data for url ${url}, returning`); return Promise.resolve(stored); } try { - logDebug(`fetchUrlCached: found no cached data for url ${url}, fetching`); - const response = await fetch(url); + logDebug(`fetchUrlCached: cache had ${stored} for url ${url}, not using; fetching`); + const response = await fetch(url, fetchOpts); const text = await response.text(); localStorage.setItem(url, text); logDebug(`fetchUrlCached: fetched data for url ${url}, returning`); From f0c85f2a393df3af5abec54c411cff179fd95997 Mon Sep 17 00:00:00 2001 From: Jijeong Lee Date: Mon, 22 Apr 2024 13:02:20 -0700 Subject: [PATCH 09/36] codecov-action version update v3 -> v4 --- .github/workflows/code-coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index b0e94db22..caf0fc7bc 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -27,7 +27,7 @@ jobs: npx jest - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: ./coverage/coverage-final.json flags: unit From 22cbf2f506af10deaae9cc9ece4381a71416de6a Mon Sep 17 00:00:00 2001 From: Jijeong Lee Date: Mon, 22 Apr 2024 13:13:41 -0700 Subject: [PATCH 10/36] add CODECOV_TOKEN as required for v4 --- .github/workflows/code-coverage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index caf0fc7bc..dc1af47ac 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -29,6 +29,7 @@ jobs: - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 with: + token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage/coverage-final.json flags: unit fail_ci_if_error: ${{ github.repository == 'e-mission/e-mission-phone' }} From 0eebe4b1a2bb3e58140e1806e90c2d3b34b47f5f Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Thu, 25 Apr 2024 21:30:41 -0700 Subject: [PATCH 11/36] Switch the data collection plugin version to the latest https://github.com/e-mission/e-mission-data-collection/pull/226 is now merged and a new release https://github.com/e-mission/e-mission-data-collection/releases/tag/v1.8.5 has been created So we can now use the version number instead of a branch while adding the plugin With this commit, the initial implementation in https://github.com/e-mission/e-mission-phone/pull/1144 is done, and we can merge it --- package.cordovabuild.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.cordovabuild.json b/package.cordovabuild.json index 7ad923049..180849033 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -121,7 +121,7 @@ "cordova-plugin-app-version": "0.1.14", "cordova-plugin-customurlscheme": "5.0.2", "cordova-plugin-device": "2.1.0", - "cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#integrate_ble", + "cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#1.8.5", "cordova-plugin-em-opcodeauth": "git+https://github.com/e-mission/cordova-jwt-auth.git#v1.7.2", "cordova-plugin-em-server-communication": "git+https://github.com/e-mission/cordova-server-communication.git#v1.2.6", "cordova-plugin-em-serversync": "git+https://github.com/e-mission/cordova-server-sync.git#v1.3.2", From 0f6aabcc18255302a1c2bcf40b2a4cde15fb67d4 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Sun, 28 Apr 2024 13:35:30 -0400 Subject: [PATCH 12/36] Lock React version to ~18.2.0 (#1148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * lock React version to ~18.2.0 React 19 and React 18.3 are out (https://react.dev/blog/2024/04/25/react-19-upgrade-guide#react-18-3), but the React Native ecosystem is still on React 18.2. We need to lock the versions down until the React Native packages update. * 💩 Temporarily pin the version to 14.1 so that the CI is successful Consisent with https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2079688203 * 💩 Revert back even further to xcode 14 Consistent with https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2080155690 * 📌 Ensure that we are using the correct version of java Before this, we set only the `JAVA_HOME` to pin the java version This was working until a couple of days ago, when, even after setting the version, the java version was not changed. This may be due to the location for the java executable changing, so let's try to set the PATH as well Also print out several environment variables to help debug further Related info: https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2079702274 https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2080155690 * 🔧 Change the environment variable to the one used in the new github actions runner Related context: https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2081318853 * 📌 Pin the mac version to 13 To be consistent with https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2081318853 Before we fix https://github.com/e-mission/e-mission-docs/issues/1060 * 📌 Pin the mac version to 13 Consistent with https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2081323518 and https://github.com/e-mission/e-mission-phone/pull/1148/commits/b5072f351e7954d0ffef9d7d55a5751b225813e2 (for iOS) * ⏪️ Revert previous attempts to fix the issue since pinning to OSX-13 fixed it https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2081331221 * 📌 Pin the xcode version again Because apparently the default version, even on the OSX 13 runner, is now xcode 15 https://github.com/e-mission/e-mission-phone/pull/1148#issuecomment-2081536981 * Ensure that the switch command is run as root --------- Co-authored-by: K. Shankari --- .github/workflows/android-build.yml | 6 +++++- .github/workflows/ios-build.yml | 8 +++++++- package.cordovabuild.json | 6 +++--- package.serve.json | 7 ++++--- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 25eb65317..90c75d147 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -22,13 +22,17 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: macos-latest + runs-on: macos-13 # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 + # Runs a single command using the runners shell + - name: Prints related environment variables so that we can know what to set + run: env | egrep "JAVA|PATH|ANDROID" + # Runs a single command using the runners shell - name: Print the java and gradle versions run: | diff --git a/.github/workflows/ios-build.yml b/.github/workflows/ios-build.yml index ad0ce2f01..ac6bb76f3 100644 --- a/.github/workflows/ios-build.yml +++ b/.github/workflows/ios-build.yml @@ -22,7 +22,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: macos-latest + runs-on: macos-13 # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -33,6 +33,12 @@ jobs: - name: Print the xcode path run: xcode-select --print-path + - name: Set xcode to 14.1 + run: | + sudo xcode-select --switch /Applications/Xcode_14.1.app + echo "After setting xcode version "`xcode-select --print-path` + + - name: Print the xcode setup run: xcodebuild -version -sdk diff --git a/package.cordovabuild.json b/package.cordovabuild.json index c3f7ec8d3..85ebd6eb9 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -26,7 +26,7 @@ "@babel/preset-typescript": "^7.21.4", "@ionic/cli": "6.20.8", "@types/luxon": "^3.3.0", - "@types/react": "^18.2.20", + "@types/react": "~18.2.0", "babel-loader": "^9.1.2", "babel-plugin-optional-require": "^0.3.1", "concurrently": "^8.0.1", @@ -152,9 +152,9 @@ "npm": "^9.6.3", "phonegap-plugin-barcodescanner": "git+https://github.com/phonegap/phonegap-plugin-barcodescanner#v8.1.0", "prop-types": "^15.8.1", - "react": "^18.2.*", + "react": "~18.2.0", "react-chartjs-2": "^5.2.0", - "react-dom": "^18.2.*", + "react-dom": "~18.2.0", "react-i18next": "^13.5.0", "react-native-paper": "^5.11.0", "react-native-paper-dates": "^0.18.12", diff --git a/package.serve.json b/package.serve.json index dceeb2267..f6f5c2ae3 100644 --- a/package.serve.json +++ b/package.serve.json @@ -26,7 +26,7 @@ "@ionic/cli": "6.20.8", "@testing-library/react-native": "^12.3.0", "@types/luxon": "^3.3.0", - "@types/react": "^18.2.20", + "@types/react": "~18.2.0", "babel-jest": "^29.7.0", "babel-loader": "^9.1.2", "babel-plugin-optional-require": "^0.3.1", @@ -40,6 +40,7 @@ "jest-environment-jsdom": "^29.7.0", "phonegap": "9.0.0+cordova.9.0.0", "process": "^0.11.10", + "react-test-renderer": "~18.2.0", "sass": "^1.62.1", "sass-loader": "^13.3.1", "style-loader": "^3.3.3", @@ -76,9 +77,9 @@ "luxon": "^3.3.0", "npm": "^9.6.3", "prop-types": "^15.8.1", - "react": "^18.2.*", + "react": "~18.2.0", "react-chartjs-2": "^5.2.0", - "react-dom": "^18.2.*", + "react-dom": "~18.2.0", "react-i18next": "^13.5.0", "react-native-paper": "^5.11.0", "react-native-paper-dates": "^0.18.12", From ccab0ca17f22a11c7c4d9c2e27c79639502e8469 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 28 Apr 2024 10:49:01 -0700 Subject: [PATCH 13/36] =?UTF-8?q?=F0=9F=90=9B=20Use=20the=20correct=20vers?= =?UTF-8?q?ion=20(v1.8.5)=20instead=20of=20just=201.8.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.cordovabuild.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.cordovabuild.json b/package.cordovabuild.json index 0a88e7133..282e3693d 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -121,7 +121,7 @@ "cordova-plugin-app-version": "0.1.14", "cordova-plugin-customurlscheme": "5.0.2", "cordova-plugin-device": "2.1.0", - "cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#1.8.5", + "cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#v1.8.5", "cordova-plugin-em-opcodeauth": "git+https://github.com/e-mission/cordova-jwt-auth.git#v1.7.2", "cordova-plugin-em-server-communication": "git+https://github.com/e-mission/cordova-server-communication.git#v1.2.6", "cordova-plugin-em-serversync": "git+https://github.com/e-mission/cordova-server-sync.git#v1.3.2", From 57819e1c6c69d8ce46a07f207ca3ea4a2a113099 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 28 Apr 2024 10:56:11 -0700 Subject: [PATCH 14/36] Only display the result of merging PRs, not of pull requests --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 121684e0a..e2a80810d 100644 --- a/README.md +++ b/README.md @@ -87,9 +87,9 @@ If you wish to connect to a different server, create your own config file accord Updating the e-mission-\* plugins or adding new plugins --- -[![osx-build-ios](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml/badge.svg)](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml) -[![osx-build-android](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml/badge.svg)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml) -[![osx-android-prereq-sdk-install](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml/badge.svg)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml) +[![osx-build-ios](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml/badge.svg?event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml?event-push) +[![osx-build-android](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml/badge.svg?event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml?event=push) +[![osx-android-prereq-sdk-install](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml/badge.svg?event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml?event=push) Pre-requisites --- From 5093541d2d6d50b4a77b5b92c6f2d1a0bcfd9a45 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 28 Apr 2024 10:59:04 -0700 Subject: [PATCH 15/36] Change all badges to only work on master and push So we don't get false negatives based on currently active pull requests --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e2a80810d..d75b62f6f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ https://github.com/e-mission/e-mission-docs/tree/master/docs/e-mission-phone Updating the UI only --- -[![osx-serve-install](https://github.com/e-mission/e-mission-phone/workflows/osx-serve-install/badge.svg)](https://github.com/e-mission/e-mission-phone/actions?query=workflow%3Aosx-serve-install) +[![osx-serve-install](https://github.com/e-mission/e-mission-phone/workflows/osx-serve-install/badge.svg?branch=master&event=push)](https://github.com/e-mission/e-mission-phone/actions?query=workflow%3Aosx-serve-install) If you want to make only UI changes, (as opposed to modifying the existing plugins, adding new plugins, etc), you can use the **new and improved** (as of June 2018) [e-mission dev app](https://github.com/e-mission/e-mission-devapp/) and install the most recent version from [releases](https://github.com/e-mission/e-mission-devapp/releases). @@ -87,9 +87,9 @@ If you wish to connect to a different server, create your own config file accord Updating the e-mission-\* plugins or adding new plugins --- -[![osx-build-ios](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml/badge.svg?event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml?event-push) -[![osx-build-android](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml/badge.svg?event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml?event=push) -[![osx-android-prereq-sdk-install](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml/badge.svg?event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml?event=push) +[![osx-build-ios](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml/badge.svg?branch=master&event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/ios-build.yml?event-push) +[![osx-build-android](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml/badge.svg?branch=master&event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-build.yml?event=push) +[![osx-android-prereq-sdk-install](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml/badge.svg?branch=master&event=push)](https://github.com/e-mission/e-mission-phone/actions/workflows/android-automated-sdk-install.yml?event=push) Pre-requisites --- From ec2057320c5af17b0b531384740466060a2347af Mon Sep 17 00:00:00 2001 From: louisg1337 Date: Fri, 3 May 2024 16:57:42 -0400 Subject: [PATCH 16/36] Added in script to change deployment for all pods --- config.cordovabuild.xml | 2 + .../before_build/ios/ios_change_deployment.js | 37 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 hooks/before_build/ios/ios_change_deployment.js diff --git a/config.cordovabuild.xml b/config.cordovabuild.xml index d3d562802..07a9573de 100644 --- a/config.cordovabuild.xml +++ b/config.cordovabuild.xml @@ -36,6 +36,8 @@ + + diff --git a/hooks/before_build/ios/ios_change_deployment.js b/hooks/before_build/ios/ios_change_deployment.js new file mode 100644 index 000000000..be1d39aac --- /dev/null +++ b/hooks/before_build/ios/ios_change_deployment.js @@ -0,0 +1,37 @@ +const fs = require('fs'); +const path = require('path'); + +function findFilePathsByFilename(directory, filename) { + const files = fs.readdirSync(directory); + const filePaths = []; + + for (const file of files) { + const filePath = path.join(directory, file); + const stats = fs.statSync(filePath); + + if (stats.isDirectory()) { + // Recursively search in subdirectories + const subdirectoryFilePaths = findFilePathsByFilename(filePath, filename); + filePaths.push(...subdirectoryFilePaths); + } else if (stats.isFile() && file === filename) { + // If the file matches the filename, add its path to the result + filePaths.push(filePath); + } + } + return filePaths; +} + + +const paths1 = findFilePathsByFilename('.', 'project.pbxproj'); +const paths2 = findFilePathsByFilename('.', 'Pods.xcodeproj'); +const paths = paths1.concat(paths2) + +console.log('Apply patch to', paths); + +for (let path of paths) { + let content = fs.readFileSync(path, { encoding: 'utf-8' }); + content = content.replace(/IPHONEOS_DEPLOYMENT_TARGET = [0-9]+.0;/g, 'IPHONEOS_DEPLOYMENT_TARGET = 12.0;'); + fs.writeFileSync(path, content); +} + +console.log('Done setting IPHONEOS_DEPLOYMENT_TARGET'); \ No newline at end of file From 8aa00107b0386a548ab033aebfaf177b9a1a8f5c Mon Sep 17 00:00:00 2001 From: louisg1337 Date: Fri, 3 May 2024 16:59:33 -0400 Subject: [PATCH 17/36] Reverted back to old workflow that uses Xcode 15 --- .github/workflows/ios-build.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ios-build.yml b/.github/workflows/ios-build.yml index ac6bb76f3..cd195b6c9 100644 --- a/.github/workflows/ios-build.yml +++ b/.github/workflows/ios-build.yml @@ -22,7 +22,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: macos-13 + runs-on: macos-lateset # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -33,12 +33,6 @@ jobs: - name: Print the xcode path run: xcode-select --print-path - - name: Set xcode to 14.1 - run: | - sudo xcode-select --switch /Applications/Xcode_14.1.app - echo "After setting xcode version "`xcode-select --print-path` - - - name: Print the xcode setup run: xcodebuild -version -sdk From 75c0c0606dcaffcd95c1f2c6ad8fb3e48b3ccf81 Mon Sep 17 00:00:00 2001 From: louisg1337 Date: Fri, 3 May 2024 17:05:47 -0400 Subject: [PATCH 18/36] Fixed typo in hook file name --- config.cordovabuild.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.cordovabuild.xml b/config.cordovabuild.xml index 07a9573de..bd88c75a3 100644 --- a/config.cordovabuild.xml +++ b/config.cordovabuild.xml @@ -37,7 +37,7 @@ - + From 79656d8bc1fe74fb06ebe8b4fdd0680d217f90ae Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 02:47:12 -0700 Subject: [PATCH 19/36] =?UTF-8?q?=F0=9F=9A=91=EF=B8=8F=20=F0=9F=90=9B=20En?= =?UTF-8?q?sure=20that=20we=20do=20show=20the=20replaced=20mode=20when=20t?= =?UTF-8?q?he=20`mode=5Fof=5Finterest`=20matches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At some point, we changed the mode and purpose user inputs as objects that were stored to be full objects, with start and end timestamps, instead of just the labels. We changed all uses of the MODE and PURPOSE to match it, but apparently forgot to change this location, where the replaced mode button is conditionally displayed. This is a quick change to make the usage here consistent with the rest of the code so that we can push this out ASAP. @JGreenlee, please let me know if there is a more principled fix, it is late and I don't want to experiment any further. Testing done: Please see PR --- www/js/survey/multilabel/confirmHelper.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/js/survey/multilabel/confirmHelper.ts b/www/js/survey/multilabel/confirmHelper.ts index 58980f3c0..cf542ec62 100644 --- a/www/js/survey/multilabel/confirmHelper.ts +++ b/www/js/survey/multilabel/confirmHelper.ts @@ -91,12 +91,12 @@ export function getLabelInputDetails(appConfigParam?) { export function labelInputDetailsForTrip(userInputForTrip, appConfigParam?) { if (appConfigParam) appConfig = appConfigParam; if (appConfig.intro.mode_studied) { - if (userInputForTrip?.['MODE']?.value == appConfig.intro.mode_studied) { - logDebug(`Found trip labeled with mode of study, ${appConfig.intro.mode_studied}. + if (userInputForTrip?.['MODE']?.data.label == appConfig.intro.mode_studied) { + logDebug(`Found trip labeled with ${userInputForTrip?.['MODE']?.data.label}, mode of study = ${appConfig.intro.mode_studied}. Needs REPLACED_MODE`); return getLabelInputDetails(); } else { - logDebug(`Found trip not labeled with mode of study, ${appConfig.intro.mode_studied}. + logDebug(`Found trip labeled with ${userInputForTrip?.['MODE']?.data.label}, not labeled with mode of study = ${appConfig.intro.mode_studied}. Doesn't need REPLACED_MODE`); return baseLabelInputDetails; } From ed61b8d4da5c442e6acd5d2c40a50b9a455e9e10 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 10:18:20 -0700 Subject: [PATCH 20/36] Handle case where there is no input through the conditional access --- www/js/survey/multilabel/confirmHelper.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/www/js/survey/multilabel/confirmHelper.ts b/www/js/survey/multilabel/confirmHelper.ts index cf542ec62..f94032f3e 100644 --- a/www/js/survey/multilabel/confirmHelper.ts +++ b/www/js/survey/multilabel/confirmHelper.ts @@ -91,12 +91,12 @@ export function getLabelInputDetails(appConfigParam?) { export function labelInputDetailsForTrip(userInputForTrip, appConfigParam?) { if (appConfigParam) appConfig = appConfigParam; if (appConfig.intro.mode_studied) { - if (userInputForTrip?.['MODE']?.data.label == appConfig.intro.mode_studied) { - logDebug(`Found trip labeled with ${userInputForTrip?.['MODE']?.data.label}, mode of study = ${appConfig.intro.mode_studied}. + if (userInputForTrip?.['MODE']?.data?.label == appConfig.intro.mode_studied) { + logDebug(`Found trip labeled with ${userInputForTrip?.['MODE']?.data?.label}, mode of study = ${appConfig.intro.mode_studied}. Needs REPLACED_MODE`); return getLabelInputDetails(); } else { - logDebug(`Found trip labeled with ${userInputForTrip?.['MODE']?.data.label}, not labeled with mode of study = ${appConfig.intro.mode_studied}. + logDebug(`Found trip labeled with ${userInputForTrip?.['MODE']?.data?.label}, not labeled with mode of study = ${appConfig.intro.mode_studied}. Doesn't need REPLACED_MODE`); return baseLabelInputDetails; } From 00a4d95e6105a1ffaf6da9426bb0c143cf6f8d9f Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 10:34:42 -0700 Subject: [PATCH 21/36] =?UTF-8?q?=F0=9F=92=A9=20Turn=20off=20the=20backgro?= =?UTF-8?q?und=20checks=20for=20fleet=20deployments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the fleet deployments are installed on work phones, and the background restriction is apparently grayed out on android work phones. https://github.com/e-mission/e-mission-docs/issues/1070 This is a hack (hence the 💩 emoji) but it will allow us to make progress through the onboarding, get the logs and then resolve the issue the right way. Or if we can't figure out how to access it the right way, this can become a config option for deployments that plan to use work phones. This should unblock https://github.com/e-mission/e-mission-docs/issues/1070#issuecomment-2094354832 --- www/js/usePermissionStatus.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/www/js/usePermissionStatus.ts b/www/js/usePermissionStatus.ts index f8fef085b..5e136655f 100644 --- a/www/js/usePermissionStatus.ts +++ b/www/js/usePermissionStatus.ts @@ -446,9 +446,10 @@ const usePermissionStatus = () => { setupAndroidFitnessChecks(); if (appConfig.tracking?.bluetooth_only) { setupAndroidBluetoothChecks(); + } else { + setupAndroidBackgroundRestrictionChecks(); } setupAndroidNotificationChecks(); - setupAndroidBackgroundRestrictionChecks(); } else if (window['device'].platform.toLowerCase() == 'ios') { setupIOSLocChecks(); setupIOSFitnessChecks(); From 8b26d2d90b26a60265e7e1f1252a9944555993a7 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 11:09:14 -0700 Subject: [PATCH 22/36] =?UTF-8?q?=E2=9C=85=20=F0=9F=90=9B=20Improve=20user?= =?UTF-8?q?=20input=20matching=20mocks=20and=20use=20them?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous unit tests for the input matching assumed that the user input would be of the form of the label options, so reused the label options as mock options. However, they actually are of the form of user input objects with data and metadata and the input as a label. We create new mock objects with the correct format, and use them in the tests. This is the reason why https://github.com/e-mission/e-mission-phone/pull/1150 was not caught for so long. And when we fixed the code, the test broke. https://github.com/e-mission/e-mission-phone/pull/1150#issuecomment-2094892441 https://github.com/e-mission/e-mission-phone/pull/1150#issuecomment-2094895504 Testing done: ``` npx jest www/__tests__/confirmHelper.test.ts Test Suites: 1 passed, 1 total Tests: 11 passed, 11 total ``` --- www/__tests__/confirmHelper.test.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/www/__tests__/confirmHelper.test.ts b/www/__tests__/confirmHelper.test.ts index 52cb9c0e8..1e24801f1 100644 --- a/www/__tests__/confirmHelper.test.ts +++ b/www/__tests__/confirmHelper.test.ts @@ -45,6 +45,16 @@ const fakeDefaultLabelOptions = { }, }, }; +const fakeInputs = { + MODE: [ + { data: { label: 'walk', start_ts: 1245, end_ts: 5678}}, + { data: { label: 'bike', start_ts: 1245, end_ts: 5678}}, + ], + PURPOSE: [ + { data: {label: 'home', start_ts: 1245, end_ts: 5678 }}, + { data: {label: 'work', start_ts: 1245, end_ts: 5678 }} + ], +}; jest.mock('../js/services/commHelper', () => ({ ...jest.requireActual('../js/services/commHelper'), @@ -62,8 +72,8 @@ describe('confirmHelper', () => { it('returns base labelInputDetails for a labelUserInput which does not have mode of study', () => { const fakeLabelUserInput = { - MODE: fakeDefaultLabelOptions.MODE[1], - PURPOSE: fakeDefaultLabelOptions.PURPOSE[0], + MODE: fakeInputs.MODE[1], + PURPOSE: fakeInputs.PURPOSE[0], }; const labelInputDetails = labelInputDetailsForTrip( fakeLabelUserInput, @@ -74,8 +84,8 @@ describe('confirmHelper', () => { 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], + MODE: fakeInputs.MODE[0], // 'walk' is mode of study + PURPOSE: fakeInputs.PURPOSE[0], }; const labelInputDetails = labelInputDetailsForTrip( fakeLabelUserInput, From 20b9e1aa5c0fe99a8d57cfea4b3d0fa6d7c6881b Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 11:19:51 -0700 Subject: [PATCH 23/36] Fix formatting --- www/__tests__/confirmHelper.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/www/__tests__/confirmHelper.test.ts b/www/__tests__/confirmHelper.test.ts index 1e24801f1..b95b10372 100644 --- a/www/__tests__/confirmHelper.test.ts +++ b/www/__tests__/confirmHelper.test.ts @@ -47,12 +47,12 @@ const fakeDefaultLabelOptions = { }; const fakeInputs = { MODE: [ - { data: { label: 'walk', start_ts: 1245, end_ts: 5678}}, - { data: { label: 'bike', start_ts: 1245, end_ts: 5678}}, + { data: { label: 'walk', start_ts: 1245, end_ts: 5678 } }, + { data: { label: 'bike', start_ts: 1245, end_ts: 5678 } }, ], PURPOSE: [ - { data: {label: 'home', start_ts: 1245, end_ts: 5678 }}, - { data: {label: 'work', start_ts: 1245, end_ts: 5678 }} + { data: { label: 'home', start_ts: 1245, end_ts: 5678 } }, + { data: { label: 'work', start_ts: 1245, end_ts: 5678 } }, ], }; From 9921c52b7c8ccd6848cc9072bbc210ff3b876764 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 11:55:52 -0700 Subject: [PATCH 24/36] =?UTF-8?q?=F0=9F=92=A9=20=F0=9F=94=A7=20Disable=20o?= =?UTF-8?q?nly=20the=20unused=20apps=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the previous commit, we had disabled all android background checks. This means that the optimization check would also be disabled, and we would not be able to start the foreground service from the background. Let's change this to only disable to unused apps check --- www/js/usePermissionStatus.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/www/js/usePermissionStatus.ts b/www/js/usePermissionStatus.ts index 5e136655f..81c4abf54 100644 --- a/www/js/usePermissionStatus.ts +++ b/www/js/usePermissionStatus.ts @@ -407,7 +407,11 @@ const usePermissionStatus = () => { refresh: checkBatteryOpt, }; let tempChecks = checkList; - tempChecks.push(unusedAppsUnrestrictedCheck, ignoreBatteryOptCheck); + if (appConfig.tracking?.bluetooth_only) { + tempChecks.push(ignoreBatteryOptCheck); + } else { + tempChecks.push(unusedAppsUnrestrictedCheck, ignoreBatteryOptCheck); + } setCheckList(tempChecks); } @@ -446,10 +450,9 @@ const usePermissionStatus = () => { setupAndroidFitnessChecks(); if (appConfig.tracking?.bluetooth_only) { setupAndroidBluetoothChecks(); - } else { - setupAndroidBackgroundRestrictionChecks(); } setupAndroidNotificationChecks(); + setupAndroidBackgroundRestrictionChecks(); } else if (window['device'].platform.toLowerCase() == 'ios') { setupIOSLocChecks(); setupIOSFitnessChecks(); From bc4c4ce7800216c6d117a2cc31261496286f47c0 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 12:17:55 -0700 Subject: [PATCH 25/36] =?UTF-8?q?=F0=9F=93=8C=20=E2=AC=86=EF=B8=8F=20=20Up?= =?UTF-8?q?grade=20the=20pinned=20version=20of=20the=20OS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - We want to upgrade the pinned version of the OS to the next one - We don't want to run on `latest` because then changes to the underlying runner will break all tests, including for pull requests, and block development. Instead, we should schedule a periodic (~ once a week) check against `latest` so we know when the latest has changed, and can fix it before again bumping up the pinned version @louisg1337 Also, there was a typo in `latest` :smile --- .github/workflows/ios-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ios-build.yml b/.github/workflows/ios-build.yml index cd195b6c9..695ed02de 100644 --- a/.github/workflows/ios-build.yml +++ b/.github/workflows/ios-build.yml @@ -22,7 +22,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: macos-lateset + runs-on: macos-14 # Steps represent a sequence of tasks that will be executed as part of the job steps: From ab2ff6ef7ad3fbcc8651232d46274a973889815f Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 12:23:57 -0700 Subject: [PATCH 26/36] =?UTF-8?q?=F0=9F=93=8C=20=E2=AC=86=EF=B8=8F=20=20Bu?= =?UTF-8?q?mp=20up=20the=20pinned=20version=20for=20iOS=20deployment=20to?= =?UTF-8?q?=2013?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the react rewrite, we require a min version of 13 https://github.com/e-mission/e-mission-docs/issues/932#issuecomment-1637153502 to https://github.com/e-mission/e-mission-docs/issues/932#issuecomment-1637170876 to https://github.com/e-mission/e-mission-docs/issues/932#issuecomment-1675644465 to https://github.com/e-mission/e-mission-docs/issues/932#issuecomment-1675703217 --- config.cordovabuild.xml | 2 +- hooks/before_build/ios/ios_change_deployment.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config.cordovabuild.xml b/config.cordovabuild.xml index bd88c75a3..3401b9b9b 100644 --- a/config.cordovabuild.xml +++ b/config.cordovabuild.xml @@ -36,7 +36,7 @@ - + diff --git a/hooks/before_build/ios/ios_change_deployment.js b/hooks/before_build/ios/ios_change_deployment.js index be1d39aac..ad381162d 100644 --- a/hooks/before_build/ios/ios_change_deployment.js +++ b/hooks/before_build/ios/ios_change_deployment.js @@ -30,8 +30,8 @@ console.log('Apply patch to', paths); for (let path of paths) { let content = fs.readFileSync(path, { encoding: 'utf-8' }); - content = content.replace(/IPHONEOS_DEPLOYMENT_TARGET = [0-9]+.0;/g, 'IPHONEOS_DEPLOYMENT_TARGET = 12.0;'); + content = content.replace(/IPHONEOS_DEPLOYMENT_TARGET = [0-9]+.0;/g, 'IPHONEOS_DEPLOYMENT_TARGET = 13.0;'); fs.writeFileSync(path, content); } -console.log('Done setting IPHONEOS_DEPLOYMENT_TARGET'); \ No newline at end of file +console.log('Done setting IPHONEOS_DEPLOYMENT_TARGET'); From f504f68b039d2d5d92be71dcbc060073fdd1bf8d Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 13:18:06 -0700 Subject: [PATCH 27/36] =?UTF-8?q?=F0=9F=92=9A=20=F0=9F=91=B7=20Remove=20th?= =?UTF-8?q?e=20check=20for=20patcher?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We removed patcher in 41ff1a772756470fc4114ac555b543271180e4be so we should not expect to see it in the file structure any more This should fix the failing build ``` + ls -al /tmp/new-install/Android/sdk + '[' '!' -d /tmp/new-install/Android/sdk/emulator ']' + '[' '!' -d /tmp/new-install/Android/sdk/build-tools ']' + '[' '!' -d /tmp/new-install/Android/sdk/patcher ']' + exit 1 ``` Also, run CI on changes to the SDK package list (i.e. `setup/android_sdk_packages`) so that such errors are caught in the PR stage in the future --- .github/workflows/android-automated-sdk-install.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/android-automated-sdk-install.yml b/.github/workflows/android-automated-sdk-install.yml index f4275a98d..2ece9029f 100644 --- a/.github/workflows/android-automated-sdk-install.yml +++ b/.github/workflows/android-automated-sdk-install.yml @@ -6,10 +6,12 @@ on: push: paths: - 'setup/prereq_android_sdk_install.sh' + - 'setup/android_sdk_packages' - '.github/workflows/android-automated-sdk-install.yml' pull_request: paths: - 'setup/prereq_android_sdk_install.sh' + - 'setup/android_sdk_packages' - '.github/workflows/android-automated-sdk-install.yml' schedule: # * is a special character in YAML so you have to quote this string @@ -64,7 +66,6 @@ jobs: ls -al $ANDROID_SDK_ROOT if [ ! -d $ANDROID_SDK_ROOT/emulator ]; then exit 1; fi if [ ! -d $ANDROID_SDK_ROOT/build-tools ]; then exit 1; fi - if [ ! -d $ANDROID_SDK_ROOT/patcher ]; then exit 1; fi if [ ! -d $ANDROID_SDK_ROOT/extras ]; then exit 1; fi if [ ! -d $ANDROID_SDK_ROOT/platforms ]; then exit 1; fi if [ ! -d $ANDROID_SDK_ROOT/platform-tools ]; then exit 1; fi From 4ec96df51481c27691dbf736c853029d3f40a9b7 Mon Sep 17 00:00:00 2001 From: "K. Shankari" Date: Sun, 5 May 2024 15:47:56 -0700 Subject: [PATCH 28/36] =?UTF-8?q?=F0=9F=92=9A=20=E2=AC=86=EF=B8=8F=20=20Up?= =?UTF-8?q?grade=20the=20pinned=20version=20of=20the=20runner=20to=2014-la?= =?UTF-8?q?rge=20(#1153)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 💚 ⬆️ Upgrade the pinned version of the runner to 14-large This should still have the correct environment variable because `14-large` runs x64 https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md `macos-14` now runs amd, and we have to figure out how to run it. Maybe we can run both as part of a schedule instead of on every pull request * 💚 Switch to the macos version supported for free public repos Switch to the environmental variable supported by it https://github.com/actions/runner-images/blob/main/images/macos/macos-14-arm64-Readme.md * 💚 Set all instances of JAVA_HOME to the new arm64 value --- .github/workflows/android-build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index 90c75d147..ddc6b2ee0 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -22,7 +22,7 @@ jobs: # This workflow contains a single job called "build" build: # The type of runner that the job will run on - runs-on: macos-13 + runs-on: macos-14 # Steps represent a sequence of tasks that will be executed as part of the job steps: @@ -39,7 +39,7 @@ jobs: echo "Default java version" java -version echo "Setting to Java 11 instead" - export JAVA_HOME=$JAVA_HOME_11_X64 + export JAVA_HOME=$JAVA_HOME_17_arm64 java -version echo "Checking gradle" which gradle @@ -53,13 +53,13 @@ jobs: - name: Setup the cordova environment shell: bash -l {0} run: | - export JAVA_HOME=$JAVA_HOME_11_X64 + export JAVA_HOME=$JAVA_HOME_17_arm64 bash setup/setup_android_native.sh - name: Check tool versions shell: bash -l {0} run: | - export JAVA_HOME=$JAVA_HOME_11_X64 + export JAVA_HOME=$JAVA_HOME_17_arm64 source setup/activate_native.sh echo "cordova version" npx cordova -version @@ -77,7 +77,7 @@ jobs: gradle -version echo "Let's rerun the activation" source setup/activate_native.sh - export JAVA_HOME=$JAVA_HOME_11_X64 + export JAVA_HOME=$JAVA_HOME_17_arm64 echo $PATH which gradle gradle --version From 1260e76523c0de2be505e167aa823d27e4632c93 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Tue, 7 May 2024 13:40:49 -0400 Subject: [PATCH 29/36] fill in new BLE fields & update types `ble_sensed_mode` and `ble_sensed_summary` were added to sections and confirmed trips in https://github.com/e-mission/e-mission-server/pull/965. We are going to need `ble_sensed_mode` to determine vehicle info. The section summaries (`ble_sensed_summary`, `cleaned_section_summary` and `inferred_section_summary`) are not used in unprocessed trips currently, but I am adding them so there is less of a gap between composite trips and unprocessed trips --- package.cordovabuild.json | 1 + package.serve.json | 1 + www/__tests__/timelineHelper.test.ts | 3 ++- www/js/diary/timelineHelper.ts | 40 +++++++++++++++++++++++++--- www/js/types/diaryTypes.ts | 6 +++++ 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/package.cordovabuild.json b/package.cordovabuild.json index 282e3693d..f5d9b6d58 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -139,6 +139,7 @@ "cordova-custom-config": "^5.1.1", "cordova-plugin-ibeacon": "git+https://github.com/louisg1337/cordova-plugin-ibeacon.git", "core-js": "^2.5.7", + "e-mission-common": "git+https://github.com/JGreenlee/e-mission-common.git", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", "fast-xml-parser": "^4.2.2", diff --git a/package.serve.json b/package.serve.json index f6f5c2ae3..af33531ae 100644 --- a/package.serve.json +++ b/package.serve.json @@ -65,6 +65,7 @@ "chartjs-adapter-luxon": "^1.3.1", "chartjs-plugin-annotation": "^3.0.1", "core-js": "^2.5.7", + "e-mission-common": "git+https://github.com/JGreenlee/e-mission-common.git", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", "fast-xml-parser": "^4.2.2", diff --git a/www/__tests__/timelineHelper.test.ts b/www/__tests__/timelineHelper.test.ts index aafe13926..c40262aae 100644 --- a/www/__tests__/timelineHelper.test.ts +++ b/www/__tests__/timelineHelper.test.ts @@ -291,7 +291,7 @@ jest.mock('../js/services/unifiedDataLoader', () => ({ })); it('works when there are no unprocessed trips...', async () => { - expect(readUnprocessedTrips(-1, -1, {} as any)).resolves.toEqual([]); + expect(readUnprocessedTrips(-1, -1, {} as any, {} as any)).resolves.toEqual([]); }); it('works when there are one or more unprocessed trips...', async () => { @@ -299,6 +299,7 @@ it('works when there are one or more unprocessed trips...', async () => { mockTLH.fakeStartTsOne, mockTLH.fakeEndTsOne, {} as any, + {} as any, ); expect(testValueOne.length).toEqual(1); expect(testValueOne[0]).toEqual( diff --git a/www/js/diary/timelineHelper.ts b/www/js/diary/timelineHelper.ts index 6e82c0fbf..f850f0074 100644 --- a/www/js/diary/timelineHelper.ts +++ b/www/js/diary/timelineHelper.ts @@ -17,12 +17,14 @@ import { BluetoothBleData, SectionData, CompositeTripLocation, + SectionSummary, } from '../types/diaryTypes'; import { getLabelInputDetails, getLabelInputs } from '../survey/multilabel/confirmHelper'; import { LabelOptions } from '../types/labelTypes'; import { EnketoUserInputEntry, filterByNameAndVersion } from '../survey/enketo/enketoHelper'; import { AppConfig } from '../types/appConfigTypes'; import { Point, Feature } from 'geojson'; +import { ble_matching } from 'e-mission-common'; const cachedGeojsons: Map = new Map(); @@ -306,10 +308,25 @@ const dateTime2localdate = (currtime: DateTime, tz: string) => ({ second: currtime.second, }); +/* Compute a section summary, which is really simple for unprocessed trips because they are + always assumed to be unimodal. +/* maybe unify with eaum.get_section_summary on e-mission-server at some point */ +const getSectionSummaryForUnprocessed = (section: SectionData, modeProp): SectionSummary => { + const baseMode = section[modeProp] || 'UNKNOWN'; + return { + count: { [baseMode]: 1 }, + distance: { [baseMode]: section.distance }, + duration: { [baseMode]: section.duration }, + }; +}; + /** * @description Given an array of location points, creates an UnprocessedTrip object. */ -function points2UnprocessedTrip(locationPoints: Array>): UnprocessedTrip { +function points2UnprocessedTrip( + locationPoints: Array>, + appConfig: AppConfig, +): UnprocessedTrip { const startPoint = locationPoints[0]; const endPoint = locationPoints[locationPoints.length - 1]; const tripAndSectionId = `unprocessed_${startPoint.data.ts}_${endPoint.data.ts}`; @@ -369,6 +386,12 @@ function points2UnprocessedTrip(locationPoints: Array> origin_key: 'UNPROCESSED_section', sensed_mode: 4, // MotionTypes.UNKNOWN (4) sensed_mode_str: 'UNKNOWN', + ble_sensed_mode: ble_matching.get_ble_sensed_vehicle_for_section( + unprocessedBleScans, + baseProps.start_ts, + baseProps.end_ts, + appConfig, + ), trip_id: { $oid: tripAndSectionId }, }; @@ -377,6 +400,9 @@ function points2UnprocessedTrip(locationPoints: Array> ...baseProps, _id: { $oid: tripAndSectionId }, additions: [], + ble_sensed_summary: getSectionSummaryForUnprocessed(singleSection, 'ble_sensed_mode'), + cleaned_section_summary: getSectionSummaryForUnprocessed(singleSection, 'sensed_mode_str'), + inferred_section_summary: getSectionSummaryForUnprocessed(singleSection, 'sensed_mode_str'), confidence_threshold: 0, expectation: { to_label: true }, inferred_labels: [], @@ -395,7 +421,10 @@ const tsEntrySort = (e1: BEMData, e2: BEMData): Promise { +function tripTransitions2UnprocessedTrip( + trip: Array, + appConfig: AppConfig, +): Promise { const tripStartTransition = trip[0]; const tripEndTransition = trip[1]; const tq = { @@ -437,7 +466,7 @@ function tripTransitions2UnprocessedTrip(trip: Array): Promise { logDebug(JSON.stringify(trip, null, 2)); }); - const tripFillPromises = tripsList.map(tripTransitions2UnprocessedTrip); + const tripFillPromises = tripsList.map((t) => + tripTransitions2UnprocessedTrip(t, appConfig), + ); return Promise.all(tripFillPromises).then( (rawTripObjs: (UnprocessedTrip | undefined)[]) => { // Now we need to link up the trips. linking unprocessed trips diff --git a/www/js/types/diaryTypes.ts b/www/js/types/diaryTypes.ts index 9757e95cf..53b618be0 100644 --- a/www/js/types/diaryTypes.ts +++ b/www/js/types/diaryTypes.ts @@ -4,6 +4,7 @@ import { BaseModeKey, MotionTypeKey } from '../diary/diaryHelper'; import useDerivedProperties from '../diary/useDerivedProperties'; +import { VehicleIdentity } from './appConfigTypes'; import { MultilabelKey } from './labelTypes'; import { BEMData, LocalDt } from './serverData'; import { FeatureCollection, Feature, Geometry, Point, Position } from 'geojson'; @@ -58,6 +59,8 @@ export type CompositeTripLocation = { export type UnprocessedTrip = { _id: ObjectId; additions: []; // unprocessed trips won't have any matched processed inputs, so this is always empty + ble_sensed_summary: SectionSummary; + cleaned_section_summary: SectionSummary; confidence_threshold: number; distance: number; duration: number; @@ -67,6 +70,7 @@ export type UnprocessedTrip = { end_ts: number; expectation: { to_label: true }; // unprocessed trips are always expected to be labeled inferred_labels: []; // unprocessed trips won't have inferred labels + inferred_section_summary: SectionSummary; key: 'UNPROCESSED_trip'; locations?: CompositeTripLocation[]; origin_key: 'UNPROCESSED_trip'; @@ -85,6 +89,7 @@ export type UnprocessedTrip = { export type CompositeTrip = { _id: ObjectId; additions: UserInputEntry[]; + ble_sensed_summary: SectionSummary; cleaned_section_summary: SectionSummary; cleaned_trip: ObjectId; confidence_threshold: number; @@ -202,6 +207,7 @@ export type SectionData = { key: string; origin_key: string; trip_id: ObjectId; + ble_sensed_mode: VehicleIdentity; sensed_mode: number; source: string; // e.x., "SmoothedHighConfidenceMotion" start_ts: number; // Unix From 71303475864c64635389ab607c48228ba6489bbd Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Tue, 7 May 2024 14:05:54 -0400 Subject: [PATCH 30/36] use only unprocessed BLE scans in label tab Since we have matching on the server, processed sections will already have BLE sensed modes filled in. We no longer need to query for all BLE scans; only unprocessed ones (ie newer than pipeline end ts). Then while we are constructing unprocessed trips, the list of unprocessed BLE scans will be used to determine BLE sensed modes. We no longer need timelineBleMap and won't treat BLE scans like user inputs anymore. For 'confirmedMode', instead of using timelineBleMap, we will use the ble_sensed_mode of the primary section. New simple function in diaryHelper to deternmine the primary section. --- www/js/diary/LabelTab.tsx | 28 +++++++++++----------------- www/js/diary/LabelTabContext.ts | 4 ++-- www/js/diary/diaryHelper.ts | 9 +++++++++ 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/www/js/diary/LabelTab.tsx b/www/js/diary/LabelTab.tsx index 938a861f3..1e9dd3521 100644 --- a/www/js/diary/LabelTab.tsx +++ b/www/js/diary/LabelTab.tsx @@ -29,11 +29,7 @@ import { getLabelOptions, labelOptionByValue } from '../survey/multilabel/confir import { displayError, displayErrorMsg, logDebug, logWarn } from '../plugin/logger'; import { useTheme } from 'react-native-paper'; import { getPipelineRangeTs } from '../services/commHelper'; -import { - getNotDeletedCandidates, - mapBleScansToTimelineEntries, - mapInputsToTimelineEntries, -} from '../survey/inputMatcher'; +import { getNotDeletedCandidates, mapInputsToTimelineEntries } from '../survey/inputMatcher'; import { configuredFilters as multilabelConfiguredFilters } from '../survey/multilabel/infinite_scroll_filters'; import { configuredFilters as enketoConfiguredFilters } from '../survey/enketo/infinite_scroll_filters'; import LabelTabContext, { @@ -45,6 +41,7 @@ import LabelTabContext, { import { readAllCompositeTrips, readUnprocessedTrips } from './timelineHelper'; import { LabelOptions, MultilabelKey } from '../types/labelTypes'; import { CompositeTrip, TimelineEntry, TimestampRange, UserInputEntry } from '../types/diaryTypes'; +import { primarySectionForTrip } from './diaryHelper'; let showPlaces; const ONE_DAY = 24 * 60 * 60; // seconds @@ -63,7 +60,6 @@ const LabelTab = () => { const [timelineMap, setTimelineMap] = useState(null); const [timelineLabelMap, setTimelineLabelMap] = useState(null); const [timelineNotesMap, setTimelineNotesMap] = useState(null); - const [timelineBleMap, setTimelineBleMap] = useState(null); const [displayedEntries, setDisplayedEntries] = useState(null); const [refreshTime, setRefreshTime] = useState(null); const [isLoading, setIsLoading] = useState('replace'); @@ -105,15 +101,8 @@ const LabelTab = () => { allEntries, appConfig, ); - setTimelineLabelMap(newTimelineLabelMap); setTimelineNotesMap(newTimelineNotesMap); - - if (appConfig.vehicle_identities?.length) { - const newTimelineBleMap = mapBleScansToTimelineEntries(allEntries, appConfig); - setTimelineBleMap(newTimelineBleMap); - } - applyFilters(timelineMap, newTimelineLabelMap); } catch (e) { displayError(e, t('errors.while-updating-timeline')); @@ -171,7 +160,7 @@ const LabelTab = () => { unprocessedNotes = ${JSON.stringify(unprocessedNotes)}`); if (appConfig.vehicle_identities?.length) { await updateUnprocessedBleScans({ - start_ts: pipelineRange.start_ts, + start_ts: pipelineRange.end_ts, end_ts: Date.now() / 1000, }); logDebug(`LabelTab: After updating unprocessedBleScans, @@ -301,7 +290,12 @@ const LabelTab = () => { .reverse() .find((trip) => trip.origin_key.includes('trip')) as CompositeTrip; } - readUnprocessedPromise = readUnprocessedTrips(pipelineRange.end_ts, nowTs, lastProcessedTrip); + readUnprocessedPromise = readUnprocessedTrips( + pipelineRange.end_ts, + nowTs, + appConfig, + lastProcessedTrip, + ); } else { readUnprocessedPromise = Promise.resolve([]); } @@ -336,8 +330,8 @@ const LabelTab = () => { * @returns Confirmed mode, which could be a vehicle identity as determined by Bluetooth scans, * or the label option from a user-given 'MODE' label, or undefined if neither exists. */ - const confirmedModeFor = (tlEntry: TimelineEntry) => - timelineBleMap?.[tlEntry._id.$oid] || labelFor(tlEntry, 'MODE'); + const confirmedModeFor = (tlEntry: CompositeTrip) => + primarySectionForTrip(tlEntry)?.ble_sensed_mode || labelFor(tlEntry, 'MODE'); function addUserInputToEntry(oid: string, userInput: any, inputType: 'label' | 'note') { const tlEntry = timelineMap?.get(oid); diff --git a/www/js/diary/LabelTabContext.ts b/www/js/diary/LabelTabContext.ts index 791cb4cd5..2feb0cce7 100644 --- a/www/js/diary/LabelTabContext.ts +++ b/www/js/diary/LabelTabContext.ts @@ -1,5 +1,5 @@ import { createContext } from 'react'; -import { TimelineEntry, TimestampRange, UserInputEntry } from '../types/diaryTypes'; +import { CompositeTrip, TimelineEntry, TimestampRange, UserInputEntry } from '../types/diaryTypes'; import { LabelOption, LabelOptions, MultilabelKey } from '../types/labelTypes'; import { EnketoUserInputEntry } from '../survey/enketo/enketoHelper'; import { VehicleIdentity } from '../types/appConfigTypes'; @@ -35,7 +35,7 @@ type ContextProps = { userInputFor: (tlEntry: TimelineEntry) => UserInputMap | undefined; notesFor: (tlEntry: TimelineEntry) => UserInputEntry[] | undefined; labelFor: (tlEntry: TimelineEntry, labelType: MultilabelKey) => LabelOption | undefined; - confirmedModeFor: (tlEntry: TimelineEntry) => VehicleIdentity | LabelOption | undefined; + confirmedModeFor: (tlEntry: CompositeTrip) => VehicleIdentity | LabelOption | undefined; addUserInputToEntry: (oid: string, userInput: any, inputType: 'label' | 'note') => void; displayedEntries: TimelineEntry[] | null; filterInputs: LabelTabFilter[]; diff --git a/www/js/diary/diaryHelper.ts b/www/js/diary/diaryHelper.ts index f02797fff..12495742d 100644 --- a/www/js/diary/diaryHelper.ts +++ b/www/js/diary/diaryHelper.ts @@ -192,6 +192,15 @@ export function getFormattedSectionProperties(trip: CompositeTrip, imperialConfi })); } +/** + * @param trip A composite trip object + * @return the primary section of the trip, i.e. the section with the greatest distance + */ +export function primarySectionForTrip(trip: CompositeTrip) { + if (!trip.sections?.length) return undefined; + return trip.sections.reduce((prev, curr) => (prev.distance > curr.distance ? prev : curr)); +} + export function getLocalTimeString(dt?: LocalDt) { if (!dt) return; const dateTime = DateTime.fromObject({ From 48eca9de7f44969c4c7dc00acab0552e6bcc0703 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Tue, 7 May 2024 14:16:13 -0400 Subject: [PATCH 31/36] use e-mission-common 0.4.4 I was just using the latest / master branch to test, but it should really be locked to a release --- package.cordovabuild.json | 2 +- package.serve.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.cordovabuild.json b/package.cordovabuild.json index f5d9b6d58..c1317a782 100644 --- a/package.cordovabuild.json +++ b/package.cordovabuild.json @@ -139,7 +139,7 @@ "cordova-custom-config": "^5.1.1", "cordova-plugin-ibeacon": "git+https://github.com/louisg1337/cordova-plugin-ibeacon.git", "core-js": "^2.5.7", - "e-mission-common": "git+https://github.com/JGreenlee/e-mission-common.git", + "e-mission-common": "git+https://github.com/JGreenlee/e-mission-common.git#0.4.4", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", "fast-xml-parser": "^4.2.2", diff --git a/package.serve.json b/package.serve.json index af33531ae..b610d6121 100644 --- a/package.serve.json +++ b/package.serve.json @@ -65,7 +65,7 @@ "chartjs-adapter-luxon": "^1.3.1", "chartjs-plugin-annotation": "^3.0.1", "core-js": "^2.5.7", - "e-mission-common": "git+https://github.com/JGreenlee/e-mission-common.git", + "e-mission-common": "git+https://github.com/JGreenlee/e-mission-common.git#0.4.4", "enketo-core": "^6.1.7", "enketo-transformer": "^4.0.0", "fast-xml-parser": "^4.2.2", From 2b0f4af73a90e9f3c0caa9e923be190c8517d83c Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Wed, 8 May 2024 16:56:29 -0400 Subject: [PATCH 32/36] don't exclude e-mission-common from transform before jest tests --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 939834e51..73521d81e 100644 --- a/jest.config.js +++ b/jest.config.js @@ -12,7 +12,7 @@ module.exports = { "^.+\\.(ts|tsx|js|jsx)$": "babel-jest" }, transformIgnorePatterns: [ - "node_modules/(?!((enketo-transformer/dist/enketo-transformer/web)|(jest-)?react-native(-.*)?|@react-native(-community)?)/)", + "node_modules/(?!((enketo-transformer/dist/enketo-transformer/web)|(jest-)?react-native(-.*)?|@react-native(-community)?|e-mission-common)/)" ], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], moduleDirectories: ["node_modules", "src"], From 02655ba28913a06caa80795aa3a07a4b15d06ef3 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Thu, 9 May 2024 15:25:54 -0400 Subject: [PATCH 33/36] refactor ENKETO userinputs to use survey name as key instead of 'SURVEY' Part of the initiative described in https://github.com/e-mission/e-mission-docs/issues/1045 This implements the "proposed" structure on the phone for Enketo user inputs. When unprocessed user inputs are read and stored into unprocessedLabels in timelineHelper, responses will now be sorted by survey name instead of all being kept in one slot 'SURVEY'. Also implement a new function, enketoHelper > resolveSurveyButtonConfig, which reads the config and determines what survey(s) are available for a button (like 'trip label', 'trip addition', 'place label', 'place addition'). Includes backwards compat so old configs (which don't have those fields) will just use the default 'TripConfirmSurvey'. Including the translations for TripConfirmSurvey's 'not-filled-in-label' directly in the backwards compat to be consistent with new-style surveys where the translations are directly in the config. This new function is used by , simplifying the code that was there previously. It now stores the returned SurveyButtonConfig object as 'survey' rather than keeping 'surveyName' and 'notFilledInLabel' separately. This allows conditionalSurveys > getSurveyForTimelineEntry to be simplified too. The new function is also used in timelineHelper > updateUnprocessedInputs. Instead of calling filterByNameAndVersion only for 'TripConfirmSurvey' and then storing those under the key of 'SURVEY', it first calls `resolveSurveyButtonConfig` to get *all* possible 'trip-label' surveys, iterates over those surveys calling filterByNameAndVersion for each, and stores unprocessed responses using the name of the survey as the key. See types updated in LabelTabContext.ts. appConfigTypes.ts updated to reflect that showsIf is optional (if not present, that survey shows unconditionally) --- www/js/diary/LabelTabContext.ts | 12 +++--- www/js/diary/timelineHelper.ts | 16 ++++++-- www/js/survey/enketo/UserInputButton.tsx | 41 +++++++++---------- www/js/survey/enketo/conditionalSurveys.ts | 16 +++----- www/js/survey/enketo/enketoHelper.ts | 28 ++++++++++++- .../survey/enketo/infinite_scroll_filters.ts | 3 +- www/js/survey/inputMatcher.ts | 18 ++++---- www/js/types/appConfigTypes.ts | 2 +- 8 files changed, 85 insertions(+), 51 deletions(-) diff --git a/www/js/diary/LabelTabContext.ts b/www/js/diary/LabelTabContext.ts index 2feb0cce7..46b58b95e 100644 --- a/www/js/diary/LabelTabContext.ts +++ b/www/js/diary/LabelTabContext.ts @@ -5,13 +5,13 @@ import { EnketoUserInputEntry } from '../survey/enketo/enketoHelper'; import { VehicleIdentity } from '../types/appConfigTypes'; export type UserInputMap = { - /* if the key here is 'SURVEY', we are in the ENKETO configuration, meaning the user input - value will have the raw 'xmlResponse' string */ - SURVEY?: EnketoUserInputEntry; -} & { - /* all other keys, (e.g. 'MODE', 'PURPOSE') are from the MULTILABEL configuration - and will have the 'label' string but no 'xmlResponse' string */ + /* If keys are 'MODE', 'PURPOSE', 'REPLACED_MODE', this is the MULTILABEL configuration. + Values are entries that have a 'label' value in their 'data' */ [k in MultilabelKey]?: UserInputEntry; +} & { + /* Otherwise we are in the ENKETO configuration, and keys are names of surveys. + Values are entries that have an 'xmlResponse' value in their 'data' */ + [k: string]: EnketoUserInputEntry | undefined; }; export type TimelineMap = Map; // Todo: update to reflect unpacked trips (origin_Key, etc) diff --git a/www/js/diary/timelineHelper.ts b/www/js/diary/timelineHelper.ts index f850f0074..85b507780 100644 --- a/www/js/diary/timelineHelper.ts +++ b/www/js/diary/timelineHelper.ts @@ -21,7 +21,11 @@ import { } from '../types/diaryTypes'; import { getLabelInputDetails, getLabelInputs } from '../survey/multilabel/confirmHelper'; import { LabelOptions } from '../types/labelTypes'; -import { EnketoUserInputEntry, filterByNameAndVersion } from '../survey/enketo/enketoHelper'; +import { + EnketoUserInputEntry, + filterByNameAndVersion, + resolveSurveyButtonConfig, +} from '../survey/enketo/enketoHelper'; import { AppConfig } from '../types/appConfigTypes'; import { Point, Feature } from 'geojson'; import { ble_matching } from 'e-mission-common'; @@ -91,7 +95,8 @@ export function compositeTrips2TimelineMap(ctList: Array, unpackPlaces?: bo } /* 'LABELS' are 1:1 - each trip or place has a single label for each label type - (e.g. 'MODE' and 'PURPOSE' for MULTILABEL configuration, or 'SURVEY' for ENKETO configuration) */ + (e.g. 'MODE' and 'PURPOSE' for MULTILABEL configuration, or the name of the survey + for ENKETO configuration) */ export let unprocessedLabels: { [key: string]: UserInputEntry[] } = {}; /* 'NOTES' are 1:n - each trip or place can have any number of notes */ export let unprocessedNotes: EnketoUserInputEntry[] = []; @@ -117,8 +122,11 @@ function updateUnprocessedInputs( // fill in the unprocessedLabels object with the labels we just read labelResults.forEach((r, i) => { if (appConfig.survey_info?.['trip-labels'] == 'ENKETO') { - const filtered = filterByNameAndVersion('TripConfirmSurvey', r, appConfig); - unprocessedLabels['SURVEY'] = filtered as UserInputEntry[]; + const tripSurveys = resolveSurveyButtonConfig(appConfig, 'trip-label'); + tripSurveys.forEach((survey) => { + const filtered = filterByNameAndVersion(survey.surveyName, r, appConfig); + unprocessedLabels[survey.surveyName] = filtered as UserInputEntry[]; + }); } else { unprocessedLabels[getLabelInputs()[i]] = r; } diff --git a/www/js/survey/enketo/UserInputButton.tsx b/www/js/survey/enketo/UserInputButton.tsx index e3e629bda..de66d5d7f 100644 --- a/www/js/survey/enketo/UserInputButton.tsx +++ b/www/js/survey/enketo/UserInputButton.tsx @@ -18,6 +18,8 @@ import LabelTabContext from '../../diary/LabelTabContext'; import useAppConfig from '../../useAppConfig'; import { getSurveyForTimelineEntry } from './conditionalSurveys'; import useDerivedProperties from '../../diary/useDerivedProperties'; +import { resolveSurveyButtonConfig } from './enketoHelper'; +import { SurveyButtonConfig } from '../../types/appConfigTypes'; type Props = { timelineEntry: any; @@ -33,28 +35,25 @@ const UserInputButton = ({ timelineEntry }: Props) => { const derivedTripProps = useDerivedProperties(timelineEntry); // which survey will this button launch? - const [surveyName, notFilledInLabel] = useMemo(() => { - if (!appConfig) return []; // no config loaded yet; show blank for now - const tripLabelConfig = appConfig?.survey_info?.buttons?.['trip-label']; - if (!tripLabelConfig) { - // config doesn't specify; use default - return ['TripConfirmSurvey', t('diary.choose-survey')]; - } - // config lists one or more surveys; find which one to use - const s = getSurveyForTimelineEntry(tripLabelConfig, timelineEntry, derivedTripProps); - const lang = i18n.resolvedLanguage || 'en'; - return [s?.surveyName, s?.['not-filled-in-label'][lang]]; + const survey = useMemo(() => { + if (!appConfig) return null; // no config loaded yet; show blank for now + const possibleSurveysForButton = resolveSurveyButtonConfig(appConfig, 'trip-label'); + // if there is only one survey, no need to check further + if (possibleSurveysForButton.length == 1) return possibleSurveysForButton[0]; + // config lists one or more surveys; find which one to use for this timeline entry + return getSurveyForTimelineEntry(possibleSurveysForButton, timelineEntry, derivedTripProps); }, [appConfig, timelineEntry, i18n.resolvedLanguage]); - // the label resolved from the survey response, or null if there is no response yet - const responseLabel = useMemo( - () => userInputFor(timelineEntry)?.['SURVEY']?.data.label || undefined, - [userInputFor(timelineEntry)?.['SURVEY']?.data.label], - ); + // the label resolved from the survey response, or undefined if there is no response yet + const responseLabel = useMemo(() => { + if (!survey) return undefined; + return userInputFor(timelineEntry)?.[survey.surveyName]?.data.label || undefined; + }, [survey, userInputFor(timelineEntry)?.[survey?.surveyName || '']?.data.label]); function launchUserInputSurvey() { + if (!survey) return displayErrorMsg('UserInputButton: no survey to launch'); logDebug('UserInputButton: About to launch survey'); - const prevResponse = userInputFor(timelineEntry)?.['SURVEY']; + const prevResponse = userInputFor(timelineEntry)?.[survey.surveyName]; if (prevResponse?.data?.xmlResponse) { setPrevSurveyResponse(prevResponse.data.xmlResponse); } @@ -65,27 +64,27 @@ const UserInputButton = ({ timelineEntry }: Props) => { if (result) { logDebug(`UserInputButton: response was saved, about to addUserInputToEntry; result = ${JSON.stringify(result)}`); - addUserInputToEntry(timelineEntry._id.$oid, { SURVEY: result }, 'label'); + addUserInputToEntry(timelineEntry._id.$oid, { [result.name]: result }, 'label'); } else { displayErrorMsg('UserInputButton: response was not saved, result=', result); } } - if (!surveyName) return <>; // no survey to launch + if (!survey) return <>; // no survey to launch return ( <> launchUserInputSurvey()}> - {responseLabel || notFilledInLabel} + {responseLabel || survey['not-filled-in-label'][i18n.resolvedLanguage || 'en']} setModalVisible(false)} onResponseSaved={onResponseSaved} - surveyName={surveyName} + surveyName={survey.surveyName} opts={{ timelineEntry, prefilledSurveyResponse: prevSurveyResponse }} /> diff --git a/www/js/survey/enketo/conditionalSurveys.ts b/www/js/survey/enketo/conditionalSurveys.ts index 607b49431..a96ee2de8 100644 --- a/www/js/survey/enketo/conditionalSurveys.ts +++ b/www/js/survey/enketo/conditionalSurveys.ts @@ -29,26 +29,22 @@ const scopedEval = (script: string, scope: { [k: string]: any }) => // the first survey in the list that passes its condition will be returned export function getSurveyForTimelineEntry( - tripLabelConfig: SurveyButtonConfig | SurveyButtonConfig[], + possibleSurveys: SurveyButtonConfig[], tlEntry: TimelineEntry, derivedProperties: DerivedProperties, ) { - // if only one survey is given, just return it - if (!(tripLabelConfig instanceof Array)) return tripLabelConfig; - if (tripLabelConfig.length == 1) return tripLabelConfig[0]; - // else we have an array of possible surveys, we need to find which one to use for this entry - for (let surveyConfig of tripLabelConfig) { - if (!surveyConfig.showsIf) return surveyConfig; // survey shows unconditionally + for (let survey of possibleSurveys) { + if (!survey.showsIf) return survey; // survey shows unconditionally const scope = { ...tlEntry, ...derivedProperties, ...conditionalSurveyFunctions, }; try { - const evalResult = scopedEval(surveyConfig.showsIf, scope); - if (evalResult) return surveyConfig; + const evalResult = scopedEval(survey.showsIf, scope); + if (evalResult) return survey; } catch (e) { - displayError(e, `Error evaluating survey condition "${surveyConfig.showsIf}"`); + displayError(e, `Error evaluating survey condition "${survey.showsIf}"`); } } // TODO if none of the surveys passed conditions?? should we return null, throw error, or return a default? diff --git a/www/js/survey/enketo/enketoHelper.ts b/www/js/survey/enketo/enketoHelper.ts index 2df2d3b2d..e90354856 100644 --- a/www/js/survey/enketo/enketoHelper.ts +++ b/www/js/survey/enketo/enketoHelper.ts @@ -8,7 +8,7 @@ import { getConfig } from '../../config/dynamicConfig'; import { DateTime } from 'luxon'; import { fetchUrlCached } from '../../services/commHelper'; import { getUnifiedDataForInterval } from '../../services/unifiedDataLoader'; -import { AppConfig, EnketoSurveyConfig } from '../../types/appConfigTypes'; +import { AppConfig, EnketoSurveyConfig, SurveyButtonConfig } from '../../types/appConfigTypes'; import { CompositeTrip, ConfirmedPlace, @@ -315,6 +315,32 @@ export function loadPreviousResponseForSurvey(dataKey: string) { ); } +/** + * @description Returns an array of surveys that could be prompted for one button in the UI (trip label, trip notes, place label, or place notes) + * (If multiple are returned, they will show conditionally in the UI based on their `showsIf` field) + * Includes backwards compats for app config fields that didn't use to exist + */ +export function resolveSurveyButtonConfig( + config: AppConfig, + button: 'trip-label' | 'trip-notes' | 'place-label' | 'place-notes', +): SurveyButtonConfig[] { + const buttonConfig = config.survey_info.buttons?.[button]; + // backwards compat: default to the trip confirm survey if this button isn't configured + if (!buttonConfig) { + return [ + { + surveyName: 'TripConfirmSurvey', + 'not-filled-in-label': { + en: 'Add Trip Details', + es: 'Agregar detalles del viaje', + lo: 'ເພີ່ມລາຍລະອຽດການເດີນທາງ', + }, + }, + ]; + } + return buttonConfig instanceof Array ? buttonConfig : [buttonConfig]; +} + export async function fetchSurvey(url: string) { const responseText = await fetchUrlCached(url); if (!responseText) return; diff --git a/www/js/survey/enketo/infinite_scroll_filters.ts b/www/js/survey/enketo/infinite_scroll_filters.ts index d4b281713..512b272c4 100644 --- a/www/js/survey/enketo/infinite_scroll_filters.ts +++ b/www/js/survey/enketo/infinite_scroll_filters.ts @@ -8,7 +8,8 @@ import i18next from 'i18next'; -const unlabeledCheck = (trip, userInputForTrip) => !userInputForTrip?.['SURVEY']; +const unlabeledCheck = (trip, userInputForTrip) => + !userInputForTrip || !Object.values(userInputForTrip).some((input) => input); const TO_LABEL = { key: 'to_label', diff --git a/www/js/survey/inputMatcher.ts b/www/js/survey/inputMatcher.ts index b1460194e..a8d518d5f 100644 --- a/www/js/survey/inputMatcher.ts +++ b/www/js/survey/inputMatcher.ts @@ -18,7 +18,7 @@ import { inputType2retKey, removeManualPrefix, } from './multilabel/confirmHelper'; -import { TimelineLabelMap, TimelineNotesMap } from '../diary/LabelTabContext'; +import { TimelineLabelMap, TimelineNotesMap, UserInputMap } from '../diary/LabelTabContext'; import { MultilabelKey } from '../types/labelTypes'; import { EnketoUserInputEntry } from './enketo/enketoHelper'; import { AppConfig } from '../types/appConfigTypes'; @@ -280,16 +280,16 @@ export function mapInputsToTimelineEntries( allEntries.forEach((tlEntry, i) => { const nextEntry = i + 1 < allEntries.length ? allEntries[i + 1] : null; if (appConfig?.survey_info?.['trip-labels'] == 'ENKETO') { - // ENKETO configuration: just look for the 'SURVEY' key in the unprocessedInputs + // ENKETO configuration: consider reponses from all surveys in unprocessedLabels const userInputForTrip = getUserInputForTimelineEntry( tlEntry, nextEntry, - unprocessedLabels['SURVEY'], + Object.values(unprocessedLabels).flat(1), ) as EnketoUserInputEntry; if (userInputForTrip) { - timelineLabelMap[tlEntry._id.$oid] = { SURVEY: userInputForTrip }; + timelineLabelMap[tlEntry._id.$oid] = { [userInputForTrip.data.name]: userInputForTrip }; } else { - let processedSurveyResponse; + let processedSurveyResponse: EnketoUserInputEntry | undefined; for (const dataKey of keysForLabelInputs(appConfig)) { const key = removeManualPrefix(dataKey); if (tlEntry.user_input?.[key]) { @@ -297,12 +297,16 @@ export function mapInputsToTimelineEntries( break; } } - timelineLabelMap[tlEntry._id.$oid] = { SURVEY: processedSurveyResponse }; + if (processedSurveyResponse) { + timelineLabelMap[tlEntry._id.$oid] = { + [processedSurveyResponse.data.name]: processedSurveyResponse, + }; + } } } else { // MULTILABEL configuration: use the label inputs from the labelOptions to determine which // keys to look for in the unprocessedInputs - const labelsForTrip: { [k: string]: UserInputEntry | undefined } = {}; + const labelsForTrip: UserInputMap = {}; Object.keys(getLabelInputDetails(appConfig)).forEach((label: MultilabelKey) => { // Check unprocessed labels first since they are more recent const userInputForTrip = getUserInputForTimelineEntry( diff --git a/www/js/types/appConfigTypes.ts b/www/js/types/appConfigTypes.ts index d5a15fe4a..e58b679f5 100644 --- a/www/js/types/appConfigTypes.ts +++ b/www/js/types/appConfigTypes.ts @@ -54,7 +54,7 @@ export type SurveyButtonConfig = { 'not-filled-in-label': { [lang: string]: string; }; - showsIf: string; // a JS expression that evaluates to a boolean + showsIf?: string; // a JS expression that evaluates to a boolean }; export type SurveyButtonsConfig = { [k in 'trip-label' | 'trip-notes' | 'place-label' | 'place-notes']: From cb1e45b2ccdafc04ae8b9ec567a7d09fba6e9f93 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Thu, 9 May 2024 15:33:56 -0400 Subject: [PATCH 34/36] don't filter deleted additions twice I found an edge case where if an old addition (ie one that was already processed) was edited, there would be duplicates; both the old and new version would show up (until the new one got processed). This is because we were first applying the 'not deleted' filter to unprocessed entries. Then after they are merged with processed entreis we applied it to the merged copy. This didn't work for the edge case because the DELETED entry was already filtered out the first time, so the processed original entry just sticks around. This fix ensures that the 'not deleted' filter ONLY happens once processed+unprocessed are merged together, this way the filter considers all entries at once --- www/js/survey/inputMatcher.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/www/js/survey/inputMatcher.ts b/www/js/survey/inputMatcher.ts index a8d518d5f..bd7e861e6 100644 --- a/www/js/survey/inputMatcher.ts +++ b/www/js/survey/inputMatcher.ts @@ -216,9 +216,8 @@ export function getAdditionsForTimelineEntry( return []; } - // get additions that have not been deleted and filter out additions that do not start within the bounds of the timeline entry - const notDeleted = getNotDeletedCandidates(additionsList); - const matchingAdditions = notDeleted.filter((ui) => + // filter out additions that do not start within the bounds of the timeline entry + const matchingAdditions = additionsList.filter((ui) => validUserInputForTimelineEntry(entry, nextEntry, ui, logsEnabled), ); From 63357d1b25092cf73f9722c0236de431ab7d0758 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Thu, 9 May 2024 16:40:16 -0400 Subject: [PATCH 35/36] fix inputMatcher and timelineHelper tests 02655ba28913a06caa80795aa3a07a4b15d06ef3 changed from using 'SURVEY' as the key for unprocessed user inputs to using the name of the survey These tests still expected 'SURVEY'; replaced with the survey name. The existing test only had 'TripConfirmSurvey'; I also changed one to 'MyCustomSurvey' to highlight that they will be stored under separate fields now. inputMatcher.test.ts tests on both MULTILABEL and ENKETO configurations. It is necessary to call updateUnprocessedInputs after testing MULTILABEL and before testing ENKETO so that unprocessed MULTILABEL inputs are not kept it memory. It is also necessary for updateUnprocessedInputs to clear unprocessedLabels before re-filling it; otherwise survey responses could linger from a previous time the function was called. Previously all responses were kept in SURVEY, so SURVEY would always get reassigned. but now each survey's responses are kept under the survey name as a key --- www/__tests__/inputMatcher.test.ts | 22 ++++++++++++++-------- www/__tests__/timelineHelper.test.ts | 4 ++-- www/js/diary/timelineHelper.ts | 1 + 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/www/__tests__/inputMatcher.test.ts b/www/__tests__/inputMatcher.test.ts index 062951b35..9ea4d9b02 100644 --- a/www/__tests__/inputMatcher.test.ts +++ b/www/__tests__/inputMatcher.test.ts @@ -1,6 +1,6 @@ import { mockBEMUserCache } from '../__mocks__/cordovaMocks'; import { mockLogger } from '../__mocks__/globalMocks'; -import { unprocessedLabels, updateLocalUnprocessedInputs } from '../js/diary/timelineHelper'; +import { updateLocalUnprocessedInputs } from '../js/diary/timelineHelper'; import * as logger from '../js/plugin/logger'; import { EnketoUserInputEntry } from '../js/survey/enketo/enketoHelper'; import { @@ -376,9 +376,9 @@ describe('mapInputsToTimelineEntries on an ENKETO configuration', () => { user_input: { trip_user_input: { data: { - name: 'TripConfirmSurvey', + name: 'MyCustomSurvey', version: 1, - xmlResponse: '', + xmlResponse: '', start_ts: 1000, end_ts: 3000, }, @@ -417,6 +417,12 @@ describe('mapInputsToTimelineEntries on an ENKETO configuration', () => { ], }, ] as any as TimelineEntry[]; + + // reset local unprocessed inputs to ensure MUTLILABEL inputs don't leak into ENKETO tests + beforeAll(async () => { + await updateLocalUnprocessedInputs({ start_ts: 1000, end_ts: 5000 }, fakeConfigEnketo); + }); + it('creates a map that has the processed responses and notes', () => { const [labelMap, notesMap] = mapInputsToTimelineEntries( timelineEntriesEnketo, @@ -424,8 +430,8 @@ describe('mapInputsToTimelineEntries on an ENKETO configuration', () => { ); expect(labelMap).toMatchObject({ trip1: { - SURVEY: { - data: { xmlResponse: '' }, + MyCustomSurvey: { + data: { xmlResponse: '' }, }, }, }); @@ -460,12 +466,12 @@ describe('mapInputsToTimelineEntries on an ENKETO configuration', () => { expect(labelMap).toMatchObject({ trip1: { - SURVEY: { - data: { xmlResponse: '' }, + MyCustomSurvey: { + data: { xmlResponse: '' }, }, }, trip2: { - SURVEY: { + TripConfirmSurvey: { data: { xmlResponse: '' }, }, }, diff --git a/www/__tests__/timelineHelper.test.ts b/www/__tests__/timelineHelper.test.ts index c40262aae..c1c130272 100644 --- a/www/__tests__/timelineHelper.test.ts +++ b/www/__tests__/timelineHelper.test.ts @@ -165,10 +165,10 @@ describe('unprocessedLabels, unprocessedNotes', () => { // update unprocessed inputs and check that the trip survey response shows up in unprocessedLabels await updateAllUnprocessedInputs({ start_ts: 4, end_ts: 6 }, mockTLH.mockConfigEnketo); - expect(unprocessedLabels['SURVEY'][0].data).toEqual(tripSurveyResponse); + expect(unprocessedLabels['TripConfirmSurvey'][0].data).toEqual(tripSurveyResponse); // the second response is ignored for now because we haven't enabled place_user_input yet // so the length is only 1 - expect(unprocessedLabels['SURVEY'].length).toEqual(1); + expect(unprocessedLabels['TripConfirmSurvey'].length).toEqual(1); }); it('has some trip- and place- level additions after they were just recorded', async () => { diff --git a/www/js/diary/timelineHelper.ts b/www/js/diary/timelineHelper.ts index 85b507780..3dae4f370 100644 --- a/www/js/diary/timelineHelper.ts +++ b/www/js/diary/timelineHelper.ts @@ -120,6 +120,7 @@ function updateUnprocessedInputs( const labelResults = comboResults.slice(0, labelsPromises.length); const notesResults = comboResults.slice(labelsPromises.length).flat(2); // fill in the unprocessedLabels object with the labels we just read + unprocessedLabels = {}; labelResults.forEach((r, i) => { if (appConfig.survey_info?.['trip-labels'] == 'ENKETO') { const tripSurveys = resolveSurveyButtonConfig(appConfig, 'trip-label'); From 2761e19bb42f2f8a9fa818b3b0406feae462f2a5 Mon Sep 17 00:00:00 2001 From: Jack Greenlee Date: Thu, 9 May 2024 16:45:28 -0400 Subject: [PATCH 36/36] remove 'choose-survey' from en.json Trip and place surveys give their own button text in the config now, so this text + translations relocated to the backwards-compat in enketoHelper > resolveSurveyButtonConfig --- www/i18n/en.json | 1 - 1 file changed, 1 deletion(-) diff --git a/www/i18n/en.json b/www/i18n/en.json index 9a8b6bb61..2834219af 100644 --- a/www/i18n/en.json +++ b/www/i18n/en.json @@ -136,7 +136,6 @@ "choose-mode": "Mode", "choose-replaced-mode": "Replaces", "choose-purpose": "Purpose", - "choose-survey": "Add Trip Details", "select-mode-scroll": "Mode (scroll for more)", "select-replaced-mode-scroll": "Replaces (scroll for more)", "select-purpose-scroll": "Purpose (scroll for more)",