Skip to content

Commit

Permalink
Merge branch 'master' into simulate_ble_scans
Browse files Browse the repository at this point in the history
  • Loading branch information
shankari committed Apr 28, 2024
2 parents 0eebe4b + 0f6aabc commit 5df032c
Show file tree
Hide file tree
Showing 24 changed files with 394 additions and 154 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/android-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/code-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ jobs:
npx jest
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
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' }}
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/ios-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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

Expand Down
5 changes: 5 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}",
],
};
6 changes: 3 additions & 3 deletions package.cordovabuild.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
7 changes: 4 additions & 3 deletions package.serve.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
7 changes: 2 additions & 5 deletions www/__tests__/timelineHelper.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ afterAll(() => {

describe('useGeojsonForTrip', () => {
it('work with an empty input', () => {
const testVal = useGeojsonForTrip({} as any, {} as any);
const testVal = useGeojsonForTrip({} as any);
expect(testVal).toBeFalsy;
});

Expand All @@ -43,10 +43,7 @@ describe('useGeojsonForTrip', () => {
};

it('works without labelMode flag', () => {
const testValue = useGeojsonForTrip(
mockTLH.mockCompDataTwo.phone_data[1].data,
mockTLH.mockLabelOptions,
) as GeoJSONData;
const testValue = useGeojsonForTrip(mockTLH.mockCompDataTwo.phone_data[1].data) as GeoJSONData;
expect(testValue).toBeTruthy;
checkGeojson(testValue);
expect(testValue.data.features.length).toBe(3);
Expand Down
6 changes: 5 additions & 1 deletion www/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@
"reminders-time-of-day": "Time of Day for Reminders ({{time}})",
"upcoming-notifications": "Upcoming Notifications",
"dummy-notification": "Dummy Notification in 5 Seconds",
"log-out": "Log Out"
"log-out": "Log Out",
"refresh-app-config": "Refresh App Configuration",
"current-version": "Current version: {{version}}",
"refreshing-app-config": "Refreshing app configuration, please wait...",
"already-up-to-date": "Already up to date!"
},

"general-settings": {
Expand Down
17 changes: 11 additions & 6 deletions www/js/config/dynamicConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' });
}
}

Expand Down Expand Up @@ -134,16 +134,21 @@ async function readConfigFromServer(studyLabel: string) {
*/
async function fetchConfig(studyLabel: string, alreadyTriedLocal?: boolean) {
logDebug('Received request to join ' + studyLabel);
const downloadURL = `https://raw.githubusercontent.com/e-mission/nrel-openpath-deploy-configs/main/configs/${studyLabel}.nrel-op.json`;
let downloadURL = `https://raw.githubusercontent.com/e-mission/nrel-openpath-deploy-configs/main/configs/${studyLabel}.nrel-op.json`;
if (!__DEV__ || alreadyTriedLocal) {
logDebug('Fetching config from github');
const r = await fetch(downloadURL);
const r = await fetch(downloadURL, { cache: 'reload' });
if (!r.ok) throw new Error('Unable to fetch config from github');
return r.json(); // TODO: validate, make sure it has required fields
} else {
logDebug('Running in dev environment, checking for locally hosted config');
try {
const r = await fetch('http://localhost:9090/configs/' + studyLabel + '.nrel-op.json');
if (window['cordova'].platformId == 'android') {
downloadURL = `http://10.0.2.2:9090/configs/${studyLabel}.nrel-op.json`;
} else {
downloadURL = `http://localhost:9090/configs/${studyLabel}.nrel-op.json`;
}
const r = await fetch(downloadURL, { cache: 'reload' });
if (!r.ok) throw new Error('Local config not found');
return r.json();
} catch (err) {
Expand Down Expand Up @@ -227,7 +232,7 @@ function extractSubgroup(token: string, config: AppConfig): string | undefined {
* @param existingVersion If the new config's version is the same, we won't update
* @returns boolean representing whether the config was updated or not
*/
function loadNewConfig(newToken: string, existingVersion?: number): Promise<boolean> {
export function loadNewConfig(newToken: string, existingVersion?: number): Promise<boolean> {
const newStudyLabel = extractStudyName(newToken);
return readConfigFromServer(newStudyLabel)
.then((downloadedConfig) => {
Expand Down
17 changes: 16 additions & 1 deletion www/js/control/ProfileSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import ControlCollectionHelper, {
helperToggleLowAccuracy,
forceTransition,
} from './ControlCollectionHelper';
import { resetDataAndRefresh } from '../config/dynamicConfig';
import { loadNewConfig, resetDataAndRefresh } from '../config/dynamicConfig';
import { AppContext } from '../App';
import { shareQR } from '../components/QrCode';
import { storageClear } from '../plugin/storage';
Expand Down Expand Up @@ -307,6 +307,16 @@ const ProfileSettings = () => {
}, 1500);
}

async function refreshConfig() {
AlertManager.addMessage({ text: t('control.refreshing-app-config') });
const updated = await loadNewConfig(authSettings.opcode, appConfig?.version);
if (updated) {
window.location.reload();
} else {
AlertManager.addMessage({ text: t('control.already-up-to-date') });
}
}

//Platform.OS returns "web" now, but could be used once it's fully a Native app
//for now, use window.cordova.platformId

Expand Down Expand Up @@ -434,6 +444,11 @@ const ProfileSettings = () => {
textKey="control.email-log"
iconName="email"
action={() => sendEmail('loggerDB')}></SettingRow>
<SettingRow
textKey="control.refresh-app-config"
desc={t('control.current-version', { version: appConfig?.version })}
iconName="cog-refresh"
action={refreshConfig}></SettingRow>
<ExpansionSection sectionTitle="control.dev-zone">
<BluetoothScanSettingRow />
<SettingRow
Expand Down
32 changes: 31 additions & 1 deletion www/js/diary/LabelTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ import {
updateLocalUnprocessedInputs,
unprocessedLabels,
unprocessedNotes,
updateUnprocessedBleScans,
unprocessedBleScans,
} from './timelineHelper';
import { fillLocationNamesOfTrip, resetNominatimLimiter } from './addressNamesHelper';
import { getLabelOptions, labelOptionByValue } from '../survey/multilabel/confirmHelper';
import { displayError, displayErrorMsg, logDebug, logWarn } from '../plugin/logger';
import { useTheme } from 'react-native-paper';
import { getPipelineRangeTs } from '../services/commHelper';
import { getNotDeletedCandidates, mapInputsToTimelineEntries } from '../survey/inputMatcher';
import {
getNotDeletedCandidates,
mapBleScansToTimelineEntries,
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, {
Expand Down Expand Up @@ -57,6 +63,7 @@ const LabelTab = () => {
const [timelineMap, setTimelineMap] = useState<TimelineMap | null>(null);
const [timelineLabelMap, setTimelineLabelMap] = useState<TimelineLabelMap | null>(null);
const [timelineNotesMap, setTimelineNotesMap] = useState<TimelineNotesMap | null>(null);
const [timelineBleMap, setTimelineBleMap] = useState<any>(null);
const [displayedEntries, setDisplayedEntries] = useState<TimelineEntry[] | null>(null);
const [refreshTime, setRefreshTime] = useState<Date | null>(null);
const [isLoading, setIsLoading] = useState<string | false>('replace');
Expand Down Expand Up @@ -102,6 +109,11 @@ const LabelTab = () => {
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'));
Expand Down Expand Up @@ -157,6 +169,15 @@ const LabelTab = () => {
logDebug(`LabelTab: After updating unprocessedInputs,
unprocessedLabels = ${JSON.stringify(unprocessedLabels)};
unprocessedNotes = ${JSON.stringify(unprocessedNotes)}`);
if (appConfig.vehicle_identities?.length) {
await updateUnprocessedBleScans({
start_ts: pipelineRange.start_ts,
end_ts: Date.now() / 1000,
});
logDebug(`LabelTab: After updating unprocessedBleScans,
unprocessedBleScans = ${JSON.stringify(unprocessedBleScans)};
`);
}
setPipelineRange(pipelineRange);
} catch (e) {
displayError(e, t('errors.while-loading-pipeline-range'));
Expand Down Expand Up @@ -310,6 +331,14 @@ const LabelTab = () => {
return chosenLabel ? labelOptionByValue(chosenLabel, labelType) : undefined;
};

/**
* @param tlEntry The trip or place object to get the confirmed mode for
* @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');

function addUserInputToEntry(oid: string, userInput: any, inputType: 'label' | 'note') {
const tlEntry = timelineMap?.get(oid);
if (!pipelineRange || !tlEntry)
Expand Down Expand Up @@ -351,6 +380,7 @@ const LabelTab = () => {
userInputFor,
labelFor,
notesFor,
confirmedModeFor,
addUserInputToEntry,
displayedEntries,
filterInputs,
Expand Down
2 changes: 2 additions & 0 deletions www/js/diary/LabelTabContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createContext } from 'react';
import { TimelineEntry, TimestampRange, UserInputEntry } from '../types/diaryTypes';
import { LabelOption, LabelOptions, MultilabelKey } from '../types/labelTypes';
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
Expand Down Expand Up @@ -34,6 +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;
addUserInputToEntry: (oid: string, userInput: any, inputType: 'label' | 'note') => void;
displayedEntries: TimelineEntry[] | null;
filterInputs: LabelTabFilter[];
Expand Down
12 changes: 6 additions & 6 deletions www/js/diary/cards/ModesIndicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import { View, StyleSheet } from 'react-native';
import color from 'color';
import LabelTabContext from '../LabelTabContext';
import { logDebug } from '../../plugin/logger';
import { getBaseModeByValue } from '../diaryHelper';
import { getBaseModeByKey, getBaseModeByValue } from '../diaryHelper';
import { Text, Icon, useTheme } from 'react-native-paper';
import { useTranslation } from 'react-i18next';

const ModesIndicator = ({ trip, detectedModes }) => {
const { t } = useTranslation();
const { labelOptions, labelFor } = useContext(LabelTabContext);
const { labelOptions, labelFor, confirmedModeFor } = useContext(LabelTabContext);
const { colors } = useTheme();

const indicatorBackgroundColor = color(colors.onPrimary).alpha(0.8).rgb().string();
let indicatorBorderColor = color('black').alpha(0.5).rgb().string();

let modeViews;
const labeledModeForTrip = labelFor(trip, 'MODE');
if (labelOptions && labeledModeForTrip?.value) {
const baseMode = getBaseModeByValue(labeledModeForTrip.value, labelOptions);
const confirmedModeForTrip = confirmedModeFor(trip);
if (labelOptions && confirmedModeForTrip?.value) {
const baseMode = getBaseModeByKey(confirmedModeForTrip.baseMode);
indicatorBorderColor = baseMode.color;
logDebug(`TripCard: got baseMode = ${JSON.stringify(baseMode)}`);
modeViews = (
Expand All @@ -32,7 +32,7 @@ const ModesIndicator = ({ trip, detectedModes }) => {
fontWeight: '500',
textDecorationLine: 'underline',
}}>
{labelFor(trip, 'MODE')?.text}
{confirmedModeForTrip.text}
</Text>
</View>
);
Expand Down
4 changes: 2 additions & 2 deletions www/js/diary/cards/TripCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ const TripCard = ({ trip, isFirstInList }: Props) => {
} = useDerivedProperties(trip);
let [tripStartDisplayName, tripEndDisplayName] = useAddressNames(trip);
const navigation = useNavigation<any>();
const { labelOptions, labelFor, notesFor } = useContext(LabelTabContext);
const { labelOptions, confirmedModeFor, notesFor } = useContext(LabelTabContext);
const tripGeojson =
trip && labelOptions && useGeojsonForTrip(trip, labelOptions, labelFor(trip, 'MODE')?.value);
trip && labelOptions && useGeojsonForTrip(trip, confirmedModeFor(trip)?.baseMode);

const isDraft = trip.key.includes('UNPROCESSED');
const flavoredTheme = getTheme(isDraft ? 'draft' : undefined);
Expand Down
Loading

0 comments on commit 5df032c

Please sign in to comment.