Skip to content
This repository has been archived by the owner on Apr 18, 2024. It is now read-only.

fix: LSDV-4600: Lead time for TextArea #1214

Merged
merged 36 commits into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3e2e3f2
fix: LSDV-4600: Lead time for TextArea
juliosgarbi Feb 24, 2023
4ad475f
add tests on text area lead_time
juliosgarbi Feb 24, 2023
1d47d7d
clear helper and remove wait between new tags
juliosgarbi Feb 24, 2023
09b09d6
fix lead_time test e2e
juliosgarbi Feb 27, 2023
94f9f48
date.now over newDate
Travis1282 Apr 12, 2023
abc0b5b
Merge remote-tracking branch 'origin/master' into fb-lsdv-4600
Travis1282 Apr 12, 2023
07193e4
Merge remote-tracking branch 'origin' into fb-lsdv-4600
Travis1282 Apr 26, 2023
9cabb1a
Draft of saving lead_time for per-regions
hlomzik Apr 26, 2023
b3db52b
Fix and update ClassificationMixed example
hlomzik Apr 26, 2023
3e4390b
rename text area regions, remove console
Travis1282 Apr 27, 2023
8cae5ea
Merge remote-tracking branch 'origin/master' into fb-lsdv-4600
Travis1282 May 1, 2023
afb1646
rename debounce interval
Travis1282 May 2, 2023
78d5285
date.now over newDate
Travis1282 May 2, 2023
f6acb68
date.now over newDate in text area
Travis1282 May 2, 2023
6313a6d
remove unused toStateJSON
Travis1282 May 2, 2023
b44c941
linting and +Date.now() fixes
hlomzik Jul 18, 2023
f98d0e6
Merge branch 'master' into fb-lsdv-4600
hlomzik Jul 19, 2023
268801a
Improve LeadTime mixin
hlomzik Jul 25, 2023
0f92fd7
Another try to fix per-region/global textareas
hlomzik Jul 25, 2023
7ce101e
Firts test for lead_time (and textarea)
hlomzik Jul 25, 2023
2a7d6e7
Fix counting time in per-regions
hlomzik Jul 26, 2023
b89230c
Add test for per-regions
hlomzik Jul 26, 2023
23f1919
Store all lead_time in results
hlomzik Jul 26, 2023
f7933fc
TextareaRegionView -> separate file and tidy up
hlomzik Jul 26, 2023
ee9090d
Fix push
hlomzik Jul 26, 2023
8d93391
Fix e2e test by adding lead_time to samples
hlomzik Jul 27, 2023
82d3a7a
Add FF for Lead Time
hlomzik Jul 27, 2023
b52d195
Switch to inertial, it feels more natural
hlomzik Jul 27, 2023
001d043
Small fixes
hlomzik Jul 27, 2023
3d346af
Fix FF usage
hlomzik Jul 27, 2023
0cf4776
Fix FF in E2E test for LeadTime
hlomzik Jul 27, 2023
3917ff1
Move lead_time to results
hlomzik Aug 3, 2023
26cab6e
Merge remote-tracking branch 'origin/master' into fb-lsdv-4600
hlomzik Aug 8, 2023
df36f6a
No *StateJSON methods!
hlomzik Aug 14, 2023
82af041
Use proper assert in `lead_time` test
hlomzik Aug 14, 2023
d76bd9c
Remove accidential `only` in test
hlomzik Aug 14, 2023
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
3 changes: 3 additions & 0 deletions e2e/examples/classification.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ const result = [
'World',
],
},
'meta': {
'lead_time': 0
},
'id': '0yMHFegGSK',
'from_name': 'txt',
'to_name': 'text',
Expand Down
11 changes: 11 additions & 0 deletions e2e/fragments/AtTextAreaView.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* global inject */
const { I } = inject();

