Skip to content

Commit 7ec24ea

Browse files
authored
feat: Import CSV/JSON Dialog (#62)
* import csv/json, import snippet also accepts uploads * fix for csv paste
1 parent 23722aa commit 7ec24ea

File tree

4 files changed

+217
-53
lines changed

4 files changed

+217
-53
lines changed

src/components/ImportPage/index.js

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { styled } from "@material-ui/core/styles"
66
import AssignmentReturnedIcon from "@material-ui/icons/AssignmentReturned"
77
import CreateNewFolderIcon from "@material-ui/icons/CreateNewFolder"
88
import TextFieldsIcon from "@material-ui/icons/TextFields"
9+
import DescriptionIcon from "@material-ui/icons/Description"
910
import PetsIcon from "@material-ui/icons/Pets"
1011
import * as colors from "@material-ui/core/colors"
1112
import PasteUrlsDialog from "../PasteUrlsDialog"
@@ -16,10 +17,19 @@ import classnames from "classnames"
1617
import { setIn } from "seamless-immutable"
1718
import useEventCallback from "use-event-callback"
1819
import ImportFromGoogleDriveDialog from "../ImportFromGoogleDriveDialog"
20+
import ImportUDTFileDialog from "../ImportUDTFileDialog"
1921
import ImportToyDataset from "../ImportToyDatasetDialog"
2022
import ImportFromYoutubeUrls from "../ImportFromYoutubeUrls"
2123
import { FaGoogleDrive, FaYoutube } from "react-icons/fa"
2224

25+
const extendWithNull = (ar, len) => {
26+
ar = [...ar]
27+
while (ar.length < len) {
28+
ar.push(null)
29+
}
30+
return ar
31+
}
32+
2333
const ButtonBase = styled(MuiButton)({
2434
width: 240,
2535
height: 140,
@@ -132,13 +142,26 @@ export default ({ oha, onChangeOHA, isDesktop }) => {
132142
}
133143
}
134144
const closeDialog = () => changeDialog(null)
135-
const onAddSamples = useEventCallback((samples) => {
136-
onChangeOHA(
137-
setIn(oha, ["taskData"], (oha.taskData || []).concat(samples)),
138-
true
139-
)
140-
closeDialog()
141-
})
145+
const onAddSamples = useEventCallback(
146+
(appendedTaskData, appendedTaskOutput) => {
147+
let newOHA = setIn(
148+
oha,
149+
["taskData"],
150+
(oha.taskData || []).concat(appendedTaskData)
151+
)
152+
if (appendedTaskOutput) {
153+
newOHA = setIn(
154+
newOHA,
155+
["taskOutput"],
156+
extendWithNull(oha.taskOutput || [], oha.taskData.length).concat(
157+
appendedTaskOutput
158+
)
159+
)
160+
}
161+
onChangeOHA(newOHA, true)
162+
closeDialog()
163+
}
164+
)
142165
return (
143166
<SelectDialogContext.Provider value={{ onChangeDialog }}>
144167
<div>
@@ -182,6 +205,13 @@ export default ({ oha, onChangeOHA, isDesktop }) => {
182205
>
183206
Import from Youtube URLs
184207
</Button>
208+
<Button
209+
isDesktop={isDesktop}
210+
dialog="import-csv-json"
211+
Icon={DescriptionIcon}
212+
>
213+
Import from CSV / JSON
214+
</Button>
185215
<ImportTextSnippetsDialog
186216
open={selectedDialog === "import-text-snippets"}
187217
onClose={closeDialog}
@@ -207,6 +237,11 @@ export default ({ oha, onChangeOHA, isDesktop }) => {
207237
onClose={closeDialog}
208238
onAddSamples={onAddSamples}
209239
/>
240+
<ImportUDTFileDialog
241+
open={selectedDialog === "import-csv-json"}
242+
onClose={closeDialog}
243+
onAddSamples={onAddSamples}
244+
/>
210245
</div>
211246
</SelectDialogContext.Provider>
212247
)

src/components/ImportTextSnippetsDialog/index.js

Lines changed: 8 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,11 @@
33
import React, { useState } from "react"
44
import SimpleDialog from "../SimpleDialog"
55
import { styled } from "@material-ui/core/styles"
6-
import { useDropzone } from "react-dropzone"
7-
import useEventCallback from "use-event-callback"
8-
import * as colors from "@material-ui/core/colors"
9-
10-
const TextArea = styled("textarea")({
11-
width: "100%",
12-
minHeight: 300,
13-
})
14-
15-
const UploadHover = styled("div")({
16-
fontSize: 24,
17-
color: colors.grey[600],
18-
textAlign: "center",
19-
padding: 48,
20-
})
21-
22-
const emptyFunc = () => null
6+
import TextAreaWithUpload from "../TextAreaWithUpload"
237

248
const ImportTextSnippetsDialog = ({ open, onClose, onAddSamples }) => {
259
const [content, changeContent] = useState("")
2610

27-
const onDrop = useEventCallback((acceptedFiles) => {
28-
const { name: fileName } = acceptedFiles[0]
29-
const reader = new FileReader()
30-
reader.onload = (e) => {
31-
const fileContent = e.target.result
32-
if (fileName.endsWith("csv") || fileName.endsWith("CSV")) {
33-
changeContent(fileContent.replace(",", "\n"))
34-
} else {
35-
changeContent(fileContent)
36-
}
37-
}
38-
reader.readAsText(acceptedFiles[0])
39-
})
40-
41-
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop })
4211
return (
4312
<SimpleDialog
4413
open={open}
@@ -59,20 +28,13 @@ const ImportTextSnippetsDialog = ({ open, onClose, onAddSamples }) => {
5928
},
6029
]}
6130
>
62-
<div {...getRootProps()} onClick={emptyFunc}>
63-
<input {...getInputProps()} />
64-
{isDragActive ? (
65-
<UploadHover>Drop the text or csv file here.</UploadHover>
66-
) : (
67-
<TextArea
68-
value={content}
69-
onChange={(e, v) => changeContent(e.target.value)}
70-
placeholder={
71-
"Paste Snippets here\nOne snippet per line.\n\nYou can also drag a text or csv file here."
72-
}
73-
/>
74-
)}
75-
</div>
31+
<TextAreaWithUpload
32+
content={content}
33+
onChangeContent={changeContent}
34+
placeholder={
35+
"Paste Snippets here\nOne snippet per line.\n\nYou can also drag a text or csv file here (double click to open file dialog)"
36+
}
37+
/>
7638
</SimpleDialog>
7739
)
7840
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// @flow weak
2+
3+
import React, { useState, useEffect } from "react"
4+
import SimpleDialog from "../SimpleDialog"
5+
import { styled } from "@material-ui/core/styles"
6+
import TextAreaWithUpload from "../TextAreaWithUpload"
7+
import * as colors from "@material-ui/core/colors"
8+
import fromUDTCSV from "../../utils/from-udt-csv.js"
9+
10+
const InfoText = styled("div")({
11+
fontFamily: "Inter",
12+
marginBottom: 20,
13+
"& a": {
14+
color: colors.blue[500],
15+
},
16+
})
17+
18+
const Error = styled("div")({
19+
color: colors.red[500],
20+
whiteSpace: "pre-wrap",
21+
marginBottom: 20,
22+
})
23+
24+
const ImportUDTFileDialog = ({ open, onClose, onAddSamples }) => {
25+
const [error, changeError] = useState(null)
26+
const [content, setContent] = useState("")
27+
28+
return (
29+
<SimpleDialog
30+
open={open}
31+
onClose={onClose}
32+
title="Import JSON/CSV Samples"
33+
actions={[
34+
{
35+
text: "Add Samples",
36+
disabled: Boolean(error),
37+
onClick: () => {
38+
let taskData, taskOutput
39+
try {
40+
;[taskData, taskOutput] = JSON.parse(content).taskData
41+
} catch (e1) {
42+
try {
43+
const udt = fromUDTCSV(content)
44+
;({ taskData, taskOutput } = udt)
45+
} catch (e2) {
46+
changeError(
47+
`JSON did not parse. CSV did not parse.\n\nJSON Error: ${e1.toString()}\nCSV Error: ${e2.toString()}`
48+
)
49+
return
50+
}
51+
}
52+
if (!taskData || taskData.length === 0) {
53+
changeError("No task data found")
54+
return
55+
}
56+
onAddSamples(taskData, taskOutput)
57+
},
58+
},
59+
]}
60+
>
61+
<InfoText>
62+
See the{" "}
63+
<a
64+
target="_blank"
65+
href="https://github.com/UniversalDataTool/udt-format"
66+
>
67+
UDT JSON format
68+
</a>{" "}
69+
or the{" "}
70+
<a
71+
target="_blank"
72+
href="https://github.com/UniversalDataTool/udt-format"
73+
>
74+
UDT CSV format
75+
</a>{" "}
76+
for formatting details. Or take a look at a{" "}
77+
<a
78+
target="_blank"
79+
href="https://github.com/UniversalDataTool/udt-format/blob/master/SAMPLE.udt.json"
80+
>
81+
sample JSON
82+
</a>{" "}
83+
or{" "}
84+
<a
85+
target="_blank"
86+
href="https://github.com/UniversalDataTool/udt-format/blob/master/SAMPLE.udt.csv"
87+
>
88+
sample CSV
89+
</a>{" "}
90+
file.
91+
</InfoText>
92+
<Error>{error}</Error>
93+
<TextAreaWithUpload
94+
content={content}
95+
onChangeContent={setContent}
96+
placeholder={
97+
"Paste CSV/JSON here. The file should be in the UDT JSON/CSV format.\n\nYou can also drag a json or csv file here (double click to open file dialog)"
98+
}
99+
/>
100+
</SimpleDialog>
101+
)
102+
}
103+
104+
export default ImportUDTFileDialog
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// @flow weak
2+
3+
import React, { useState } from "react"
4+
import { styled } from "@material-ui/core/styles"
5+
import useEventCallback from "use-event-callback"
6+
import { useDropzone } from "react-dropzone"
7+
import * as colors from "@material-ui/core/colors"
8+
9+
const TextArea = styled("textarea")({
10+
width: "100%",
11+
minHeight: 300,
12+
})
13+
14+
const UploadHover = styled("div")({
15+
fontSize: 24,
16+
color: colors.grey[600],
17+
textAlign: "center",
18+
padding: 48,
19+
})
20+
21+
const emptyFunc = () => null
22+
23+
export default ({ content, onChangeContent, placeholder }) => {
24+
const onDrop = useEventCallback((acceptedFiles) => {
25+
const { name: fileName } = acceptedFiles[0]
26+
const reader = new FileReader()
27+
reader.onload = (e) => {
28+
const fileContent = e.target.result
29+
if (fileName.endsWith("csv") || fileName.endsWith("CSV")) {
30+
onChangeContent(fileContent.replace(",", "\n"))
31+
} else {
32+
onChangeContent(fileContent)
33+
}
34+
}
35+
reader.readAsText(acceptedFiles[0])
36+
})
37+
38+
const [lastClickTime, changeLastClickTime] = useState(0)
39+
const onClick = useEventCallback((e) => {
40+
if (Date.now() - lastClickTime < 400) {
41+
getRootProps().onClick(e)
42+
}
43+
changeLastClickTime(Date.now())
44+
})
45+
46+
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop })
47+
48+
return (
49+
<div {...getRootProps()} onClick={emptyFunc}>
50+
<input {...getInputProps()} />
51+
{isDragActive ? (
52+
<UploadHover>Drop the text or csv file here.</UploadHover>
53+
) : (
54+
<TextArea
55+
value={content}
56+
onChange={(e, v) => onChangeContent(e.target.value)}
57+
onClick={onClick}
58+
placeholder={placeholder}
59+
/>
60+
)}
61+
</div>
62+
)
63+
}

0 commit comments

Comments
 (0)