8000 Download feature added in Data Export Box by Aarsh2101 · Pull Request #58 · urban-toolkit/curio · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Download feature added in Data Export Box #58

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
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
147 changes: 142 additions & 5 deletions utk_curio/frontend/urban-workflows/src/components/DataExportBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,29 @@ import DescriptionModal from "./DescriptionModal";
import TemplateModal from "./TemplateModal";
import { useUserContext } from "../providers/UserProvider";
import { InputIcon } from "./edges/InputIcon";
import { fetchData } from "../services/api";

function DataExportBox({ data, isConnectable }) {
const [output, setOutput] = useState<{ code: string; content: string, outputType: string }>({
code: "",
content: "",
outputType: ""
}); // stores the output produced by the last execution of this box

const [downloadFormat, setDownloadFormat] = useState<string>("csv");
const [code, setCode] = useState<string>("");
const [sendCode, setSendCode] = useState();

const sendCode = async () => {
// Set output to "exec" to trigger loading spinner in the UI
setOutput({ code: "exec", content: "", outputType: downloadFormat });

// Perform the data download (export) operation
await downloadData();

// Set output to "success" to hide spinner and indicate completion
setOutput({ code: "success", content: "Download complete.", outputType: downloadFormat });
};

const [templateData, setTemplateData] = useState<Template | any>({});

const [newTemplateFlag, setNewTemplateFlag] = useState(false);
Expand Down Expand Up @@ -83,10 +97,131 @@ function DataExportBox({ data, isConnectable }) {
editUserTemplate(template);
};

const setSendCodeCallback = (_sendCode: any) => {
setSendCode(() => _sendCode);
const customWidgetsCallback = (div: HTMLElement) => {
// Create and configure the label for the export format dropdown
const label = document.createElement("label");
label.setAttribute("for", "exportFormat");
label.style.marginRight = "5px";
label.textContent = "Export format: ";

// Create the select (dropdown) element for choosing export format
const select = document.createElement("select");
select.setAttribute("name", "exportFormat");
select.setAttribute("id", data.nodeId + "_select_format");

// Add export format options to the dropdown
["csv", "json", "geojson"].forEach((optionText) => {
const option = document.createElement("option");
option.setAttribute("value", optionText);
option.textContent = optionText.toUpperCase();
select.appendChild(option);
});

// Set the dropdown's current value to the selected download format
select.value = downloadFormat;

// Update the download format state when the user selects a new option
select.addEventListener("change", (event) => {
if (event.target) {
const target = event.target as HTMLOptionElement;
setDownloadFormat(target.value);
}
});

// Add the label and dropdown to the provided container
div.appendChild(label);
div.appendChild(select);
};

// Function to handle the data download based on the selected format
const downloadData = async () => {
// Determine the file path from the input data
let filePath = "";
if (data.input && typeof data.input === "object" && data.input.path) {
filePath = data.input.path;
}

// If no file path is available, exit early
if (!filePath) return;

try {
// Fetch the data from the backend or source
const result: any = await fetchData(`${filePath}`);
let fileName = "data_export";
let fileContent = "";

// --- CSV Export ---
// If the user selected CSV and the data is a dataframe or geodataframe
if (
downloadFormat === "csv" &&
result.data &&
(result.dataType === "dataframe" || result.dataType === "geodataframe")
) {
const csvRows: string[] = [];

// Handle regular dataframe export to CSV
if (result.dataType === "dataframe") {
const columns = Object.keys(result.data);
const rows = result.data[columns[0]]?.length || 0;
csvRows.push(columns.join(","));
for (let i = 0; i < rows; i++) {
const row = columns.map(col => JSON.stringify(result.data[col][i] ?? ""));
csvRows.push(row.join(","));
}
}
// Handle geodataframe export to CSV (geometry column stringified)
else if (result.dataType === "geodataframe" && result.data.features) {
const features = result.data.features;
// Flatten properties and stringify geometry for each feature
const properties = features.map((f: any) => ({
...f.properties,
geometry: JSON.stringify(f.geometry),
}));

const columns = Object.keys(properties[0]);
csvRows.push(columns.join(","));
for (const row of properties) {
const values = columns.map(col => JSON.stringify(row[col] ?? ""));
csvRows.push(values.join(","));
}
}

fileContent = csvRows.join("\n");
fileName += ".csv";
}

// --- GeoJSON Export ---
// If the user selected GeoJSON, stringify the data as GeoJSON
else if (downloadFormat === "geojson") {
fileContent = JSON.stringify(result.data);
fileName += ".geojson";
}

// --- JSON Export (default fallback) ---
// For all other cases, export as plain JSON
else {
fileContent = JSON.stringify(result.data);
fileName += ".json";
}

// Create a blob and trigger the download in the browser
const blob = new Blob([fileContent], { type: "text/plain;charset=utf-8" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (err) {
// Log any errors that occur during the download process
console.error("Failed to download data", err);
}
};

useEffect(() => {
setOutput({ code: "success", content: "", outputType: downloadFormat });
}, [data.input, downloadFormat]);

const iconStyle: CSS.Properties = {
fontSize: "1.5em",
color: "#888787",
Expand Down Expand Up @@ -137,11 +272,13 @@ function DataExportBox({ data, isConnectable }) {
/>

<BoxEditor
setSendCodeCallback={setSendCodeCallback}
code={true}
setSendCodeCallback={(_: any) => {}}
code={false}
grammar={false}
widgets={true}
provenance={false}
setOutputCallback={setOutput}
customWidgetsCallback={customWidgetsCallback}
data={data}
output={output}
boxType={BoxType.DATA_EXPORT}
Expand Down
0