statistics.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. /**
  2. * TraCINg-Server - Gathering and visualizing cyber incidents on the world
  3. *
  4. * Copyright 2013 Matthias Gazzari, Annemarie Mattmann, André Wolski
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. var detailChartType = "typeDate";
  19. /*
  20. * queries data, filters it, updates the chart
  21. *
  22. * options: object
  23. * - chart: reference to a highcharts Chart
  24. * - name: string, name of the data that should be displayed
  25. * - detail: boolean, whether to fetch detailed data or just data for the master chart (optional, default: false)
  26. */
  27. function updateChart(options){
  28. var rawSeries;
  29. var title, subtitle, xTitle, yTitle;
  30. // get the chart and delete it from options
  31. var chart = options.chart;
  32. delete options.chart;
  33. options.filter = filter.getFilter();
  34. // fetch the series
  35. if(options.detail){
  36. options.detailChartType = detailChartType;
  37. // patch start and end into the filter
  38. options.filter["start"] = currentStartDate;
  39. options.filter["end"] = currentEndDate;
  40. if(detailChartType == "typeDate"){
  41. options.pie = true;
  42. title = {text: "Types of Incidents"};
  43. subtitle = {text: "per day"};
  44. yTitle = {text: "Incidents per day"};
  45. }
  46. else if(detailChartType == "countryDate"){
  47. options.pie = true;
  48. title = {text: "Source countries"};
  49. subtitle = {text: "per day"};
  50. yTitle = {text: "Incidents per day"};
  51. }
  52. else{
  53. console.log("error: unknown type: " + detailChartType);
  54. return;
  55. }
  56. }
  57. else{
  58. // nothing to do..
  59. }
  60. // some formatting of the chart
  61. if(title || subtitle)
  62. chart.setTitle(title, subtitle);
  63. if(xTitle)
  64. chart.xAxis[0].setTitle(xTitle);
  65. if(yTitle)
  66. chart.yAxis[0].setTitle(yTitle);
  67. chart.showLoading("loading..");
  68. socket.emit("getStatistics", options, function(data){
  69. console.log("getStatistics returned: ", data);
  70. if(!data || data.length == 0){
  71. chart.showLoading("No data for that selection!");
  72. return;
  73. }
  74. // filter the series
  75. var chartSeries = createChartSeries(data, options);
  76. // display the series
  77. setChartData(chart, chartSeries);
  78. });
  79. }
  80. /*
  81. * setChartData
  82. * removes all series from a chart and adds the new series
  83. *
  84. * chart: reference to a highcharts Chart
  85. * data: array of series to be added
  86. */
  87. function setChartData(chart, data){
  88. var oldLenght = chart.series.length;
  89. // add new series
  90. for(var i = 0; i < data.length; i++){
  91. chart.addSeries(data[i], false); // false: do not redraw
  92. }
  93. chart.redraw();
  94. // remove old series
  95. for(var count = 0; count < oldLenght; count++){
  96. chart.series[0].remove(false); // do not redraw
  97. }
  98. chart.hideLoading();
  99. chart.redraw();
  100. $(".highcharts-legend-item > span").each(function(i, el){el.onclick = null;});
  101. }
  102. /*
  103. * createChartSeries
  104. * creates series for the chart from the given series, also calculates the pie serie (if options.pie is true)
  105. *
  106. * arguments:
  107. * * series: array of all series that should be displayed
  108. * * options: optional object of options:
  109. * - pie: boolean if the last serie is a pie
  110. */
  111. function createChartSeries(series, options){
  112. console.log("createChartSeries", series, options);
  113. // store here the data
  114. var result = [];
  115. var pie;
  116. if(options.pie){
  117. pie = {
  118. type: "pie",
  119. name: "Total incidents",
  120. center: [160, 55],
  121. size: 100,
  122. showInLegend: false,
  123. data: [], // the data is calculated below
  124. };
  125. }
  126. // for each serie
  127. for(var i = 0; i < series.length; i++){
  128. var c = series[i];
  129. function getColor(i){
  130. //return Highcharts.getOptions().colors[i];
  131. if(i % 8 == 0)
  132. return "#0000FF"; // blue
  133. else if(i % 8 == 1)
  134. return "#FF0000"; // red
  135. else if(i % 8 == 2)
  136. return "#00FF00"; // green
  137. else if(i % 8 == 3)
  138. return "#FFFF00"; // yellow
  139. else if(i % 8 == 4)
  140. return "#FF00FF"; // magenta
  141. else if(i % 8 == 5)
  142. return "#00FFFF"; // cyan
  143. else if(i % 8 == 6)
  144. return "#FF7F00"; // orange
  145. else if(i % 8 == 7)
  146. return "#808080"; // grey
  147. /*else if(i % 9 == 8)
  148. return "#964B00"; // brown*/
  149. }
  150. // add the serie to the result
  151. result.push({name: c.name, data: c.data, color: getColor(i)});
  152. if(options.pie){
  153. var sum = 0;
  154. // for each entry of the serie
  155. for(var j = 0; j < c.data.length; j++){
  156. // sum the data of this serie for the pie
  157. sum += c.data[j][1];
  158. }
  159. // add the summed serie to the pie
  160. pie.data.push({name: c.name, y: sum, color: getColor(i)});
  161. }
  162. }
  163. if(options.pie){
  164. // add the pie to the result
  165. result.push(pie);
  166. }
  167. return result;
  168. }
  169. // initial timespan for the detailChart
  170. var defaultEndDate = Date.now();
  171. var defaultStartDate = defaultEndDate - (2 * 7 * 24 * 60 * 60 * 1000); // two weeks ago
  172. var currentStartDate = defaultStartDate;
  173. var currentEndDate = defaultEndDate;
  174. var detailChart, masterChart;
  175. // detailed statistics chart
  176. function createDetailChart(masterChart) {
  177. detailChart = $('#chartdivdetail').highcharts({
  178. chart: {
  179. type: 'area'
  180. },
  181. xAxis: {
  182. type: 'datetime'
  183. },
  184. legend: {useHTML: true},
  185. plotOptions: {
  186. area: {
  187. stacking: 'normal',
  188. lineColor: '#666666',
  189. lineWidth: 1,
  190. marker: {
  191. lineWidth: 1,
  192. lineColor: '#666666'
  193. }
  194. }
  195. },
  196. tooltip: {
  197. //formatter: function() {
  198. // return false;
  199. //}
  200. },
  201. }).highcharts();
  202. updateChart({
  203. chart: detailChart,
  204. detail: true,
  205. });
  206. }
  207. // timeline chart
  208. function createMasterChart(){
  209. masterChart = $("#chartdivmaster").highcharts({
  210. title: {text: null},
  211. reflow: false,
  212. borderWidth: 0,
  213. chart: {
  214. type: 'area',
  215. events: {
  216. // listen to the selection event on the master chart to update the detail chart
  217. selection: function(event) {
  218. // get the selection
  219. var extremesObject = event.xAxis[0];
  220. var min = extremesObject.min;
  221. var max = extremesObject.max;
  222. currentStartDate = min;
  223. currentEndDate = max;
  224. // select and filter data for the detailChart and update it
  225. updateChart({
  226. chart: detailChart,
  227. detail: true,
  228. });
  229. // move the plot band to reflect the new detail span
  230. var xAxis = this.xAxis[0];
  231. xAxis.removePlotBand('mask');
  232. xAxis.addPlotBand({
  233. id: 'mask',
  234. from: min,
  235. to: max,
  236. color: 'rgba(255, 144, 0, 0.6)' // #FF9000; alpha = 60%
  237. });
  238. // TODO: for what is the return value?
  239. return false;
  240. }
  241. },
  242. zoomType: 'x',
  243. },
  244. xAxis: {
  245. type: 'datetime',
  246. plotBands: [{
  247. id: 'mask',
  248. from: defaultStartDate,
  249. to: defaultEndDate,
  250. color: 'rgba(255, 144, 0, 0.6)'
  251. }],
  252. },
  253. yAxis: {
  254. gridLineWidth: 0,
  255. title: {text: null},
  256. labels: {enabled: false},
  257. },
  258. legend: {enabled: false},
  259. credits: {enabled: false},
  260. tooltip: {
  261. formatter: function() {
  262. return false;
  263. }
  264. },
  265. plotOptions: {
  266. series: {
  267. fillColor: {
  268. linearGradient: [0, 0, 0, 70],
  269. stops: [
  270. [0, '#4572A7'],
  271. [1, 'rgba(0,0,0,0)']
  272. ]
  273. },
  274. lineWidth: 1,
  275. marker: {
  276. enabled: false
  277. },
  278. shadow: false,
  279. states: {
  280. hover: {
  281. lineWidth: 1
  282. }
  283. },
  284. enableMouseTracking: false
  285. }
  286. },
  287. }, function(masterChart){
  288. createDetailChart(masterChart);
  289. }).highcharts();
  290. updateChart({
  291. chart: masterChart,
  292. detail: false,
  293. });
  294. }
  295. var createdCharts = false;
  296. /*
  297. * create Chart container and objects if not done
  298. * returns if it created or not
  299. */
  300. function createCharts(){
  301. if(!createdCharts){
  302. createdCharts = true;
  303. var container = $('#stats');
  304. container.append("<div id='chartdivdetail' class='chart'></div>");
  305. container.append("<div id='chartdivmaster' class='chart' style='height: 100px;' onHelpActive='help-border'></div><span class='help-block'>Click and drag the mouse across the above timeline to choose a timespan.</span>");
  306. createMasterChart();
  307. return true;
  308. }
  309. return false;
  310. }
  311. function loadStats(detailType, updateMaster){
  312. if(detailType)
  313. detailChartType = detailType;
  314. var created = createCharts();
  315. if(!created){ // createCharts loads the charts with the correct data; if there are already charts we need to update them
  316. updateChart({
  317. chart: detailChart,
  318. detail: true,
  319. });
  320. if(updateMaster){
  321. updateChart({
  322. chart: masterChart,
  323. detail: false,
  324. });
  325. }
  326. }
  327. }
  328. // called when the filter was updated
  329. function filterUpdateStats(){
  330. loadStats(null, true);
  331. }