Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add logging for unsuccessful AICamera flows #2537

Merged
merged 5 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ PODS:
- hermes-engine/Pre-built (= 0.73.7)
- hermes-engine/Pre-built (0.73.7)
- libevent (2.1.12)
- MMKV (1.3.9):
- MMKVCore (~> 1.3.9)
- MMKVCore (1.3.9)
- MMKV (2.0.0):
- MMKVCore (~> 2.0.0)
- MMKVCore (2.0.0)
- RCT-Folly (2022.05.16.00):
- boost
- DoubleConversion
Expand Down Expand Up @@ -1482,8 +1482,8 @@ SPEC CHECKSUMS:
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
hermes-engine: 39589e9c297d024e90fe68f6830ff86c4e01498a
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
MMKV: 817ba1eea17421547e01e087285606eb270a8dcb
MMKVCore: af055b00e27d88cd92fad301c5fecd1ff9b26dd9
MMKV: f7d1d5945c8765f97f39c3d121f353d46735d801
MMKVCore: c04b296010fcb1d1638f2c69405096aac12f6390
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: 77f73950d15b8c1a2b48ba5b79020c3003d1c9b5
RCTTypeSafety: ede1e2576424d89471ef553b2aed09fbbcc038e3
Expand Down
6 changes: 3 additions & 3 deletions ios/iNaturalistReactNative.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
197A169E2A7C2567001A03DC /* taxonomy.json in Resources */ = {isa = PBXBuildFile; fileRef = 197A169C2A7C2567001A03DC /* taxonomy.json */; };
3DE2C0F71E184822B561A1ED /* Lato-Italic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3A54425709BB4844AFBC680D /* Lato-Italic.ttf */; };
54EB1EFEC1F74152902EED02 /* Lato-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 8C2D97D72EED451C887998A8 /* Lato-BoldItalic.ttf */; };
5A8D64AB921678B40E0229C8 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
5A8D64AB921678B40E0229C8 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
725BA058C5384A9185E8036A /* Lato-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 00752F4ADC554701A45A848A /* Lato-Bold.ttf */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
8B65ED3129F575C10054CCEF /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B65ED2F29F575C10054CCEF /* MainInterface.storyboard */; };
Expand Down Expand Up @@ -122,7 +122,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5A8D64AB921678B40E0229C8 /* BuildFile in Frameworks */,
5A8D64AB921678B40E0229C8 /* (null) in Frameworks */,
03BE06B8FED98F5CD10273BB /* libPods-iNaturalistReactNative.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -774,7 +774,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CURRENT_PROJECT_VERSION = 131;
DEVELOPMENT_TEAM = N5J7L4P93Z;
DEVELOPMENT_TEAM = 5P5U24AB8K;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
HEADER_SEARCH_PATHS = (
"$(inherited)",
Expand Down
2 changes: 2 additions & 0 deletions src/appConstants/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ export const photoUploadPath = `${RNFS.DocumentDirectoryPath}/photoUploads`;

export const rotatedOriginalPhotosPath = `${RNFS.DocumentDirectoryPath}/rotatedOriginalPhotos`;

export const sentinelFilePath = `${RNFS.DocumentDirectoryPath}/sentinelFiles`;

export const soundUploadPath = `${RNFS.DocumentDirectoryPath}/soundUploads`;
2 changes: 2 additions & 0 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Realm from "realm";
import clearCaches from "sharedHelpers/clearCaches.ts";
import { log } from "sharedHelpers/logger";
import { addARCameraFiles } from "sharedHelpers/mlModel.ts";
import { findAndLogSentinelFiles } from "sharedHelpers/sentinelFiles.ts";
import {
useCurrentUser,
useIconicTaxa,
Expand Down Expand Up @@ -95,6 +96,7 @@ const App = ( { children }: Props ): Node => {

useEffect( ( ) => {
addARCameraFiles( );
findAndLogSentinelFiles( );
}, [] );

useEffect( ( ) => {
Expand Down
14 changes: 13 additions & 1 deletion src/components/Camera/AICamera/AICamera.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @flow

import { useNavigation } from "@react-navigation/native";
import classnames from "classnames";
import FadeInOutView from "components/Camera/FadeInOutView";
import useRotation from "components/Camera/hooks/useRotation.ts";
Expand All @@ -13,11 +14,12 @@ import LinearGradient from "react-native-linear-gradient";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { convertOfflineScoreToConfidence } from "sharedHelpers/convertScores.ts";
import { log } from "sharedHelpers/logger";
import { deleteSentinelFile, logStage } from "sharedHelpers/sentinelFiles.ts";
import {
useDebugMode, usePerformance, useTranslation
} from "sharedHooks";
import { isDebugMode } from "sharedHooks/useDebugMode";
// import type { UserLocation } from "sharedHooks/useWatchPosition";
import useStore from "stores/useStore";
import colors from "styles/tailwindColors";

import {
Expand Down Expand Up @@ -70,6 +72,9 @@ const AICamera = ( {
setAiSuggestion,
userLocation
}: Props ): Node => {
const navigation = useNavigation( );
const sentinelFileName = useStore( state => state.sentinelFileName );

const hasFlash = device?.hasFlash;
const { isDebug } = useDebugMode( );
const {
Expand Down Expand Up @@ -126,6 +131,7 @@ const AICamera = ( {
};

const handleTakePhoto = async ( ) => {
await logStage( sentinelFileName, "take_photo_start" );
setAiSuggestion( showPrediction && result );
await takePhotoAndStoreUri( {
replaceExisting: true,
Expand All @@ -134,6 +140,11 @@ const AICamera = ( {
} );
};

const handleClose = async ( ) => {
await deleteSentinelFile( sentinelFileName );
navigation.goBack( );
};

return (
<>
{device && (
Expand Down Expand Up @@ -226,6 +237,7 @@ const AICamera = ( {
flipCamera={onFlipCamera}
fps={fps}
hasFlash={hasFlash}
handleClose={handleClose}
modelLoaded={modelLoaded}
numStoredResults={numStoredResults}
rotatableAnimatedStyle={rotatableAnimatedStyle}
Expand Down
4 changes: 3 additions & 1 deletion src/components/Camera/AICamera/AICameraButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface Props {
cropRatio?: string;
flipCamera: ( _event: GestureResponderEvent ) => void;
fps?: number;
handleClose: ( ) => void;
hasFlash: boolean;
modelLoaded: boolean;
numStoredResults?: number;
Expand All @@ -48,6 +49,7 @@ const AICameraButtons = ( {
cropRatio,
flipCamera,
fps,
handleClose,
hasFlash,
modelLoaded,
numStoredResults,
Expand Down Expand Up @@ -89,7 +91,7 @@ const AICameraButtons = ( {
className="absolute left-0 bottom-[17px] h-full justify-end flex gap-y-9"
pointerEvents="box-none"
>
<View><Close /></View>
<View><Close handleClose={handleClose} /></View>
</View>
<View
className="absolute right-0 bottom-[6px] h-full justify-end items-end flex gap-y-9"
Expand Down
28 changes: 21 additions & 7 deletions src/components/Camera/AICamera/FrameProcessorCamera.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import {
modelVersion,
taxonomyPath
} from "sharedHelpers/mlModel.ts";
import { logStage } from "sharedHelpers/sentinelFiles.ts";
import {
orientationPatchFrameProcessor,
usePatchedRunAsync
} from "sharedHelpers/visionCameraPatches";
import { useDeviceOrientation } from "sharedHooks";
// import type { UserLocation } from "sharedHooks/useWatchPosition";
import useStore from "stores/useStore";

type Props = {
// $FlowIgnore
Expand Down Expand Up @@ -77,6 +78,7 @@ const FrameProcessorCamera = ( {
resetCameraOnFocus,
userLocation
}: Props ): Node => {
const sentinelFileName = useStore( state => state.sentinelFileName );
const { deviceOrientation } = useDeviceOrientation();
const [lastTimestamp, setLastTimestamp] = useState( undefined );

Expand Down Expand Up @@ -113,7 +115,7 @@ const FrameProcessorCamera = ( {
} );

return unsubscribeBlur;
}, [navigation, resetCameraOnFocus] );
}, [navigation, resetCameraOnFocus, sentinelFileName] );

const handleResults = Worklets.createRunOnJS( ( result, timeTaken ) => {
setLastTimestamp( result.timestamp );
Expand Down Expand Up @@ -148,7 +150,7 @@ const FrameProcessorCamera = ( {
}
}

patchedRunAsync( frame, () => {
patchedRunAsync( frame, ( ) => {
"worklet";

// Reminder: this is a worklet, running on a C++ thread. Make sure to check the
Expand Down Expand Up @@ -202,10 +204,22 @@ const FrameProcessorCamera = ( {
cameraRef={cameraRef}
device={device}
frameProcessor={frameProcessor}
onCameraError={onCameraError}
onCaptureError={onCaptureError}
onClassifierError={onClassifierError}
onDeviceNotSupported={onDeviceNotSupported}
onCameraError={async ( ) => {
await logStage( sentinelFileName, "fallback_camera_error" );
onCameraError( );
}}
onCaptureError={async ( ) => {
await logStage( sentinelFileName, "camera_capture_error" );
onCaptureError( );
}}
onClassifierError={async ( ) => {
await logStage( sentinelFileName, "camera_classifier_error" );
onClassifierError( );
}}
onDeviceNotSupported={async ( ) => {
await logStage( sentinelFileName, "camera_device_not_supported_error" );
onDeviceNotSupported( );
}}
pinchToZoom={pinchToZoom}
inactive={inactive}
/>
Expand Down
10 changes: 6 additions & 4 deletions src/components/Camera/Buttons/Close.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { useNavigation } from "@react-navigation/native";
import { TransparentCircleButton } from "components/SharedComponents";
import React from "react";
import { useTranslation } from "sharedHooks";

const Close = ( ) => {
interface Props {
handleClose: ( ) => void;
}

const Close = ( { handleClose }: Props ) => {
const { t } = useTranslation( );
const navigation = useNavigation( );

return (
<TransparentCircleButton
onPress={( ) => navigation.goBack( )}
onPress={handleClose}
accessibilityLabel={t( "Close" )}
accessibilityHint={t( "Navigates-to-previous-screen" )}
icon="close"
Expand Down
54 changes: 49 additions & 5 deletions src/components/Camera/CameraContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from "components/Camera/helpers/visionCameraWrapper";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState
Expand All @@ -12,6 +13,7 @@ import { Alert, StatusBar } from "react-native";
import type {
TakePhotoOptions
} from "react-native-vision-camera";
import { createSentinelFile, deleteSentinelFile, logStage } from "sharedHelpers/sentinelFiles.ts";
import { useDeviceOrientation, useTranslation, useWatchPosition } from "sharedHooks";
import useLocationPermission from "sharedHooks/useLocationPermission.tsx";
import useStore from "stores/useStore";
Expand All @@ -28,6 +30,24 @@ const CameraContainer = ( ) => {
const setCameraState = useStore( state => state.setCameraState );
const evidenceToAdd = useStore( state => state.evidenceToAdd );
const cameraUris = useStore( state => state.cameraUris );
const sentinelFileName = useStore( state => state.sentinelFileName );
const setSentinelFileName = useStore( state => state.setSentinelFileName );

const { params } = useRoute( );
const cameraType = params?.camera;

const logStageIfAICamera = useCallback( async (
stageName: string,
stageData: string
) => {
if ( cameraType !== "AI" ) { return; }
await logStage( sentinelFileName, stageName, stageData );
}, [cameraType, sentinelFileName] );

const deleteStageIfAICamera = useCallback( async ( ) => {
if ( cameraType !== "AI" ) { return; }
await deleteSentinelFile( sentinelFileName );
}, [cameraType, sentinelFileName] );

const { deviceOrientation } = useDeviceOrientation( );
// Check if location permission granted b/c usePrepareStoreAndNavigate and
Expand All @@ -44,8 +64,6 @@ const CameraContainer = ( ) => {
} );
const navigation = useNavigation( );
const { t } = useTranslation( );
const { params } = useRoute( );
const cameraType = params?.camera;

const [cameraPosition, setCameraPosition] = useState<"front" | "back">( "back" );
// https://react-native-vision-camera.com/docs/guides/devices#selecting-multi-cams
Expand All @@ -71,6 +89,23 @@ const CameraContainer = ( ) => {

const camera = useRef<Camera>( null );

useEffect( () => {
const generateSentinelFile = async ( ) => {
const fileName = await createSentinelFile( "AICamera" );
setSentinelFileName( fileName );
};
if ( cameraType !== "AI" ) { return; }
generateSentinelFile( );
}, [setSentinelFileName, cameraType] );

const logFetchingLocation = !!( hasPermissions && sentinelFileName );

useEffect( ( ) => {
if ( logFetchingLocation ) {
logStageIfAICamera( "fetch_user_location_start" );
}
}, [logStageIfAICamera, logFetchingLocation] );

const {
hasPermissions: hasSavePhotoPermission,
hasBlockedPermissions: hasBlockedSavePhotoPermission,
Expand Down Expand Up @@ -105,23 +140,29 @@ const CameraContainer = ( ) => {
const handleNavigation = useCallback( async ( newPhotoState = {} ) => {
await prepareStoreAndNavigate( {
...navigationOptions,
newPhotoState
newPhotoState,
logStageIfAICamera,
deleteStageIfAICamera
} );
}, [
prepareStoreAndNavigate,
navigationOptions
navigationOptions,
logStageIfAICamera,
deleteStageIfAICamera
] );

const handleCheckmarkPress = useCallback( async newPhotoState => {
if ( !showPhotoPermissionsGate ) {
await handleNavigation( newPhotoState );
} else {
await logStageIfAICamera( "request_save_photo_permission_start" );
requestSavePhotoPermission( );
}
}, [
handleNavigation,
requestSavePhotoPermission,
showPhotoPermissionsGate
showPhotoPermissionsGate,
logStageIfAICamera
] );

const toggleFlash = ( ) => {
Expand Down Expand Up @@ -167,7 +208,9 @@ const CameraContainer = ( ) => {
// this does leave a short period of time where the camera preview is still active
// after taking the photo which we might to revisit if it doesn't look good.
const cameraPhoto = await camera?.current?.takePhoto( takePhotoOptions );
await logStageIfAICamera( "take_photo_complete" );
if ( !cameraPhoto ) {
await logStageIfAICamera( "take_photo_error" );
throw new Error( "Failed to take photo: missing camera" );
}
if ( options?.inactivateCallback ) options.inactivateCallback();
Expand Down Expand Up @@ -214,6 +257,7 @@ const CameraContainer = ( ) => {
onRequestGranted: ( ) => console.log( "granted in save photo permission gate" ),
onRequestBlocked: ( ) => console.log( "blocked in save photo permission gate" ),
onModalHide: async ( ) => {
await logStageIfAICamera( "request_save_photo_permission_complete" );
await handleNavigation( {
cameraUris,
evidenceToAdd
Expand Down
Loading
Loading