diff --git a/controller/404.go b/controller/404.go deleted file mode 100644 index 702b912..0000000 --- a/controller/404.go +++ /dev/null @@ -1,16 +0,0 @@ -package controller - -import ( - //"encoding/json" - "fmt" - "github.com/julienschmidt/httprouter" - "net/http" -) - -// IndexGET displays the home page -func NotFound404(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - - http.Redirect(w, r, "/404.html", 404) - - fmt.Fprint(w, "test") -} diff --git a/controller/api/testApi.go b/controller/api/testApi.go new file mode 100644 index 0000000..0ba6975 --- /dev/null +++ b/controller/api/testApi.go @@ -0,0 +1,29 @@ +package api + +import ( + "encoding/json" + "fmt" + "github.com/julienschmidt/httprouter" + "net/http" +) + +type ApiCall struct { + Fname string + Lname string +} + +// Redirect to discord +func TestApiCall(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + + s := new(ApiCall) + + s.Fname = ps.ByName("fname") + s.Lname = ps.ByName("lname") + + response, _ := json.MarshalIndent(s, "", " ") + + w.Header().Set("Content-Type", "application/json") + + fmt.Fprint(w, string(response)) + +} diff --git a/controller/index.go b/controller/index.go deleted file mode 100644 index a10dced..0000000 --- a/controller/index.go +++ /dev/null @@ -1,18 +0,0 @@ -package controller - -import ( - //"encoding/json" - "fmt" - "github.com/julienschmidt/httprouter" - "net/http" -) - -// IndexGET displays the home page -func IndexGet(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - - w.Header().Set("Content-Type", "application/json") - response := "{\"test\" : 123}" - - fmt.Fprint(w, response) - -} diff --git a/controller/redirect.go b/controller/redirect.go new file mode 100644 index 0000000..54ad480 --- /dev/null +++ b/controller/redirect.go @@ -0,0 +1,13 @@ +package controller + +import ( + "github.com/julienschmidt/httprouter" + "net/http" +) + +// Redirect to discord +func Discord(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + + http.Redirect(w, r, "https://discordapp.com/invite/0Z2tzxKECEj2BHwj", 301) + +} diff --git a/public/404.html b/public/404.html deleted file mode 100644 index bca257a..0000000 --- a/public/404.html +++ /dev/null @@ -1 +0,0 @@ -404: Page not found \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..0feff06 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,330 @@ +html { + overflow-y: scroll; +} + +body { + font-family: Century Gothic,CenturyGothic,AppleGothic,sans-serif; +} + +a { + color: #00B7FF; +} + +.textCenter { + text-align: center; +} + +.resume{ + font-size: 12pt; + font-weight: 400; +} +.header { + height: 400px; + background: url("/public/images/headerBackground.jpg") center no-repeat; + background-size: cover; +} + +.headerText{ + font-size: 80px; + color: white; + margin-top: 0px; +} + +.large-text{ + font-size: 100px; +} + +.centerDiv{ + position: relative; + top: 50%; + transform: translateY(-50%); +} + +.form-signin { + max-width: 500px; + padding: 15px; + margin: 0 auto; +} +.form-signin .form-control { + position: relative; + height: auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 10px; + font-size: 16px; +} + +.form-file { + max-width: 500px; + padding: 15px; + margin: 0 auto; +} +.form-file .form-control { + position: relative; + height: 34px; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 10px; + font-size: 16px; +} + +.colorRed { + color: red; +} +.colorGreen { + color: green; +} + +.timeForm { + width: 30%; + display: inline; +} + +.aboutMeImage { + width: 100%; + height: 100%; +} + +.center { + margin-left: auto; + margin-right: auto; + display: block; +} + +/* + * Override Bootstrap's default container. + */ + +@media (min-width: 1200px) { + .container { + width: 970px; + } +} + + +/* + * Masthead for nav + */ + +.blog-masthead { + background-color: #428bca; + -webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); + box-shadow: inset 0 -2px 5px rgba(0,0,0,.1); +} + +/* Nav links */ +.blog-nav-item { + position: relative; + display: inline-block; + padding: 10px; + font-weight: 500; + color: #cdddeb; +} +.blog-nav-item:hover, +.blog-nav-item:focus { + color: #fff; + text-decoration: none; +} + +/* Active state gets a caret at the bottom */ +.blog-nav .active { + color: #fff; +} +.blog-nav .active:after { + position: absolute; + bottom: 0; + left: 50%; + width: 0; + height: 0; + margin-left: -5px; + vertical-align: middle; + content: " "; + border-right: 5px solid transparent; + border-bottom: 5px solid; + border-left: 5px solid transparent; +} + + +/* + * Blog name and description + */ + +.blog-header { + padding-top: 20px; + padding-bottom: 20px; +} +.blog-title { + margin-top: 30px; + margin-bottom: 0; + font-size: 60px; + font-weight: normal; +} +.blog-description { + font-size: 20px; + color: #999; +} + + +/* + * Main column and sidebar layout + */ + +.blog-main { + font-size: 18px; + line-height: 1.5; +} + +/* Sidebar modules for boxing content */ +.sidebar-module { + padding: 15px; + margin: 0 -15px 15px; +} +.sidebar-module-inset { + padding: 15px; + background-color: #f5f5f5; + border-radius: 4px; +} +.sidebar-module-inset p:last-child, +.sidebar-module-inset ul:last-child, +.sidebar-module-inset ol:last-child { + margin-bottom: 0; +} + + +/* Pagination */ +.pager { + margin-bottom: 60px; + text-align: left; +} +.pager > li > a { + width: 140px; + padding: 10px 20px; + text-align: center; + border-radius: 30px; +} + + +/* + * Blog posts + */ + +.blog-post { + margin-bottom: 60px; +} +.blog-post-title { + margin-bottom: 5px; + font-size: 40px; +} +.blog-post-meta { + margin-bottom: 20px; + color: #999; +} + + +/* + * Footer + */ + +.blog-footer { + padding: 40px 0; + color: #999; + text-align: center; + background-color: #f9f9f9; + border-top: 1px solid #e5e5e5; +} +.blog-footer p:last-child { + margin-bottom: 0; +} + + +.lowerLeft{ + color: white; + display: table-cell; + vertical-align: bottom; + height: 400px; + width: 800px; +} + +.colorWhite{ + color: white; +} + +.mitchell-navbar{ + border-radius: 0 !important; + background-color: #262626; + border: 0; +} + +.gist { + width:600px !important; +} + +.gist-file{ + +} + +.gist-data{ + max-height: 500px; + max-width: 600px; +} + +.chart-legend li span{ + display: inline-block; + width: 12px; + height: 12px; + margin-right: 5px; +} + +.modal { + display: none; + position: fixed; + z-index: 1000; + top: 0; + left: 0; + height: 100%; + width: 100%; + background: rgba( 255, 255, 255, .8 ) + url('http://i.stack.imgur.com/FhHRx.gif') + 50% 50% + no-repeat; +} + +/* When the body has the loading class, we turn + the scrollbar off with overflow:hidden */ +body.loading { + overflow: hidden; +} + +/* Anytime the body has the loading class, our + modal element will be visible */ +body.loading .modal { + display: block; +} + +.btn-file { + position: relative; + overflow: hidden; +} + +.btn-file input[type=file] { + position: absolute; + top: 0; + right: 0; + min-width: 100%; + min-height: 100%; + font-size: 100px; + text-align: right; + filter: alpha(opacity=0); + opacity: 0; + outline: none; + background: white; + cursor: inherit; + display: block; +} + +input[readonly] { + background-color: white !important; + cursor: text !important; +} \ No newline at end of file diff --git a/public/images/aboutme.jpg b/public/images/aboutme.jpg new file mode 100644 index 0000000..261599d Binary files /dev/null and b/public/images/aboutme.jpg differ diff --git a/public/images/headerBackground.jpg b/public/images/headerBackground.jpg new file mode 100644 index 0000000..352000e Binary files /dev/null and b/public/images/headerBackground.jpg differ diff --git a/public/images/headerBackground1.jpg b/public/images/headerBackground1.jpg new file mode 100644 index 0000000..6270e9e Binary files /dev/null and b/public/images/headerBackground1.jpg differ diff --git a/public/images/sensors.png b/public/images/sensors.png new file mode 100644 index 0000000..5f406b8 Binary files /dev/null and b/public/images/sensors.png differ diff --git a/public/js/app.js b/public/js/app.js new file mode 100644 index 0000000..347c920 --- /dev/null +++ b/public/js/app.js @@ -0,0 +1,49 @@ +var app = angular.module('app', ['ngRoute']); + +app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { + $locationProvider.html5Mode({ + enabled: true, + requireBase: false + }); + + $routeProvider. + when('/', { + templateUrl: '/public/view/index.html', + controller: 'IndexController' + }). + when('/sensors', { + templateUrl: '/public/view/404.html', + controller: 'SensorsController' + }). + when('/post/:postName', { + templateUrl: '/public/view/post.html', + controller: 'PostController' + }). + when('/discord', { + redirect: 'https://discordapp.com/invite/0Z2tzxKECEj2BHwj' + }). + otherwise({ + templateUrl: '/public/view/404.html' + }); + }]); + +app.controller('IndexController', function($scope) { + + $scope.message = 'This is Add new order screen'; + +}); + + +app.controller('SensorsController', function($scope) { + + $scope.message = 'This is Show orders screen'; + +}); + + +//handle each post page after individual posts are selected +app.controller('PostController', function($scope, $route, $routeParams) { + + $scope.post = "/public/posts/" + $routeParams.postName + ".html"; + +}); \ No newline at end of file diff --git a/public/posts/1-1-2016.html b/public/posts/1-1-2016.html new file mode 100644 index 0000000..c8be7ab --- /dev/null +++ b/public/posts/1-1-2016.html @@ -0,0 +1,55 @@ +
+

Temperature Sensor - Server Side

+ +

January 1, 2016 by Mitchell

+ +

The server side coding is a bit more complicated than programming the ESP8266 itself. I use a NoSQL database to store the information and some of the queries are complex and can be confusing to understand. I also make use of REST API's to send the data to the client side. +

+ +
+ +

Storing Information

+ +

For this project I am using MongoDB to store all of my information because it goes well with my server, which is coded in Node.js, but any database could be used. I also wanted to learn about NoSQL databases because I already have experience with SQL databases. Information on coding the ESP8266 can be found here in a previous post. +

+ + mitchellg.me/temperature?temperature=0&humidity=0&location=0&key=0 + +

Above is the GET request that is sent to the server from the ESP8266. Data is tranferred through GET parameters. The key is an authentication code that I set to prevent unwanted HTTP requests. This is similar to an API key. Below is the code implemented to handle the HTTP request from the ESP8266. +

+ + + +

Creating the REST API

+ +

There are many ways that I could display the temperature information on a graph, but I was trying to come up with a quick and easy way that was also efficient. I also wanted reusability in case I wanted to add or change things down the road. I decided I was going to use Chart.js because it is open source and free. These graphs are implemented on the front end using javascript. Because of this I needed to figure out a way to send the sensor data to the client side. I felt that the best way for me to do it would be creating a REST API. I plan on making another post in the future explaining all of my client side code along with how to use Chart.js. +

+ +

Now that I know how I want to display my information I just need to think of what information I want to display. I thought it would be cool to display a few different graphs. As of right now I have one graph that displays data by the year and one that displays by each month. The maximum and minimum temperature of each day are displayed as well as the average humidity for that day. The user can also select which year or which month to display and the graph will adjust accordingly. +

+ +

Now that I know what information I need I can start developing my REST API. I have a MongoDB collection called "temperature", which stores temperature, humidity, location, and time updated. Updated is a type of Date, which I will use in all of the queries to group by each day. To do the grouping in MongoDB I needed to use the aggregation functionality of MongoDB. Aggregation allows me to essentially perform queries on top of queries using the MongoDB "pipeline". This is similar to an SQL query when a selection is performed within a selection. +

+ + + +

Explanation of MongoDB Queries

+ +

The first function takes in a location and year and returns maximum and minimum temperature readings as well as average humidity for each day in the selected year. The results are also returned sorted from newest to oldest. +

+ +

In the first part of the aggregation pipeline, which is $project, I am just selecting the temperature, humidity, year, month, and day. This part is important because it allows me to pull the year, month, and day from the date that is stored in the database. This way I do not have to store each of these entries separately in the database. +

+ +

This data is then passed to the next part of the aggregation pipeline. The $match pipeline stage selects out information from the collection which match with the selected year and location. +

+ +

The $group aggregation operator is the stage in which I actually group the data by the location, year, month, and day. For each group I also take the max and min temperatures along with the average humidity by using the correct accumulator operators. Now that I have the appropriate information I need, I just use the $sort pipeline operator to sort the data based on time updated. +

+ +

The function to display by month is the exact same, except I take in the month attribute and add it to the $match operator within the aggregation pipeline. Just like that the query is complete and all I need to do is send the response back to the client. Chart.js uses JSON format, which makes things extremely easy because MongoDB querie results are in JSON. I set the content type to JSON, and use JSON.stringify() to convert the JSON to a readable format for debugging purposes. An example API request can be tested out here. +

+ +

/api/sensorbylocation/year?location=Winona%20Apartment&year=2016 +

+
\ No newline at end of file diff --git a/public/test.html b/public/test.html deleted file mode 100644 index 30d74d2..0000000 --- a/public/test.html +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file diff --git a/public/view/404.html b/public/view/404.html new file mode 100644 index 0000000..089ddce --- /dev/null +++ b/public/view/404.html @@ -0,0 +1,23 @@ + +
+
+
+
+
+ +

404

+

This page may be in production, or it may not exist.

+ +
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/view/index.html b/public/view/index.html new file mode 100644 index 0000000..2b74c00 --- /dev/null +++ b/public/view/index.html @@ -0,0 +1,25 @@ +
+ +
+
+ +
+ +
+

About Me

+ + +
+ + + +
+ +
+ +
\ No newline at end of file diff --git a/public/view/post.html b/public/view/post.html new file mode 100644 index 0000000..0e20b5c --- /dev/null +++ b/public/view/post.html @@ -0,0 +1,27 @@ +
+ +
+
+ + + +
+ +
+

About Me

+ + +
+ + + +
+ +
+ +
\ No newline at end of file diff --git a/public/view/template.html b/public/view/template.html new file mode 100644 index 0000000..bf2a26d --- /dev/null +++ b/public/view/template.html @@ -0,0 +1,100 @@ + + + + + mitchel.io + + + + + + + + + + + +
+ +
+ +
+

mitchel.io

+

A site in which I share information about my personal projects.

+
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..6860c0f --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +## Converting to Go back end and Angular front end diff --git a/route/route.go b/route/route.go index 3961d06..cc04114 100644 --- a/route/route.go +++ b/route/route.go @@ -1,24 +1,42 @@ package route import ( - //"encoding/json" - //"fmt" "github.com/julienschmidt/httprouter" "net/http" - "github.com/mgerb42/mywebsite/controller" + "github.com/mgerb42/mywebsite/controller/api" ) func Routes() *httprouter.Router { r := httprouter.New() - r.GET("/", controller.IndexGet) + r.GET("/api/:fname/:lname", api.TestApiCall) //set up public folder path r.ServeFiles("/public/*filepath", http.Dir("./public")) - //404 not found - r.NotFound = http.NotFoundHandler() + //route every invalid request to template file + //routing is all handled on the client side with angular + r.NotFound = http.HandlerFunc(fileHandler("./public/view/template.html")) + return r } + +//route requests to static files +func routerFileHandler(path string) httprouter.Handle { + return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + + http.ServeFile(w, r, path) + + } +} + +//function to serve files with standard net/http library +func fileHandler(path string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + + http.ServeFile(w, r, path) + + } +} diff --git a/testing_files/404.go b/testing_files/404.go new file mode 100644 index 0000000..ea39c34 --- /dev/null +++ b/testing_files/404.go @@ -0,0 +1,12 @@ +package controller + +import ( + "net/http" +) + +// IndexGET displays the home page +func PageNotFound(w http.ResponseWriter, r *http.Request) { + + http.ServeFile(w, r, "./public/404.html") + +} diff --git a/testing_files/index.go b/testing_files/index.go new file mode 100644 index 0000000..34da4e2 --- /dev/null +++ b/testing_files/index.go @@ -0,0 +1,77 @@ +package controller + +import ( + "encoding/json" + "fmt" + "github.com/julienschmidt/httprouter" + "net/http" +) + +type Person struct { + Location City + Name string + Age int + Car []Car +} + +type City struct { + Coords Coordinate + Population int +} + +type Coordinate struct { + Latitude int + Longitude int +} + +type Car struct { + Make string + Year int +} + +// IndexGET displays the home page +func IndexGet(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + + params := p.ByName("test") + fmt.Println(params) + + w.Header().Set("Content-Type", "application/json") + + js := Person{ + Location: City{ + Coords: Coordinate{ + Latitude: 23, + Longitude: 32, + }, + Population: 5000, + }, + Name: "Mitchell", + Age: 22, + Car: []Car{ + Car{ + Make: "Mitz", + Year: 2003, + }, + Car{ + Make: "Honda", + Year: 2016, + }, + }, + } + + b, err := json.MarshalIndent(js, "", " ") + + if err != nil { + fmt.Println(err.Error) + } + + s := string(b) + + fmt.Fprint(w, s) + +} + +func Api(w http.ResponseWriter, r *http.Request, p httprouter.Params) { + param := p.ByName("name") + fmt.Fprint(w, param) +} diff --git a/testing_files/redirect.go b/testing_files/redirect.go new file mode 100644 index 0000000..54ad480 --- /dev/null +++ b/testing_files/redirect.go @@ -0,0 +1,13 @@ +package controller + +import ( + "github.com/julienschmidt/httprouter" + "net/http" +) + +// Redirect to discord +func Discord(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { + + http.Redirect(w, r, "https://discordapp.com/invite/0Z2tzxKECEj2BHwj", 301) + +}