Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upload csv feature #265

Merged
merged 9 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
fixes
  • Loading branch information
karthikscale3 committed Sep 26, 2024
commit d867ff91ee7d166cff85a0b7b0d49ade2864ae6b
5 changes: 2 additions & 3 deletions app/api/project/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,9 @@ export async function POST(req: NextRequest) {
});
}

const session = await getServerSession(authOptions);
const userEmail = session?.user?.email ?? "anonymous";
await captureEvent(project.id, "project_created", {
project_name: project.name,
project_name: name,
project_description: description,
project_type: projectType,
});
}
Expand Down
263 changes: 173 additions & 90 deletions components/project/dataset/upload-csv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,90 +18,109 @@ import {
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {Select, SelectContent, SelectItem, SelectLabel, SelectValue} from "@/components/ui/select"
import { SelectGroup, SelectTrigger } from "@radix-ui/react-select";
import {
Select,
SelectContent,
SelectItem,
SelectValue,
} from "@/components/ui/select";
import { zodResolver } from "@hookform/resolvers/zod";
import { PlusIcon } from "@radix-ui/react-icons";
import { useState,useRef, useEffect } from "react";
import { PlusIcon, UploadIcon } from "@radix-ui/react-icons";
import { SelectGroup, SelectTrigger } from "@radix-ui/react-select";
import papa from "papaparse";
import React, { useEffect, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import { useQueryClient } from "react-query";
import { toast } from "sonner";
import { z } from "zod";
import papa from 'papaparse';
import React from "react";

export function UploadCsv({
datasetId,
projectId,
disabled = false,
variant = "default",
className = "",
}: {
datasetId?: string;
projectId?: string;
disabled?: boolean;
variant?: any;
className?: string;
}) {
const csvFileRef = useRef<HTMLInputElement>(null)
const csvFileRef = useRef<HTMLInputElement>(null);
const queryClient = useQueryClient();
const [open, setOpen] = useState<boolean>(false);
const [busy, setBusy] = useState<boolean>(false);
const [csvFields,setCsvFields] = useState<string[]>([]);
const [selectedInputOption, setSelectedInputOption] = useState<string>()
const [selectedOutputOption, setSelectedOutputOption] = useState<string>()
const [selectedNoteOption, setSelectedNoteOption] = useState<string>("")
const [csvFields, setCsvFields] = useState<string[]>([]);
const [selectedInputOption, setSelectedInputOption] = useState<string>();
const [selectedOutputOption, setSelectedOutputOption] = useState<string>();
const [selectedNoteOption, setSelectedNoteOption] = useState<string>("");
const [myData, setMyData] = useState<any[]>([]);

const MAX_UPLOAD_SIZE = 1024 * 1024 * 0.5 // 0.5MB = 512KB
const ACCEPTED_FILE_TYPES = ["text/csv"];
const MAX_UPLOAD_SIZE = 1024 * 1024 * 0.5; // 0.5MB = 512KB
const ACCEPTED_FILE_TYPES = ["text/csv"];
const schema = z.object({
file: z.custom<File>(val => val instanceof File, 'Please upload a file').refine(
file => ACCEPTED_FILE_TYPES.includes(file.type),
{ message: 'Please select csv files only' }
).refine(
file => file.size <= MAX_UPLOAD_SIZE,
{ message: 'Your file is too large, file has to be a max of 512KB' }
),
file: z
.custom<File>((val) => val instanceof File, "Please upload a file")
.refine((file) => ACCEPTED_FILE_TYPES.includes(file.type), {
message: "Please select csv files only",
})
.refine((file) => file.size <= MAX_UPLOAD_SIZE, {
message: "Your file is too large, file has to be a max of 512KB",
}),
input: z.string().min(1),
output: z.string().min(1),
note: z.string().min(1).optional()
})
note: z.string().min(1).optional(),
});

const CreateDataForm = useForm({
resolver: zodResolver(schema),
});

const fileRef = CreateDataForm.register("file");

let papa_data:any[] = [];


useEffect(()=>{if (open == false){setSelectedInputOption(""); setSelectedOutputOption(""); setSelectedNoteOption(""); setCsvFields(["upload a csv file"])}else{if (csvFields.length== 0) {setSelectedInputOption("");setCsvFields(["upload a csv file"])}}},[open])

const fetchParseData = (files:FileList) =>{
let file:any = files[0];
papa.parse(file, { skipEmptyLines:true, header: true, complete: ((result) => { const papa_data:any[] = result.data.map((row)=>{return row}); setMyData([...papa_data]); const newFields:string[] = result.meta.fields?.map((field)=>{return field})!; setCsvFields([...newFields]); }) })

useEffect(() => {
if (open == false) {
setSelectedInputOption("");
setSelectedOutputOption("");
setSelectedNoteOption("");
setCsvFields(["upload a csv file"]);
} else {
if (csvFields.length == 0) {
setSelectedInputOption("");
setCsvFields(["upload a csv file"]);
}
}
}, [open]);

const fetchParseData = (files: FileList) => {
let file: any = files[0];
papa.parse(file, {
skipEmptyLines: true,
header: true,
complete: (result) => {
const papa_data: any[] = result.data.map((row) => {
return row;
});
setMyData([...papa_data]);
const newFields: string[] = result.meta.fields?.map((field) => {
return field;
})!;
setCsvFields([...newFields]);
},
});
};

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button disabled={disabled} variant={variant} className={className}>
Upload Csv <PlusIcon className="ml-2" />
<Button disabled={disabled} variant={"outline"} className={className}>
Upload csv <UploadIcon className="ml-2" />
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Create Data</DialogTitle>
<DialogDescription>
Create a new data by filling out the form below.
Upload a csv file to create data for the dataset.
</DialogDescription>
</DialogHeader>


<Form {...CreateDataForm}>
<form
onSubmit={CreateDataForm.handleSubmit(async (data) => {
Expand All @@ -113,8 +132,15 @@ const ACCEPTED_FILE_TYPES = ["text/csv"];
data.output = JSON.stringify(data.output);
}
setBusy(true);

let datas = myData.map((d)=>{return ({input:d[selectedInputOption!], output:d[selectedOutputOption!], note: (selectedNoteOption!== "") ? d[selectedNoteOption!]: ""})})

let datas = myData.map((d) => {
return {
input: d[selectedInputOption!],
output: d[selectedOutputOption!],
note:
selectedNoteOption !== "" ? d[selectedNoteOption!] : "",
};
});
await fetch("/api/data", {
method: "POST",
headers: {
Expand Down Expand Up @@ -156,16 +182,23 @@ const ACCEPTED_FILE_TYPES = ["text/csv"];
/>
</FormLabel>
<FormControl>
<Input

type="file"
<Input
type="file"
placeholder="I'm good, how can I help you?"
className="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-100 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400"
onChange={(event)=>{setSelectedInputOption(""); setSelectedOutputOption(""); setSelectedNoteOption(""); field.onChange(event.target?.files?.[0] ?? undefined); console.log(event.target.files![0].type); ( event.target.files!.length > 0 && event.target.files![0].type == "text/csv")? fetchParseData( csvFileRef.current?.files!) : setCsvFields([])}}
onChange={(event) => {
setSelectedInputOption("");
setSelectedOutputOption("");
setSelectedNoteOption("");
field.onChange(event.target?.files?.[0] ?? undefined);
console.log(event.target.files![0].type);
event.target.files!.length > 0 &&
event.target.files![0].type == "text/csv"
? fetchParseData(csvFileRef.current?.files!)
: setCsvFields([]);
}}
ref={csvFileRef}

/>

</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -186,29 +219,47 @@ const ACCEPTED_FILE_TYPES = ["text/csv"];
/>
</FormLabel>
<FormControl>
<Select {...field} value={selectedInputOption} onValueChange={(el)=>{setSelectedInputOption(el); field.onChange(el ?? undefined) }} >

<SelectTrigger className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50" aria-label="Food">
<Select
{...field}
value={selectedInputOption}
onValueChange={(el) => {
setSelectedInputOption(el);
field.onChange(el ?? undefined);
}}
>
<SelectTrigger
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
aria-label="Food"
>
<SelectValue placeholder="Select a column header…" />

</SelectTrigger>

<SelectContent className="SelectContent">
</SelectTrigger>

<SelectContent className="SelectContent">
<SelectGroup>
{csvFields.map(function(val,index){return( <React.Fragment key={index}> <SelectItem disabled= {(val=="upload a csv file")? true : false}value={val}>{val}</SelectItem> </React.Fragment>)})}

{csvFields.map(function (val, index) {
return (
<React.Fragment key={index}>
{" "}
<SelectItem
disabled={
val == "upload a csv file" ? true : false
}
value={val}
>
{val}
</SelectItem>{" "}
</React.Fragment>
);
})}
</SelectGroup>

</SelectContent>
</SelectContent>
</Select>

</FormControl>
<FormMessage />
</FormItem>
)}
/>

<FormField
disabled={busy}
control={CreateDataForm.control}
Expand All @@ -223,30 +274,46 @@ const ACCEPTED_FILE_TYPES = ["text/csv"];
/>
</FormLabel>
<FormControl>
<Select {...field} value={selectedOutputOption} onValueChange={(el)=>{setSelectedOutputOption(el); field.onChange(el ?? undefined)}}>

<SelectTrigger className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50" aria-label="Food">
<Select
{...field}
value={selectedOutputOption}
onValueChange={(el) => {
setSelectedOutputOption(el);
field.onChange(el ?? undefined);
}}
>
<SelectTrigger
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
aria-label="Food"
>
<SelectValue placeholder="Select a column header…" />

</SelectTrigger>

<SelectContent className="SelectContent">
</SelectTrigger>

<SelectContent className="SelectContent">
<SelectGroup>
{csvFields.map(function(val,index){return( <React.Fragment key={index}><SelectItem disabled= {(val=="upload a csv file")? true : false}value={val}>{val}</SelectItem> </React.Fragment>)})}


{csvFields.map(function (val, index) {
return (
<React.Fragment key={index}>
<SelectItem
disabled={
val == "upload a csv file" ? true : false
}
value={val}
>
{val}
</SelectItem>{" "}
</React.Fragment>
);
})}
</SelectGroup>

</SelectContent>
</SelectContent>
</Select>

</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
<FormField
disabled={busy}
control={CreateDataForm.control}
name="note"
Expand All @@ -260,24 +327,41 @@ const ACCEPTED_FILE_TYPES = ["text/csv"];
/>
</FormLabel>
<FormControl>
<Select {...field} value={selectedNoteOption} onValueChange={(el)=>{setSelectedNoteOption(el); field.onChange(el ?? undefined)}}>

<SelectTrigger className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50" aria-label="Food">
<Select
{...field}
value={selectedNoteOption}
onValueChange={(el) => {
setSelectedNoteOption(el);
field.onChange(el ?? undefined);
}}
>
<SelectTrigger
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
aria-label="Food"
>
<SelectValue placeholder="Select a column header…" />

</SelectTrigger>

<SelectContent className="SelectContent">
</SelectTrigger>

<SelectContent className="SelectContent">
<SelectGroup>
{csvFields.map(function(val,index){return( <React.Fragment key={index}> <SelectItem disabled= {(val=="upload a csv file")? true : false}value={val}>{val}</SelectItem> </React.Fragment>)})}


{csvFields.map(function (val, index) {
return (
<React.Fragment key={index}>
{" "}
<SelectItem
disabled={
val == "upload a csv file" ? true : false
}
value={val}
>
{val}
</SelectItem>{" "}
</React.Fragment>
);
})}
</SelectGroup>

</SelectContent>
</SelectContent>
</Select>

</FormControl>
<FormMessage />
</FormItem>
Expand All @@ -291,7 +375,6 @@ const ACCEPTED_FILE_TYPES = ["text/csv"];
</DialogFooter>
</form>
</Form>

</DialogContent>
</Dialog>
);
Expand Down
Loading