house, power, energy data generators, savers

This commit is contained in:
Eric Hulburd
2016-02-07 15:32:02 -06:00
parent d1e230c6fd
commit 3cb2320300
47 changed files with 246 additions and 89685 deletions

View File

@@ -1,4 +0,0 @@
var getbabelRelayPlugin = require('babel-relay-plugin'),
schema = require('../config/graphql/schema.json');
module.exports = getbabelRelayPlugin(schema.data, {abortOnError: true});

View File

@@ -1,3 +0,0 @@
1,Johnson
2,Beverley
3,Thompson
1 1 Johnson
2 2 Beverley
3 3 Thompson

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +0,0 @@
tom,1
fred,1
bethany,2
sally,3
saray,2
1 tom 1
2 fred 1
3 bethany 2
4 sally 3
5 saray 2

View File

@@ -1,9 +1,8 @@
import gulp from 'gulp';
import yargs from 'yargs';
import DB from './config/database';
import {PowerDataSeed, HouseSeed, UserSeed} from './lib/tasks/seed_data';
import updateSchema from './lib/tasks/update_schema';
import DB from './server/config/database';
import {PowerDataSeed, HouseSeed} from './server/lib/tasks/seed_data';
gulp.task('generate_power_csv', function(done){
DB.sync().then(()=>{
@@ -22,9 +21,3 @@ gulp.task('save_house_csv', function(done){
HouseSeed.saveCsv(yargs.argv, done);
});
});
gulp.task('save_user_csv', function(done){
DB.sync().then(()=>{
UserSeed.saveCsv(yargs.argv, done);
});
});

View File

@@ -1,28 +0,0 @@
import React from 'react';
import Relay from 'react-relay';
class App extends React.Component {
render() {
var viewer = this.props.viewer,
house = viewer.house;
return (
<div>
<h1>Hi, {viewer.username}</h1>
<p>You are living in the {house.name} house!!!!</p>
</div>
);
}
}
export default Relay.createContainer(App, {
fragments: {
viewer: () => Relay.QL`
fragment on User {
username
house {
name
}
}
`,
},
});

View File

@@ -1,12 +0,0 @@
import Relay from 'react-relay';
export default class extends Relay.Route {
static queries = {
viewer: () => Relay.QL`
query {
viewer
}
`,
};
static routeName = 'AppHomeRoute';
}

View File

@@ -1,35 +0,0 @@
import fs from 'fs';
import path from 'path';
import { graphql, GraphQLSchema } from 'graphql';
import { introspectionQuery, printSchema } from 'graphql/utilities';
import DB from './../../config/database';
import schema from './../../config/graphql/schema';
DB.sync().then(()=>{
var Schema = schema();
// Save JSON of full schema introspection for Babel Relay Plugin to use
(async () => {
var result = await (graphql(Schema, introspectionQuery));
if (result.errors) {
console.error(
'ERROR introspecting schema: ',
JSON.stringify(result.errors, null, 2)
);
} else {
fs.writeFileSync(
path.join(__dirname, './../../config/graphql/schema.json'),
JSON.stringify(result, null, 2)
);
}
})();
// Save user readable type system shorthand of schema
fs.writeFileSync(
path.join(__dirname, './../../config/graphql/schema.graphql'),
printSchema(Schema)
);
});

View File

@@ -1,100 +0,0 @@
import {
GraphQLNonNull,
GraphQLObjectType,
GraphQLInt,
GraphQLString,
GraphQLList
} from 'graphql';
import {
globalIdField,
connectionDefinitions,
connectionArgs
} from 'graphql-relay';
import extend from 'extend';
import DB from "./../config/database";
import {nodeInterface} from './../config/graphql/node';
const NAME = 'House';
/**
* Sequelize Definition
*/
var House = DB.sequelize.define(NAME, {
id: {
type: DB.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true // Automatically gets converted to SERIAL for postgres
},
name: DB.Sequelize.STRING
}, {
paranoid: true,
underscored: true,
tableName: "houses",
instanceMethods: {
},
classMethods: {
set: ()=>{
House.associate();
House.defineGraphQLType();
},
associate: ()=>{
House.hasMany(DB.PowerDatum, {as: 'PowerData'});
House.hasMany(DB.User, {as: 'Habitants'});
},
defineGraphQLType: ()=>{
House.graphql_type = new GraphQLObjectType({
name: NAME,
description: 'A house',
fields: () => {
return {
id: globalIdField(NAME),
name: {
type: new GraphQLNonNull(GraphQLString)
},
power_data: {
type: new GraphQLList(DB.PowerDatum.graphql_type),
description: "Returns house's power data.",
args: connectionArgs,
resolve: (house, args) => {
return house.getPowerDataByTime(args);
}
},
habitants: {
type: new GraphQLList(DB.User.graphql_type),
description: "Returns list of house's habitants.",
args: connectionArgs,
resolve: (house, args) => {
var params = extend({
order: 'name ASC',
limit: 50,
offset: 0,
}, args);
delete params.where; // don't allow any additional query params.
return house.getHabitants(params);
}
}
};
},
interfaces: [nodeInterface]
});
},
getPowerDataByTime: (start_date, end_date, page)=>{
var params = extend({
order: 'time ASC',
limit: 500
}, args.page);
params.where = {time: {}};
if (start_date) params.where.time.$gt = moment.utc(start_date).toDate();
if (end_date) params.where.time.$lt = moment.utc(end_date).toDate();
return House.getPowerData(params);
}
}
});
House.name = NAME;
module.exports = House;

View File

@@ -1,67 +0,0 @@
import {
GraphQLFloat,
GraphQLInt,
GraphQLObjectType
} from 'graphql';
import {
globalIdField
} from 'graphql-relay';
import DB from "./../config/database";
import {nodeInterface} from './../config/graphql/node';
const NAME = 'PowerDatum'
/**
* Define your own types here
*/
var PowerDatum = DB.sequelize.define(NAME, {
id: {
type: DB.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true // Automatically gets converted to SERIAL for postgres
},
time: DB.Sequelize.DATE,
power: DB.Sequelize.FLOAT
}, {
paranoid: true,
underscored: true,
tableName: "power_data",
instanceMethods: {
},
classMethods: {
set: ()=>{
PowerDatum.associate();
PowerDatum.defineGraphQLType();
},
associate: ()=>{
PowerDatum.belongsTo(DB.House);
},
defineGraphQLType: ()=>{
PowerDatum.graphql_type = new GraphQLObjectType({
name: NAME,
description: 'A person who uses our app',
fields: () => ({
id: globalIdField(NAME),
power: {
type: GraphQLFloat,
},
time: {
type: GraphQLInt,
description: "Time the power was recorded.",
resolve: (power_datum, _) => {
return power_datum.time.getTime();
}
},
}),
interfaces: [nodeInterface]
});
}
}
});
PowerDatum.name = NAME;
module.exports = PowerDatum;

View File

@@ -1,73 +0,0 @@
import {
GraphQLString,
GraphQLNonNull,
GraphQLObjectType
} from 'graphql';
import {
globalIdField,
connectionDefinitions,
connectionArgs
} from 'graphql-relay';
import DB from "./../config/database";
import {nodeInterface} from './../config/graphql/node';
const NAME = 'User';
/**
* Sequelize Definition
*/
var User = DB.sequelize.define(NAME, {
id: {
type: DB.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true // Automatically gets converted to SERIAL for postgres
},
username: {
type: DB.Sequelize.STRING,
unique: true
}
}, {
paranoid: true,
underscored: true,
tableName: "users",
instanceMethods: {
},
classMethods: {
set: ()=>{
User.associate();
User.defineGraqhQLType()
},
associate: ()=>{
User.belongsTo(DB.House);
},
defineGraqhQLType: ()=>{
User.graphql_type = new GraphQLObjectType({
name: NAME,
description: 'A house',
fields: () => {
return {
id: globalIdField(NAME),
username: {
type: new GraphQLNonNull(GraphQLString)
},
house: {
type: DB.House.graphql_type,
description: "Returns user's house.",
resolve: (user, args) => {
return user.getHouse();
}
}
};
},
interfaces: [nodeInterface]
});
}
}
});
User.name = NAME;
module.exports = User;

View File

@@ -3,8 +3,7 @@
"version": "0.0.0",
"private": true,
"scripts": {
"start": "babel-node ./app.express.js",
"update-schema": "babel-node ./lib/tasks/update_schema.js"
"start": "babel-node ./app.express.js"
},
"dependencies": {
"body-parser": "~1.12.0",
@@ -26,21 +25,15 @@
"babel-preset-es2015": "6.3.13",
"babel-preset-react": "6.3.13",
"babel-preset-stage-0": "6.3.13",
"babel-relay-plugin": "0.6.0",
"babel-core": "6.3.21",
"babel-loader": "6.2.0",
"babel-polyfill": "6.3.14",
"babel-preset-es2015": "6.3.13",
"babel-preset-react": "6.3.13",
"babel-preset-stage-0": "6.3.13",
"babel-relay-plugin": "0.6.0",
"express": "4.13.3",
"express-graphql": "0.4.5",
"graphql": "0.4.14",
"graphql-relay": "0.3.6",
"react": "0.14.3",
"react-dom": "0.14.3",
"react-relay": "0.6.0",
"webpack": "1.12.9",
"webpack-dev-server": "1.14.0",
"jquery": "2.2.0",
@@ -51,13 +44,14 @@
"style-loader": "^0.12.3",
"connect-assets":"~4.7.0",
"node-sass": "3.4.2",
"moment": "2.11.1",
"moment-timezone":"0.5.0",
"yargs": "3.32.0",
"extend": "3.0.0"
},
"devDependencies": {
"gulp": "^3.9.0",
"babel-cli": "^6.3.17"
"babel-cli": "^6.3.17",
"babel-standalone": "6.4.4",
"react-templates": "0.4.1"
}
}

View File

@@ -14,19 +14,10 @@ import DB from './config/database';
const APP_PORT = 3000;
const GRAPHQL_PORT = 8080;
var graphql_server = express();
var rest_api = express();
DB.sync().then(()=>{
console.log("db done syncing")
// Expose a GraphQL endpoint
graphql_server.use('/', graphQLHTTP({
graphiql: true,
pretty: true,
schema: schema(),
}));
graphql_server.listen(GRAPHQL_PORT, () => console.log(
`GraphQL Server is now running on http://localhost:${GRAPHQL_PORT}`
));
rest_api
});
/*

View File

@@ -3,7 +3,7 @@
import fs from "fs";
import Sequelize from 'sequelize';
var sequelize = new Sequelize("postgres://spikeuser:123456@localhost:5432/spike_proto", {
var sequelize = new Sequelize("postgres://spikeuser:123456@localhost:5432/spike2", {
pool: {
max: 5,
min: 0,

View File

@@ -0,0 +1,13 @@
import DB from './../config/database.js';
class EnergyController{
static index(req, res){
DB.House.findOne({where: {name: req.housename}}).then((house)=>{
house.getEnergyDataByTime(req.params.start_time, req.params.end_time).then((energy_data){
req.json(energy_data);
});
});
}
}

View File

@@ -0,0 +1,11 @@
import DB from './../config/database.js';
class HousesController{
static index(req, res){
DB.House.findAll({attributes: ['id', 'name']}).then((houses){
res.json(houses);
});
}
}

View File

@@ -0,0 +1,13 @@
import DB from './../config/database.js';
class PowerController{
static index(req, res){
DB.House.findOne({where: {name: req.housename}}).then((house)=>{
house.getPowerDataByTime(req.params.start_time, req.params.end_time).then((power_data){
res.json(power_data);
});
});
}
}

View File

@@ -2,21 +2,23 @@ import extend from "extend";
import moment from "moment";
import csv from "fast-csv";
import fs from 'fs';
import MathUtils from "./../utils/math"
import MathUtils from "./../../../shared/utils/math"
import DB from './../../config/database';
const DATA_PATH = __dirname + '/../../../shared/data/'
export class PowerDataSeed {
static saveCsv(opts, done){
opts = extend({
path: __dirname + "/../../data/power_data.csv"
path: DATA_PATH + "power_data.csv"
}, opts || {});
var stream = fs.createReadStream(opts.path),
csvStream = csv.fromStream(stream, {headers: ['house_id', 'time', 'power']}),
csvStream = csv.fromStream(stream, {headers: ['house_id', 'time', 'consumption', 'production']}),
rows = [];
csvStream.on("data", function(data){
data.time = moment.utc(parseInt(data.time)).format();
data.time = moment.utc(parseInt(data.time * 1000)).format();
rows.push(data);
if (rows.length % 100 === 0){
DB.PowerDatum.bulkCreate(rows, {validate: true}).then(()=>{
@@ -25,9 +27,20 @@ export class PowerDataSeed {
}
});
csvStream.on("end", function(){
console.log("all rows parsed")
DB.PowerDatum.bulkCreate(rows, {validate: true}).then(()=>{
return DB.House.findAll().then((houses)=>{
var promise = Promise.resolve();
for (var house of houses){
promise = promise.then(()=>{
return house.aggregatePowerToEnergyData();
});
}
return promise;
});
}).then(()=>{
console.log("DONE!")
done();
});
});
}
@@ -36,9 +49,9 @@ export class PowerDataSeed {
opts = extend({
start_date: moment().subtract(2, "months").unix(),
end_date: moment().unix(),
interval: 180, // every 3 minutes (in s)
interval: 900, // every 15 minutes (in s)
average: 1400, // Wh
path: __dirname + "/../../data/power_data.csv"
path: DATA_PATH + "power_data.csv"
}, opts || {});
var row_date = opts.start_date,
@@ -46,29 +59,34 @@ export class PowerDataSeed {
writableStream = fs.createWriteStream(opts.path),
house_ids = opts.house_ids.split(",")
csvStream.pipe(writableStream);
writableStream.on("finish", ()=>{
console.log("DONE!")
done();
});
DB.House.findAll({where: {id: house_ids}}).then((houses)=>{
while (row_date <= opts.end_date){
for (var house_id of house_ids){
csvStream.write([house_id, row_date, MathUtils.normal(opts.average)]);
csvStream.pipe(writableStream);
writableStream.on("finish", ()=>{
console.log("DONE!")
done();
});
while (row_date <= opts.end_date){
for (var house of houses){
var consumption = MathUtils.normal(opts.average),
production = MathUtils.normal(opts.average) * house.productionMultiplier(row_date * 1000);
csvStream.write([house.id, row_date, consumption, production]);
}
row_date += opts.interval;
}
row_date += opts.interval;
}
csvStream.end();
csvStream.end();
});
}
}
export class HouseSeed {
static saveCsv(opts, done){
opts = extend({
path: __dirname + "/../../data/houses.csv"
path: DATA_PATH + "houses.csv"
}, opts || {});
var stream = fs.createReadStream(opts.path),
csvStream = csv.fromStream(stream, {headers: ['id', 'name']}),
csvStream = csv.fromStream(stream, {headers: ['id', 'name', 'timezone']}),
rows = [];
csvStream.on("data", function(data){
@@ -84,26 +102,3 @@ export class HouseSeed {
});
}
}
export class UserSeed {
static saveCsv(opts, done){
opts = extend({
path: __dirname + "/../../data/users.csv"
}, opts || {});
var stream = fs.createReadStream(opts.path),
csvStream = csv.fromStream(stream, {headers: ['username', 'house_id']}),
rows = [];
csvStream.on("data", function(data){
console.log(JSON.stringify(data))
rows.push(data);
});
csvStream.on("end", function(){
console.log(rows);
DB.User.bulkCreate(rows, {validate: true}).then(()=>{
console.log("DONE!")
done();
});
});
}
}

View File

@@ -0,0 +1,36 @@
import DB from "./../config/database";
const NAME = 'EnergyDatum';
/**
* Define your own types here
*/
var EnergyDatum = DB.sequelize.define(NAME, {
id: {
type: DB.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true // Automatically gets converted to SERIAL for postgres
},
day: DB.Sequelize.DATEONLY,
production: DB.Sequelize.FLOAT,
consumption: DB.Sequelize.FLOAT
}, {
paranoid: true,
underscored: true,
tableName: "energy_data",
instanceMethods: {
},
classMethods: {
set: ()=>{
EnergyDatum.associate();
},
associate: ()=>{
EnergyDatum.belongsTo(DB.House);
}
}
});
EnergyDatum.name = NAME;
module.exports = EnergyDatum;

80
server/models/house.js Normal file
View File

@@ -0,0 +1,80 @@
import moment from 'moment-timezone';
import DB from "./../config/database";
import {nodeInterface} from './../config/graphql/node';
const NAME = 'House';
/**
* Sequelize Definition
*/
var House = DB.sequelize.define(NAME, {
id: {
type: DB.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true // Automatically gets converted to SERIAL for postgres
},
timezone: DB.Sequelize.STRING,
name: DB.Sequelize.STRING
}, {
paranoid: true,
underscored: true,
tableName: "houses",
instanceMethods: {
productionMultiplier: function(timestamp){
var house = this,
minute = moment.tz(timestamp, house.timezone).hour() * 60 + moment.tz(timestamp, house.timezone).minute(),
multiplier = 0;
if (minute > 420 && minute < 1140){
multiplier = 1 - Math.abs(780 - minute) / 360;
}
return multiplier;
},
timeToDateString: function(timestamp){
var house = this;
return moment.tz(timestamp, house.timezone).format("YYYY-MM-DD");
},
aggregatePowerToEnergyData: function(){
var house = this;
return DB.EnergyDatum.destroy({where: {house_id: house.id}})
.then(()=>{
return house.getPowerData();
})
.then((power_data)=>{
var energy_data = new Map();
power_data.forEach((power_datum)=>{
var day = house.timeToDateString(power_datum.time),
energy_datum = energy_data.get(day) || {production: 0, consumption: 0, day: day, house_id: house.id};
console.log(power_datum.time)
console.log(day)
energy_datum.production += power_datum.production;
energy_datum.consumption += power_datum.consumption;
energy_data.set(day, energy_datum);
});
console.log(Array.from(energy_data.values()))
return DB.EnergyDatum.bulkCreate(Array.from(energy_data.values()), {validate: true});
});
}
},
classMethods: {
set: ()=>{
House.associate();
},
associate: ()=>{
House.hasMany(DB.PowerDatum, {as: 'PowerData'});
},
getPowerDataByTime: (start_date, end_date)=>{
var params = {
where: {time: {}},
attributes: ['time', 'consumption', 'production']
};
if (start_date) params.where.time.$gt = moment.utc(start_date).toDate();
if (end_date) params.where.time.$lt = moment.utc(end_date).toDate();
return House.getPowerData(params);
}
}
});
House.name = NAME;
module.exports = House;

View File

@@ -0,0 +1,36 @@
import DB from "./../config/database";
const NAME = 'PowerDatum'
/**
* Define your own types here
*/
var PowerDatum = DB.sequelize.define(NAME, {
id: {
type: DB.Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true // Automatically gets converted to SERIAL for postgres
},
time: DB.Sequelize.DATE,
consumption: DB.Sequelize.FLOAT,
production: DB.Sequelize.FLOAT
}, {
paranoid: true,
underscored: true,
tableName: "power_data",
instanceMethods: {
},
classMethods: {
set: ()=>{
PowerDatum.associate();
},
associate: ()=>{
PowerDatum.belongsTo(DB.House);
}
}
});
PowerDatum.name = NAME;
module.exports = PowerDatum;

9
server/routes.js Normal file
View File

@@ -0,0 +1,9 @@
import Controllers from 'controllers'
export default function(app){
app.use('/data/v1/savings/:housename', Controllers.Energy.savings);
app.use('/data/v1/production/:housename', Controllers.Energy.production);
app.use('/data/v1/houses/:housename');
};