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

Commit 2a5de53

Browse files
superdev808hlomzikmakseqnick-skriabin
authored
feat: DEV-1602: Update Table Tag to show csv data (#478)
* Update Table Tag to show csv data * Fix comments * Fix comments * Fix comments Co-authored-by: hlomzik <[email protected]> Co-authored-by: Max Tkachenko <[email protected]> Co-authored-by: nicholasrq <[email protected]>
1 parent 64c42c5 commit 2a5de53

File tree

11 files changed

+196
-88
lines changed

11 files changed

+196
-88
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"annotations": [
3+
{
4+
"result": [
5+
{
6+
"value": {
7+
"choices": [
8+
"Tuna"
9+
]
10+
},
11+
"id": "55NfiU7T4t",
12+
"from_name": "sub",
13+
"to_name": "menu",
14+
"type": "choices",
15+
"origin": "manual"
16+
}
17+
]
18+
}
19+
],
20+
"id": 1
21+
}

examples/table_csv/config.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<View>
2+
<Table name="csv-table" value="$text" valuetype="csv|url|separator=," />
3+
<Header>Choose ingredients</Header>
4+
<Choices name="sub" toName="csv-table">
5+
<Choice value="Tuna" />
6+
<Choice value="Vegetables" />
7+
<Choice value="Chicken" />
8+
<Choice value="Spicy" />
9+
</Choices>
10+
</View>

examples/table_csv/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import config from "./config.xml";
2+
import tasks from "./tasks.json";
3+
import annotation from "./annotations/0.json";
4+
5+
export const TableCsv = { config, tasks, annotation };

examples/table_csv/tasks.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"data": {
4+
"text": "https://htx-pub.s3.amazonaws.com/samples/time-series-random.csv"
5+
},
6+
"predictions": []
7+
}
8+
]

src/env/development.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import { RichTextPlainRemote } from "../examples/rich_text_plain_remote";
5555
import { Pairwise } from "../examples/pairwise";
5656
import { Repeater } from "../examples/repeater";
5757
import { Table } from "../examples/table";
58+
import { TableCsv } from "../examples/table_csv";
5859

5960
import { TimeSeries } from "../examples/timeseries";
6061
import { TimeSeriesSingle } from "../examples/timeseries_single";
@@ -64,7 +65,7 @@ import { TimeSeriesSingle } from "../examples/timeseries_single";
6465
*/
6566
// import { AllTypes } from "../examples/all_types";
6667

67-
const data = ImageTools;
68+
const data = TableCsv;
6869

