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 all 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
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
22 changes: 20 additions & 2 deletions 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 @@ -14,11 +15,12 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
import { VolumeManager } from "react-native-volume-manager";
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 @@ -69,6 +71,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 @@ -127,6 +132,7 @@ const AICamera = ( {
};

const handleTakePhoto = useCallback( async ( ) => {
await logStage( sentinelFileName, "take_photo_start" );
setHasTakenPhoto( true );
setAiSuggestion( showPrediction && result );
await takePhotoAndStoreUri( {
Expand All @@ -135,7 +141,13 @@ const AICamera = ( {
navigateImmediately: true
} );
setHasTakenPhoto( false );
}, [setAiSuggestion, takePhotoAndStoreUri, result, showPrediction] );
}, [
setAiSuggestion,
sentinelFileName,
takePhotoAndStoreUri,
result,
showPrediction
] );

useEffect( () => {
if ( initialVolume === null ) {
Expand Down Expand Up @@ -165,6 +177,11 @@ const AICamera = ( {
};
}, [handleTakePhoto, hasTakenPhoto, initialVolume] );

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

return (
<>
{device && (
Expand Down Expand Up @@ -255,6 +272,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