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

Export group #83

Merged
merged 29 commits into from
Sep 13, 2017
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
aef9792
Changes to linechart component to enable export of data. currently ab…
kooolbreez1 Mar 28, 2017
d3bb015
Changes to linechart component to enable export of data. currently ab…
kooolbreez1 Apr 4, 2017
e381bd2
Changes to LineChartComponent, Readings.js in actions and LineChartCo…
kooolbreez1 Apr 5, 2017
5f9996a
did stuff
RagingOcelot Apr 5, 2017
5e811fa
Merge branch 'master' into exportGroup
kooolbreez1 Apr 5, 2017
c3f6752
Merge branch 'master' into exportGroup
RagingOcelot Apr 5, 2017
7783382
Merge remote-tracking branch 'origin/exportGroup' into exportGroup
kooolbreez1 Apr 5, 2017
950b045
Able to access (compressed?) readings via the UIOptionsComponent.
kooolbreez1 Apr 6, 2017
1d56556
Able to format and download compressed meter data as a CSV file.
kooolbreez1 Apr 7, 2017
539401e
Merge branch 'master' into exportGroup
kooolbreez1 Apr 7, 2017
d610906
Merge branch 'master' into exportGroup
RagingOcelot Apr 7, 2017
eda9804
Merge remote-tracking branch 'origin/exportGroup' into exportGroup
kooolbreez1 Apr 7, 2017
ff4f005
added timestamp to exported Data
kooolbreez1 Apr 7, 2017
199d31d
Merge branch 'master' into exportGroup
RagingOcelot Apr 10, 2017
5082823
Merge branch 'master' into exportGroup
RagingOcelot Apr 13, 2017
b659757
changed name of the exported file to "exportedDataOED" instead of pro…
kooolbreez1 Apr 13, 2017
c3fc89f
fixed a comment I accidentally changed in dashboardContainer
kooolbreez1 Apr 13, 2017
3ae6e54
compressed filename variable in exportData.js to 1 line and removed u…
kooolbreez1 Apr 15, 2017
40dea5e
Moved exportData.js to new services directory.
kooolbreez1 Apr 15, 2017
9db2746
preparing to merge with master
kooolbreez1 Apr 23, 2017
3c22a47
Merging branch 'master' into exportGroup
kooolbreez1 Apr 23, 2017
6c0937f
Now works with the updated UIOptions component and barcharts. can cur…
kooolbreez1 Apr 25, 2017
dc03958
Successfully exports the displayed data from bar charts (for full dat…
kooolbreez1 Apr 25, 2017
538b85c
Successfully exports the displayed data from bar charts (for full dat…
kooolbreez1 Apr 25, 2017
c91b9a6
the name of the exported file now dynamically changes based on the ti…
kooolbreez1 Apr 26, 2017
16f4e3a
Name of exported file includes its chartType, the export button is wo…
kooolbreez1 May 3, 2017
4c6b4ec
updated a line of code to mitigate a likely bug that would appear if …
kooolbreez1 May 3, 2017
a8ca745
Export component is now rendered as art of the UI options component f…
kooolbreez1 May 3, 2017
394c155
ExportComponent is now a functional component instead of a class
kooolbreez1 May 3, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 48 additions & 0 deletions src/client/app/actions/exportData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* 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 = 'id:,readings:,timestamp:\n';
items.forEach(set => {
const data = set.exportVals;
const id = set.id;
data.forEach(reading => {
const info = reading.y;
const timeStamp = moment(reading.x).format('dddd MMM DD YYYY hh:mm a');
csvOutput += `${id},${info} kwh, ${timeStamp} \n`;
});
});
return csvOutput;
}
/**
* Function to download the formatted CSV file to the users computer.
* @param inputCSV A String containing the formatted CSV data.
*/
function downloadCSV(inputCSV) {
const csvContent = `data:text/csv;charset=utf-8,${inputCSV}`;
const encodedUri = encodeURI(csvContent);
const link = document.createElement('a');
const fileName = 'exportedDataOED.csv';
link.setAttribute('href', encodedUri);
link.setAttribute('download', fileName);
document.body.appendChild(link);

link.click(); // This will download the data file
}
/**
* 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.
*/
export default function graphExport(dataSets) {
const dataToExport = convertToCSV(dataSets);
downloadCSV(dataToExport);
}
1 change: 0 additions & 1 deletion src/client/app/actions/readings.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ function fetchManyReadings(meterIDs, timeInterval) {
}).then(response => dispatch(receiveManyReadings(meterIDs, timeInterval, response.data)));
};
}