6970
function getData(task) {
7071
if (task && task.data) {

src/mixins/ProcessAttrs.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { flow, types } from "mobx-state-tree";
22
import Papa from "papaparse";
33

4-
import { parseValue } from "../utils/data";
4+
import { parseTypeAndOption, parseValue } from "../utils/data";
55

66
const resolvers = {
77
// @todo comments/types
@@ -42,25 +42,13 @@ const ProcessAttrsMixin = types
4242
resolveValue: flow(function * (value) {
4343
if (!self.resolver) return value;
4444

45-
const [, type, sep] = self.resolver.match(/^(\w+)(.)?/) ?? [];
45+
const { type, options } = parseTypeAndOption(self.resolver);
4646

4747
if (!Object.prototype.hasOwnProperty.call(resolvers, type)) {
4848
console.error(`Resolver "${type ?? self.resolver}" looks unfamiliar`);
4949
return value;
5050
}
5151

52-
const options = {};
53-
54-
if (sep) {
55-
const pairs = self.resolver.split(sep).slice(1);
56-
57-
pairs.forEach(pair => {
58-
const [k, v] = pair.split("=", 2);
59-
60-
options[k] = v ?? true; // options without values are `true`
61-
});
62-
}
63-
6452
// @todo checks for url
6553
// @todo error handling
6654
const response = yield fetch(value);

src/tags/object/Table.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import React from "react";
2+
import { Table } from "antd";
3+
import { inject, observer } from "mobx-react";
4+
import { flow, types } from "mobx-state-tree";
5+
import Papa from "papaparse";
6+
7+
import { errorBuilder } from "../../core/DataValidator/ConfigValidator";
8+
import Registry from "../../core/Registry";
9+
import { AnnotationMixin } from "../../mixins/AnnotationMixin";
10+
import ProcessAttrsMixin from "../../mixins/ProcessAttrs";
11+
import Base from "./Base";
12+
import { parseTypeAndOption, parseValue } from "../../utils/data";
13+
import messages from "../../utils/messages";
14+
15+
/**
16+
* Use the Table tag to display object keys and values in a table.
17+
* @example
18+
* <!-- Basic labeling configuration for text in a table -->
19+
* <View>
20+
* <Table name="text-1" value="$text"></Table>
21+
* </View>
22+
* @name Table
23+
* @meta_title Table Tag to Display Keys & Values in Tables
24+
* @meta_description Customize Label Studio by displaying key-value pairs in tasks for machine learning and data science projects.
25+
* @param {string} valuetype Value to define the data type in Table
26+
* @param {string} value Data field value containing JSON type for Table
27+
*/
28+
const Model = types
29+
.model({
30+
name: types.identifier,
31+
type: "table",
32+
value: types.maybeNull(types.string),
33+
_value: types.frozen([]),
34+
valuetype: types.optional(types.string, "json"),
35+
})
36+
.views(self => ({
37+
get dataSource() {
38+
const { type } = parseTypeAndOption(self.valuetype);
39+
40+
if (type === "json") {
41+
return Object.keys(self._value).map(k => {
42+
let val = self._value[k];
43+
44+
if (typeof val === "object") val = JSON.stringify(val);
45+
return { type: k, value: val };
46+
});
47+
} else {
48+
return self._value;
49+
}
50+
},
51+
get columns() {
52+
if (self.valuetype === "json" || !self._value[0]) {
53+
return [
54+
{ title: "Name", dataIndex: "type" },
55+
{ title: "Value", dataIndex: "value" },
56+
];
57+
} else {
58+
return Object.keys(self._value[0]).map(value => ({ title: value, dataIndex: value }));
59+
}
60+
},
61+
}))
62+
.actions(self => ({
63+
updateValue: flow(function*(store) {
64+
const { type, options } = parseTypeAndOption(self.valuetype);
65+
let originData = parseValue(self.value, store.task.dataObj);
66+
67+
if (options.url) {
68+
try {
69+
const response = yield fetch(originData);
70+
const { ok, status, statusText } = response;
71+
72+
if (!ok) throw new Error(`${status} ${statusText}`);
73+
74+
originData = yield response.text();
75+
} catch (error) {
76+
const message = messages.ERR_LOADING_HTTP({ attr: self.value, error: String(error), url: originData });
77+
78+
self.annotationStore.addErrors([errorBuilder.generalError(message)]);
79+
}
80+
}
81+
82+
switch (type) {
83+
case "csv":
84+
{
85+
Papa.parse(originData, {
86+
delimiter: options.separator,
87+
header: !options.headless,
88+
download: false,
89+
complete: ({ data }) => {
90+
self._value = data;
91+
},
92+
});
93+
}
94+
break;
95+
default:
96+
self._value = typeof originData === "string" ? JSON.parse(originData) : originData;
97+
break;
98+
}
99+
}),
100+
}));
101+
102+
const TableModel = types.compose("TableModel", Base, ProcessAttrsMixin, AnnotationMixin, Model);
103+
104+
const HtxTable = inject("store")(
105+
observer(({ item }) => {
106+
return <Table bordered dataSource={item.dataSource} columns={item.columns} pagination={{ hideOnSinglePage: true }} />;
107+
}),
108+
);
109+
110+
Registry.addTag("table", TableModel, HtxTable);
111+
Registry.addObjectType(TableModel);
112+
113+
export { HtxTable, TableModel };

src/tags/object/index.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
11
import { AudioModel } from "./Audio";
22
import { AudioPlusModel } from "./AudioPlus";
33
import { ImageModel } from "./Image";
4+
import { ParagraphsModel } from "./Paragraphs";
45
import { RichTextModel } from "./RichText";
6+
import { TableModel } from "./Table";
57
import { TimeSeriesModel } from "./TimeSeries";
6-
import { ParagraphsModel } from "./Paragraphs";
78
import { VideoModel } from "./Video";
89

910
// stub files to keep docs of these tags
1011
import "./HyperText";
1112
import "./Text";
1213

13-
export { AudioModel, AudioPlusModel, ImageModel, ParagraphsModel, TimeSeriesModel, RichTextModel, VideoModel };
14+
export {
15+
AudioModel,
16+
AudioPlusModel,
17+
ImageModel,
18+
ParagraphsModel,
19+
RichTextModel,
20+
TableModel,
21+
TimeSeriesModel,
22+
VideoModel
23+
};

src/tags/visual/Table.js

Lines changed: 0 additions & 69 deletions
This file was deleted.

src/tags/visual/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { CollapseModel } from "./Collapse";
22
import { DialogModel } from "./Dialog";
33
import { HeaderModel } from "./Header";
4-
import { TableModel } from "./Table";
54
import { ViewModel } from "./View";
65
import { StyleModel } from "./Style";
76
import { FilterModel } from "./Filter";
87

9-
export { CollapseModel, DialogModel, HeaderModel, TableModel, ViewModel, StyleModel, FilterModel };
8+
export { CollapseModel, DialogModel, HeaderModel, ViewModel, StyleModel, FilterModel };

0 commit comments

Comments
 (0)