diff --git a/db3.js b/db3.js new file mode 100644 index 0000000..3fec2aa --- /dev/null +++ b/db3.js @@ -0,0 +1,64 @@ +"use strict"; + +var MongoClient = require('mongodb').MongoClient; +var ObjectId = require('mongodb').ObjectID; +var assert = require('assert'); +var url = 'mongodb://localhost:27017/gstv'; +var db; +MongoClient.connect(url, function(err, my_db) { + assert.equal(null, err); + db = my_db; +}); + +var db_module = (function() { + function db_module() { + this.process_query = function(query, callback) { + db.collection(query.collection, function (error, objects) { + if (error) { + console.error(error); + return; + } + if (query.object._id && typeof query.object._id != "object") { + query.object._id = new ObjectId(query.object._id); + } + if (query.object.foreignId && typeof query.object.foreignId != "object") { + query.object.foreignId = new ObjectId(query.object.foreignId); + } + if (query.verb == "post") { + objects.save(query.object, {safe: true}, function (error, result) { + if (error) { + console.log(error); + } + var data = result, response = {data: data}; + callback(null, response); + }); + } + else if (query.verb == "get") { + objects.find(query.object, function (error, result) { + if (error) { + console.error(error); + return; + } + result.toArray(function (error, results) { + var data = results, response = {data: data}; + callback(null, response); + }); + }); + } + else if (query.verb == "put") { + objects.save(query.object, {safe: true}, function (error, result) { + if (error) { + console.error(error); + } + var data = result, response = {data: data}; + callback(null, response); + }); + } + }); + } + } + + return db_module; +})(); + +module.exports = db_module; \ No newline at end of file diff --git a/models/time.js b/models/time.js new file mode 100644 index 0000000..8d633b6 --- /dev/null +++ b/models/time.js @@ -0,0 +1,17 @@ +var time = (function () { + //timezone + //daylight savings time + function time(value_start, value_end, day, time_start, time_end, is_24, owner_id, deleted, _id) { + this.value_start = value_start; + this.value_end = value_end; + this.day = day; + this.time_start = time_start; + this.time_end = time_end; + this.is_24 = is_24; + this.owner_id = owner_id.toString(); + this.deleted = deleted ? deleted : null; + this._id = _id; + } + return time; +})(); +exports.time = time; diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..35f9650 --- /dev/null +++ b/notes.txt @@ -0,0 +1,22 @@ +Notes: + +This took me a bit longer than I expected. I had not used Mongodb since discovering firebase(really awesome, check it out) over a year ago +and had to update mongodb, how I connected to it and my node version as well. The last time I was using mongodb was with socket io and redis +trying to make something similar to firebase, I had it mostly working but shortly after found firebase. If you wish I can walk you through that +project as well. I tried to stay focused on the backend, but the frontend needed a lot of functionality. Some is copy and paste from previous projects +so not all is es6 perfect. Obviously there is a lot that could be done, optimizations, unit testing, better ui, bring in mongoose, etc, but I hope +this demonstrates some of my abilities. Everything is working on my end, so please let me know if you have any issues. + + +cd C:\Users\Tim\websitesWorking\GSTV\node-coding-exercise +node server + +if on another pc, move mongodb install to this location, copy paste. +C:\mongodb\Server\3.2\bin\mongod + + + + + + + diff --git a/package.json b/package.json index 74d77ee..f9054f9 100644 --- a/package.json +++ b/package.json @@ -18,5 +18,15 @@ "bugs": { "url": "https://github.com/GasStationTV/node-coding-exercise/issues" }, - "homepage": "https://github.com/GasStationTV/node-coding-exercise#readme" + "homepage": "https://github.com/GasStationTV/node-coding-exercise#readme", + "dependencies": { + "assert": "^1.3.0", + "co-body": "^4.0.0", + "koa": "^1.1.2", + "koa-router": "^5.3.0", + "koa-send": "^3.1.0", + "mongod": "^1.3.0", + "path": "^0.12.7", + "validate.js": "^0.9.0" + } } diff --git a/public/components_riot/layout/layout.js b/public/components_riot/layout/layout.js new file mode 100644 index 0000000..9a55b2b --- /dev/null +++ b/public/components_riot/layout/layout.js @@ -0,0 +1,7 @@ +riot.tag2('layout', '

Time Schedule

', 'layout h1,[riot-tag="layout"] h1{ margin: 10px 0 0 0; }', '', function(opts) { +"use strict"; + +var owner_id = 1; + +this.on('mount', function () {}); +}, '{ }'); \ No newline at end of file diff --git a/public/components_riot/layout/layout.tag b/public/components_riot/layout/layout.tag new file mode 100644 index 0000000..14a6794 --- /dev/null +++ b/public/components_riot/layout/layout.tag @@ -0,0 +1,35 @@ + + +

Time Schedule

+ + + + + +
\ No newline at end of file diff --git a/public/components_riot/time_form/time_form.js b/public/components_riot/time_form/time_form.js new file mode 100644 index 0000000..fe04942 --- /dev/null +++ b/public/components_riot/time_form/time_form.js @@ -0,0 +1,111 @@ +riot.tag2('time_form', '
Open 24hrs!
', 'time_form .hidden,[riot-tag="time_form"] .hidden{ display: none; }', '', function(opts) { +"use strict"; + +var _this = this; + +var time_start = undefined; +var time_end = undefined; + +var is_24_elem = undefined; +this.is_24 = this.opts.is_24 ? true : false; + +function validate_form(time_start_value, time_end_value) { + var message = undefined; + if (time_start_value >= time_end_value) { + message = 'Unable to Create/Update: The start time must be before the end time. '; + } + if (time_start_value === 0) { + message += 'Unable to Create/Update: start time is required. '; + } + if (time_end_value === 0) { + message += 'Unable to Create/Update: end time is required. '; + } + + return message ? message : true; +} + +this.submit_form = function (event) { + var is_valid = undefined; + event.preventDefault(); + is_valid = validate_form(parseInt(time_start.value), parseInt(time_end.value)); + if (is_valid === true) { + _this.error_message.style.display = 'none'; + _this.opts.submit_callback(_this.calculate_time_value({ time_start: time_start.options[time_start.selectedIndex].value, + time_end: time_end.options[time_end.selectedIndex].value, + day: _this.opts.day, + is_24: _this.is_24, + _id: _this.opts._id })); + } else { + _this.error_message.style.display = 'block'; + _this.error_message.innerText = is_valid; + } +}; +this.checked_changed = function (event) { + _this.is_24 = is_24_elem.checked; + _this.update(); +}; +this.delete_time = function (event) { + _this.opts.delete_callback({ time_start: time_start.selectedIndex != -1 ? time_start.options[time_start.selectedIndex].value : 1, + time_end: time_end.selectedIndex != -1 ? time_end.options[time_end.selectedIndex].value : 1, + day: _this.opts.day, + is_24: _this.is_24, + _id: _this.opts._id }); +}; +this.on('mount', function () { + setTimeout(function () { + if (_this.opts._id) { + time_start = document.getElementById('time_start' + _this.opts._id); + time_end = document.getElementById('time_end' + _this.opts._id); + + is_24_elem = document.getElementById('is_24_check' + _this.opts._id); + time_start.value = _this.opts.time_start; + time_end.value = _this.opts.time_end; + } else { + time_start = _this.time_start; + time_end = _this.time_end; + } + }, 1); +}); + +this.calculate_time_value = function (time_to_add) { + var value = 0, + value_start = 0, + value_end = 0; + switch (time_to_add.day) { + case "1": + value = 0; + break; + case "2": + value = 48; + break; + case "3": + value = 96; + break; + case "4": + value = 144; + break; + case "5": + value = 192; + break; + case "6": + value = 240; + break; + default: + value = 288; + } + if (time_to_add.day === "7") { + value_start = value + parseInt(time_to_add.time_start); + if (parseInt(time_to_add.time_end) > 48) { + value_end = parseInt(time_to_add.time_end); + } else { + value_end = value + parseInt(time_to_add.time_end); + } + } else { + value_start = value + parseInt(time_to_add.time_start); + value_end = value + parseInt(time_to_add.time_end); + } + time_to_add.value_start = value_start; + time_to_add.value_end = value_end; + return time_to_add; +}; +}, '{ }'); \ No newline at end of file diff --git a/public/components_riot/time_form/time_form.tag b/public/components_riot/time_form/time_form.tag new file mode 100644 index 0000000..9f90bdc --- /dev/null +++ b/public/components_riot/time_form/time_form.tag @@ -0,0 +1,259 @@ + + + +
+ + + + + + + + + + + + + + + + + + + Open 24hrs! + + + + +
+ + + + +
\ No newline at end of file diff --git a/public/components_riot/times_added/times_added.js b/public/components_riot/times_added/times_added.js new file mode 100644 index 0000000..ffcb2a6 --- /dev/null +++ b/public/components_riot/times_added/times_added.js @@ -0,0 +1,156 @@ +riot.tag2('times_added', '

Sunday

Monday

Tuesday

Wednesday

Thursday

Friday

Saturday

', 'times_added h2,[riot-tag="times_added"] h2{ margin-bottom:0; } times_added .hidden,[riot-tag="times_added"] .hidden{ display: none; }', '', function(opts) { +"use strict"; + +var _this = this; + +var self = this; +var owner_id = 1; +this.times = []; + +this.expand = function (event) { + var container = event.target.parentNode.querySelector('.container'); + event.target.classList.add('hidden'); + container.classList.remove('hidden'); +}; +this.close = function (event) { + var container = event.target.parentNode; + container.classList.add('hidden'); + event.target.parentNode.parentNode.querySelector('input.hidden').classList.remove('hidden'); +}; + +function validate_day(day, times, value_start, value_end) { + var my_array = times.find(function (time) { + if (time.day !== day && (time.value_start > value_end && time.value_start < value_start || time.value_end > value_end && time.value_end < value_start)) return time.day !== day && (time.value_start > value_end && time.value_start < value_start || time.value_end > value_end && time.value_end < value_start) ? true : false; + }); + return !my_array || my_array.length == 0 ? true : false; +} + +this.process_response = function (event) { + if (event.target.readyState == 4 && event.target.status == 200) { + var response = JSON.parse(event.target.responseText); + if (response.error) { + alert('Cannot add/update time: ' + response.error); + } else { + _this.get_times(owner_id); + } + } +}; + +this.time_added_24 = function (event) { + var xmlHttp = new XMLHttpRequest(); + var day = event.target.dataset.day; + var value = undefined, + is_valid = undefined, + day_name = undefined; + switch (day) { + case "1": + value = 0;day_name = 'sunday'; + break; + case "2": + value = 48;day_name = 'monday'; + break; + case "3": + value = 96;day_name = 'tuesday'; + break; + case "4": + value = 144;day_name = 'wednesday'; + break; + case "5": + value = 192;day_name = 'thursday'; + break; + case "6": + value = 240;day_name = 'friday'; + break; + default: + value = 288;day_name = 'saturday'; + } + if (validate_day(day, _this.times, value, value + 48)) { + _this[day_name].forEach(function (time) { + _this.time_deleted(time); + }); + + setTimeout(function () { + xmlHttp.onreadystatechange = _this.process_response; + xmlHttp.open("POST", "/owners/" + owner_id + "/times/"); + xmlHttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xmlHttp.send(JSON.stringify({ time_start: 1, + time_end: 1, + day: day, + is_24: true, + value_start: value, + value_end: value + 48, + _id: null })); + }, 500); + } else { + alert('Unable to Create/Update: there is at least one overlapping time slot'); + } +}; +this.time_added = function (form) { + var xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = _this.process_response; + xmlHttp.open("POST", "/owners/" + owner_id + "/times/"); + xmlHttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xmlHttp.send(JSON.stringify(form)); +}; +this.time_edited = function (form) { + form.owner_id = owner_id; + var xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = _this.process_response; + xmlHttp.open("PUT", "/times/" + form._id); + xmlHttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xmlHttp.send(JSON.stringify(form)); +}; +this.time_deleted = function (form) { + form.owner_id = owner_id; + var xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = _this.process_response; + + xmlHttp.open("DELETE", "/times/" + form._id); + xmlHttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + xmlHttp.send(JSON.stringify(form)); +}; + +this.seperate_days = function (times) { + _this.sunday = _this.times.filter(function (time) { + return time.day === "1" ? true : false; + }); + _this.monday = _this.times.filter(function (time) { + return time.day === "2" ? true : false; + }); + _this.tuesday = _this.times.filter(function (time) { + return time.day === "3" ? true : false; + }); + _this.wednesday = _this.times.filter(function (time) { + return time.day === "4" ? true : false; + }); + _this.thursday = _this.times.filter(function (time) { + return time.day === "5" ? true : false; + }); + _this.friday = _this.times.filter(function (time) { + return time.day === "6" ? true : false; + }); + _this.saturday = _this.times.filter(function (time) { + return time.day === "7" ? true : false; + }); +}; + +this.get_times = function (owner_id) { + var xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = function () { + if (xmlHttp.readyState === 4 && xmlHttp.status === 200) { + if (xmlHttp.responseText) { + _this.times = JSON.parse(xmlHttp.responseText).times; + _this.seperate_days(_this.times); + } else { + _this.times = []; + } + _this.update(); + } + }; + xmlHttp.open("GET", "/owners/" + owner_id + "/times/", true); + xmlHttp.send(); +}; +self.on('mount', function () { + this.get_times(owner_id); +}); +}, '{ }'); \ No newline at end of file diff --git a/public/components_riot/times_added/times_added.tag b/public/components_riot/times_added/times_added.tag new file mode 100644 index 0000000..2142eeb --- /dev/null +++ b/public/components_riot/times_added/times_added.tag @@ -0,0 +1,274 @@ + + +
+

Sunday

+ + +
+
+

Monday

+ + +
+
+

Tuesday

+ + +
+
+

Wednesday

+ + +
+
+

Thursday

+ + +
+
+

Friday

+ + +
+
+

Saturday

+ + +
+ + + +
\ No newline at end of file diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..4700d4c --- /dev/null +++ b/public/index.html @@ -0,0 +1,18 @@ + + + + + GSTV Coding Exercise + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/lib/riot.min.js b/public/lib/riot.min.js new file mode 100644 index 0000000..e688e06 --- /dev/null +++ b/public/lib/riot.min.js @@ -0,0 +1,2 @@ +/* Riot v2.3.1, @license MIT, (c) 2015 Muut Inc. + contributors */ +(function(e,t){"use strict";var n={version:"v2.3.1",settings:{}},r=0,i=[],o={},f="riot-",u=f+"tag",a="string",s="object",c="undefined",l="function",p=/^(?:opt(ion|group)|tbody|col|t[rhd])$/,d=["_item","_id","_parent","update","root","mount","unmount","mixin","isMounted","isLoop","tags","parent","opts","trigger","on","off","one"],g=(e&&e.document||{}).documentMode|0;n.observable=function(e){e=e||{};var t={},n=function(e,t){e.replace(/\S+/g,t)},r=function(t,n){Object.defineProperty(e,t,{value:n,enumerable:false,writable:false,configurable:false})};r("on",function(r,i){if(typeof i!="function")return e;n(r,function(e,n){(t[e]=t[e]||[]).push(i);i.typed=n>0});return e});r("off",function(r,i){if(r=="*")t={};else{n(r,function(e){if(i){var n=t[e];for(var r=0,o;o=n&&n[r];++r){if(o==i)n.splice(r--,1)}}else delete t[e]})}return e});r("one",function(t,n){function r(){e.off(t,r);n.apply(e,arguments)}return e.on(t,r)});r("trigger",function(r){var i=arguments.length-1,o=new Array(i);for(var f=0;fa-zA-Z0-9'",;\\]/.test(e)){throw new Error('Unsupported brackets "'+e+'"')}c=n.concat(e.replace(/(?=[[\]()*+?.^$|])/g,"\\").split(" "));s=d}c[4]=s(c[1].length>1?/(?:^|[^\\]){[\S\s]*?}/:/(?:^|[^\\]){[^}]*}/);c[5]=s(/\\({|})/g);c[6]=s(/(\\?)({)/g);c[7]=l("(\\\\?)(?:([[({])|("+c[3]+"))|"+o,t);c[9]=l(/^\s*{\^?\s*([$\w]+)(?:\s*,\s*(\S+))?\s+in\s+(\S+)\s*}/);c[8]=e}v.settings.brackets=a=e}function h(e){if(a!==e){g(e)}}function v(e){h(v.settings.brackets);return e instanceof RegExp?s(e):c[e]}v.split=function m(e,t){var n=[],r,i,o,f,a=v(6);i=o=a.lastIndex=0;while(r=a.exec(e)){f=r.index;if(i){if(r[2]){a.lastIndex=l(r[2],a.lastIndex);continue}if(!r[3])continue}if(!r[1]){s(e.slice(o,f));o=a.lastIndex;a=c[6+(i^=1)];a.lastIndex=o}}if(e&&o2||r[0]){var i,o,f=[];for(i=o=0;i2&&!t?""+(n.push(e)-1)+"~":e}).replace(/\s+/g," ").trim().replace(/\ ?([[\({},?\.:])\ ?/g,"$1");if(e){var r=[],i=0,o;while(e&&(o=e.match(s))&&!o.index){var u,a,c=/,|([[{(])|$/g;e=RegExp.rightContext;u=o[2]?n[o[2]].slice(1,-1).trim().replace(/\s+/g," "):o[1];while(a=(o=c.exec(e))[1])l(a,c);a=e.slice(0,o.index);e=RegExp.rightContext;r[i++]=g(a,1,u)}e=!i?g(e,t):i>1?"["+r.join(",")+'].join(" ").trim()':r[0]}return e;function l(t,n){var r,i=1,o=t==="("?/[()]/g:t==="["?/[[\]]/g:/[{}]/g;o.lastIndex=n.lastIndex;while(r=o.exec(e)){if(r[0]===t)++i;else if(!--i)break}n.lastIndex=i?e.length:o.lastIndex}}var p='"in this?this:'+(typeof e!=="object"?"global":"window")+").";var d=/[,{][$\w]+:|(^ *|[^$\w\.])(?!(?:typeof|true|false|null|undefined|in|instanceof|is(?:Finite|NaN)|void|NaN|new|Date|RegExp|Math)(?![$\w]))([$_A-Za-z][$\w]*)/g;function g(e,n,r){var i=t;e=e.replace(d,function(e,t,n,r,o){if(n){r=i?0:r+e.length;if(n!=="this"&&n!=="global"&&n!=="window"){e=t+'("'+n+p+n;if(r)i=(o=o[r])==="."||o==="("||o==="["}else if(r)i=!/^(?=(\.[$\w]+))\1(?:[^.[(]|$)/.test(o.slice(r))}return e});if(i){e="try{return "+e+"}catch(e){E(e,this)}"}if(r){e=(i?"function(){"+e+"}.call(this)":"("+e+")")+'?"'+r+'":""'}else if(n){e="function(v){"+(i?e.replace("return ","v="):"v=("+e+")")+';return v||v===0?v:""}.call(this)'}return e}r.parse=function(e){return e};return r}();var m=function(e){var t={tr:"tbody",th:"tr",td:"tr",tbody:"table",col:"colgroup"},n="div";e=e&&e<10;function r(r){var o=r&&r.match(/^\s*<([-\w]+)/),f=o&&o[1].toLowerCase(),u=t[f]||n,a=W(u);a.stub=true;if(e&&f&&(o=f.match(p)))i(a,r,f,!!o[1]);else a.innerHTML=r;return a}function i(e,t,r,i){var o=W(n),f=i?"select>":"table>",u;o.innerHTML="<"+f+t+"r){var i=t[--n];t.splice(n,1);i.unmount()}}function w(e,t){Object.keys(e.tags).forEach(function(n){var r=e.tags[n];if(q(r))$(r,function(e){B(e,n,t)});else B(r,n,t)})}function x(e,t,n){var r=e._root;e._virts=[];while(r){var i=r.nextSibling;if(n)t.insertBefore(r,n._root);else t.appendChild(r);e._virts.push(r);r=i}}function _(e,t,n,r){var i=e._root;for(var o=0;o(<\/\1>)?/gi,t||"")}function Y(e,t){return(t||document).querySelectorAll(e)}function ee(e,t){return(t||document).querySelector(e)}function te(e){function t(){}t.prototype=e;return new t}function ne(e){return H(e,"id")||H(e,"name")}function re(e,t,n){var r=ne(e),i=function(i){if(V(n,r))return;var o=q(i);if(!i)t[r]=e;else if(!o||o&&!V(i,e)){if(o)i.push(e);else t[r]=[i,e]}};if(!r)return;if(v.hasExpr(r))t.one("updated",function(){r=ne(e);i(t[r])});else i(t[r])}function ie(e,t){return e.slice(0,t.length)===t}var oe=function(){if(!e)return;var t=W("style"),n=ee("style[type=riot]");j(t,"type","text/css");if(n){n.parentNode.replaceChild(t,n);n=null}else document.getElementsByTagName("head")[0].appendChild(t);return t.styleSheet?function(e){t.styleSheet.cssText+=e}:function(e){t.innerHTML+=e}}();function fe(e,t,n){var r=o[t],f=e._innerHTML=e._innerHTML||e.innerHTML;e.innerHTML="";if(r&&e)r=new M(r,{root:e,opts:n},f);if(r&&r.mount){r.mount();if(!V(i,r))i.push(r)}return r}n.util={brackets:h,tmpl:v};n.mixin=function(){var e={};return function(t,n){if(!n)return e[t];e[t]=n}}();n.tag=function(e,t,n,r,i){if(k(r)){i=r;if(/^[\w\-]+\s?=/.test(n)){r=n;n=""}else r=""}if(n){if(k(n))i=n;else if(oe)oe(n)}o[e]={name:e,tmpl:t,attrs:r,fn:i};return e};n.tag2=function(e,t,n,r,i,f){if(n&&oe)oe(n);o[e]={name:e,tmpl:t,attrs:r,fn:i};return e};n.mount=function(e,t,n){var r,i,f=[];function c(e){var t="";$(e,function(e){t+=", *["+u+'="'+e.trim()+'"]'});return t}function l(){var e=Object.keys(o);return e+c(e)}function p(e){var r;if(e.tagName){if(t&&(!(r=H(e,u))||r!=t))j(e,u,t);var i=fe(e,t||e.getAttribute(u)||e.tagName.toLowerCase(),n);if(i)f.push(i)}else if(e.length)$(e,p)}if(typeof t===s){n=t;t=0}if(typeof e===a){if(e==="*")e=i=l();else e+=c(e.split(","));r=e?Y(e):[]}else r=e;if(t==="*"){t=i||l();if(r.tagName)r=Y(t,r);else{var d=[];$(r,function(e){d.push(Y(t,e))});r=d}t=0}if(r.tagName)p(r);else $(r,p);return f};n.update=function(){return $(i,function(e){e.update()})};n.Tag=M;if(typeof exports===s)module.exports=n;else if(typeof define==="function"&&define.amd)define(function(){return e.riot=n});else e.riot=n})(typeof window!="undefined"?window:void 0); diff --git a/public/lib/riotcontrol.js b/public/lib/riotcontrol.js new file mode 100644 index 0000000..405a505 --- /dev/null +++ b/public/lib/riotcontrol.js @@ -0,0 +1,17 @@ +var RiotControl = { + _stores: [], + addStore: function(store) { + this._stores.push(store); + } +}; + +['on','one','off','trigger'].forEach(function(api){ + RiotControl[api] = function() { + var args = [].slice.call(arguments); + this._stores.forEach(function(el){ + el[api].apply(null, args); + }); + }; +}); + +if (typeof(module) !== 'undefined') module.exports = RiotControl; diff --git a/public/scripts/main.js b/public/scripts/main.js new file mode 100644 index 0000000..4c591b7 --- /dev/null +++ b/public/scripts/main.js @@ -0,0 +1,5 @@ +/** + * Created by Tim on 1/14/2016. + */ +"use strict"; +riot.mount('layout'); \ No newline at end of file diff --git a/public/styles/layout.css b/public/styles/layout.css new file mode 100644 index 0000000..84c978f --- /dev/null +++ b/public/styles/layout.css @@ -0,0 +1,3 @@ + + +/*# sourceMappingURL=layout.css.map */ diff --git a/public/styles/layout.scss b/public/styles/layout.scss new file mode 100644 index 0000000..e7b71e1 --- /dev/null +++ b/public/styles/layout.scss @@ -0,0 +1,3 @@ +body{ + +} \ No newline at end of file diff --git a/routes/routes.js b/routes/routes.js new file mode 100644 index 0000000..8283c81 --- /dev/null +++ b/routes/routes.js @@ -0,0 +1,10 @@ +var modTimes = require("./times"); + +var routes = (function () { + function routes() { + this.times = new modTimes(); + } + return routes; +})(); + +module.exports = routes; diff --git a/routes/times.js b/routes/times.js new file mode 100644 index 0000000..ce2f74a --- /dev/null +++ b/routes/times.js @@ -0,0 +1,260 @@ +"use strict"; +var dbMod = require("../db3") + , db = new dbMod() + , ObjectId = require('mongodb').ObjectID + , times_model = require("../models/time") + , validate = require("validate.js") + , Q = require('q'); + +var timesRoute = (function () { + function timesRoute() { + + let validation_constraints = { + + value_start: { + numericality: { + onlyInteger: true, + greaterThan: 0, + lessThanOrEqualTo: 288, + //even: true, + message: "Unable to Create/Update: time slot, end value does not match the expected format." + } + } + , value_end: { + numericality: { + onlyInteger: true, + greaterThan: 0, + lessThanOrEqualTo: 349, + //even: true, + message: "Unable to Create/Update: time slot, start value does not match the expected format." + } + } + , time_start: { + numericality: { + greaterThan: 0, + lessThanOrEqualTo: 61, + //even: true, + message: "Unable to Create/Update: time slot, start time does not match the expected format." + } + } + , time_end: { + numericality: { + //onlyInteger: true, + greaterThan: 0, + lessThanOrEqualTo: 61, + //even: true, + message: "Unable to Create/Update: time slot, end time does not match the expected format." + } + } + , day: { + numericality: { + //onlyInteger: true, + greaterThan: 0, + lessThanOrEqualTo: 7, + //even: true, + message: "Unable to Create/Update: time slot, day does not match the expected format." + } + } + , is_24: { + inclusion: { + within: [true, false], + message: "Unable to Create/Update: time slot, is_24 does not match the expected format." + } + } + , owner_id: { + numericality: { + //onlyInteger: true, + greaterThan: 0, + lessThanOrEqualTo: 1000000, + //even: true, + message: "Unable to Create/Update: time slot, owner_id does not match the expected format." + } + } + , deleted: { + inclusion: { + within: [true, false, null], + message: "Unable to Create/Update: time slot, deleted does not match the expected format." + } + } + , _id: { + length: { + maximum: 100, + message: "Unable to Create/Update: time slot, message does not match the expected format." + } + } + }; + + + let equal = (x, y) => { + if (x === y) return true; + // if both x and y are null or undefined and exactly the same + + if (!( x instanceof Object ) || !( y instanceof Object )) return false; + // if they are not strictly equal, they both need to be Objects + + if (x.constructor !== y.constructor) return false; + // they must have the exact same prototype chain, the closest we can do is + // test there constructor. + + for (var p in x) { + if (!x.hasOwnProperty(p)) continue; + // other properties were tested using x.constructor === y.constructor + + if (!y.hasOwnProperty(p)) return false; + // allows to compare x[ p ] and y[ p ] when set to undefined + + if (x[p] === y[p]) continue; + // if they have the same strict value or identity then they are equal + + if (typeof( x[p] ) !== "object") return false; + // Numbers, Strings, Functions, Booleans must be strictly equal + + if (!Object.equals(x[p], y[p])) return false; + // Objects and Arrays must be tested recursively + } + + for (p in y) { + if (y.hasOwnProperty(p) && !x.hasOwnProperty(p)) return false; + // allows x[ p ] to be set to undefined + } + return true; + } + this.checkTime = function (time) { + //validate times + let message, time_start = parseInt(time.time_start), time_end = parseInt(time.time_end); + console.log(time_start); + console.log(time_end); + console.log(time); + function getTime(query) { + return qGetTime(query).then(function (times) { + if (times.data.length) { + if (times.data.length === 1 && times.data[0]._id.toString() === time._id) { + times.data[0]._id = times.data[0]._id.toString(); + // got the current record + // check if current record is the same + console.log(times.data[0]); + var time_test = new times_model.time(times.data[0].value_start, times.data[0].value_end, times.data[0].day, + times.data[0].time_start, times.data[0].time_end, times.data[0].is_24, times.data[0].owner_id, + times.data[0].deleted, times.data[0]._id); + if (equal(time, time_test)) { + return {error: 'Unable to Create/Update: the time slot has not been changed'}; + } else { + return true; + } + } else { + return {error: 'Unable to Create/Update: there is at least one overlapping time slot'}; + } + } else { + return true; + } + }); + } + message = validate(time, validation_constraints); + + if (!time.is_24) { + if (time_start >= time_end) { + message += 'Unable to Create/Update: The start time must be before the end time. ' + } + } + if (message) { + console.log(message); + return {error: message} + } + + try { + var qGetTime = Q.nbind(db.process_query, db); + + var query2 = { + object: { + owner_id: time.owner_id, deleted: {$ne: true} + , $or: [{$or: [{value_start: {$gte: time.value_start, $lt: time.value_end }}, {value_end: {$gt: time.value_start, $lte: time.value_end}}]} + , {$and: [{value_start: {$lte: time.value_start}}, {value_end: {$gte: time.value_start}}]} + , {_id: {$eq: ObjectId(time._id)}}] + }, + verb: 'get', + collection: 'times' + }; + var query = { + object: { + owner_id: time.owner_id, deleted: {$ne: true} + , $or: [{$or: [{value_start: {$gte: time.value_start, $lt: time.value_end }}, {value_end: {$gt: time.value_start, $lte: time.value_end}}]} + , {$and: [{value_start: {$lte: time.value_start}}, {value_end: {$gt: time.value_start}}]} + , {_id: {$eq: ObjectId(time._id)}}] + }, + verb: 'get', + collection: 'times' + }; + return getTime(query); + } catch (e) { + return ({error: e.message}); + } + + }; + + + this.update_create = function *(time, verb) { + const myTime = new times_model.time(time.value_start, time.value_end, time.day, time.time_start, time.time_end, time.is_24, time.owner_id, time.deleted, time._id), + isTimeOk = yield this.checkTime(myTime), + qTimeUpdate = Q.nbind(db.process_query, db), + query = { + collection: 'times', + verb: verb, + object: myTime + }; + function timesUpdate(query) { + return qTimeUpdate(query).then(function (result) { + return ({message: "Time updated/created successful."}); + }); + } + + if (isTimeOk != true) { + return ({error: isTimeOk}); + } + + return timesUpdate(query); + }; + this.get = function (query_object) { + function getTime(query) { + return qGetTime(query).then(function (times) { + return {times: times.data}; + }); + } + + try { + var qGetTime = Q.nbind(db.process_query, db); + var query = { + object: query_object, + verb: 'get', + collection: 'times' + }; + return getTime(query); + } catch (e) { + return ({error: e.message}); + } + }; + this.delete = function (query_object) { + function getTime(query) { + return qGetTime(query).then(function (times) { + return {times: times.data}; + }); + } + + try { + var qGetTime = Q.nbind(db.process_query, db); + var query = { + object: query_object, + verb: 'get', + collection: 'times' + }; + return getTime(query); + } catch (e) { + return ({error: e.message}); + } + }; + + } + + return timesRoute; +})(); + +module.exports = timesRoute; diff --git a/server.js b/server.js new file mode 100644 index 0000000..a8d2373 --- /dev/null +++ b/server.js @@ -0,0 +1,70 @@ +"use strict"; +let send = require('koa-send'), + app = require('koa')(), + path = require('path'), + routesMod = require("./routes/routes"), + routes = new routesMod(), + router = require('koa-router')(), + parse = require('co-body'), + appPath = path.dirname(require.main.filename).replace(/\\/g,"/").replace('C', 'c') + '/public'; + +router + .get('/index', function *(next) { + yield send(this, '/index.html', { root: appPath, maxage: 10 * 24 * 60 * 60 }); + }) + .get('/times/:time_id', function *(next) { + var response = yield routes.times.get({ _id: this.params.time_id, deleted: { $ne: false } }); + if(response.error){ + this.body = response.error; + }else{ + this.body = response; + } + }) + .get('/owners/:owner_id/times/', function *(next) { + var response = yield routes.times.get({ owner_id: this.params.owner_id, deleted: { $ne: true } }); + if(response.error){ + this.body = response.error; + }else{ + this.body = response; + } + }) + .post('/owners/:owner_id/times', function *(next) { + var time = yield parse.json(this, { limit: '1kb' }); + time.owner_id = this.params.owner_id; + var response = yield routes.times.update_create(time, 'post'); + if(response.error){ + this.body = response.error; + }else{ + this.body = response; + } + }) + .put('/times/:time_id', function *(next) { + var time = yield parse.json(this, { limit: '1kb' }); + var response = yield routes.times.update_create(time, 'put'); + if(response.error){ + this.body = response.error; + }else{ + this.body = response; + } + }) + .del('/times/:time_id', function *(next) { + var time = yield parse.json(this, { limit: '1kb' }); + time.deleted = true; + var response = yield routes.times.update_create(time, 'put'); + if(response.error){ + this.body = response.error; + }else{ + this.body = response; + } + }) + .redirect('/', 'index'); + +app + .use(router.routes()) + .use(router.allowedMethods()); +app.use(function *(){ + yield send(this, this.path, { root: appPath, maxage: 10 * 24 * 60 * 60 }); +}) + +app.listen(4567); +console.log('listening on port 4567');