diff --git a/README.md b/README.md index 7b408c8..acd0e97 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join the chat at https://gitter.im/nupic-community/nupic.visualizations](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nupic-community/nupic.visualizations?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -Work with NuPIC results interactively in a [nice web UI](https://nupic-visualizations.firebaseapp.com/). This app is designed to consume and render CSV files output by NuPIC, but it can read any CSV file with numerical data that has a timestamp as one of its fields. +Work with NuPIC results interactively in a [nice web UI](https://nupic-visualizations.firebaseapp.com/). This app is designed to consume and render CSV files output by NuPIC, but it can read any CSV file with numerical data that represent some time-series. ![NuPIC Visualizations screenshot](https://raw.githubusercontent.com/wiki/nupic-community/nupic.visualizations/images/nupic_visualizations.png) @@ -56,6 +56,7 @@ There are demo files in `examples/` for NuPIC OPF, NAB and generic CSV files. * Select a local file by clicking 'Browse...', this will start rendering a new graph. * You may get warning/error labels, that can be closed. + * You can also enter a URL of a remote file for streaming (in supported by the server). * To zoom in on the graph, click and drag with the cursor. To zoom out, double-click. To pan on a zoomed graph, hold the ALT key (on Linux, hold the SHIFT key), while dragging the cursor. * The options to the right of the graph allow the user to: - Set the visibility of certain series @@ -67,7 +68,7 @@ There are demo files in `examples/` for NuPIC OPF, NAB and generic CSV files. The goals of this project are: * nice, convenient & easy to use interactive graph visualizations * seamlessly runs everywhere (in web-browser, or [cloud](https://nupic-visualizations.firebaseapp.com/)) -* plot as much as possible - generic CSV +* plot as much as possible - generic CSV (local or remote files) * tries to parse timestamp/x-data from many formats * no restrictions on the header format * can plot even non-standart data - strings (planned) diff --git a/build/app.js b/build/app.js index e63790f..fb88676 100644 --- a/build/app.js +++ b/build/app.js @@ -1 +1 @@ -angular.module("app",["ui.bootstrap"]),angular.module("app").constant("appConfig",{TIMESTAMP:"timestamp",EXCLUDE_FIELDS:[],HEADER_SKIPPED_ROWS:1,ZOOM:"HighlightSelector",NONE_VALUE_REPLACEMENT:0,WINDOW_SIZE:1e4,MAX_FILE_SIZE:62914560,LOCAL_CHUNK_SIZE:65536,REMOTE_CHUNK_SIZE:65536,HIGHLIGHT_RADIUS:10}),angular.module("app").controller("appCtrl",["$scope","$http","$timeout","appConfig",function(e,i,n,t){function a(i,n,a){function d(e,t,l){var o=a.toDomXCoord(e),r=a.toDomXCoord(t),d=r-o;i.fillStyle=l,i.fillRect(o,n.y,d,n.h)}function s(e,i,n){var t=[],a=o.indexOf(i);if(-1===a)return S("Highlighting cannot work, field "+i+" not found!","danger",!0),[];for(var l=0;l=n){var d=e[l][g];t.push(d)}}return t}for(var g=o.indexOf(t.TIMESTAMP),h=0;h=v&&(d(u[m]-f,u[m]+f,p),v=u[m]+f)}}}e.view={fieldState:[],graph:null,dataField:null,optionsVisible:!0,filePath:"",loadedFileName:"",errors:[],loading:!1,windowing:{threshold:t.MAX_FILE_SIZE,size:-1,show:!1,paused:!1,aborted:!1,update_interval:1}};var l=[],o=[],r=[],d={},s=!1,g=0,h=-1,u=null,f=!1;e.toggleOptions=function(){e.view.optionsVisible=!e.view.optionsVisible,e.view.graph&&(d.resize=n(function(){e.view.graph.resize()}))},e.getRemoteFile=function(){e.view.windowing.show=!1,e.view.windowing.paused=!1,e.view.windowing.aborted=!1,e.$broadcast("fileUploadChange"),e.view.loading=!0,i.head(e.view.filePath,{headers:{Range:"bytes=0-32"}}).then(function(n){206===n.status?i.head(e.view.filePath).then(function(i){var n=i.headers("Content-Length");n>e.view.windowing.threshold&&-1!==e.view.windowing.threshold&&(e.view.windowing.show=!0,e.view.windowing.size=t.WINDOW_SIZE,S("File too large, automatic sliding window enabled.","warning")),m(e.view.filePath)}):v(e.view.filePath)},function(){v(e.view.filePath)})},e.canDownload=function(){var i=e.view.filePath.split("://");return("https"===i[0]||"http"===i[0])&&i.length>1&&i[1].length>0?!0:!1},e.abortParse=function(){angular.isDefined(u)&&angular.isDefined(u.abort)&&(u.abort(),e.view.windowing.paused=!1,e.view.windowing.aborted=!0)},e.pauseParse=function(){angular.isDefined(u)&&angular.isDefined(u.pause)&&(u.pause(),e.view.windowing.paused=!0)},e.resumeParse=function(){angular.isDefined(u)&&angular.isDefined(u.resume)&&(u.resume(),e.view.windowing.paused=!1)},e.getLocalFile=function(i){e.view.filePath=i.target.files[0].name,i.target.files[0].size>e.view.windowing.threshold&&-1!==e.view.windowing.threshold&&(e.view.windowing.show=!0,e.view.windowing.size=t.WINDOW_SIZE,S("File too large, automatic sliding window enabled.","warning")),e.view.loading=!0,E(i.target.files[0])};var p=function(e){var i=e.split("/");return i[i.length-1]},w=function(i){for(var n=-1,a=0;a=w&&1!==i[a][h]){S("Your time is not monotonic at row "+g+"! Graphs are incorrect.","danger",!1),console.log("Incorrect timestamp!");break}n=w}else"None"===w&&(w=t.NONE_VALUE_REPLACEMENT);d.push(w)}d.length===o.length?(l.push(d),r.push(angular.extend([],d)),-1!==e.view.windowing.size&&l.length>e.view.windowing.size&&(l.shift(),r.shift())):console.log("Incomplete row loaded "+d+"; skipping.")}null===e.view.graph?P():g%e.view.windowing.update_interval===0&&e.view.graph.updateOptions({file:l}),f||(e.$apply(),f=!0)},c=function(){e.view.fieldState.length=0,e.view.graph=null,e.view.dataField=null,e.view.errors.length=0,e.view.loadedFileName="",s=!1,g=0,l.length=0,o.length=0,f=!1},v=function(i){c(),Papa.parse(i,{download:!0,skipEmptyLines:!0,header:!0,dynamicTyping:!0,worker:!1,comments:"#",complete:function(n){angular.isDefined(n.data)?(e.view.loadedFileName=p(i),o=I(n.data,t.EXCLUDE_FIELDS),n.data.splice(0,t.HEADER_SKIPPED_ROWS),w(n.data)):S("An error occurred when attempting to download file.","danger"),e.view.loading=!1,e.$apply()},error:function(i){e.view.loading=!1,S("Could not download file.","danger")}})},m=function(i){c(),Papa.RemoteChunkSize=t.REMOTE_CHUNK_SIZE;Papa.parse(i,{download:!0,skipEmptyLines:!0,header:!0,dynamicTyping:!0,worker:!1,comments:"#",chunk:function(e,i){u=i,w(e.data)},beforeFirstChunk:function(n){e.view.loadedFileName=p(i);var a=n.split(/\r\n|\r|\n/);return o=I(a,t.EXCLUDE_FIELDS),a.splice(1,t.HEADER_SKIPPED_ROWS),e.view.loading=!1,a=a.join("\n")},error:function(i){S("Could not stream file.","danger"),e.view.loading=!1}})},E=function(i){c(),Papa.LocalChunkSize=t.LOCAL_CHUNK_SIZE;Papa.parse(i,{skipEmptyLines:!0,header:!0,dynamicTyping:!0,worker:!1,comments:"#",chunk:function(e,i){u=i,w(e.data)},beforeFirstChunk:function(n){e.view.loadedFileName=i.name;var a=n.split(/\r\n|\r|\n/);return o=I(a,t.EXCLUDE_FIELDS),a.splice(1,t.HEADER_SKIPPED_ROWS),e.view.loading=!1,a=a.join("\n")},error:function(i){S(i,"danger"),e.view.loading=!1}})},S=function(i,n,t){if(t="undefined"!=typeof t?t:!1,exists=!1,t){errs=e.view.errors;for(var a=0;a1&&l.length>1)return S("Could not parse the timestamp: "+e,"warning",!0),null;if(l.length>2)t.push(l[0]),t.push(l[1]),t.push(l[2]);else{if(!(a.length>2))return S("There was something wrong with the date in the timestamp field.","warning",!0),null;t.push(a[2]),t.push(a[0]),t.push(a[1])}if(n[1]){var o=n[1].split(":");t=t.concat(o)}for(var r=0;r
{{field.name}}
',link:function(e,i,n){var t={};t.normalized=e.$watch("field.normalized",function(i,n){i?e.normalizeField(e.field.id):i||i===n||e.denormalizeField(e.field.id)}),t.isData=e.$watch("view.dataField",function(){e.renormalize()}),e.$on("$destroy",function(){angular.forEach(t,function(e){e()})})}}}),angular.module("app").directive("fileUploadChange",function(){return{restrict:"A",link:function(e,i,n){var t=e.$eval(n.fileUploadChange);i.on("change",t);var a=e.$on("fileUploadChange",function(){angular.element(i).val(null)});e.$on("$destroy",function(){i.off(),a()})}}}),angular.module("app").directive("highlightField",[function(){return{restrict:"A",scope:{highlightFn:"=",highlightField:"="},link:function(e,i,n){i.bind("keyup",function(i){e.highlightFn(e.highlightField)}),e.$on("$destroy",function(){i.unbind("keyup")})}}}]),angular.module("app").filter("bytes",function(){return function(e,i){if(isNaN(parseFloat(e))||!isFinite(e))return"-";"undefined"==typeof i&&(i=1);var n=["bytes","kB","MB","GB","TB","PB"],t=Math.floor(Math.log(e)/Math.log(1024));return(e/Math.pow(1024,Math.floor(t))).toFixed(i)+" "+n[t]}}); \ No newline at end of file +angular.module("app",["ui.bootstrap"]),angular.module("app").constant("appConfig",{TIMESTAMP:"timestamp",EXCLUDE_FIELDS:[],HEADER_SKIPPED_ROWS:1,ZOOM:"HighlightSelector",NONE_VALUE_REPLACEMENT:0,WINDOW_SIZE:1e4,MAX_FILE_SIZE:62914560,LOCAL_CHUNK_SIZE:65536,REMOTE_CHUNK_SIZE:65536,HIGHLIGHT_RADIUS:10}),angular.module("app").controller("appCtrl",["$scope","$http","$timeout","appConfig",function(e,i,n,t){function a(i,n,a){function d(e,t,l){var o=a.toDomXCoord(e),r=a.toDomXCoord(t),d=r-o;i.fillStyle=l,i.fillRect(o,n.y,d,n.h)}function s(e,i,n){var t=[],a=o.indexOf(i);if(-1===a)return S("Highlighting cannot work, field "+i+" not found!","danger",!0),[];for(var l=0;l=n){var d=e[l][g];t.push(d)}}return t}for(var g=o.indexOf(t.TIMESTAMP),h=0;h=v&&(d(u[m]-f,u[m]+f,p),v=u[m]+f)}}}e.view={fieldState:[],graph:null,dataField:null,optionsVisible:!0,filePath:"",loadedFileName:"",errors:[],loading:!1,windowing:{threshold:t.MAX_FILE_SIZE,size:-1,show:!1,paused:!1,aborted:!1,update_interval:1}};var l=[],o=[],r=[],d={},s=!1,g=0,h=-1,u=null,f=!1;e.toggleOptions=function(){e.view.optionsVisible=!e.view.optionsVisible,e.view.graph&&(d.resize=n(function(){e.view.graph.resize()}))},e.getRemoteFile=function(){e.view.windowing.show=!1,e.view.windowing.paused=!1,e.view.windowing.aborted=!1,e.$broadcast("fileUploadChange"),e.view.loading=!0,i.head(e.view.filePath,{headers:{Range:"bytes=0-32"}}).then(function(n){206===n.status?i.head(e.view.filePath).then(function(i){var n=i.headers("Content-Length");n>e.view.windowing.threshold&&-1!==e.view.windowing.threshold&&(e.view.windowing.show=!0,e.view.windowing.size=t.WINDOW_SIZE,S("File too large, automatic sliding window enabled.","warning")),m(e.view.filePath)}):v(e.view.filePath)},function(){v(e.view.filePath)})},e.canDownload=function(){var i=e.view.filePath.split("://");return("https"===i[0]||"http"===i[0])&&i.length>1&&i[1].length>0?!0:!1},e.abortParse=function(){angular.isDefined(u)&&angular.isDefined(u.abort)&&(u.abort(),e.view.windowing.paused=!1,e.view.windowing.aborted=!0)},e.pauseParse=function(){angular.isDefined(u)&&angular.isDefined(u.pause)&&(u.pause(),e.view.windowing.paused=!0)},e.resumeParse=function(){angular.isDefined(u)&&angular.isDefined(u.resume)&&(u.resume(),e.view.windowing.paused=!1)},e.getLocalFile=function(i){e.view.filePath=i.target.files[0].name,i.target.files[0].size>e.view.windowing.threshold&&-1!==e.view.windowing.threshold&&(e.view.windowing.show=!0,e.view.windowing.size=t.WINDOW_SIZE,S("File too large, automatic sliding window enabled.","warning")),e.view.loading=!0,E(i.target.files[0])};var p=function(e){var i=e.split("/");return i[i.length-1]},w=function(i){for(var n=-1,a=0;a=w&&1!==i[a][h]){S("Your time is not monotonic at row "+g+"! Graphs are incorrect.","danger",!1),console.log("Incorrect timestamp!");break}n=w}else"None"===w&&(w=t.NONE_VALUE_REPLACEMENT);d.push(w)}d.length===o.length?(l.push(d),r.push(angular.extend([],d)),-1!==e.view.windowing.size&&l.length>e.view.windowing.size&&(l.shift(),r.shift())):console.log("Incomplete row loaded "+d+"; skipping.")}null===e.view.graph?P():g%e.view.windowing.update_interval===0&&e.view.graph.updateOptions({file:l}),f||(e.$apply(),f=!0)},c=function(){e.view.fieldState.length=0,e.view.graph=null,e.view.dataField=null,e.view.errors.length=0,e.view.loadedFileName="",s=!1,g=0,l.length=0,o.length=0,f=!1},v=function(i){c(),Papa.parse(i,{download:!0,skipEmptyLines:!0,header:!0,dynamicTyping:!0,worker:!1,comments:"#",complete:function(n){angular.isDefined(n.data)?(e.view.loadedFileName=p(i),o=I(n.data,t.EXCLUDE_FIELDS),n.data.splice(0,t.HEADER_SKIPPED_ROWS),w(n.data)):S("An error occurred when attempting to download file.","danger"),e.view.loading=!1,e.$apply()},error:function(i){e.view.loading=!1,S("Could not download file.","danger")}})},m=function(i){c(),Papa.RemoteChunkSize=t.REMOTE_CHUNK_SIZE;Papa.parse(i,{download:!0,skipEmptyLines:!0,header:!0,dynamicTyping:!0,worker:!1,comments:"#",chunk:function(e,i){u=i,w(e.data)},beforeFirstChunk:function(n){e.view.loadedFileName=p(i);var a=n.split(/\r\n|\r|\n/);return o=I(a,t.EXCLUDE_FIELDS),a.splice(1,t.HEADER_SKIPPED_ROWS),e.view.loading=!1,a=a.join("\n")},error:function(i){S("Could not stream file.","danger"),e.view.loading=!1}})},E=function(i){c(),Papa.LocalChunkSize=t.LOCAL_CHUNK_SIZE;Papa.parse(i,{skipEmptyLines:!0,header:!0,dynamicTyping:!0,worker:!1,comments:"#",chunk:function(e,i){u=i,w(e.data)},beforeFirstChunk:function(n){e.view.loadedFileName=i.name;var a=n.split(/\r\n|\r|\n/);return o=I(a,t.EXCLUDE_FIELDS),a.splice(1,t.HEADER_SKIPPED_ROWS),e.view.loading=!1,a=a.join("\n")},error:function(i){S(i,"danger"),e.view.loading=!1}})},S=function(i,n,t){if(t="undefined"!=typeof t?t:!1,exists=!1,t){errs=e.view.errors;for(var a=0;a1&&l.length>1)return S("Could not parse the timestamp: "+e,"warning",!0),null;if(l.length>2)t.push(l[0]),t.push(l[1]),t.push(l[2]);else{if(!(a.length>2))return S("There was something wrong with the date in the timestamp field.","warning",!0),null;t.push(a[2]),t.push(a[0]),t.push(a[1])}if(n[1]){var o=n[1].split(":");t=t.concat(o)}for(var r=0;r
{{field.name}}
',link:function(e,i,n){var t={};t.normalized=e.$watch("field.normalized",function(i,n){i?e.normalizeField(e.field.id):i||i===n||e.denormalizeField(e.field.id)}),t.isData=e.$watch("view.dataField",function(){e.renormalize()}),e.$on("$destroy",function(){angular.forEach(t,function(e){e()})})}}}),angular.module("app").directive("fileUploadChange",function(){return{restrict:"A",link:function(e,i,n){var t=e.$eval(n.fileUploadChange);i.on("change",t);var a=e.$on("fileUploadChange",function(){angular.element(i).val(null)});e.$on("$destroy",function(){i.off(),a()})}}}),angular.module("app").directive("highlightField",[function(){return{restrict:"A",scope:{highlightFn:"=",highlightField:"="},link:function(e,i,n){i.bind("keyup",function(i){e.highlightFn(e.highlightField)}),e.$on("$destroy",function(){i.unbind("keyup")})}}}]),angular.module("app").filter("bytes",function(){return function(e,i){if(isNaN(parseFloat(e))||!isFinite(e))return"-";"undefined"==typeof i&&(i=1);var n=["bytes","kB","MB","GB","TB","PB"],t=Math.floor(Math.log(e)/Math.log(1024));return(e/Math.pow(1024,Math.floor(t))).toFixed(i)+" "+n[t]}}); diff --git a/build/index.html b/build/index.html index da35425..6383827 100644 --- a/build/index.html +++ b/build/index.html @@ -28,11 +28,11 @@
- +
- Browse… + Browse…