mirror of
https://github.com/ANL-CEEESA/RELOG.git
synced 2025-12-06 23:58:51 -06:00
Initial amount CSV upload/download
This commit is contained in:
@@ -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>;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user