From 9106345c2003b5604002486d17e4873d5443d52d Mon Sep 17 00:00:00 2001 From: Vardan Mkhitaryan Date: Wed, 31 Jan 2024 21:55:37 +0400 Subject: [PATCH 1/5] feature(streaming) M2-4928, M2-4929 Added more information to drawing and abTrails to be sent over the socket --- src/entities/abTrail/lib/types/test.ts | 10 ++++ src/entities/abTrail/ui/AbCanvas.tsx | 61 +++++++++++++++++++------ src/entities/drawer/lib/types/draw.ts | 2 + src/entities/drawer/ui/DrawingBoard.tsx | 11 +++-- src/entities/drawer/ui/DrawingTest.tsx | 4 +- 5 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/entities/abTrail/lib/types/test.ts b/src/entities/abTrail/lib/types/test.ts index 9bde68c05..2a4b85e24 100644 --- a/src/entities/abTrail/lib/types/test.ts +++ b/src/entities/abTrail/lib/types/test.ts @@ -29,10 +29,20 @@ export type OnResultLog = { currentIndex: number; }; +export const enum StreamEventError { + NotDefined = '?', + OVER_RIGHT_POINT = 'E0', + OVER_WRONG_POINT = 'E1', + OVER_UNDEFINED_POINT = 'E2', +} export type StreamEventPoint = { x: number; y: number; time: number; + line_number: number; + error: StreamEventError; + correct_path: string; + actual_path: string; }; export type AbTestResult = { diff --git a/src/entities/abTrail/ui/AbCanvas.tsx b/src/entities/abTrail/ui/AbCanvas.tsx index cdf57915e..d347d4096 100644 --- a/src/entities/abTrail/ui/AbCanvas.tsx +++ b/src/entities/abTrail/ui/AbCanvas.tsx @@ -23,6 +23,7 @@ import { MessageType, OnResultLog, StreamEventPoint, + StreamEventError, } from '../lib'; import { getDistance, transformCoordinates } from '../lib/utils'; @@ -195,24 +196,44 @@ const AbCanvas: FC = props => { return foundNode ?? null; }; - const createLogPoint = (point: Point): LogPoint => { + const getCurrentAndNextNodeLabels = () => { const index = getCurrentIndex(); const currentNode = findNodeByIndex(index); const nextNode = findNodeByIndex(index + 1); + return [currentNode.label, nextNode.label]; + }; + + const createLogPoint = (point: Point): LogPoint => { + const [currentNodeLabel, nextNodeLabel] = getCurrentAndNextNodeLabels(); + const logPoint: LogPoint = { x: point.x, y: point.y, time: new Date().getTime(), valid: null, - start: currentNode.label, - end: nextNode.label, + start: currentNodeLabel, + end: nextNodeLabel, actual: null, }; return logPoint; }; + const createStreamEventPoint = (point: Point): StreamEventPoint => { + const [currentNodeLabel, nextNodeLabel] = getCurrentAndNextNodeLabels(); + + return { + x: (point.x * width) / 100, + y: (point.y * width) / 100, + time: Date.now(), + line_number: logLines?.length - 1, + error: StreamEventError.NotDefined, + correct_path: `${currentNodeLabel} ~ ${nextNodeLabel}`, + actual_path: '-1', + }; + }; + const addLogLine = ({ x, y }: Point): void => { const newLine: LogLine = { points: [createLogPoint({ x, y })], @@ -274,11 +295,8 @@ const AbCanvas: FC = props => { reCreatePath(point); drawPath(); reRender(); - onLog({ - x: (touchInfo.x * width) / 100, - y: (touchInfo.y * width) / 100, - time: Date.now(), - }); + + onLog(createStreamEventPoint(point)); }; const onTouchProgress = (touchInfo: TouchInfo) => { @@ -289,6 +307,7 @@ const AbCanvas: FC = props => { } const point: Point = { x: touchInfo.x, y: touchInfo.y }; + const streamEventPoint = createStreamEventPoint(point); currentPath.lineTo(point.x, point.y); @@ -305,12 +324,6 @@ const AbCanvas: FC = props => { resetCloseToNextRerendered(); } - onLog({ - x: (touchInfo.x * width) / 100, - y: (touchInfo.y * width) / 100, - time: Date.now(), - }); - if (isOverNext(point) && isOverLast(point)) { markLastLogPoints({ valid: true }); keepPathInState(); @@ -321,6 +334,11 @@ const AbCanvas: FC = props => { }); onMessage(MessageType.Completed); onComplete(); + + streamEventPoint.error = StreamEventError.OVER_RIGHT_POINT; + streamEventPoint.actual_path = streamEventPoint.correct_path; + + onLog(streamEventPoint); return; } @@ -329,6 +347,11 @@ const AbCanvas: FC = props => { keepPathInState(); reCreatePath(point); incrementCurrentIndex(); + + streamEventPoint.error = StreamEventError.OVER_RIGHT_POINT; + streamEventPoint.actual_path = streamEventPoint.correct_path; + + onLog(streamEventPoint); return; } @@ -339,7 +362,12 @@ const AbCanvas: FC = props => { resetCurrentPath(); setFlareGreenPointIndex({ index: getCurrentIndex() }); onMessage(MessageType.IncorrectLine); + + streamEventPoint.error = StreamEventError.OVER_WRONG_POINT; + streamEventPoint.actual_path = node.label; } + + onLog(streamEventPoint); }; const onTouchEnd = (touchInfo: TouchInfo) => { @@ -351,12 +379,17 @@ const AbCanvas: FC = props => { const point: Point = { x: touchInfo.x, y: touchInfo.y }; + const streamEventPoint = createStreamEventPoint(point); + const node = findNodeByPoint(point); if (!node) { setFlareGreenPointIndex({ index: getCurrentIndex() }); } + streamEventPoint.error = StreamEventError.OVER_UNDEFINED_POINT; + + onLog(streamEventPoint); markLastLogPoints({ valid: false, actual: node?.label ?? 'none' }); }; diff --git a/src/entities/drawer/lib/types/draw.ts b/src/entities/drawer/lib/types/draw.ts index eef95a689..8678951da 100644 --- a/src/entities/drawer/lib/types/draw.ts +++ b/src/entities/drawer/lib/types/draw.ts @@ -26,3 +26,5 @@ export type CachedBezierItem = { curveResult: Point[]; curveToInterpolate: Point[]; }; + +export type LogPoint = DrawPoint & { line_number: number }; diff --git a/src/entities/drawer/ui/DrawingBoard.tsx b/src/entities/drawer/ui/DrawingBoard.tsx index 46ed60ee7..2461cf15c 100644 --- a/src/entities/drawer/ui/DrawingBoard.tsx +++ b/src/entities/drawer/ui/DrawingBoard.tsx @@ -3,14 +3,14 @@ import React, { useRef, FC, useMemo } from 'react'; import { StreamEventLoggable } from '@shared/lib'; import { Box, SketchCanvas, SketchCanvasRef, useOnUndo } from '@shared/ui'; -import { DrawLine, ResponseSerializer, DrawResult } from '../lib'; +import { DrawLine, ResponseSerializer, DrawResult, LogPoint } from '../lib'; import DrawPoint from '../lib/utils/DrawPoint'; type Props = { value: Array; onResult: (result: DrawResult) => void; width: number; -} & StreamEventLoggable; +} & StreamEventLoggable; const DrawingBoard: FC = props => { const { value, onResult, width, onLog } = props; @@ -50,8 +50,9 @@ const DrawingBoard: FC = props => { startTime: Date.now(), points: [drawPoint], }; + const logPoint = drawPoint.scale(vector) as DrawPoint; - onLog(drawPoint.scale(vector)); + onLog({ ...logPoint, line_number: value?.length }); }; const onTouchProgress = (x: number, y: number) => { @@ -59,7 +60,9 @@ const DrawingBoard: FC = props => { drawingValueLineRef.current.points.push(drawPoint); - onLog(drawPoint.scale(vector)); + const logPoint = drawPoint.scale(vector); + + onLog({ ...logPoint, line_number: value?.length }); }; const onTouchEnd = () => { diff --git a/src/entities/drawer/ui/DrawingTest.tsx b/src/entities/drawer/ui/DrawingTest.tsx index 6bfc2fe7a..d0f402f77 100644 --- a/src/entities/drawer/ui/DrawingTest.tsx +++ b/src/entities/drawer/ui/DrawingTest.tsx @@ -7,7 +7,7 @@ import { Box, BoxProps, XStack } from '@app/shared/ui'; import { StreamEventLoggable } from '@shared/lib'; import DrawingBoard from './DrawingBoard'; -import { DrawLine, DrawPoint, DrawResult, SvgFileManager } from '../lib'; +import { DrawLine, DrawResult, LogPoint, SvgFileManager } from '../lib'; const RectPadding = 15; @@ -17,7 +17,7 @@ type Props = { backgroundImageUrl: string | null; onResult: (result: DrawResult) => void; toggleScroll: (isScrollEnabled: boolean) => void; -} & StreamEventLoggable & +} & StreamEventLoggable & BoxProps; const DrawingTest: FC = props => { From d16ff8c56179469deb05e9e9e6fed1dd13131bb3 Mon Sep 17 00:00:00 2001 From: Vardan Mkhitaryan Date: Thu, 1 Feb 2024 17:16:47 +0400 Subject: [PATCH 2/5] feature(streaming) M2-4928, M2-4929 Code improvements --- src/entities/abTrail/lib/types/test.ts | 22 ++++-- src/entities/abTrail/lib/utils/index.ts | 1 + .../abTrail/lib/utils/streamEventMapper.ts | 29 ++++++++ src/entities/abTrail/ui/AbCanvas.tsx | 73 ++++++++++++------- src/entities/abTrail/ui/AbTest.tsx | 4 +- src/entities/drawer/lib/types/draw.ts | 2 +- src/entities/drawer/ui/DrawingBoard.tsx | 9 ++- src/entities/drawer/ui/DrawingTest.tsx | 4 +- 8 files changed, 107 insertions(+), 37 deletions(-) create mode 100644 src/entities/abTrail/lib/utils/streamEventMapper.ts diff --git a/src/entities/abTrail/lib/types/test.ts b/src/entities/abTrail/lib/types/test.ts index 2a4b85e24..45106b0f5 100644 --- a/src/entities/abTrail/lib/types/test.ts +++ b/src/entities/abTrail/lib/types/test.ts @@ -29,18 +29,30 @@ export type OnResultLog = { currentIndex: number; }; -export const enum StreamEventError { +export const enum StreamEventErrorType { NotDefined = '?', - OVER_RIGHT_POINT = 'E0', - OVER_WRONG_POINT = 'E1', - OVER_UNDEFINED_POINT = 'E2', + OverRightPoint = 'E0', + OverWrongPoint = 'E1', + OverUndefinedPoint = 'E2', } + export type StreamEventPoint = { + x: number; + y: number; + time: number; + lineNumber: number; + error: StreamEventErrorType; + currentNodeLabel: string; + nextNodeLabel: string; + wrongPointLabel?: string; +}; + +export type StreamEventDto = { x: number; y: number; time: number; line_number: number; - error: StreamEventError; + error: StreamEventErrorType; correct_path: string; actual_path: string; }; diff --git a/src/entities/abTrail/lib/utils/index.ts b/src/entities/abTrail/lib/utils/index.ts index 8ee1a2740..d52540cf3 100644 --- a/src/entities/abTrail/lib/utils/index.ts +++ b/src/entities/abTrail/lib/utils/index.ts @@ -1 +1,2 @@ export * from './calculation'; +export * from './streamEventMapper'; diff --git a/src/entities/abTrail/lib/utils/streamEventMapper.ts b/src/entities/abTrail/lib/utils/streamEventMapper.ts new file mode 100644 index 000000000..a8868c1ce --- /dev/null +++ b/src/entities/abTrail/lib/utils/streamEventMapper.ts @@ -0,0 +1,29 @@ +import { + StreamEventDto, + StreamEventErrorType, + StreamEventPoint, +} from '@entities/abTrail'; + +export const mapStreamEventToDto = ( + streamEvent: StreamEventPoint, +): StreamEventDto => { + let actualPath = '-1'; + if (streamEvent.wrongPointLabel) { + actualPath = streamEvent.wrongPointLabel; + } + if (streamEvent.error === StreamEventErrorType.OverRightPoint) { + actualPath = `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`; + } + + const dto = { + x: streamEvent.x, + y: streamEvent.y, + time: streamEvent.time, + line_number: streamEvent.lineNumber, + error: streamEvent.error, + correct_path: `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`, + actual_path: actualPath, + }; + + return dto; +}; diff --git a/src/entities/abTrail/ui/AbCanvas.tsx b/src/entities/abTrail/ui/AbCanvas.tsx index d347d4096..0ed17a27a 100644 --- a/src/entities/abTrail/ui/AbCanvas.tsx +++ b/src/entities/abTrail/ui/AbCanvas.tsx @@ -23,9 +23,14 @@ import { MessageType, OnResultLog, StreamEventPoint, - StreamEventError, + StreamEventErrorType, + StreamEventDto, } from '../lib'; -import { getDistance, transformCoordinates } from '../lib/utils'; +import { + getDistance, + mapStreamEventToDto, + transformCoordinates, +} from '../lib/utils'; const paint = Skia.Paint(); paint.setColor(Skia.Color('black')); @@ -42,7 +47,7 @@ type Props = { onLogResult: (data: OnResultLog) => void; onMessage: (message: MessageType) => void; onComplete: () => void; -} & StreamEventLoggable & +} & StreamEventLoggable & BoxProps; const AbCanvas: FC = props => { @@ -79,7 +84,7 @@ const AbCanvas: FC = props => { onMessage, width, readonly, - onLog, + onLog: onAddPointToStream, } = props; const canvasData = useMemo( @@ -227,13 +232,41 @@ const AbCanvas: FC = props => { x: (point.x * width) / 100, y: (point.y * width) / 100, time: Date.now(), - line_number: logLines?.length - 1, - error: StreamEventError.NotDefined, - correct_path: `${currentNodeLabel} ~ ${nextNodeLabel}`, - actual_path: '-1', + lineNumber: logLines?.length - 1, + error: StreamEventErrorType.NotDefined, + currentNodeLabel, + nextNodeLabel, }; }; + const addOverRightPointToStream = (point: Point) => { + const streamEventPoint = createStreamEventPoint(point); + streamEventPoint.error = StreamEventErrorType.OverRightPoint; + + onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + }; + + const addOverWrongPointToStream = (point: Point, wrongPointLabel: string) => { + const streamEventPoint = createStreamEventPoint(point); + streamEventPoint.error = StreamEventErrorType.OverWrongPoint; + streamEventPoint.wrongPointLabel = wrongPointLabel; + + onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + }; + + const addPointToStream = (point: Point) => { + const streamEventPoint = createStreamEventPoint(point); + + onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + }; + + const addOverUndefinedPointToStream = (point: Point) => { + const streamEventPoint = createStreamEventPoint(point); + streamEventPoint.error = StreamEventErrorType.OverUndefinedPoint; + + onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + }; + const addLogLine = ({ x, y }: Point): void => { const newLine: LogLine = { points: [createLogPoint({ x, y })], @@ -296,7 +329,7 @@ const AbCanvas: FC = props => { drawPath(); reRender(); - onLog(createStreamEventPoint(point)); + onAddPointToStream(mapStreamEventToDto(createStreamEventPoint(point))); }; const onTouchProgress = (touchInfo: TouchInfo) => { @@ -307,7 +340,6 @@ const AbCanvas: FC = props => { } const point: Point = { x: touchInfo.x, y: touchInfo.y }; - const streamEventPoint = createStreamEventPoint(point); currentPath.lineTo(point.x, point.y); @@ -335,10 +367,7 @@ const AbCanvas: FC = props => { onMessage(MessageType.Completed); onComplete(); - streamEventPoint.error = StreamEventError.OVER_RIGHT_POINT; - streamEventPoint.actual_path = streamEventPoint.correct_path; - - onLog(streamEventPoint); + addOverRightPointToStream(point); return; } @@ -348,10 +377,7 @@ const AbCanvas: FC = props => { reCreatePath(point); incrementCurrentIndex(); - streamEventPoint.error = StreamEventError.OVER_RIGHT_POINT; - streamEventPoint.actual_path = streamEventPoint.correct_path; - - onLog(streamEventPoint); + addOverRightPointToStream(point); return; } @@ -363,11 +389,11 @@ const AbCanvas: FC = props => { setFlareGreenPointIndex({ index: getCurrentIndex() }); onMessage(MessageType.IncorrectLine); - streamEventPoint.error = StreamEventError.OVER_WRONG_POINT; - streamEventPoint.actual_path = node.label; + addOverWrongPointToStream(point, node.label); + return; } - onLog(streamEventPoint); + addPointToStream(point); }; const onTouchEnd = (touchInfo: TouchInfo) => { @@ -379,17 +405,14 @@ const AbCanvas: FC = props => { const point: Point = { x: touchInfo.x, y: touchInfo.y }; - const streamEventPoint = createStreamEventPoint(point); - const node = findNodeByPoint(point); if (!node) { setFlareGreenPointIndex({ index: getCurrentIndex() }); } - streamEventPoint.error = StreamEventError.OVER_UNDEFINED_POINT; + addOverUndefinedPointToStream(point); - onLog(streamEventPoint); markLastLogPoints({ valid: false, actual: node?.label ?? 'none' }); }; diff --git a/src/entities/abTrail/ui/AbTest.tsx b/src/entities/abTrail/ui/AbTest.tsx index 67e30d4f8..57ffa4c8f 100644 --- a/src/entities/abTrail/ui/AbTest.tsx +++ b/src/entities/abTrail/ui/AbTest.tsx @@ -11,13 +11,13 @@ import { AbTestResult, MessageType, MessageTypeStrings, - StreamEventPoint, + StreamEventDto, } from '../lib'; type Props = { testData: AbTestPayload; onResponse?: (response: AbTestResult) => void; -} & StreamEventLoggable & +} & StreamEventLoggable & BoxProps; const ShapesRectPadding = 15; diff --git a/src/entities/drawer/lib/types/draw.ts b/src/entities/drawer/lib/types/draw.ts index 8678951da..62738aa31 100644 --- a/src/entities/drawer/lib/types/draw.ts +++ b/src/entities/drawer/lib/types/draw.ts @@ -27,4 +27,4 @@ export type CachedBezierItem = { curveToInterpolate: Point[]; }; -export type LogPoint = DrawPoint & { line_number: number }; +export type StreamLogPoint = DrawPoint & { line_number: number }; diff --git a/src/entities/drawer/ui/DrawingBoard.tsx b/src/entities/drawer/ui/DrawingBoard.tsx index 2461cf15c..9864251bb 100644 --- a/src/entities/drawer/ui/DrawingBoard.tsx +++ b/src/entities/drawer/ui/DrawingBoard.tsx @@ -3,14 +3,19 @@ import React, { useRef, FC, useMemo } from 'react'; import { StreamEventLoggable } from '@shared/lib'; import { Box, SketchCanvas, SketchCanvasRef, useOnUndo } from '@shared/ui'; -import { DrawLine, ResponseSerializer, DrawResult, LogPoint } from '../lib'; +import { + DrawLine, + ResponseSerializer, + DrawResult, + StreamLogPoint, +} from '../lib'; import DrawPoint from '../lib/utils/DrawPoint'; type Props = { value: Array; onResult: (result: DrawResult) => void; width: number; -} & StreamEventLoggable; +} & StreamEventLoggable; const DrawingBoard: FC = props => { const { value, onResult, width, onLog } = props; diff --git a/src/entities/drawer/ui/DrawingTest.tsx b/src/entities/drawer/ui/DrawingTest.tsx index d0f402f77..9ee9765eb 100644 --- a/src/entities/drawer/ui/DrawingTest.tsx +++ b/src/entities/drawer/ui/DrawingTest.tsx @@ -7,7 +7,7 @@ import { Box, BoxProps, XStack } from '@app/shared/ui'; import { StreamEventLoggable } from '@shared/lib'; import DrawingBoard from './DrawingBoard'; -import { DrawLine, DrawResult, LogPoint, SvgFileManager } from '../lib'; +import { DrawLine, DrawResult, StreamLogPoint, SvgFileManager } from '../lib'; const RectPadding = 15; @@ -17,7 +17,7 @@ type Props = { backgroundImageUrl: string | null; onResult: (result: DrawResult) => void; toggleScroll: (isScrollEnabled: boolean) => void; -} & StreamEventLoggable & +} & StreamEventLoggable & BoxProps; const DrawingTest: FC = props => { From 5a5b31dc9bb21e28067cb3f333676ccfe9b45bde Mon Sep 17 00:00:00 2001 From: Vardan Mkhitaryan Date: Fri, 2 Feb 2024 13:40:40 +0400 Subject: [PATCH 3/5] feature(streaming) M2-4928, M2-4929 Code improvements --- src/entities/abTrail/lib/types/test.ts | 28 ------- src/entities/abTrail/lib/utils/index.ts | 1 - .../abTrail/lib/utils/streamEventMapper.ts | 29 ------- src/entities/abTrail/ui/AbCanvas.tsx | 50 +++++------- src/entities/abTrail/ui/AbTest.tsx | 11 +-- src/entities/drawer/lib/types/draw.ts | 2 - src/entities/drawer/ui/DrawingBoard.tsx | 15 ++-- src/entities/drawer/ui/DrawingTest.tsx | 6 +- .../ui/StabilityTrackerItem.tsx | 21 ++--- src/features/pass-survey/ui/ActivityItem.tsx | 12 ++- src/shared/lib/tcp/lib/streamEventMapper.ts | 81 +++++++++++++++++++ src/shared/lib/tcp/types.ts | 66 +++++++++++++++ src/shared/lib/tcp/useSendLiveEvent.ts | 18 ++--- 13 files changed, 205 insertions(+), 135 deletions(-) delete mode 100644 src/entities/abTrail/lib/utils/streamEventMapper.ts create mode 100644 src/shared/lib/tcp/lib/streamEventMapper.ts diff --git a/src/entities/abTrail/lib/types/test.ts b/src/entities/abTrail/lib/types/test.ts index 45106b0f5..90c03961a 100644 --- a/src/entities/abTrail/lib/types/test.ts +++ b/src/entities/abTrail/lib/types/test.ts @@ -29,34 +29,6 @@ export type OnResultLog = { currentIndex: number; }; -export const enum StreamEventErrorType { - NotDefined = '?', - OverRightPoint = 'E0', - OverWrongPoint = 'E1', - OverUndefinedPoint = 'E2', -} - -export type StreamEventPoint = { - x: number; - y: number; - time: number; - lineNumber: number; - error: StreamEventErrorType; - currentNodeLabel: string; - nextNodeLabel: string; - wrongPointLabel?: string; -}; - -export type StreamEventDto = { - x: number; - y: number; - time: number; - line_number: number; - error: StreamEventErrorType; - correct_path: string; - actual_path: string; -}; - export type AbTestResult = { width: number; startTime: number; diff --git a/src/entities/abTrail/lib/utils/index.ts b/src/entities/abTrail/lib/utils/index.ts index d52540cf3..8ee1a2740 100644 --- a/src/entities/abTrail/lib/utils/index.ts +++ b/src/entities/abTrail/lib/utils/index.ts @@ -1,2 +1 @@ export * from './calculation'; -export * from './streamEventMapper'; diff --git a/src/entities/abTrail/lib/utils/streamEventMapper.ts b/src/entities/abTrail/lib/utils/streamEventMapper.ts deleted file mode 100644 index a8868c1ce..000000000 --- a/src/entities/abTrail/lib/utils/streamEventMapper.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - StreamEventDto, - StreamEventErrorType, - StreamEventPoint, -} from '@entities/abTrail'; - -export const mapStreamEventToDto = ( - streamEvent: StreamEventPoint, -): StreamEventDto => { - let actualPath = '-1'; - if (streamEvent.wrongPointLabel) { - actualPath = streamEvent.wrongPointLabel; - } - if (streamEvent.error === StreamEventErrorType.OverRightPoint) { - actualPath = `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`; - } - - const dto = { - x: streamEvent.x, - y: streamEvent.y, - time: streamEvent.time, - line_number: streamEvent.lineNumber, - error: streamEvent.error, - correct_path: `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`, - actual_path: actualPath, - }; - - return dto; -}; diff --git a/src/entities/abTrail/ui/AbCanvas.tsx b/src/entities/abTrail/ui/AbCanvas.tsx index 0ed17a27a..22d0f374c 100644 --- a/src/entities/abTrail/ui/AbCanvas.tsx +++ b/src/entities/abTrail/ui/AbCanvas.tsx @@ -13,24 +13,16 @@ import { } from '@shopify/react-native-skia'; import { AbTestPayload, Point, TestNode } from '@app/abstract/lib'; -import { StreamEventLoggable } from '@shared/lib'; +import { + AbTestStreamEvent, + AbTestStreamEventErrorType, + StreamEventLoggable, +} from '@shared/lib'; import { Box, BoxProps } from '@shared/ui'; import AbShapes from './AbShapes'; -import { - LogLine, - LogPoint, - MessageType, - OnResultLog, - StreamEventPoint, - StreamEventErrorType, - StreamEventDto, -} from '../lib'; -import { - getDistance, - mapStreamEventToDto, - transformCoordinates, -} from '../lib/utils'; +import { LogLine, LogPoint, MessageType, OnResultLog } from '../lib'; +import { getDistance, transformCoordinates } from '../lib/utils'; const paint = Skia.Paint(); paint.setColor(Skia.Color('black')); @@ -47,7 +39,7 @@ type Props = { onLogResult: (data: OnResultLog) => void; onMessage: (message: MessageType) => void; onComplete: () => void; -} & StreamEventLoggable & +} & StreamEventLoggable & BoxProps; const AbCanvas: FC = props => { @@ -225,7 +217,7 @@ const AbCanvas: FC = props => { return logPoint; }; - const createStreamEventPoint = (point: Point): StreamEventPoint => { + const createStreamEventPoint = (point: Point): AbTestStreamEvent => { const [currentNodeLabel, nextNodeLabel] = getCurrentAndNextNodeLabels(); return { @@ -233,38 +225,38 @@ const AbCanvas: FC = props => { y: (point.y * width) / 100, time: Date.now(), lineNumber: logLines?.length - 1, - error: StreamEventErrorType.NotDefined, + error: AbTestStreamEventErrorType.NotDefined, currentNodeLabel, nextNodeLabel, }; }; - const addOverRightPointToStream = (point: Point) => { + const addOverCorrectPointToStream = (point: Point) => { const streamEventPoint = createStreamEventPoint(point); - streamEventPoint.error = StreamEventErrorType.OverRightPoint; + streamEventPoint.error = AbTestStreamEventErrorType.OverCorrectPoint; - onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + onAddPointToStream(streamEventPoint); }; const addOverWrongPointToStream = (point: Point, wrongPointLabel: string) => { const streamEventPoint = createStreamEventPoint(point); - streamEventPoint.error = StreamEventErrorType.OverWrongPoint; + streamEventPoint.error = AbTestStreamEventErrorType.OverWrongPoint; streamEventPoint.wrongPointLabel = wrongPointLabel; - onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + onAddPointToStream(streamEventPoint); }; const addPointToStream = (point: Point) => { const streamEventPoint = createStreamEventPoint(point); - onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + onAddPointToStream(streamEventPoint); }; const addOverUndefinedPointToStream = (point: Point) => { const streamEventPoint = createStreamEventPoint(point); - streamEventPoint.error = StreamEventErrorType.OverUndefinedPoint; + streamEventPoint.error = AbTestStreamEventErrorType.OverUndefinedPoint; - onAddPointToStream(mapStreamEventToDto(streamEventPoint)); + onAddPointToStream(streamEventPoint); }; const addLogLine = ({ x, y }: Point): void => { @@ -329,7 +321,7 @@ const AbCanvas: FC = props => { drawPath(); reRender(); - onAddPointToStream(mapStreamEventToDto(createStreamEventPoint(point))); + onAddPointToStream(createStreamEventPoint(point)); }; const onTouchProgress = (touchInfo: TouchInfo) => { @@ -358,6 +350,7 @@ const AbCanvas: FC = props => { if (isOverNext(point) && isOverLast(point)) { markLastLogPoints({ valid: true }); + addOverCorrectPointToStream(point); keepPathInState(); resetCurrentPath(); onLogResult({ @@ -367,17 +360,16 @@ const AbCanvas: FC = props => { onMessage(MessageType.Completed); onComplete(); - addOverRightPointToStream(point); return; } if (isOverNext(point)) { markLastLogPoints({ valid: true }); + addOverCorrectPointToStream(point); keepPathInState(); reCreatePath(point); incrementCurrentIndex(); - addOverRightPointToStream(point); return; } diff --git a/src/entities/abTrail/ui/AbTest.tsx b/src/entities/abTrail/ui/AbTest.tsx index 57ffa4c8f..16e1a43ec 100644 --- a/src/entities/abTrail/ui/AbTest.tsx +++ b/src/entities/abTrail/ui/AbTest.tsx @@ -3,21 +3,16 @@ import { FC, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { AbTestPayload } from '@app/abstract/lib'; -import { colors, StreamEventLoggable } from '@shared/lib'; +import { AbTestStreamEvent, colors, StreamEventLoggable } from '@shared/lib'; import { Box, BoxProps, Text, XStack } from '@shared/ui'; import AbCanvas from './AbCanvas'; -import { - AbTestResult, - MessageType, - MessageTypeStrings, - StreamEventDto, -} from '../lib'; +import { AbTestResult, MessageType, MessageTypeStrings } from '../lib'; type Props = { testData: AbTestPayload; onResponse?: (response: AbTestResult) => void; -} & StreamEventLoggable & +} & StreamEventLoggable & BoxProps; const ShapesRectPadding = 15; diff --git a/src/entities/drawer/lib/types/draw.ts b/src/entities/drawer/lib/types/draw.ts index 62738aa31..eef95a689 100644 --- a/src/entities/drawer/lib/types/draw.ts +++ b/src/entities/drawer/lib/types/draw.ts @@ -26,5 +26,3 @@ export type CachedBezierItem = { curveResult: Point[]; curveToInterpolate: Point[]; }; - -export type StreamLogPoint = DrawPoint & { line_number: number }; diff --git a/src/entities/drawer/ui/DrawingBoard.tsx b/src/entities/drawer/ui/DrawingBoard.tsx index 9864251bb..8cd2b00a7 100644 --- a/src/entities/drawer/ui/DrawingBoard.tsx +++ b/src/entities/drawer/ui/DrawingBoard.tsx @@ -1,21 +1,16 @@ import React, { useRef, FC, useMemo } from 'react'; -import { StreamEventLoggable } from '@shared/lib'; +import { DrawingStreamEvent, StreamEventLoggable } from '@shared/lib'; import { Box, SketchCanvas, SketchCanvasRef, useOnUndo } from '@shared/ui'; -import { - DrawLine, - ResponseSerializer, - DrawResult, - StreamLogPoint, -} from '../lib'; +import { DrawLine, ResponseSerializer, DrawResult } from '../lib'; import DrawPoint from '../lib/utils/DrawPoint'; type Props = { value: Array; onResult: (result: DrawResult) => void; width: number; -} & StreamEventLoggable; +} & StreamEventLoggable; const DrawingBoard: FC = props => { const { value, onResult, width, onLog } = props; @@ -57,7 +52,7 @@ const DrawingBoard: FC = props => { }; const logPoint = drawPoint.scale(vector) as DrawPoint; - onLog({ ...logPoint, line_number: value?.length }); + onLog({ ...logPoint, lineNumber: value?.length }); }; const onTouchProgress = (x: number, y: number) => { @@ -67,7 +62,7 @@ const DrawingBoard: FC = props => { const logPoint = drawPoint.scale(vector); - onLog({ ...logPoint, line_number: value?.length }); + onLog({ ...logPoint, lineNumber: value?.length }); }; const onTouchEnd = () => { diff --git a/src/entities/drawer/ui/DrawingTest.tsx b/src/entities/drawer/ui/DrawingTest.tsx index 9ee9765eb..112cac1a5 100644 --- a/src/entities/drawer/ui/DrawingTest.tsx +++ b/src/entities/drawer/ui/DrawingTest.tsx @@ -4,10 +4,10 @@ import { FC, useState } from 'react'; import { CachedImage } from '@georstat/react-native-image-cache'; import { Box, BoxProps, XStack } from '@app/shared/ui'; -import { StreamEventLoggable } from '@shared/lib'; +import { DrawingStreamEvent, StreamEventLoggable } from '@shared/lib'; import DrawingBoard from './DrawingBoard'; -import { DrawLine, DrawResult, StreamLogPoint, SvgFileManager } from '../lib'; +import { DrawLine, DrawResult, SvgFileManager } from '../lib'; const RectPadding = 15; @@ -17,7 +17,7 @@ type Props = { backgroundImageUrl: string | null; onResult: (result: DrawResult) => void; toggleScroll: (isScrollEnabled: boolean) => void; -} & StreamEventLoggable & +} & StreamEventLoggable & BoxProps; const DrawingTest: FC = props => { diff --git a/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx b/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx index 4ad95f862..2ee162a66 100644 --- a/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx +++ b/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx @@ -11,8 +11,11 @@ import { orientation } from 'react-native-sensors'; import Svg, { Circle } from 'react-native-svg'; import { useToast } from 'react-native-toast-notifications'; -import { StabilityTrackerAnswerValue } from '@shared/api'; -import { useForceUpdate, StreamEventLoggable } from '@shared/lib'; +import { + useForceUpdate, + StreamEventLoggable, + StabilityTrackerEvent, +} from '@shared/lib'; import { YStack } from '@shared/ui'; import ControlBar from './ControlBar'; @@ -65,7 +68,7 @@ type Props = { onComplete: (response: StabilityTrackerResponse) => void; onMaxLambdaChange: (contextKey: string, contextValue: unknown) => void; maxLambda?: number; -} & StreamEventLoggable; +} & StreamEventLoggable; const StabilityTrackerItemScreen = (props: Props) => { const toast = useToast(); @@ -344,17 +347,7 @@ const StabilityTrackerItemScreen = (props: Props) => { lambdaSlope: lambdaSlope.current, }; - const liveEvent: StabilityTrackerAnswerValue = { - timestamp: response.timestamp, - stimPos: response.circlePosition, - targetPos: response.targetPosition, - userPos: response.userPosition, - score: response.score, - lambda: response.lambda, - lambdaSlope: response.lambdaSlope, - }; - - onLog(liveEvent); + onLog(response); responses.current.push(response); }; diff --git a/src/features/pass-survey/ui/ActivityItem.tsx b/src/features/pass-survey/ui/ActivityItem.tsx index 53ad4bad1..008759346 100644 --- a/src/features/pass-survey/ui/ActivityItem.tsx +++ b/src/features/pass-survey/ui/ActivityItem.tsx @@ -22,7 +22,12 @@ import { useAppletStreamingStatus } from '@entities/applet/lib/hooks'; import { DrawingTest } from '@entities/drawer'; import { HtmlFlanker, NativeIosFlanker } from '@entities/flanker'; import { StabilityTracker } from '@entities/stabilityTracker'; -import { IS_ANDROID, useSendEvent, wait } from '@shared/lib'; +import { + IS_ANDROID, + StreamEventActivityItemType, + useSendEvent, + wait, +} from '@shared/lib'; import { RadioActivityItem, SurveySlider, @@ -67,7 +72,10 @@ function ActivityItem({ const [scrollEnabled, setScrollEnabled] = useState(initialScrollEnabled); - const { sendLiveEvent } = useSendEvent(streamEnabled); + const { sendLiveEvent } = useSendEvent( + type as StreamEventActivityItemType, + streamEnabled, + ); const { next } = useContext(HandlersContext); diff --git a/src/shared/lib/tcp/lib/streamEventMapper.ts b/src/shared/lib/tcp/lib/streamEventMapper.ts new file mode 100644 index 000000000..64344958f --- /dev/null +++ b/src/shared/lib/tcp/lib/streamEventMapper.ts @@ -0,0 +1,81 @@ +import { + AbTestStreamEventDto, + AbTestStreamEventErrorType, + AbTestStreamEvent, + StreamEventActivityItemType, + DrawingStreamEvent, + DrawingStreamEventDto, + StabilityTrackerEvent, + StabilityTrackerEventDto, + LiveEvent, +} from '../types'; + +const mapABTestStreamEventToDto = ( + streamEvent: AbTestStreamEvent, +): AbTestStreamEventDto => { + let actualPath = '-1'; + if (streamEvent.wrongPointLabel) { + actualPath = streamEvent.wrongPointLabel; + } + if (streamEvent.error === AbTestStreamEventErrorType.OverCorrectPoint) { + actualPath = `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`; + } + + const dto = { + x: streamEvent.x, + y: streamEvent.y, + time: streamEvent.time, + line_number: streamEvent.lineNumber, + error: streamEvent.error, + correct_path: `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`, + actual_path: actualPath, + }; + + return dto; +}; +const mapDrawingStreamEventToDto = ( + streamEvent: DrawingStreamEvent, +): DrawingStreamEventDto => { + const dto = { + x: streamEvent.x, + y: streamEvent.y, + line_number: streamEvent.lineNumber, + time: streamEvent.time, + }; + + return dto; +}; + +const mapStabilityTrackerStreamEventToDto = ( + streamEvent: StabilityTrackerEvent, +): StabilityTrackerEventDto => { + const dto = { + timestamp: streamEvent.timestamp, + stimPos: streamEvent.circlePosition, + targetPos: streamEvent.targetPosition, + userPos: streamEvent.userPosition, + score: streamEvent.score, + lambda: streamEvent.lambda, + lambdaSlope: streamEvent.lambdaSlope, + }; + + return dto; +}; + +export const mapStreamEventToDto = ( + type: StreamEventActivityItemType, + streamEvent: LiveEvent, +) => { + switch (type) { + case 'AbTest': + return mapABTestStreamEventToDto(streamEvent as AbTestStreamEvent); + case 'DrawingTest': + return mapDrawingStreamEventToDto(streamEvent as DrawingStreamEvent); + case 'StabilityTracker': + return mapStabilityTrackerStreamEventToDto( + streamEvent as StabilityTrackerEvent, + ); + default: + return streamEvent; + } +}; diff --git a/src/shared/lib/tcp/types.ts b/src/shared/lib/tcp/types.ts index 82900de1c..91f6bf961 100644 --- a/src/shared/lib/tcp/types.ts +++ b/src/shared/lib/tcp/types.ts @@ -1,3 +1,5 @@ +import { DrawPoint } from '@entities/drawer'; + export type FlankerLiveEvent = { trial_index: number; duration: number; @@ -14,3 +16,67 @@ export type FlankerLiveEvent = { export type StreamEventLoggable = { onLog: (event: T) => void; }; + +export type LiveEvent = + | AbTestStreamEvent + | DrawingStreamEvent + | StabilityTrackerEvent + | FlankerLiveEvent; + +export type StabilityTrackerEventDto = { + timestamp: number; + stimPos: number[]; + userPos: number[]; + targetPos: number[]; + lambda: number; + score: number; + lambdaSlope: number; +}; + +export type StabilityTrackerEvent = { + timestamp: number; + circlePosition: number[]; + userPosition: number[]; + targetPosition: number[]; + lambda: number; + score: number; + lambdaSlope: number; +}; + +export type StreamEventActivityItemType = + | 'AbTest' + | 'StabilityTracker' + | 'DrawingTest' + | 'Flanker'; + +export type DrawingStreamEvent = DrawPoint & { lineNumber: number }; + +export type DrawingStreamEventDto = DrawPoint & { line_number: number }; + +export const enum AbTestStreamEventErrorType { + NotDefined = '?', + OverCorrectPoint = 'E0', + OverWrongPoint = 'E1', + OverUndefinedPoint = 'E2', +} + +export type AbTestStreamEvent = { + x: number; + y: number; + time: number; + lineNumber: number; + error: AbTestStreamEventErrorType; + currentNodeLabel: string; + nextNodeLabel: string; + wrongPointLabel?: string; +}; + +export type AbTestStreamEventDto = { + x: number; + y: number; + time: number; + line_number: number; + error: AbTestStreamEventErrorType; + correct_path: string; + actual_path: string; +}; diff --git a/src/shared/lib/tcp/useSendLiveEvent.ts b/src/shared/lib/tcp/useSendLiveEvent.ts index 45f140545..b459da5ef 100644 --- a/src/shared/lib/tcp/useSendLiveEvent.ts +++ b/src/shared/lib/tcp/useSendLiveEvent.ts @@ -1,14 +1,13 @@ import { useCallback } from 'react'; -import { DrawPoint } from '@entities/drawer'; -import { StabilityTrackerAnswerValue } from '@shared/api'; - -import { FlankerLiveEvent } from './types'; +import { mapStreamEventToDto } from './lib/streamEventMapper'; +import { LiveEvent, StreamEventActivityItemType } from './types'; import { useTCPSocket } from './useTCPSocket'; -type LiveEvent = DrawPoint | StabilityTrackerAnswerValue | FlankerLiveEvent; - -export function useSendEvent(streamEnabled: boolean) { +export function useSendEvent( + type: StreamEventActivityItemType, + streamEnabled: boolean, +) { const { sendMessage, connected } = useTCPSocket({ onClosed: () => {}, }); @@ -18,15 +17,16 @@ export function useSendEvent(streamEnabled: boolean) { if (!connected || !streamEnabled) { return; } + const dataDto = mapStreamEventToDto(type, data); const liveEvent = { type: 'live_event', - data, + data: dataDto, }; sendMessage(JSON.stringify(liveEvent)); }, - [sendMessage, connected, streamEnabled], + [type, sendMessage, connected, streamEnabled], ); return { From d11f33dab7082534c574a4cd9d239b25228469f3 Mon Sep 17 00:00:00 2001 From: Vardan Mkhitaryan Date: Mon, 5 Feb 2024 18:07:08 +0400 Subject: [PATCH 4/5] feature(streaming) M2-4928, M2-4929 Code improvements, added mapper for liveStreaming, typescript type improvements --- src/entities/abTrail/ui/AbCanvas.tsx | 1 + src/entities/drawer/ui/DrawingBoard.tsx | 4 +- .../flanker/ui/HtmlFlanker/HtmlFlanker.tsx | 47 +++---- .../ui/NativeIosFlanker/NativeIosFlanker.tsx | 11 +- .../ui/StabilityTrackerItem.tsx | 3 +- src/features/pass-survey/model/index.ts | 1 + .../pass-survey/model/streamEventMapper.ts | 126 ++++++++++++++++++ .../ScoresCalculator.collectMaxScores.test.ts | 1 + ...resCalculator.collectScoreForRadio.test.ts | 1 + src/features/pass-survey/ui/ActivityItem.tsx | 29 ++-- src/shared/lib/tcp/lib/streamEventMapper.ts | 81 ----------- src/shared/lib/tcp/types.ts | 40 ++++-- src/shared/lib/tcp/useSendLiveEvent.ts | 14 +- 13 files changed, 206 insertions(+), 153 deletions(-) create mode 100644 src/features/pass-survey/model/streamEventMapper.ts delete mode 100644 src/shared/lib/tcp/lib/streamEventMapper.ts diff --git a/src/entities/abTrail/ui/AbCanvas.tsx b/src/entities/abTrail/ui/AbCanvas.tsx index 22d0f374c..c41190c42 100644 --- a/src/entities/abTrail/ui/AbCanvas.tsx +++ b/src/entities/abTrail/ui/AbCanvas.tsx @@ -228,6 +228,7 @@ const AbCanvas: FC = props => { error: AbTestStreamEventErrorType.NotDefined, currentNodeLabel, nextNodeLabel, + type: 'AbTest', }; }; diff --git a/src/entities/drawer/ui/DrawingBoard.tsx b/src/entities/drawer/ui/DrawingBoard.tsx index 8cd2b00a7..19b617b0e 100644 --- a/src/entities/drawer/ui/DrawingBoard.tsx +++ b/src/entities/drawer/ui/DrawingBoard.tsx @@ -52,7 +52,7 @@ const DrawingBoard: FC = props => { }; const logPoint = drawPoint.scale(vector) as DrawPoint; - onLog({ ...logPoint, lineNumber: value?.length }); + onLog({ ...logPoint, lineNumber: value?.length, type: 'DrawingTest' }); }; const onTouchProgress = (x: number, y: number) => { @@ -62,7 +62,7 @@ const DrawingBoard: FC = props => { const logPoint = drawPoint.scale(vector); - onLog({ ...logPoint, lineNumber: value?.length }); + onLog({ ...logPoint, lineNumber: value?.length, type: 'DrawingTest' }); }; const onTouchEnd = () => { diff --git a/src/entities/flanker/ui/HtmlFlanker/HtmlFlanker.tsx b/src/entities/flanker/ui/HtmlFlanker/HtmlFlanker.tsx index 1b647bdd2..302bbd06f 100644 --- a/src/entities/flanker/ui/HtmlFlanker/HtmlFlanker.tsx +++ b/src/entities/flanker/ui/HtmlFlanker/HtmlFlanker.tsx @@ -46,34 +46,6 @@ const HtmlFlanker: FC = props => { configuration.buttons, )}); \n ${parsedConfiguration}`; - const generateLiveEvent = ( - data: FlankerWebViewLogRecord, - ): FlankerLiveEvent => { - let screenCountPerTrial = 1; - - if (configuration.showFeedback) { - screenCountPerTrial++; - } - if (configuration.showFixation) { - screenCountPerTrial++; - } - - const liveEvent = { - trial_index: Math.ceil((data.trial_index + 1) / screenCountPerTrial), - duration: data.rt, - question: data.stimulus, - button_pressed: data.button_pressed, - start_time: data.image_time, - correct: data.correct, - start_timestamp: data.start_timestamp, - offset: data.start_timestamp - data.start_time, - tag: data.tag, - response_touch_timestamp: data.rt ? data.start_timestamp + data.rt : null, - }; - - return liveEvent; - }; - return ( = props => { } if (type === 'response') { - const liveEvent = generateLiveEvent( - data as FlankerWebViewLogRecord, - ); + const responseData = parsed.data as FlankerWebViewLogRecord; + + const liveEvent: FlankerLiveEvent = { + trialIndex: responseData.trial_index, + duration: responseData.rt, + question: responseData.stimulus, + buttonPressed: responseData.button_pressed, + imageTime: responseData.image_time, + startTime: responseData.start_time, + correct: responseData.correct, + startTimestamp: responseData.start_timestamp, + tag: responseData.tag, + showFeedback: configuration.showFeedback, + showFixation: configuration.showFixation, + type: 'Flanker', + }; props.onLog(liveEvent); diff --git a/src/entities/flanker/ui/NativeIosFlanker/NativeIosFlanker.tsx b/src/entities/flanker/ui/NativeIosFlanker/NativeIosFlanker.tsx index 2265e0ee4..74817dcc1 100644 --- a/src/entities/flanker/ui/NativeIosFlanker/NativeIosFlanker.tsx +++ b/src/entities/flanker/ui/NativeIosFlanker/NativeIosFlanker.tsx @@ -60,16 +60,17 @@ const NativeIosFlanker: FC = props => { } const liveEvent: FlankerLiveEvent = { - trial_index: parsed.trial_index, + trialIndex: parsed.trial_index, duration: parsed.rt, question: parsed.stimulus, correct: parsed.correct, - response_touch_timestamp: parsed.response_touch_timestamp, + responseTouchTimeStamp: parsed.response_touch_timestamp, tag: parsed.tag, - start_time: parsed.start_time, - start_timestamp: parsed.image_time, + startTime: parsed.start_time, + startTimestamp: parsed.image_time, offset: 0, - button_pressed: parsed.button_pressed, + buttonPressed: parsed.button_pressed, + type: 'Flanker', }; if (type === 'response') { diff --git a/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx b/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx index 2ee162a66..6a065cf94 100644 --- a/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx +++ b/src/entities/stabilityTracker/ui/StabilityTrackerItem.tsx @@ -337,7 +337,7 @@ const StabilityTrackerItemScreen = (props: Props) => { }; const saveResponses = () => { - const response = { + const response: StabilityTrackerEvent = { timestamp: new Date().getTime(), circlePosition: [circlePosition.current[1] / PANEL_RADIUS - 1], userPosition: [userPosition.current[1] / PANEL_RADIUS - 1], @@ -345,6 +345,7 @@ const StabilityTrackerItemScreen = (props: Props) => { lambda: lambdaValue.current, score: score.current, lambdaSlope: lambdaSlope.current, + type: 'StabilityTracker', }; onLog(response); diff --git a/src/features/pass-survey/model/index.ts b/src/features/pass-survey/model/index.ts index 3ee2be554..8087026cb 100644 --- a/src/features/pass-survey/model/index.ts +++ b/src/features/pass-survey/model/index.ts @@ -4,3 +4,4 @@ export * from './hooks'; export * from './ActivityRecordInitializer'; export { default as AlertsExtractor } from './AlertsExtractor'; export { default as ScoresExtractor } from './ScoresExtractor'; +export * from './streamEventMapper'; diff --git a/src/features/pass-survey/model/streamEventMapper.ts b/src/features/pass-survey/model/streamEventMapper.ts new file mode 100644 index 000000000..8f8db1cde --- /dev/null +++ b/src/features/pass-survey/model/streamEventMapper.ts @@ -0,0 +1,126 @@ +import { FlankerLiveEvent, IS_ANDROID, LiveEventDto } from '@shared/lib'; +import { + AbTestStreamEventDto, + AbTestStreamEventErrorType, + AbTestStreamEvent, + DrawingStreamEvent, + DrawingStreamEventDto, + StabilityTrackerEvent, + StabilityTrackerEventDto, + LiveEvent, +} from '@shared/lib/tcp/types'; +const mapABTestStreamEventToDto = ( + streamEvent: AbTestStreamEvent, +): AbTestStreamEventDto => { + let actualPath = '-1'; + if (streamEvent.wrongPointLabel) { + actualPath = streamEvent.wrongPointLabel; + } + if (streamEvent.error === AbTestStreamEventErrorType.OverCorrectPoint) { + actualPath = `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`; + } + + const dto = { + x: streamEvent.x, + y: streamEvent.y, + time: streamEvent.time, + line_number: streamEvent.lineNumber, + error: streamEvent.error, + correct_path: `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`, + actual_path: actualPath, + }; + + return dto; +}; +const mapDrawingStreamEventToDto = ( + streamEvent: DrawingStreamEvent, +): DrawingStreamEventDto => { + const dto = { + x: streamEvent.x, + y: streamEvent.y, + line_number: streamEvent.lineNumber, + time: streamEvent.time, + }; + + return dto; +}; + +const mapStabilityTrackerStreamEventToDto = ( + streamEvent: StabilityTrackerEvent, +): StabilityTrackerEventDto => { + const dto = { + timestamp: streamEvent.timestamp, + stimPos: streamEvent.circlePosition, + targetPos: streamEvent.targetPosition, + userPos: streamEvent.userPosition, + score: streamEvent.score, + lambda: streamEvent.lambda, + lambdaSlope: streamEvent.lambdaSlope, + }; + + return dto; +}; + +const mapFlankerStreamEventToDto = (streamEvent: FlankerLiveEvent) => { + if (IS_ANDROID) { + let screenCountPerTrial = 1; + + if (streamEvent.showFeedback) { + screenCountPerTrial++; + } + if (streamEvent.showFixation) { + screenCountPerTrial++; + } + + const dto = { + trial_index: Math.ceil( + (streamEvent.trialIndex + 1) / screenCountPerTrial, + ), + duration: streamEvent.duration, + question: streamEvent.question, + button_pressed: streamEvent.buttonPressed, + start_time: streamEvent.imageTime, + correct: streamEvent.correct, + start_timestamp: streamEvent.startTimestamp, + offset: streamEvent.startTimestamp - streamEvent.startTime, + tag: streamEvent.tag, + response_touch_timestamp: streamEvent.duration + ? streamEvent.startTimestamp + streamEvent.duration + : null, + }; + + return dto; + } + + const dto = { + trial_index: streamEvent.trialIndex, + duration: streamEvent.duration, + question: streamEvent.question, + correct: streamEvent.correct, + response_touch_timestamp: streamEvent.responseTouchTimeStamp, + tag: streamEvent.tag, + start_time: streamEvent.startTime, + start_timestamp: streamEvent.startTimestamp, + offset: 0, + button_pressed: streamEvent.buttonPressed, + }; + + return dto; +}; + +export const mapStreamEventToDto = (streamEvent: LiveEvent): LiveEventDto => { + const type = streamEvent.type; + + switch (type) { + case 'AbTest': + return mapABTestStreamEventToDto(streamEvent); + case 'DrawingTest': + return mapDrawingStreamEventToDto(streamEvent); + case 'Flanker': + return mapFlankerStreamEventToDto(streamEvent); + case 'StabilityTracker': + return mapStabilityTrackerStreamEventToDto(streamEvent); + default: + return streamEvent; + } +}; diff --git a/src/features/pass-survey/model/tests/ScoresCalculator.collectMaxScores.test.ts b/src/features/pass-survey/model/tests/ScoresCalculator.collectMaxScores.test.ts index 23ae745a7..6e30f5686 100644 --- a/src/features/pass-survey/model/tests/ScoresCalculator.collectMaxScores.test.ts +++ b/src/features/pass-survey/model/tests/ScoresCalculator.collectMaxScores.test.ts @@ -59,6 +59,7 @@ const getEmptyRadioItem = (name: string): RadioPipelineItem => { setAlerts: false, setPalette: false, options: [], + autoAdvance: false, }, type: 'Radio', }; diff --git a/src/features/pass-survey/model/tests/ScoresCalculator.collectScoreForRadio.test.ts b/src/features/pass-survey/model/tests/ScoresCalculator.collectScoreForRadio.test.ts index 7c95bccf5..0a7695066 100644 --- a/src/features/pass-survey/model/tests/ScoresCalculator.collectScoreForRadio.test.ts +++ b/src/features/pass-survey/model/tests/ScoresCalculator.collectScoreForRadio.test.ts @@ -34,6 +34,7 @@ const getEmptyItem = (): RadioPipelineItem => { setAlerts: false, setPalette: false, options: [], + autoAdvance: false, }, type: 'Radio', }; diff --git a/src/features/pass-survey/ui/ActivityItem.tsx b/src/features/pass-survey/ui/ActivityItem.tsx index b77d0d52c..a292ab8b6 100644 --- a/src/features/pass-survey/ui/ActivityItem.tsx +++ b/src/features/pass-survey/ui/ActivityItem.tsx @@ -22,12 +22,7 @@ import { useAppletStreamingStatus } from '@entities/applet/lib/hooks'; import { DrawingTest } from '@entities/drawer'; import { HtmlFlanker, NativeIosFlanker } from '@entities/flanker'; import { StabilityTracker } from '@entities/stabilityTracker'; -import { - IS_ANDROID, - StreamEventActivityItemType, - useSendEvent, - wait, -} from '@shared/lib'; +import { IS_ANDROID, LiveEvent, useSendEvent, wait } from '@shared/lib'; import { RadioActivityItem, SurveySlider, @@ -45,6 +40,7 @@ import { PipelineItemResponse, ActivityIdentityContext, } from '../lib'; +import { mapStreamEventToDto } from '../model'; type Props = ActivityItemProps & PipelineItemAnswer & { @@ -72,10 +68,13 @@ function ActivityItem({ const [scrollEnabled, setScrollEnabled] = useState(initialScrollEnabled); - const { sendLiveEvent } = useSendEvent( - type as StreamEventActivityItemType, - streamEnabled, - ); + const { sendLiveEvent } = useSendEvent(streamEnabled); + + const processLiveEvent = (streamEvent: LiveEvent) => { + const liveEventDto = mapStreamEventToDto(streamEvent); + + sendLiveEvent(liveEventDto); + }; const { next } = useContext(HandlersContext); @@ -120,7 +119,7 @@ function ActivityItem({ ); @@ -137,7 +136,7 @@ function ActivityItem({ }} onMaxLambdaChange={onContextChange} maxLambda={context?.maxLambda as number} - onLog={sendLiveEvent} + onLog={processLiveEvent} /> ); @@ -155,7 +154,7 @@ function ActivityItem({ lines: value?.answer?.lines ?? [], }} onResult={onResponse} - onLog={sendLiveEvent} + onLog={processLiveEvent} /> ); @@ -169,7 +168,7 @@ function ActivityItem({ onResponse(data); moveToNextItem(); }} - onLog={sendLiveEvent} + onLog={processLiveEvent} /> ) : ( ); break; diff --git a/src/shared/lib/tcp/lib/streamEventMapper.ts b/src/shared/lib/tcp/lib/streamEventMapper.ts deleted file mode 100644 index 64344958f..000000000 --- a/src/shared/lib/tcp/lib/streamEventMapper.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { - AbTestStreamEventDto, - AbTestStreamEventErrorType, - AbTestStreamEvent, - StreamEventActivityItemType, - DrawingStreamEvent, - DrawingStreamEventDto, - StabilityTrackerEvent, - StabilityTrackerEventDto, - LiveEvent, -} from '../types'; - -const mapABTestStreamEventToDto = ( - streamEvent: AbTestStreamEvent, -): AbTestStreamEventDto => { - let actualPath = '-1'; - if (streamEvent.wrongPointLabel) { - actualPath = streamEvent.wrongPointLabel; - } - if (streamEvent.error === AbTestStreamEventErrorType.OverCorrectPoint) { - actualPath = `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`; - } - - const dto = { - x: streamEvent.x, - y: streamEvent.y, - time: streamEvent.time, - line_number: streamEvent.lineNumber, - error: streamEvent.error, - correct_path: `${streamEvent.currentNodeLabel} ~ ${streamEvent.nextNodeLabel}`, - actual_path: actualPath, - }; - - return dto; -}; -const mapDrawingStreamEventToDto = ( - streamEvent: DrawingStreamEvent, -): DrawingStreamEventDto => { - const dto = { - x: streamEvent.x, - y: streamEvent.y, - line_number: streamEvent.lineNumber, - time: streamEvent.time, - }; - - return dto; -}; - -const mapStabilityTrackerStreamEventToDto = ( - streamEvent: StabilityTrackerEvent, -): StabilityTrackerEventDto => { - const dto = { - timestamp: streamEvent.timestamp, - stimPos: streamEvent.circlePosition, - targetPos: streamEvent.targetPosition, - userPos: streamEvent.userPosition, - score: streamEvent.score, - lambda: streamEvent.lambda, - lambdaSlope: streamEvent.lambdaSlope, - }; - - return dto; -}; - -export const mapStreamEventToDto = ( - type: StreamEventActivityItemType, - streamEvent: LiveEvent, -) => { - switch (type) { - case 'AbTest': - return mapABTestStreamEventToDto(streamEvent as AbTestStreamEvent); - case 'DrawingTest': - return mapDrawingStreamEventToDto(streamEvent as DrawingStreamEvent); - case 'StabilityTracker': - return mapStabilityTrackerStreamEventToDto( - streamEvent as StabilityTrackerEvent, - ); - default: - return streamEvent; - } -}; diff --git a/src/shared/lib/tcp/types.ts b/src/shared/lib/tcp/types.ts index 91f6bf961..0ca46a028 100644 --- a/src/shared/lib/tcp/types.ts +++ b/src/shared/lib/tcp/types.ts @@ -1,13 +1,30 @@ import { DrawPoint } from '@entities/drawer'; export type FlankerLiveEvent = { + trialIndex: number; + duration: number; + question: string; + buttonPressed: string; + imageTime?: number; + correct: boolean; + tag: string; + startTime: number; + startTimestamp: number; + offset?: number; + responseTouchTimeStamp?: number; + showFixation?: boolean; + showFeedback?: boolean; + type: 'Flanker'; +}; + +export type FlankerLiveEventDto = { trial_index: number; duration: number; question: string; correct: boolean; - response_touch_timestamp: number | null; + response_touch_timestamp?: number | null; tag: string; - start_time: number; + start_time?: number; start_timestamp: number; offset: number; button_pressed: string; @@ -41,15 +58,13 @@ export type StabilityTrackerEvent = { lambda: number; score: number; lambdaSlope: number; + type: 'StabilityTracker'; }; -export type StreamEventActivityItemType = - | 'AbTest' - | 'StabilityTracker' - | 'DrawingTest' - | 'Flanker'; - -export type DrawingStreamEvent = DrawPoint & { lineNumber: number }; +export type DrawingStreamEvent = DrawPoint & { + lineNumber: number; + type: 'DrawingTest'; +}; export type DrawingStreamEventDto = DrawPoint & { line_number: number }; @@ -69,6 +84,7 @@ export type AbTestStreamEvent = { currentNodeLabel: string; nextNodeLabel: string; wrongPointLabel?: string; + type: 'AbTest'; }; export type AbTestStreamEventDto = { @@ -80,3 +96,9 @@ export type AbTestStreamEventDto = { correct_path: string; actual_path: string; }; + +export type LiveEventDto = + | AbTestStreamEventDto + | DrawingStreamEventDto + | FlankerLiveEventDto + | StabilityTrackerEventDto; diff --git a/src/shared/lib/tcp/useSendLiveEvent.ts b/src/shared/lib/tcp/useSendLiveEvent.ts index b459da5ef..46aaa5dba 100644 --- a/src/shared/lib/tcp/useSendLiveEvent.ts +++ b/src/shared/lib/tcp/useSendLiveEvent.ts @@ -1,23 +1,19 @@ import { useCallback } from 'react'; -import { mapStreamEventToDto } from './lib/streamEventMapper'; -import { LiveEvent, StreamEventActivityItemType } from './types'; +import { LiveEventDto } from './types'; import { useTCPSocket } from './useTCPSocket'; -export function useSendEvent( - type: StreamEventActivityItemType, - streamEnabled: boolean, -) { +export function useSendEvent(streamEnabled: boolean) { const { sendMessage, connected } = useTCPSocket({ onClosed: () => {}, }); const sendLiveEvent = useCallback( - (data: LiveEvent) => { + (dataDto: LiveEventDto) => { + console.log(dataDto); if (!connected || !streamEnabled) { return; } - const dataDto = mapStreamEventToDto(type, data); const liveEvent = { type: 'live_event', @@ -26,7 +22,7 @@ export function useSendEvent( sendMessage(JSON.stringify(liveEvent)); }, - [type, sendMessage, connected, streamEnabled], + [sendMessage, connected, streamEnabled], ); return { From d12995ee29ef1f5b77cc7937ae4e5d0657449886 Mon Sep 17 00:00:00 2001 From: Vardan Mkhitaryan Date: Tue, 6 Feb 2024 14:33:18 +0400 Subject: [PATCH 5/5] feature(streaming) M2-4928, M2-4929 Removed console.log --- src/shared/lib/tcp/useSendLiveEvent.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shared/lib/tcp/useSendLiveEvent.ts b/src/shared/lib/tcp/useSendLiveEvent.ts index 46aaa5dba..913c68387 100644 --- a/src/shared/lib/tcp/useSendLiveEvent.ts +++ b/src/shared/lib/tcp/useSendLiveEvent.ts @@ -10,7 +10,6 @@ export function useSendEvent(streamEnabled: boolean) { const sendLiveEvent = useCallback( (dataDto: LiveEventDto) => { - console.log(dataDto); if (!connected || !streamEnabled) { return; }