module.exports = {
_inputSelector: '.ant-form-horizontal .ant-form-item .ant-form-item-control .ant-form-item-control-input .ant-form-item-control-input-content input',

addNewTextTag(value) {
I.fillField(this._inputSelector, value);
I.pressKeyDown('Enter');
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Data(imageExamples).Scenario('Classification Readonly Annotations', async ({
current,
LabelStudio,
}) => {
LabelStudio.setFeatureFlags({
'fflag_fix_front_lsdv_4600_lead_time_27072023_short': true,
});

I.amOnPage('/');
const { config, result, data } = current.example;

Expand Down
39 changes: 39 additions & 0 deletions e2e/tests/text-area.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

/* global Feature, Scenario */

const assert = require('assert');
const { serialize } = require('./helpers');

Feature('Text Area');

const config = `
<View>
<Text name="text" size="10" value="$text"/>
<TextArea name="ta" toName="text"></TextArea>
</View>
`;

const data = {
text: 'To have faith is to trust yourself to the water',
};

const params = { annotations: [{ id: 'test', result: [] }], config, data };

Scenario('Check if text area is saving lead_time', async function({ I, LabelStudio, AtTextAreaView }) {
I.amOnPage('/');
LabelStudio.setFeatureFlags({
fflag_fix_front_lsdv_4600_lead_time_27072023_short: true,
});

LabelStudio.init(params);

AtTextAreaView.addNewTextTag('abcabc');

AtTextAreaView.addNewTextTag('abc abc abc abc');

AtTextAreaView.addNewTextTag('cba cba cba');

const result = await I.executeScript(serialize);

assert.notEqual(result[0]?.meta?.lead_time ?? 0, 0, 'Lead time is not saved');
});
10 changes: 9 additions & 1 deletion examples/classification_mixed/config.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
<View>
<Text name="text" value="Hello"></Text>
<Labels name="label" toName="text">
<Label value="Simple" />
<Label value="Complex" />
</Labels>
<Text name="text" value="Just some random text to label"></Text>

<Number name="num" toName="text" min="1" max="10"/>
<DateTime name="dt" toName="text" showDate="true" showTime="true"/>
<TextArea name="txt" toName="text" editable="true"/>
<View visibleWhen="region-selected">
<Header size="3">per-region TextArea</Header>
<TextArea name="txt2" toName="text" editable="true" perRegion="true"/>
</View>

<Choices name="choices" toName="text">
<Choice value="Choice 1" background="#5b5"/>
Expand Down
2 changes: 1 addition & 1 deletion examples/classification_mixed/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import config from './config.xml';
import tasks from './tasks.json';
import annotation from './annotations/1.json';

export const ImageBbox = { config, tasks, annotation };
export const ClassificationMixed = { config, tasks, annotation };
4 changes: 2 additions & 2 deletions src/components/Entity/Entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export default observer(({ store, annotation }) => {
type="delete"
style={{ cursor: 'pointer' }}
onClick={() => {
node.deleteMetaInfo();
node.deleteMetaText();
}}
/>
</Text>
Expand Down Expand Up @@ -205,7 +205,7 @@ export default observer(({ store, annotation }) => {
<Form
style={{ marginTop: '0.5em', marginBottom: '0.5em' }}
onFinish={() => {
node.setMetaInfo(node.normInput);
node.setMetaText(node.normInput);
setEditMode(false);
}}
>
Expand Down
4 changes: 2 additions & 2 deletions src/components/SidePanels/DetailsPanel/RegionDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export const RegionDetailsMeta: FC<RegionDetailsMetaProps> = observer(({
const input = useRef<HTMLTextAreaElement | null>();

const saveMeta = (value: string) => {
region.setMetaInfo(value);
region.setMetaText(value);
region.setNormInput(value);
};

Expand Down Expand Up @@ -185,7 +185,7 @@ export const RegionDetailsMeta: FC<RegionDetailsMetaProps> = observer(({
// type="delete"
// style={{ cursor: "pointer" }}
// onClick={() => {
// region.deleteMetaInfo();
// region.deleteMetaText();
// }}
// />
// </Elem>
Expand Down
12 changes: 6 additions & 6 deletions src/components/SidePanels/OutlinerPanel/ViewControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,25 @@ export const ViewControls: FC<ViewControlsProps> = observer(({
const grouping = regions.group;
const context = useContext(SidePanelsContext);
const getGroupingLabels = useCallback((value: GroupingOptions): LabelInfo => {
switch(value) {
switch (value) {
case 'manual': return {
label: 'Group Manually',
selectedLabel: isFF(FF_DEV_3873) ? 'Manual': 'Manual Grouping',
selectedLabel: isFF(FF_DEV_3873) ? 'Manual' : 'Manual Grouping',
icon: <IconList/>,
tooltip: 'Manually Grouped',
};
case 'label': return {
label: 'Group by Label',
selectedLabel: isFF(FF_DEV_3873) ?
(isFF(FF_LSDV_4992) ? 'By Label' :'Label')
(isFF(FF_LSDV_4992) ? 'By Label' : 'Label')
: 'Grouped by Label',
icon: <IconTagAlt/>,
tooltip: 'Grouped by Label',
};
case 'type': return {
label: 'Group by Tool',
selectedLabel: isFF(FF_DEV_3873) ?
(isFF(FF_LSDV_4992) ? 'By Tool' :'Tool')
selectedLabel: isFF(FF_DEV_3873) ?
(isFF(FF_LSDV_4992) ? 'By Tool' : 'Tool')
: 'Grouped by Tool',
icon: <IconCursor/>,
tooltip: 'Grouped by Tool',
Expand All @@ -77,7 +77,7 @@ export const ViewControls: FC<ViewControlsProps> = observer(({
}, []);

const getOrderingLabels = useCallback((value: OrderingOptions): LabelInfo => {
switch(value) {
switch (value) {
case 'date': return {
label: 'Order by Time',
selectedLabel: 'By Time',
Expand Down
2 changes: 1 addition & 1 deletion src/env/development.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ import { ClassificationMixed } from '../examples/classification_mixed';
*/
// import { AllTypes } from "../examples/all_types";

const data = Buckets;
const data = ClassificationMixed;

function getData(task) {
if (task && task.data) {
Expand Down
93 changes: 93 additions & 0 deletions src/mixins/LeadTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { types } from 'mobx-state-tree';

// both approaches are valid, so code is ready for both,
// but currently we use instant logic hardcoded
enum LEAD_TIME_LOGIC_OPTIONS {
'inertial', // every action counted, minus overlaps
'instant', // close events are recorded from first to last with no extra time
}

// stored length of interactions in ms, also works as a debounce time
const LEAD_TIME_INTERACTION = 500;

/**
* Calculate lead time just by calling `countTime()` on every interaction.
* Result stored in `leadTime`.
*
* Explanation of how we count time + difference between inertial and instant logic:
*
* INERTIAL 2nd event out of
* 3 events debounce frame
* debounce with overlaps 1 event counted separately
* frame | | | | | || |
* --|---|-------*--*---*------------*------------*----*--*------> events on timeline
* | | | | | || |
* last event same same 2 events inside
* stops counting, counting debounce frame
* INSTANT no extra time no extra time
*/
const LeadTimeMixin = types
.model({
leadTime: 0,
})
.volatile(() => ({
leadTimeLogic: LEAD_TIME_LOGIC_OPTIONS.inertial,
// when did the last event happen, used only for instant logic
lastRecordedTime: 0,
// time of the end of the current debounce frame
debouncedTime: 0,
}))
.actions(self => ({
_countTimeInertial() {
const now = Date.now();

// new sequence of events
if (self.debouncedTime < now) {
self.leadTime += LEAD_TIME_INTERACTION;
} else {
// debounced call
// substracting overlapped time
self.leadTime += LEAD_TIME_INTERACTION - (self.debouncedTime - now);
}

self.debouncedTime = now + LEAD_TIME_INTERACTION;
},
_countTimeInstant() {
const now = Date.now();

// new sequence of events
if (self.debouncedTime < now) {
self.leadTime += LEAD_TIME_INTERACTION;
self.lastRecordedTime = now + LEAD_TIME_INTERACTION;
} else {
// debounced call.
// if event happened inside of initial debounced frame, it won't be counted.
// if it happened after some events, we store the time passed after the last one.
// debounced frame will be increased by LEAD_TIME_INTERACTION in both cases.
if (now > self.lastRecordedTime) {
self.leadTime += now - self.lastRecordedTime;
self.lastRecordedTime = now;
}
}

self.debouncedTime = now + LEAD_TIME_INTERACTION;
},
}))
.actions(self => ({
/**
* Calculate leadTime; call it on every interaction.
*/
countTime() {
if (self.leadTimeLogic === LEAD_TIME_LOGIC_OPTIONS.inertial) {
self._countTimeInertial();
} else if (self.leadTimeLogic === LEAD_TIME_LOGIC_OPTIONS.instant) {
self._countTimeInstant();
}
},
resetLeadTimeCounters() {
self.lastRecordedTime = 0;
self.debouncedTime = 0;
},
}));

export default LeadTimeMixin;
31 changes: 19 additions & 12 deletions src/mixins/Normalization.js → src/mixins/Normalization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { types } from 'mobx-state-tree';

/**
* @todo rework this into MetaMixin for all the meta data
* @todo it's used by too much files, so that's for later
* Meta Information
* For normalize many labels to one value
* Additional information for regions and their results, like text and lead_time
*/
const NormalizationMixin = types
.model({
meta: types.frozen({}),
meta: types.frozen<{ text?: string[] }>({}),
// @todo do we really need it? it's used to store current value from input
normInput: types.maybeNull(types.string),
})
.preProcessSnapshot((sn) => {
Expand All @@ -18,13 +20,17 @@ const NormalizationMixin = types
};
})
.actions(self => ({
setMetaValue(key: string, value: any) {
self.meta = { ...self.meta, [key]: value };
},

/**
* Set meta text
* @param {*} val
* @param {*} text
*/
setMetaInfo(val) {
if (val) {
self.meta = { ...self.meta, text: [val] };
setMetaText(text: string) {
if (text) {
self.meta = { ...self.meta, text: [text] };
} else {
const adjusted = { ...self.meta };

Expand All @@ -33,15 +39,16 @@ const NormalizationMixin = types
}
},

setNormInput(val: string) {
self.normInput = val;
},
}))
.actions(self => ({
/**
* Delete meta text
*/
deleteMetaInfo() {
self.setMetaInfo('');
},

setNormInput(val) {
self.normInput = val;
deleteMetaText() {
self.setMetaText('');
},
}));

Expand Down
4 changes: 3 additions & 1 deletion src/regions/Area.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { types } from 'mobx-state-tree';
import Registry from '../core/Registry';
import Tree from '../core/Tree';
import { AreaMixin } from '../mixins/AreaMixin';
import NormalizationMixin from '../mixins/Normalization';
import RegionsMixin from '../mixins/Regions';
import { RectRegionModel } from './RectRegion';
import { KeyPointRegionModel } from './KeyPointRegion';
import { AreaMixin } from '../mixins/AreaMixin';
import { AudioRegionModel } from './AudioRegion';
import { PolygonRegionModel } from './PolygonRegion';
import { EllipseRegionModel } from './EllipseRegion';
Expand All @@ -18,6 +19,7 @@ import { VideoRectangleRegionModel } from './VideoRectangleRegion';
const ClassificationArea = types.compose(
'ClassificationArea',
RegionsMixin,
NormalizationMixin,
AreaMixin,
types
.model({
Expand Down
13 changes: 11 additions & 2 deletions src/regions/Result.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const Result = types
sequence: types.frozen(),
}),
// info about object and region
// meta: types.frozen(),
meta: types.frozen(),
})
.views(self => ({
get perRegionStates() {
Expand Down Expand Up @@ -258,13 +258,18 @@ const Result = types
self.parentID = id;
},

setMetaValue(key, value) {
self.meta = { ...self.meta, [key]: value };
},

// update region appearence based on it's current states, for
// example bbox needs to update its colors when you change the
// label, becuase it takes color from the label
updateAppearenceFromState() { },

serialize(options) {
const { type, score, value, ...sn } = getSnapshot(self);
const sn = getSnapshot(self);
const { type, score, value, meta } = sn;
const { valueType } = self.from_name;
const data = self.area ? self.area.serialize(options) : {};
// cut off annotation id
Expand Down Expand Up @@ -294,6 +299,10 @@ const Result = types
data.meta = { ...data.meta, ...areaMeta };
}

if (meta) {
data.meta = { ...data.meta, ...meta };
}

if (self.area.parentID) {
data.parentID = self.area.parentID.replace(/#.*/, '');
}
Expand Down
Loading
Loading