Commit d4ba0495 authored by David Haynes's avatar David Haynes

Merge branch 'master' into issue4

parents c6c5ec44 567984a6
......@@ -18,3 +18,5 @@ Session.vim
# auto-generated tag files
tags
weather/node_modules
cd weather
meteor --settings config/settings.json
cd ..
......@@ -6,19 +6,19 @@
meteor-base@1.0.4 # Packages every Meteor app needs to have
mobile-experience@1.0.4 # Packages for a great mobile UX
mongo@1.1.12 # The database Meteor supports right now
mongo@1.1.14 # The database Meteor supports right now
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
session@1.1.6 # Client-side reactive dictionary for your app
jquery@1.11.9 # Helpful client-side library
tracker@1.1.0 # Meteor's client-side reactive programming library
session@1.1.7 # Client-side reactive dictionary for your app
jquery@1.11.10 # Helpful client-side library
tracker@1.1.1 # Meteor's client-side reactive programming library
es5-shim@4.6.14 # ECMAScript 5 compatibility for older browsers.
ecmascript@0.5.8 # Enable ECMAScript2015+ syntax in app code
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
ecmascript@0.6.1 # Enable ECMAScript2015+ syntax in app code
zodiase:mdl
mquandalle:bower
http@1.2.9
standard-minifier-css@1.2.0
standard-minifier-js@1.2.0
shell-server
http@1.2.10
standard-minifier-css@1.3.2
standard-minifier-js@1.2.1
shell-server@0.2.1
aldeed:simple-schema # Schema support for db
aldeed:simple-schema@1.5.3
allow-deny@1.0.5
autoupdate@1.3.11
babel-compiler@6.9.1
babel-runtime@0.1.11
base64@1.0.9
binary-heap@1.0.9
blaze@2.1.8
blaze-html-templates@1.0.4
blaze-tools@1.0.9
boilerplate-generator@1.0.9
caching-compiler@1.1.7
caching-html-compiler@1.0.6
callback-hook@1.0.9
check@1.2.3
autoupdate@1.3.12
babel-compiler@6.13.0
babel-runtime@1.0.1
base64@1.0.10
binary-heap@1.0.10
blaze@2.3.0
blaze-html-templates@1.1.0
blaze-tools@1.0.10
boilerplate-generator@1.0.11
caching-compiler@1.1.9
caching-html-compiler@1.1.0
callback-hook@1.0.10
check@1.2.4
ddp@1.2.5
ddp-client@1.3.1
ddp-common@1.2.6
ddp-server@1.3.10
ddp-client@1.3.2
ddp-common@1.2.8
ddp-server@1.3.12
deps@1.0.12
diff-sequence@1.0.6
ecmascript@0.5.8
ecmascript-runtime@0.3.14
ejson@1.0.12
es5-shim@4.6.14
fastclick@1.0.12
fourseven:scss@3.9.0
geojson-utils@1.0.9
diff-sequence@1.0.7
ecmascript@0.6.1
ecmascript-runtime@0.3.15
ejson@1.0.13
es5-shim@4.6.15
fastclick@1.0.13
fourseven:scss@3.13.0
geojson-utils@1.0.10
hot-code-push@1.0.4
html-tools@1.0.10
htmljs@1.0.10
http@1.2.9
id-map@1.0.8
jquery@1.11.9
launch-screen@1.0.12
html-tools@1.0.11
htmljs@1.0.11
http@1.2.10
id-map@1.0.9
jquery@1.11.10
launch-screen@1.1.0
livedata@1.0.18
logging@1.1.15
mdg:validation-error@0.2.0
meteor@1.2.17
logging@1.1.16
mdg:validation-error@0.5.1
meteor@1.6.0
meteor-base@1.0.4
minifier-css@1.2.14
minifier-js@1.2.14
minimongo@1.0.17
minifier-css@1.2.15
minifier-js@1.2.15
minimongo@1.0.19
mobile-experience@1.0.4
mobile-status-bar@1.0.12
modules@0.7.6
modules-runtime@0.7.6
mongo@1.1.12
mongo-id@1.0.5
mobile-status-bar@1.0.13
modules@0.7.7
modules-runtime@0.7.8
mongo@1.1.14
mongo-id@1.0.6
mquandalle:bower@1.5.2_1
npm-mongo@1.5.49
observe-sequence@1.0.12
ordered-dict@1.0.8
promise@0.8.4
npm-mongo@2.2.16_1
observe-sequence@1.0.14
ordered-dict@1.0.9
promise@0.8.8
random@1.0.10
reactive-dict@1.1.8
reactive-var@1.0.10
reload@1.1.10
retry@1.0.8
routepolicy@1.0.11
session@1.1.6
reactive-var@1.0.11
reload@1.1.11
retry@1.0.9
routepolicy@1.0.12
session@1.1.7
shell-server@0.2.1
spacebars@1.0.12
spacebars-compiler@1.0.12
standard-minifier-css@1.2.0
standard-minifier-js@1.2.0
templating@1.2.14
templating-tools@1.0.4
tracker@1.1.0
ui@1.0.11
underscore@1.0.9
url@1.0.10
webapp@1.3.11
spacebars@1.0.13
spacebars-compiler@1.1.0
standard-minifier-css@1.3.2
standard-minifier-js@1.2.1
templating@1.3.0
templating-compiler@1.3.0
templating-runtime@1.3.0
templating-tools@1.1.0
tracker@1.1.1
ui@1.0.12
underscore@1.0.10
url@1.0.11
webapp@1.3.12
webapp-hashing@1.0.9
zodiase:check@0.0.4
zodiase:material-design-icons-fonts@2.2.3
zodiase:mdl@1.2.0
zodiase:mdl-assets@1.2.0_1
zodiase:check@0.0.5
zodiase:material-design-icons-fonts@3.0.1
zodiase:mdl@1.3.0
zodiase:mdl-assets@1.3.0
......@@ -5,7 +5,8 @@
.header-bg > .mdl-layout__header,
.header-bg > .mdl-layout__header-row,
.header-bg > .mdl-layout__drawer,
.header-bg > .mdl-layout__tab-bar {
.header-bg > .mdl-layout__tab-bar,
.header-bg > .mdl-layout__tab {
background-color: #006633;
}
......@@ -29,13 +30,19 @@
color: #fff;
}
.alert-card.mdl-card {
margin: auto;
width: auto;
background: #ee1111;
}
.forecast-grid.mdl-grid {
}
.out-of-focus-card.mdl-card {
margin: auto;
width: auto;
height: 30px;
/* height: 30px; */
}
.hourly-table.mdl-data-table {
......@@ -46,3 +53,43 @@
.table-full-width {
width: 100%;
}
.degree0.mdl-card {
background-color: #000000;
}
.degree10.mdl-card {
background-color: #222222;
}
.degree20.mdl-card {
background-color: #444444;
}
.degree30.mdl-card {
background-color: #666666;
}
.degree40.mdl-card {
background-color: #777777;
}
.degree50.mdl-card {
background-color: #888888;
}
.degree60.mdl-card {
background-color: #999999;
}
.degree70.mdl-card {
background-color: #aaaaaa;
}
.degree80.mdl-card {
background-color: #bbbbbb;
}
.degree90.mdl-card {
background-color: #dddddd;
}
.degree100.mdl-card {
background-color: #ffffff;
}
.mdl-layout__tab-bar-right-button {
background-color: #006633;
}
.mdl-layout__tab-bar-left-button {
background-color: #006633;
}
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="SRCT Weather beautifully displays live weather and forecasts for Mason's campuses.">
<title>SRCT Weather &bull; 76&#176; F</title>
<title>SRCT Weather</title>
<link rel="stylesheet" href="weather.css">
<link rel="icon" type="image/x-icon" href="lib/favicon.ico" />
</head>
<body>
{{> home}}
</body>
// client entry point, imports all client code
import { Template } from 'meteor/templating';
import '../imports/ui/body.js';
import './main.html';
<template name="footer">
<footer class="mdl-mini-footer">
<div class="mdl-mini-footer__left-section">
<div class="mdl-logo">SRCT Weather | Powered by <a href="https://darksky.net/">Dark Sky</a></div>
</div>
<div class="mdl-mini-footer__right-section" style="align: right">
A project of Mason <a href="https://srct.gmu.edu/"><strong>SRCT</strong></a>, a registered student org.
<br />Read and <a href="https://git.gmu.edu//srct/weather/milestones/">contribute</a>
to our <a href="https://www.gnu.org/licenses/quick-guide-gplv3.html">freely-licensed</a>
<a href="https://git.gmu.edu/srct/weather/" target="_blank">source code</a>.
</div>
</footer>
</template>
<template name="home">
<!-- MDL Fixed Layout Container -->
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header header-bg">
<!-- Header Container -->
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<!-- Title of Header -->
<span class="mdl-layout-title">Weather</span>
</div>
<!-- Tab Bar Container , and Tab links -->
<div class="mdl-layout__tab-bar mdl-js-ripple-effect header-bg" style="background: #006633;">
<a id="fairfax-tab-button" href="#fairfax" class="mdl-layout__tab is-active">Fairfax</a>
<a href="#arlington" class="mdl-layout__tab">Arlington</a>
<a href="#scitech" class="mdl-layout__tab">SciTech</a>
<a href="#korea" class="mdl-layout__tab">Mason Korea</a>
</div>
</header>
<!-- MDL Layout Header -->
<main class="mdl-layout__content">
<!-- "is-active" class to set the default active tab -->
<section class="mdl-layout__tab-panel is-active" id="fairfax">
<div class="page-content">
<div class="page-content">{{> weather}}</div>
</div>
</section>
<!-- Tab No 2-->
<section class="mdl-layout__tab-panel" id="arlington">
<div class="page-content">
<div class="page-content">{{> weather}}</div>
</div>
</section>
<!-- Tab No 3-->
<section class="mdl-layout__tab-panel" id="scitech">
<div class="page-content">
<div class="page-content">{{> weather}}</div>
</div>
</section>
<!-- Tab No 4-->
<section class="mdl-layout__tab-panel" id="korea">
<div class="page-content">
<div class="page-content">{{> weather}}</div>
</div>
</section>
{{> footer}}
</main>
</div>
</template>
Template.home.events({
'click #fairfax-tab-button' (event) {
},
'click a.mdl-layout__tab-bar' (event) {
console.log(event);
}
});
Template.home.onRendered(function() {
document.getElementById("#fairfax-tab-button").addEventListener("click", function(){
});
});
<template name="weather">
<div class="mdl-grid">
{{#if weatherData.alerts.title}}
<div class="mdl-cell mdl-cell--12-col">
<div class="alert-card weather-card mdl-card mdl-shadow--4dp">
<div class="mdl-card__title">
<h1 class="mdl-card__title-text"><a href="{{weatherData.alerts.url}}">{{weatherData.alerts.title}}</a> until {{formatTimestamp weatherData.alerts.expires}}<br /></h1>
</div>
<div class="mdl-card__supporting-text">
{{waatherData.alerts.description}}
</div>
</div>
</div>
{{/if}}
<div class="mdl-cell mdl-cell--12-col">
<div class="{{getColorStyle (roundNumber weatherData.data.currently.temperature)}} weather-card mdl-card mdl-shadow--4dp">
<div class="mdl-card__title">
<h1 class="mdl-card__title-text">Current Conditions<br /></h1>
</div>
<div class="mdl-card__supporting-text">
<h4>{{weatherData.data.currently.summary}}</h4>
<div class="temp-size">
<i class="wi {{convertIconName weatherData.data.currently.icon}} wi-fw"></i> {{roundNumber weatherData.data.currently.temperature}}<i class="wi wi-fahrenheit"></i>
</div>
<div class="detail-size">
Feels like {{roundNumber weatherData.data.currently.apparentTemperature}}<i class="wi wi-fahrenheit"></i>
<br /><br /><i class="wi wi-{{precipitationIcons weatherData.data.currently.precipProbability}}"></i> {{precipitationWords weatherData.data.currently.precipProbability}} &nbsp;&nbsp;&nbsp;<i class="wi wi-wind wi-towards-{{windDirectionClass}}"></i> {{roundNumber weatherData.data.currently.windSpeed}}mph {{windDirection}}
<!-- TODO: Change the icon based on time -->
<br /><br /><i class="wi wi-sunset"></i> {{formatTimestamp weatherData.data.currently.time}}
</div>
</div>
</div>
</div>
<div class="hourly-table mdl-cell mdl-cell--6-col mdl-shadow--4dp table-full-width">
<table class="mdl-data-table mdl-js-data-table">
<thead>
<tr>
<th class="mdl-data-table__cell--non-numeric table-full-width">Hourly Forcast</th>
<th class="mdl-data-table__cell--non-numeric">Condition</th>
<th class="mdl-data-table__cell--non-numeric"><i class="wi wi-fahrenheit"></i></th>
<th class="mdl-data-table__cell--non-numeric">Precipitation</th>
<th class="mdl-data-table__cell--non-numeric">Wind</th>
</tr>
</thead>
<tbody>
{{#each hourWeather in getNext12 weatherData.data.hourly.data}}
<tr>
<td class="mdl-data-table__cell--non-numeric">{{formatTimestampToHour hourWeather.time}}</td>
<td class="mdl-data-table__cell--non-numeric"><i class="wi {{convertIconName hourWeather.icon}} wi-fw"></i> {{hourWeather.summary}}</td>
<td>{{roundNumber hourWeather.temperature}}<i class="wi wi-fahrenheit"></i></td>
<td>{{roundNumber hourWeather.precipProbability}}%</td>
<td>{{roundNumber hourWeather.windSpeed}}mph</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
{{#each dayWeather in weatherData.data.daily.data}}
<div class="mdl-cell mdl-cell--4-col">
<div class="{{getColorStyle (roundNumber dayWeather.temperatureMax)}} weather-card mdl-card mdl-shadow--4dp">
<div class="mdl-card__title">
<h1 class="mdl-card__title-text">{{getDayNameFromTime dayWeather.time}}<br /></h1>
</div>
<div class="mdl-card__supporting-text">
<h5>{{dayWeather.summary}}</h5>
<div class="oof-size">
<i class="wi {{convertIconName dayWeather.icon}} wi-fw"></i> {{roundNumber dayWeather.temperatureMax}}<i class="wi wi-fahrenheit"></i> / {{roundNumber dayWeather.temperatureMin}}<i class="wi wi-fahrenheit"></i>
</div>
<div class="detail-size">
<br />
<i class="wi wi-{{precipitationIcons dayWeather.precipProbability}}"></i> {{precipitationWords dayWeather.precipProbability}}
<br /><br />
</div>
</div>
</div>
</div>
{{/each}}
</div>
</template>
Template.weather.helpers({
weatherData: function() {
weatherDataDependency.depend();
if(weatherData === undefined) return "...";
return weatherData;
},
//Convert precipitation percentage to words
precipitationWords: function(precipProb) {
weatherDataDependency.depend();
if(weatherData === undefined) return "...";
if(precipProb === 0) return "No Rain Expected";
if(precipProb > 50 && precipProb < 95) return "Potential for Rain";
if(precipProb > 95) return "Bring an Umbrella";
if(precipProb > 100) return "Wat."; //Wait.
return precipProb+"% Precipitation"; // Otherwise, return the percentage
},
//Convert precipitation percentage to icons
precipitationIcons: function(precipProb) {
weatherDataDependency.depend();
if(weatherData === undefined) return "...";
if(precipProb === 0) return "cloud";
if(precipProb > 50 && precipProb < 95) return "showers";
if(precipProb > 95) return "umbrella";
if(precipProb > 100) return "alien"; //Wait.
return precipProb+"% Precipitation"; // Otherwise, return the percentage
},
//Converts degrees to words
windDirection: function() {
weatherDataDependency.depend();
if(weatherData === undefined) return "...";
return degreesToWords(weatherData.data.currently.windBearing);
},
//Gets the class name to be used by the wind icon
windDirectionClass: function() {
weatherDataDependency.depend();
if(weatherData === undefined) return "...";
return degreesToWords(weatherData.data.currently.windBearing).toLowerCase();
},
//Rounds a number
roundNumber: function(number) {
if(number === undefined) return "...";
return Math.round(number);
},
//Formats a unix timestamp a full human readable time
formatTimestamp: function(timestamp) {
if(timestamp === undefined) return "...";
var d = new Date(timestamp*1000);
console.log("GOT: "+timestamp)
return d.toLocaleString("en-us", { hour: 'numeric', minute: 'numeric', timeZoneName:'short'});
},
//Formats unix time to just a 12 hour time
formatTimestampToHour: function(timestamp) {
if(timestamp === undefined) return "...";
var d = new Date(timestamp*1000);
console.log("GOT: "+timestamp)
return d.toLocaleString("en-us", { hour: 'numeric'});
},
//Converts icon names from darksky to those that can be used by our css library
convertIconName: function(apiIcon) {
var iconMap = {
"clear-day": "wi-day-sunny",
"clear-night": "wi-night-clear",
"rain": "wi-rain",
"snow": "wi-snow",
"sleet": "wi-sleet",
"wind": "wi-strong-wind",
"fog": "wi-fog",
"cloudy": "wi-cloudy",
"partly-cloudy-day": "wi-day-cloudy",
"partly-cloudy-night": "wi-night-cloudy"
}
//It is possible for this to be undefined but it should be ok to show no icon
var icon = iconMap[apiIcon];
return icon;
},
//Returns current unix time
currentLinuxTime: function() {
return (new Date).getTime()/1000;
},
//Used to check if a unix timestamp is in the future
timeAfterNow: function(time) {
return time>((new Date).getTime()/1000);
},
//This is used to trim the hourly data array down to the next 12 hours since it return 2 days worth of data
getNext12: function(hourDataArray) {
//I'm sure this can be cleaned but. oh well.
var startIndex = 0;
for(var i = 0; i < hourDataArray.length; i++) {
if(hourDataArray[i].time > ((new Date).getTime()/1000)) {
startIndex = i;
break;
}
}
debugger;
return hourDataArray.slice(startIndex, startIndex+12)
},
getDayNameFromTime: function(timestamp) {
var given = new Date(timestamp);
var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
return days[ given.getDay() ];
},
getColorStyle: function(temp) {
if(temp < 10) {
return "degree0";
}
else if(temp >= 10 && temp < 20) {
return "degree10";
}
else if(temp >= 20 && temp < 30) {
return "degree20";
}
else if(temp >= 30 && temp < 40) {
return "degree30";
}
else if(temp >= 40 && temp < 50) {
return "degree40";
}
else if(temp >= 50 && temp < 60) {
return "degree50";
}
else if(temp >= 60 && temp < 70) {
return "degree60";
}
else if(temp >= 70 && temp < 80) {
return "degree70";
}
else if(temp >= 80 && temp < 90) {
return "degree80";
}
else if(temp >= 90 && temp < 100) {
return "degree90";
}
else if(temp >= 100) {
return "degree100";
}
else {
Console.log(temp);
return temp;
}
}
});
var weatherData = {};
var weatherDataDependency = new Tracker.Dependency;
Template.weather.onCreated(function(){
Session.set("locationName", "FAIRFAX");
Tracker.autorun(function () {
var locName = Session.get("locationName");
var location = LOCATIONS[locName];
Meteor.call('getWeather', location.lat, location.long, function(error, result) {
weatherData = result;
document.title = "SRCT Weather • "+Math.round(result.data.currently.temperature)+"° F"
weatherDataDependency.changed();
});
});
});
//Converts the bearing to a compass direction
function degreesToWords(d) {
if (typeof d !== 'number' || isNaN(d)) {
return -1;
}
// keep within the range: 0 <= d < 360
d = d % 360;
if (11.25 <= d && d < 33.75) {
return "NNE";
} else if (33.75 <= d && d < 56.25) {
return "NE";
} else if (56.25 <= d && d < 78.75) {
return "ENE";
} else if (78.75 <= d && d < 101.25) {
return "E";
} else if (101.25 <= d && d < 123.75) {
return "ESE";
} else if (123.75 <= d && d < 146.25) {
return "SE";
} else if (146.25 <= d && d < 168.75) {
return "SSE";
} else if (168.75 <= d && d < 191.25) {
return "S";
} else if (191.25 <= d && d < 213.75) {
return "SSW";
} else if (213.75 <= d && d < 236.25) {
return "SW";
} else if (236.25 <= d && d < 258.75) {
return "WSW";
} else if (258.75 <= d && d < 281.25) {
return "W";
} else if (281.25 <= d && d < 303.75) {
return "WNW";
} else if (303.75 <= d && d < 326.25) {
return "NW";
} else if (326.25 <= d && d < 348.75) {
return "NNW";
} else {
return "N";
}
};
WeatherData = new Meteor.Collection("weatherdata");
if(Meteor.isClient) {
Meteor.subscribe("weatherdata");
}
if(Meteor.isServer) {
Meteor.publish("weatherdata", function () {
return WeatherData.find();
});
WeatherData.deny({
//No one is allowed to insert but server
insert: function () {
return false;
}
});
}
import { Mongo } from 'meteor/mongo';
export const WeatherData = new Mongo.Collection('WeatherDatas');
WeatherData.schema = new SimpleSchema({
name: {
type: String,
label: "The name of the GMU Campus"
},
lat: {
type: Number,
label: "The latitude of the GMU Campus"
},
long: {
type: Number,
label: "The logiute of the GMU Campus"
},
data: {
type: Object,
label: "The most current weather entry for this location"
},
lastUpdated: {
type: Date,
label: "The last time that this campus's current forecast was updated"
}
})
Meteor.methods({
'weatherDataForLoc': function (lat, long) {
// API_KEY is an environmental veriable, you can set it with a JS file in
// server/lib or in your server control panel.
var API_KEY = process.env.API_KEY;
var apiURL = 'https://api.forecast.io/forecast/' + API_KEY + '/' + lat + ',' + long;
var response = HTTP.get(apiURL).data;
return response;
}
});
export function refreshData() {
for(var key in LOCATIONS) {
const current = LOCATIONS[key];
var currentData
Meteor.call('weatherDataForLoc', current.lat, current.long, function(err, res){
currentData = res
});
/**
WeatherData.insert ({
name: current.name,
lat: current.lat,
long: current.long,
data: currentData,
lastUpdated: new Date(),
});
*/