Browse Source

add csv parser

tumao 4 years ago
parent
commit
86b103a3b6

+ 2 - 0
client/package.json

@@ -14,6 +14,7 @@
     "@testing-library/user-event": "^12.1.10",
     "@types/jest": "^26.0.15",
     "@types/node": "^12.0.0",
+    "@types/papaparse": "^5.2.6",
     "@types/react": "^17.0.0",
     "@types/react-dom": "^17.0.0",
     "@types/react-highlight-words": "^0.16.2",
@@ -21,6 +22,7 @@
     "axios": "^0.21.1",
     "dayjs": "^1.10.5",
     "i18next": "^20.3.1",
+    "papaparse": "^5.3.1",
     "react": "^17.0.2",
     "react-app-rewired": "^2.1.8",
     "react-dom": "^17.0.2",

+ 24 - 11
client/src/components/insert/Container.tsx

@@ -13,6 +13,7 @@ import {
   InsertStepperEnum,
 } from './Types';
 import { Option } from '../customSelector/Types';
+import { parse } from 'papaparse';
 
 const getStyles = makeStyles((theme: Theme) => ({
   icon: {
@@ -48,17 +49,10 @@ const InsertContainer: FC<InsertContentProps> = ({
     label: s._fieldName,
     value: s._fieldId,
   }));
-  const isContainFieldNamesOptions: Option[] = [
-    {
-      label: 'Yes',
-      value: 1,
-    },
-    { label: 'No', value: 0 },
-  ];
 
   const { t: insertTrans } = useTranslation('insert');
   const { t: btnTrans } = useTranslation('btn');
-  const { handleCloseDialog } = useContext(rootContext);
+  const { handleCloseDialog, openSnackBar } = useContext(rootContext);
   const [activeStep, setActiveStep] = useState<InsertStepperEnum>(
     InsertStepperEnum.import
   );
@@ -72,6 +66,7 @@ const InsertContainer: FC<InsertContentProps> = ({
     useState<string>(selectedPartition);
   // use contain field names yes as default
   const [isContainFieldNames, setIsContainFieldNames] = useState<number>(1);
+  const [fileName, setFileName] = useState<string>('');
 
   const BackIcon = icons.back;
 
@@ -111,8 +106,25 @@ const InsertContainer: FC<InsertContentProps> = ({
     };
   }, [insertStatus]);
 
-  const handleUploadedData = (data: string) => {
-    console.log('----- data 102', data);
+  const checkUploadFileValidation = (fieldNamesLength: number): boolean => {
+    // return schemaOptions.length === fieldNamesLength;
+    return true;
+  };
+
+  const handleUploadedData = (csv: string) => {
+    // use !! to convert number(0 or 1) to boolean
+    const { data } = parse(csv, { header: !!isContainFieldNames });
+    const uploadFieldNamesLength = !!isContainFieldNames
+      ? data.length
+      : (data as string[])[0].length;
+    const validation = checkUploadFileValidation(uploadFieldNamesLength);
+    if (!validation) {
+      // open snackbar
+      openSnackBar(insertTrans('uploadFieldNamesLenWarning'), 'error');
+      // reset filename
+      setFileName('');
+      return;
+    }
   };
 
   const handleInsertData = () => {
@@ -162,7 +174,6 @@ const InsertContainer: FC<InsertContentProps> = ({
           <InsertImport
             collectionOptions={collectionOptions}
             partitionOptions={partitionOptions}
-            isContainedOptions={isContainFieldNamesOptions}
             selectedCollection={collectionValue}
             selectedPartition={partitionValue}
             isContainFieldNames={isContainFieldNames}
@@ -170,6 +181,8 @@ const InsertContainer: FC<InsertContentProps> = ({
             handlePartitionChange={setPartitionValue}
             handleIsContainedChange={setIsContainFieldNames}
             handleUploadedData={handleUploadedData}
+            fileName={fileName}
+            setFileName={setFileName}
           />
         );
       case InsertStepperEnum.preview:

+ 11 - 3
client/src/components/insert/Import.tsx

@@ -2,11 +2,12 @@ import { useTranslation } from 'react-i18next';
 import Typography from '@material-ui/core/Typography';
 import { makeStyles, Theme, Divider } from '@material-ui/core';
 import CustomSelector from '../customSelector/CustomSelector';
-import { FC, useState } from 'react';
+import { FC } from 'react';
 import { InsertImportProps } from './Types';
 import Uploader from '../uploader/Uploader';
 import { INSERT_CSV_SAMPLE } from '../../consts/Insert';
 import { parseByte } from '../../utils/Format';
+import { Option } from '../customSelector/Types';
 
 const getStyles = makeStyles((theme: Theme) => ({
   tip: {
@@ -99,7 +100,6 @@ const getStyles = makeStyles((theme: Theme) => ({
 const InsertImport: FC<InsertImportProps> = ({
   collectionOptions,
   partitionOptions,
-  isContainedOptions,
 
   selectedCollection,
   selectedPartition,
@@ -110,12 +110,20 @@ const InsertImport: FC<InsertImportProps> = ({
   handleIsContainedChange,
 
   handleUploadedData,
+  fileName,
+  setFileName,
 }) => {
   const { t: insertTrans } = useTranslation('insert');
   const { t: collectionTrans } = useTranslation('collection');
   const { t: partitionTrans } = useTranslation('partition');
   const classes = getStyles();
-  const [fileName, setFileName] = useState<string>('');
+  const isContainedOptions: Option[] = [
+    {
+      label: 'Yes',
+      value: 1,
+    },
+    { label: 'No', value: 0 },
+  ];
 
   return (
     <section>

+ 2 - 1
client/src/components/insert/Types.ts

@@ -30,7 +30,6 @@ export interface InsertImportProps {
   // selectors options
   collectionOptions: Option[];
   partitionOptions: Option[];
-  isContainedOptions: Option[];
   // selectors value
   selectedCollection: string;
   selectedPartition: string;
@@ -41,6 +40,8 @@ export interface InsertImportProps {
   handlePartitionChange: (partitionName: string) => void;
   // handle uploaded data
   handleUploadedData: (data: string) => void;
+  fileName: string;
+  setFileName: (fileName: string) => void;
 }
 
 export interface InsertPreviewProps {

+ 2 - 0
client/src/i18n/cn/insert.ts

@@ -13,6 +13,8 @@ const insertTrans = {
   ],
   overSizeWarning: 'File data size should less than 5MB',
   isContainFieldNames: 'First row contains field names?',
+  uploadFieldNamesLenWarning:
+    'Uploaded data column count is not equal to schema count',
 };
 
 export default insertTrans;

+ 2 - 0
client/src/i18n/en/insert.ts

@@ -13,6 +13,8 @@ const insertTrans = {
   ],
   overSizeWarning: 'File data size should less than 5MB',
   isContainFieldNames: 'First row contains field names?',
+  uploadFieldNamesLenWarning:
+    'Uploaded data column count is not equal to schema count',
 };
 
 export default insertTrans;

+ 12 - 0
client/yarn.lock

@@ -1931,6 +1931,13 @@
   resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
   integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==
 
+"@types/papaparse@^5.2.6":
+  version "5.2.6"
+  resolved "https://registry.yarnpkg.com/@types/papaparse/-/papaparse-5.2.6.tgz#0bba18de4d15eff65883bc7c0794e0134de9e7c7"
+  integrity sha512-xGKSd0UTn58N1h0+zf8mW863Rv8BvXcGibEgKFtBIXZlcDXAmX/T4RdDO2mwmrmOypUDt5vRgo2v32a78JdqUA==
+  dependencies:
+    "@types/node" "*"
+
 "@types/parse-json@^4.0.0":
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@@ -8107,6 +8114,11 @@ pako@~1.0.5:
   resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
   integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
 
+papaparse@^5.3.1:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.1.tgz#770b7a9124d821d4b2132132b7bd7dce7194b5b1"
+  integrity sha512-Dbt2yjLJrCwH2sRqKFFJaN5XgIASO9YOFeFP8rIBRG2Ain8mqk5r1M6DkfvqEVozVcz3r3HaUGw253hA1nLIcA==
+
 parallel-transform@^1.1.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc"