Initial amount CSV upload/download

This commit is contained in:
2022-03-15 16:06:18 -05:00
parent af2a8b67be
commit 56b673fb9e
4 changed files with 709 additions and 4 deletions

View File

@@ -1,5 +1,7 @@
import form_styles from './Form.module.css';
import Button from './Button';
import { useRef } from 'react';
const FileInputRow = (props) => {
let tooltip = "";
@@ -7,13 +9,55 @@ const FileInputRow = (props) => {
tooltip = <Button label="?" kind="inline" tooltip={props.tooltip} />;
}
const fileElem = useRef();
const onClickUpload = () => {
fileElem.current.click();
};
const onFileSelected = () => {
const file = fileElem.current.files[0];
if (file) {
const reader = new FileReader();
reader.addEventListener("load", () => {
props.onFile(reader.result);
});
reader.readAsText(file);
}
fileElem.current.value = "";
};
return <div className={form_styles.FormRow}>
<label>{props.label}</label>
<input type="text" value={props.value} disabled="disabled" />
<Button label="Upload" kind="inline" />
<Button label="Clear" kind="inline" />
<Button label="Template" kind="inline" />
<Button
label="Upload"
kind="inline"
onClick={onClickUpload}
/>
<Button
label="Download"
kind="inline"
onClick={props.onDownload}
/>
<Button
label="Clear"
kind="inline"
onClick={props.onClear}
/>
<Button
label="Template"
kind="inline"
onClick={props.onTemplate}
/>
{tooltip}
<input
type="file"
ref={fileElem}
accept={props.accept}
style={{ display: "none" }}
onChange={onFileSelected}
/>
</div>;
};

View File

@@ -5,6 +5,7 @@ import Form from './Form';
import TextInputRow from './TextInputRow';
import FileInputRow from './FileInputRow';
import DictInputRow from './DictInputRow';
import * as d3 from 'd3';
const ProductBlock = (props) => {
const onChange = (field, val) => {
@@ -13,7 +14,86 @@ const ProductBlock = (props) => {
props.onChange(newProduct);
};
const onInitialAmountsFile = (contents) => {
const data = d3.csvParse(contents);
const T = data.columns.length - 3;
// Construct list of required columns
let isValid = true;
const requiredCols = ["latitude (deg)", "longitude (deg)", "name"];
for (let t = 0; t < T; t++) {
requiredCols.push(t + 1);
}
// Check required columns
requiredCols.forEach(col => {
if (!(col in data[0])) {
console.log(`Column "${col}" not found in CSV file.`);
isValid = false;
}
});
if (!isValid) return;
// Construct initial amounts dict
const result = {};
data.forEach(el => {
let amounts = [];
for (let t = 0; t < T; t++) {
amounts.push(el[t + 1]);
}
result[el["name"]] = {
"latitude (deg)": el["latitude (deg)"],
"longitude (deg)": el["longitude (deg)"],
"amount (tonne)": amounts,
};
});
onChange("initial amounts", result);
};
const onInitialAmountsClear = () => {
onChange("initial amounts", {});
};
const onInitialAmountsTemplate = () => {
exportToCsv(
"Initial amounts - Template.csv", [
["name", "latitude (deg)", "longitude (deg)", "1", "2", "3", "4", "5"],
["Washakie County", "43.8356", "-107.6602", "21902", "6160", "2721", "12917", "18048"],
["Platte County", "42.1314", "-104.9676", "16723", "8709", "22584", "12278", "7196"],
["Park County", "44.4063", "-109.4153", "14731", "11729", "15562", "7703", "23349"],
["Goshen County", "42.0853", "-104.3534", "23266", "16299", "11470", "20107", "21592"],
]);
};
const onInitialAmountsDownload = () => {
const result = [];
for (const [locationName, locationDict] of Object.entries(props.value["initial amounts"])) {
// Add header
if (result.length == 0) {
const T = locationDict["amount (tonne)"].length;
const row = ["name", "latitude (deg)", "longitude (deg)"];
for (let t = 0; t < T; t++) {
row.push(t + 1);
}
result.push(row);
}
// Add content row
const row = [locationName, locationDict["latitude (deg)"], locationDict["longitude (deg)"]];
locationDict["amount (tonne)"].forEach(el => {
row.push(el);
});
result.push(row);
}
exportToCsv(`Initial amounts - ${props.name}`, result);
};
let description = "Not initially available";
const nCenters = Object.keys(props.value["initial amounts"]).length;
if (nCenters > 0) {
description = `${nCenters} collection centers`;
}
return (
<>
@@ -22,9 +102,14 @@ const ProductBlock = (props) => {
<Form>
<h1>General information</h1>
<FileInputRow
value={`${nCenters} collection centers`}
value={description}
label="Initial amounts"
tooltip="A dictionary mapping the name of each location to its description (see below). If this product is not initially available, this key may be omitted."
accept=".csv"
onFile={onInitialAmountsFile}
onDownload={onInitialAmountsDownload}
onClear={onInitialAmountsClear}
onTemplate={onInitialAmountsTemplate}
/>
<TextInputRow
label="Acquisition cost"
@@ -85,4 +170,43 @@ const ProductBlock = (props) => {
);
};
function exportToCsv(filename, rows) {
var processRow = function (row) {
var finalVal = "";
for (var j = 0; j < row.length; j++) {
var innerValue = row[j] === null ? "" : row[j].toString();
if (row[j] instanceof Date) {
innerValue = row[j].toLocaleString();
}
var result = innerValue.replace(/"/g, '""');
if (result.search(/("|,|\n)/g) >= 0) result = '"' + result + '"';
if (j > 0) finalVal += ",";
finalVal += result;
}
return finalVal + "\n";
};
var csvFile = "";
for (var i = 0; i < rows.length; i++) {
csvFile += processRow(rows[i]);
}
var blob = new Blob([csvFile], { type: "text/csv;charset=utf-8;" });
if (navigator.msSaveBlob) {
// IE 10+
navigator.msSaveBlob(blob, filename);
} else {
var link = document.createElement("a");
if (link.download !== undefined) {
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
export default ProductBlock;