diff --git a/package.json b/package.json
index 5215741df..78efafa3f 100644
--- a/package.json
+++ b/package.json
@@ -40,6 +40,7 @@
"nodemailer": "~2.6.4",
"pg-promise": "^5.3.5",
"react": "^15.4.2",
+ "react-bootstrap": "^0.31.0",
"react-chartjs-2": "^2.0.5",
"react-dom": "^15.4.2",
"react-rangeslider": "^2.0.1",
diff --git a/src/client/app/components/ExportComponent.jsx b/src/client/app/components/ExportComponent.jsx
new file mode 100644
index 000000000..8029a6834
--- /dev/null
+++ b/src/client/app/components/ExportComponent.jsx
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import React from 'react';
+import { Button } from 'react-bootstrap';
+import moment from 'moment';
+import graphExport from '../services/exportData';
+
+const ExportComponent = props => {
+ /**
+ * Called when Export button is clicked.
+ * Passes an object containing the selected meter data to a function for export.
+ */
+ const exportReading = () => {
+ const compressedData = props.exportVals.datasets;
+ let time = compressedData[0].exportVals[0].x;
+ const chart = compressedData[0].currentChart;
+ time = moment(time).format('ddddMMMDDYYYY');
+ const name = `oedExport${time}${chart}.csv`;
+ graphExport(compressedData, name);
+ };
+ return (
+
+
+
+ );
+};
+export default ExportComponent;
diff --git a/src/client/app/components/HomeComponent.jsx b/src/client/app/components/HomeComponent.jsx
index c53f049df..2b67fe976 100644
--- a/src/client/app/components/HomeComponent.jsx
+++ b/src/client/app/components/HomeComponent.jsx
@@ -6,6 +6,7 @@ import React from 'react';
import HeaderComponent from './HeaderComponent';
import DashboardContainer from '../containers/DashboardContainer';
+
/**
* Top-level React component that controls the home page
* @return JSX to create the home page
diff --git a/src/client/app/components/UIOptionsComponent.jsx b/src/client/app/components/UIOptionsComponent.jsx
index f052f0306..cec9dce2c 100644
--- a/src/client/app/components/UIOptionsComponent.jsx
+++ b/src/client/app/components/UIOptionsComponent.jsx
@@ -7,6 +7,8 @@ import Slider from 'react-rangeslider';
import moment from 'moment';
import 'react-rangeslider/lib/index.css';
import { chartTypes } from '../reducers/graph';
+import ExportContainer from '../containers/ExportContainer';
+
export default class UIOptionsComponent extends React.Component {
/**
@@ -104,6 +106,9 @@ export default class UIOptionsComponent extends React.Component {
Bar chart interval (days):
+
+
+
);
diff --git a/src/client/app/containers/ExportContainer.js b/src/client/app/containers/ExportContainer.js
new file mode 100644
index 000000000..af5508a42
--- /dev/null
+++ b/src/client/app/containers/ExportContainer.js
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { connect } from 'react-redux';
+import ExportComponent from '../components/ExportComponent';
+import { chartTypes } from '../reducers/graph';
+
+/**
+ * @param {State} state
+ * @return {{meterInfo: *, selectedMeters: Array}}
+ */
+function mapStateToProps(state) {
+ const timeInterval = state.graph.timeInterval;
+ const data = { datasets: [] };
+ let readingsData;
+ const chart = state.graph.chartToRender;
+ const barDuration = state.graph.barDuration;
+
+ for (const meterID of state.graph.selectedMeters) {
+ if (chart === chartTypes.line) {
+ readingsData = state.readings.line.byMeterID[meterID][timeInterval];
+ } else if (chart === chartTypes.bar) { readingsData = state.readings.bar.byMeterID[meterID][timeInterval][barDuration]; }
+ if (readingsData !== undefined && !readingsData.isFetching && chart === chartTypes.line) {
+ data.datasets.push({
+ label: state.meters.byMeterID[meterID].name,
+ id: state.meters.byMeterID[meterID].id,
+ timestamp: state.readings.line.byMeterID[meterID][timeInterval].start_timestamp,
+ currentChart: chart,
+ exportVals: state.readings.line.byMeterID[meterID][timeInterval].readings.map(arr => ({ x: arr[0], y: arr[1] }))
+ });
+ } else if (readingsData !== undefined && !readingsData.isFetching && chart === chartTypes.bar) {
+ data.datasets.push({
+ label: state.meters.byMeterID[meterID].name,
+ id: state.meters.byMeterID[meterID].id,
+ timestamp: state.readings.bar.byMeterID[meterID][timeInterval][barDuration].timestamp,
+ currentChart: chart,
+ exportVals: state.readings.bar.byMeterID[meterID][timeInterval][barDuration].readings.map(arr => ({ x: arr[0], y: arr[1] }))
+ });
+ }
+ }
+ return {
+ selectedMeters: state.graph.selectedMeters,
+ exportVals: data
+ };
+}
+
+/**
+ * Connects changes to the Redux store to UIOptionsComponent via mapStateToProps
+ */
+export default connect(mapStateToProps)(ExportComponent);
diff --git a/src/client/app/services/exportData.js b/src/client/app/services/exportData.js
new file mode 100644
index 000000000..6a0cba5b0
--- /dev/null
+++ b/src/client/app/services/exportData.js
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import moment from 'moment';
+
+
+/**
+ * Function to converts the compressed meter data into a CSV formatted string.
+ * @param items The compressed meter data.
+ * @returns output A string containing the CSV formatted compressed meter data.
+ */
+
+function convertToCSV(items) {
+ let csvOutput = 'Label,Readings,Start Timestamp\n';
+ items.forEach(set => {
+ const data = set.exportVals;
+ const label = set.label;
+ data.forEach(reading => {
+ const info = reading.y;
+ const startTimeStamp = moment(reading.x).format('dddd MMM DD YYYY hh:mm a');
+ csvOutput += `${label},${info} kwh, ${startTimeStamp}\n`; // this assumes that meter readings are in kwh
+ });
+ });
+ return csvOutput;
+}
+/**
+ * Function to download the formatted CSV file to the users computer.
+ * @param inputCSV A String containing the formatted CSV data.
+ * @param fileName A string representing the name of the file.
+ */
+function downloadCSV(inputCSV, fileName) {
+ const element = document.createElement('a');
+ element.setAttribute('href', `data:text/csv;charset=utf-8,${encodeURIComponent(inputCSV)}`);
+ element.setAttribute('download', fileName);
+
+ element.style.display = 'none';
+ document.body.appendChild(element);
+
+ element.click();
+
+ document.body.removeChild(element);
+}
+
+/**
+ * Function to export compressed data from the graph currently displaying. May be used for routing if more export options are added
+ * @param dataSets An Object. The compressed data from each meter currently selected in the graph.
+ * @param name the name of the file.
+ */
+export default function graphExport(dataSets, name) {
+ const dataToExport = convertToCSV(dataSets);
+ downloadCSV(dataToExport, name);
+}