/**
* Fetches readings for the given meterIDs if they are not already fetched or being fetched
* @param {Array.<int>} meterIDs
Expand Down
13 changes: 13 additions & 0 deletions src/client/app/components/UIOptionsComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import React from 'react';
import graphExport from '../actions/exportData';

export default class UIOptionsComponent extends React.Component {
/**
Expand All @@ -12,6 +13,7 @@ export default class UIOptionsComponent extends React.Component {
constructor(props) {
super(props);
this.handleMeterSelect = this.handleMeterSelect.bind(this);
this.exportReading = this.exportReading.bind(this);
}

/**
Expand All @@ -22,6 +24,14 @@ export default class UIOptionsComponent extends React.Component {
this.props.fetchMetersDataIfNeeded();
}

/**
* Called when Export button is clicked.
* Passes an object containing the selected meter data to a function for export.
*/
exportReading() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm really not a fan of putting this logic in UIOptionsComponent. It's a discrete piece of functionality that only depends on redux state, so I think you should extract it to its own component and include that in UIOptionsComponent. UIOptionsComponent is really getting to be a god object that violates single responsibility principle and we should try to fight that hard. I think the entire thing should be refactored, but that's a matter for a different pull request.

const compressedData = this.props.exportVals.datasets;
graphExport(compressedData);
}
handleMeterSelect(e) {
e.preventDefault();
const options = e.target.options;
Expand Down Expand Up @@ -92,7 +102,10 @@ export default class UIOptionsComponent extends React.Component {
</div>
<br />
<button type="button" id="changeButton" className="btn btn-primary">Change!</button>
<br />
<button onClick={this.exportReading}>Export!</button>
</div>

</div>
);
}
Expand Down
17 changes: 16 additions & 1 deletion src/client/app/containers/UIOptionsContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,24 @@ import { fetchMetersDataIfNeeded } from '../actions/meters';
*/
function mapStateToProps(state) {
const sortedMeters = _.sortBy(_.values(state.meters.byMeterID).map(meter => ({ id: meter.id, name: meter.name.trim() })), 'name');

const timeInterval = state.graph.timeInterval;
const data = { datasets: [] };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of the ambiguous naming here. It's hard to figure out what part of UIOptionComponent's functionality this pertains to. Refactoring this into its own container/component should mostly fix this issue, but some more detailed naming (and a comment or two) might still be a good idea.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data seems to just be a wrapper around datasets. Why does it exist at all? Why not just use the datasets list as your variable?

for (const meterID of state.graph.selectedMeters) {
const readingsData = state.readings.byMeterID[meterID][timeInterval];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As of right now, this is completely correct. However, when the bar chart is added, readings will be retrieved from state.[line OR bar].readings...
Will the intention be to only grab line chart readings, bar chart readings, or both? If both, some additional logic will eventually be required here.

if (readingsData !== undefined && !readingsData.isFetching) {
data.datasets.push({
label: state.meters.byMeterID[meterID].name,
id: state.meters.byMeterID[meterID].id,
timestamp: state.readings.byMeterID[meterID][timeInterval].start_timestamp,
exportVals: state.readings.byMeterID[meterID][timeInterval].readings.map(arr => ({ x: arr[0], y: arr[1] }))
});
}
}
return {
meters: sortedMeters,
selectedMeters: state.graph.selectedMeters
selectedMeters: state.graph.selectedMeters,
exportVals: data
};
}

Expand Down
18 changes: 9 additions & 9 deletions src/server/services/readMetasysData.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ const Meter = require('./../models/Meter');
async function readMetasysData(filePath) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for cleaning this up a bit while you were doing other things. It slipped through linting somehow. I see there are a few other linting errors in here, but they aren't your code so it's no big deal.

const readingArr = [];
let i = 1;
//getting filename
const fileNameArray = filePath.split("/");
// getting filename
const fileNameArray = filePath.split('/');
const fileName = fileNameArray.pop();
//list of readings
// list of readings
const rows = await readCsv(filePath);
//meterInformation
// meterInformation
const meter = await Meter.getByName(fileName.replace('.csv', ''));

for (const row of rows) {
//timestamp. end time stamp
// timestamp. end time stamp
const timestamp = row[0].toLocaleString();
const start_timestamp = new Date(timestamp);
let end_timestamp = new Date(timestamp);
end_timestamp.setHours(end_timestamp.getHours()+1);
const end_timestamp = new Date(timestamp);
end_timestamp.setHours(end_timestamp.getHours() + 1);


//meterReading
// meterReading
let meterReading = row[3];
meterReading = meterReading.replace('kWh', '');
meterReading = parseInt(meterReading);
Expand All @@ -41,7 +41,7 @@ async function readMetasysData(filePath) {
i++;
}
try {
//inserting all the data from an array into database and catching error when it occurs.
// inserting all the data from an array into database and catching error when it occurs.
Reading.insertAll(readingArr);
} catch (err) {
console.error(err);
Expand Down