sw2at-ui 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3a5404de1a4d2e5bc0345da18a15c682e7a0b95b
4
- data.tar.gz: 05addc5cc180d16f16a6865612076df959e9f92d
3
+ metadata.gz: 7857e82e50e84a809a23b3954a5e09d0d8d2cf11
4
+ data.tar.gz: 01c616fd52714cf6109fe0fb072a26d57f11702d
5
5
  SHA512:
6
- metadata.gz: 41174a6f3ad946fb9a140f45d699d075230c739816cf7ea553c61a70af63811b5b70a772dc8be406e3573eb0e76b5ec8f31256ae5d003ee16afb8ec439981089
7
- data.tar.gz: aeafc9c19be4f8d5af4af4e9e655c5229b455309f2cc8c420a2b145e9e09d35762b3ab5f8e3e83ed18fe73638a6aea4f123f1559819492cccdbde2ab17ed36aa
6
+ metadata.gz: 4a34ae34569c6985f5f9736d260a2834f431381884d0b5ac088bf57f44b9c3388a29671059bec6534befa0edfa9be47a3198b72b67789b8695f9d409457e1e93
7
+ data.tar.gz: 40f01fde652e0c708f8f381db0ff20144da05007208fbb145553239b31cf36f52ae50fa5b5ac0a7a553a1c07c569930a129709c0209fd84b445b19b0693d576a
data/Gemfile CHANGED
@@ -2,7 +2,7 @@ source 'http://rubygems.org'
2
2
 
3
3
  gem 'rails', '>= 3.1'
4
4
 
5
- gem 'fire-model', '~> 0.0.15'
5
+ gem 'fire-model', '~> 0.0.17'
6
6
  gem 'slim-rails'
7
7
  gem 'sass-rails'
8
8
  gem 'coffee-rails'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.8
1
+ 0.0.9
@@ -4,6 +4,11 @@
4
4
  #= require_tree ./factories
5
5
  #= require_tree ./controllers
6
6
 
7
+ window.Swat =
8
+ debug: true#false
9
+ log: (message)->
10
+ return unless window.Swat.debug
11
+ console.log(message)
7
12
 
8
13
  App = angular.module 'SWAT', [
9
14
  'ngResource'
@@ -11,6 +16,7 @@ App = angular.module 'SWAT', [
11
16
  'ui.router'
12
17
  'ui.bootstrap'
13
18
  'ngClipboard'
19
+ 'highcharts-ng'
14
20
  ]
15
21
 
16
22
  App.config ($urlRouterProvider, $stateProvider) ->
@@ -29,6 +35,13 @@ App.config ($urlRouterProvider, $stateProvider) ->
29
35
  templateUrl: "/swat/pages/revisions/show.html"
30
36
  controller: 'RevisionCtrl'
31
37
  )
38
+ .state("summary",
39
+ url: "/swat/summary/revisions/:branch/:user/:time"
40
+ views:
41
+ content:
42
+ templateUrl: "/swat/pages/revisions/summary.html"
43
+ controller: 'SummaryCtrl'
44
+ )
32
45
  App.config(['ngClipProvider', (ngClipProvider)->
33
46
  ngClipProvider.setPath("/assets/swat/bower_components/zeroclipboard/dist/ZeroClipboard.swf")
34
47
  ])
@@ -1,7 +1,7 @@
1
1
  angular.module("SWAT").controller "RevisionsCtrl", ($rootScope, $scope, $state, RevisionService)->
2
2
 
3
3
  $scope.init = ->
4
- console.log('Revisions Controller initalized!')
4
+ window.Swat.log('Revisions Controller initalized!')
5
5
  $scope.initRevisions()
6
6
 
7
7
  $scope.initRevisions = ->
@@ -1,7 +1,7 @@
1
1
  angular.module("SWAT").controller "RootCtrl", ($rootScope, $scope, $state) ->
2
2
 
3
3
  $scope.init = ->
4
- console.log 'Swat Root initialized.'
4
+ window.Swat.log 'Swat Root initialized.'
5
5
 
6
6
  $scope.init()
7
7
 
@@ -0,0 +1,72 @@
1
+ angular.module("SWAT").controller "SummaryCtrl", ($rootScope, $scope, $state, $stateParams
2
+ RevisionService, FailsGraph, SwatHelpers) ->
3
+
4
+ $scope.init = ->
5
+ $scope.summary = {}
6
+ $scope.reloadData()
7
+
8
+ $scope.reloadData = ->
9
+ return if $scope.revisionPromise && !$scope.revisionPromise.$resolved
10
+ params = { branch: $stateParams.branch, user: $stateParams.user, time: $stateParams.time }
11
+ $scope.revisionPromise = RevisionService.get(params)
12
+ $scope.revisionPromise.$promise.then($scope.initInformation)
13
+
14
+ $scope.initInformation = (revision)->
15
+ $scope.revision = revision
16
+ $scope.tests = _.flatten(_.map($scope.revision.data.threads, (thread)-> thread.tests || []))
17
+ window.Swat.log($scope.tests)
18
+ $scope.initFails()
19
+ $scope.initExceptions()
20
+ $scope.initMetrics()
21
+ $scope.initFailsStatsGraph()
22
+
23
+ $scope.initFails = ->
24
+ $scope.summary.fails = _.select($scope.tests, (t)->(t.exception) )
25
+
26
+ $scope.initExceptions = ->
27
+ groups = _.groupBy($scope.summary.fails, (f)->(f.exception.message) )
28
+ window.Swat.log(groups)
29
+
30
+ result = []
31
+ for exMessage in _.keys(groups)
32
+ exception =
33
+ message: exMessage
34
+ backtrace: groups[exMessage][0].exception.backtrace
35
+ tests: groups[exMessage]
36
+ result.push(exception)
37
+
38
+ window.Swat.log(result)
39
+ $scope.summary.exceptions = result
40
+
41
+ $scope.initFailsStatsGraph = ->
42
+ $scope.failsStats = new FailsGraph($scope.tests, $scope.summary.fails)
43
+ return
44
+ $scope.failsStats =1
45
+
46
+ $scope.initMetrics = ->
47
+ result = []
48
+ totalTests = $scope.tests.length
49
+ totalFailedTests = $scope.summary.fails.length
50
+ totalDuration = _.sum($scope.tests, 'run_time')
51
+ totalThreadDuration = _.sum($scope.revision.data.threads, 'total_runtime')
52
+ successPercentage = (($scope.tests.length - $scope.summary.fails.length) / $scope.tests.length*100)
53
+
54
+ result.push({ name: 'Revision Name', value: $scope.revision.data.name })
55
+ result.push({ name: 'Revision Status', value: $scope.revision.data.status.name })
56
+ result.push({ name: 'Revision Branch', value: $scope.revision.data.branch })
57
+ result.push({ name: 'Revisor', value: $scope.revision.data.user })
58
+ result.push({ name: 'Threads Count', value: $scope.revision.data.threads_count })
59
+
60
+ result.push({ name: 'Total Tests', value: totalTests })
61
+ result.push({ name: 'Total Failed Tests', value: totalFailedTests })
62
+
63
+ result.push({ name: 'Total Tests Duration', value: SwatHelpers.formatTime(totalDuration) })
64
+ result.push({ name: 'Total Threads Duration', value: SwatHelpers.formatTime(totalThreadDuration) })
65
+
66
+ result.push({ name: 'Success Percentage', value: (successPercentage.toFixed(2)+'%') })
67
+
68
+ $scope.summary.metrics = result
69
+
70
+ $scope.init()
71
+
72
+
@@ -0,0 +1,45 @@
1
+ class FailsGraph
2
+ constructor: (@tests, @fails) ->
3
+ conf: ->
4
+
5
+ res =
6
+ options:
7
+ chart:
8
+ plotBackgroundColor: null
9
+ plotBorderWidth: null
10
+ plotShadow: false
11
+ type: 'pie'
12
+ title:
13
+ text: 'Fail Rate'
14
+
15
+ tooltip:
16
+ pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
17
+ plotOptions: pie:
18
+ allowPointSelect: true
19
+ cursor: 'pointer'
20
+ dataLabels:
21
+ enabled: true
22
+ format:
23
+ '<b>{point.name}</b>: {point.percentage:.1f} %'
24
+ style:
25
+ color:
26
+ Highcharts.theme and Highcharts.theme.contrastTextColor or 'black'
27
+ series: [ {
28
+ name: 'Results'
29
+ colors: [ '#1FEC33', '#EC243B', '#90ed7d', '#f7a35c', '#8085e9', '#f15c80', '#e4d354', '#2b908f', '#f45b5b', '#91e8e1']
30
+ colorByPoint: true
31
+ data: [
32
+ {
33
+ name: 'Passed Examples'
34
+ y: (@tests.length - @fails.length)
35
+ }
36
+ {
37
+ name: 'Failed Examples'
38
+ y: @fails.length
39
+ }
40
+ ]
41
+ } ]
42
+
43
+ angular.module("SWAT").factory "FailsGraph", ->
44
+ (tests, fails)->
45
+ new FailsGraph(tests, fails).conf()
@@ -0,0 +1,7 @@
1
+ angular.module("SWAT").factory "SwatHelpers", ->
2
+ Helpers =
3
+ formatTime: (seconds) ->
4
+ hh = Math.floor(seconds / 3600)
5
+ mm = Math.floor(seconds / 60) % 60
6
+ ss = Math.floor(seconds) % 60
7
+ (if hh then (if hh < 10 then '0' else '') + hh + ':' else '') + (if mm < 10 and hh then '0' else '') + mm + ':' + (if ss < 10 then '0' else '') + ss
@@ -1,8 +1,7 @@
1
1
  angular.module("SWAT").factory "RevisionModel", ->
2
2
  class RevisionModel
3
3
  constructor: (@data) ->
4
- #@prepareThreads()
5
- console.log(@data)
4
+ window.Swat.log(@data)
6
5
 
7
6
  prepareThreads: ->
8
7
  for thread in @data.threads
@@ -12,4 +12,7 @@
12
12
  #= require swat/bower_components/angular-bootstrap/ui-bootstrap.js
13
13
  #= require swat/bower_components/angular-bootstrap/ui-bootstrap-tpls.js
14
14
  #= require swat/bower_components/ng-clip/src/ngClip
15
- #= require swat/bower_components/zeroclipboard/dist/ZeroClipboard.js
15
+ #= require swat/bower_components/zeroclipboard/dist/ZeroClipboard.js
16
+
17
+ #= require swat/lib/highcharts.src.js
18
+ #= require swat/lib/highcharts-ng.js
@@ -0,0 +1,396 @@
1
+ if (typeof module !== 'undefined' && typeof exports !== 'undefined' && module.exports === exports){
2
+ module.exports = 'highcharts-ng';
3
+ }
4
+ (function () {
5
+ 'use strict';
6
+ /*global angular: false, Highcharts: false */
7
+
8
+ angular.module('highcharts-ng', [])
9
+ .factory('highchartsNGUtils', highchartsNGUtils)
10
+ .directive('highchart', ['highchartsNGUtils', '$timeout', highchart]);
11
+
12
+ function highchartsNGUtils() {
13
+
14
+ return {
15
+
16
+ //IE8 support
17
+ indexOf: function (arr, find, i /*opt*/) {
18
+ if (i === undefined) i = 0;
19
+ if (i < 0) i += arr.length;
20
+ if (i < 0) i = 0;
21
+ for (var n = arr.length; i < n; i++)
22
+ if (i in arr && arr[i] === find)
23
+ return i;
24
+ return -1;
25
+ },
26
+
27
+ prependMethod: function (obj, method, func) {
28
+ var original = obj[method];
29
+ obj[method] = function () {
30
+ var args = Array.prototype.slice.call(arguments);
31
+ func.apply(this, args);
32
+ if (original) {
33
+ return original.apply(this, args);
34
+ } else {
35
+ return;
36
+ }
37
+
38
+ };
39
+ },
40
+
41
+ deepExtend: function deepExtend(destination, source) {
42
+ //Slightly strange behaviour in edge cases (e.g. passing in non objects)
43
+ //But does the job for current use cases.
44
+ if (angular.isArray(source)) {
45
+ destination = angular.isArray(destination) ? destination : [];
46
+ for (var i = 0; i < source.length; i++) {
47
+ destination[i] = deepExtend(destination[i] || {}, source[i]);
48
+ }
49
+ } else if (angular.isObject(source)) {
50
+ for (var property in source) {
51
+ destination[property] = deepExtend(destination[property] || {}, source[property]);
52
+ }
53
+ } else {
54
+ destination = source;
55
+ }
56
+ return destination;
57
+ }
58
+ };
59
+ }
60
+
61
+ function highchart(highchartsNGUtils, $timeout) {
62
+
63
+ // acceptable shared state
64
+ var seriesId = 0;
65
+ var ensureIds = function (series) {
66
+ var changed = false;
67
+ angular.forEach(series, function(s) {
68
+ if (!angular.isDefined(s.id)) {
69
+ s.id = 'series-' + seriesId++;
70
+ changed = true;
71
+ }
72
+ });
73
+ return changed;
74
+ };
75
+
76
+ // immutable
77
+ var axisNames = [ 'xAxis', 'yAxis' ];
78
+ var chartTypeMap = {
79
+ 'stock': 'StockChart',
80
+ 'map': 'Map',
81
+ 'chart': 'Chart'
82
+ };
83
+
84
+ var getMergedOptions = function (scope, element, config) {
85
+ var mergedOptions = {};
86
+
87
+ var defaultOptions = {
88
+ chart: {
89
+ events: {}
90
+ },
91
+ title: {},
92
+ subtitle: {},
93
+ series: [],
94
+ credits: {},
95
+ plotOptions: {},
96
+ navigator: {enabled: false}
97
+ };
98
+
99
+ if (config.options) {
100
+ mergedOptions = highchartsNGUtils.deepExtend(defaultOptions, config.options);
101
+ } else {
102
+ mergedOptions = defaultOptions;
103
+ }
104
+ mergedOptions.chart.renderTo = element[0];
105
+
106
+ angular.forEach(axisNames, function(axisName) {
107
+ if(angular.isDefined(config[axisName])) {
108
+ mergedOptions[axisName] = angular.copy(config[axisName]);
109
+
110
+ if(angular.isDefined(config[axisName].currentMin) ||
111
+ angular.isDefined(config[axisName].currentMax)) {
112
+
113
+ highchartsNGUtils.prependMethod(mergedOptions.chart.events, 'selection', function(e){
114
+ var thisChart = this;
115
+ if (e[axisName]) {
116
+ scope.$apply(function () {
117
+ scope.config[axisName].currentMin = e[axisName][0].min;
118
+ scope.config[axisName].currentMax = e[axisName][0].max;
119
+ });
120
+ } else {
121
+ //handle reset button - zoom out to all
122
+ scope.$apply(function () {
123
+ scope.config[axisName].currentMin = thisChart[axisName][0].dataMin;
124
+ scope.config[axisName].currentMax = thisChart[axisName][0].dataMax;
125
+ });
126
+ }
127
+ });
128
+
129
+ highchartsNGUtils.prependMethod(mergedOptions.chart.events, 'addSeries', function(e){
130
+ scope.config[axisName].currentMin = this[axisName][0].min || scope.config[axisName].currentMin;
131
+ scope.config[axisName].currentMax = this[axisName][0].max || scope.config[axisName].currentMax;
132
+ });
133
+ }
134
+ }
135
+ });
136
+
137
+ if(config.title) {
138
+ mergedOptions.title = config.title;
139
+ }
140
+ if (config.subtitle) {
141
+ mergedOptions.subtitle = config.subtitle;
142
+ }
143
+ if (config.credits) {
144
+ mergedOptions.credits = config.credits;
145
+ }
146
+ if(config.size) {
147
+ if (config.size.width) {
148
+ mergedOptions.chart.width = config.size.width;
149
+ }
150
+ if (config.size.height) {
151
+ mergedOptions.chart.height = config.size.height;
152
+ }
153
+ }
154
+ return mergedOptions;
155
+ };
156
+
157
+ var updateZoom = function (axis, modelAxis) {
158
+ var extremes = axis.getExtremes();
159
+ if(modelAxis.currentMin !== extremes.dataMin || modelAxis.currentMax !== extremes.dataMax) {
160
+ axis.setExtremes(modelAxis.currentMin, modelAxis.currentMax, false);
161
+ }
162
+ };
163
+
164
+ var processExtremes = function(chart, axis, axisName) {
165
+ if(axis.currentMin || axis.currentMax) {
166
+ chart[axisName][0].setExtremes(axis.currentMin, axis.currentMax, true);
167
+ }
168
+ };
169
+
170
+ var chartOptionsWithoutEasyOptions = function (options) {
171
+ return highchartsNGUtils.deepExtend({}, options, {data: null, visible: null});
172
+ };
173
+
174
+ var getChartType = function(scope) {
175
+ if (scope.config === undefined) return 'Chart';
176
+ return chartTypeMap[('' + scope.config.chartType).toLowerCase()] ||
177
+ (scope.config.useHighStocks ? 'StockChart' : 'Chart');
178
+ };
179
+
180
+ return {
181
+ restrict: 'EAC',
182
+ replace: true,
183
+ template: '<div></div>',
184
+ scope: {
185
+ config: '=',
186
+ disableDataWatch: '='
187
+ },
188
+ link: function (scope, element, attrs) {
189
+ // We keep some chart-specific variables here as a closure
190
+ // instead of storing them on 'scope'.
191
+
192
+ // prevSeriesOptions is maintained by processSeries
193
+ var prevSeriesOptions = {};
194
+
195
+ var processSeries = function(series) {
196
+ var i;
197
+ var ids = [];
198
+
199
+ if(series) {
200
+ var setIds = ensureIds(series);
201
+ if(setIds) {
202
+ //If we have set some ids this will trigger another digest cycle.
203
+ //In this scenario just return early and let the next cycle take care of changes
204
+ return false;
205
+ }
206
+
207
+ //Find series to add or update
208
+ angular.forEach(series, function(s) {
209
+ ids.push(s.id);
210
+ var chartSeries = chart.get(s.id);
211
+ if (chartSeries) {
212
+ if (!angular.equals(prevSeriesOptions[s.id], chartOptionsWithoutEasyOptions(s))) {
213
+ chartSeries.update(angular.copy(s), false);
214
+ } else {
215
+ if (s.visible !== undefined && chartSeries.visible !== s.visible) {
216
+ chartSeries.setVisible(s.visible, false);
217
+ }
218
+ chartSeries.setData(angular.copy(s.data), false);
219
+ }
220
+ } else {
221
+ chart.addSeries(angular.copy(s), false);
222
+ }
223
+ prevSeriesOptions[s.id] = chartOptionsWithoutEasyOptions(s);
224
+ });
225
+
226
+ // Shows no data text if all series are empty
227
+ if(scope.config.noData) {
228
+ var chartContainsData = false;
229
+
230
+ for(i = 0; i < series.length; i++) {
231
+ if (series[i].data && series[i].data.length > 0) {
232
+ chartContainsData = true;
233
+
234
+ break;
235
+ }
236
+ }
237
+
238
+ if (!chartContainsData) {
239
+ chart.showLoading(scope.config.noData);
240
+ } else {
241
+ chart.hideLoading();
242
+ }
243
+ }
244
+ }
245
+
246
+ //Now remove any missing series
247
+ for(i = chart.series.length - 1; i >= 0; i--) {
248
+ var s = chart.series[i];
249
+ if (s.options.id !== 'highcharts-navigator-series' && highchartsNGUtils.indexOf(ids, s.options.id) < 0) {
250
+ s.remove(false);
251
+ }
252
+ }
253
+
254
+ return true;
255
+ };
256
+
257
+ // chart is maintained by initChart
258
+ var chart = false;
259
+ var initChart = function() {
260
+ if (chart) chart.destroy();
261
+ prevSeriesOptions = {};
262
+ var config = scope.config || {};
263
+ var mergedOptions = getMergedOptions(scope, element, config);
264
+ var func = config.func || undefined;
265
+ var chartType = getChartType(scope);
266
+
267
+ chart = new Highcharts[chartType](mergedOptions, func);
268
+
269
+ for (var i = 0; i < axisNames.length; i++) {
270
+ if (config[axisNames[i]]) {
271
+ processExtremes(chart, config[axisNames[i]], axisNames[i]);
272
+ }
273
+ }
274
+ if(config.loading) {
275
+ chart.showLoading();
276
+ }
277
+ config.getHighcharts = function() {
278
+ return chart;
279
+ };
280
+
281
+ };
282
+ initChart();
283
+
284
+
285
+ if(scope.disableDataWatch){
286
+ scope.$watchCollection('config.series', function (newSeries, oldSeries) {
287
+ processSeries(newSeries);
288
+ chart.redraw();
289
+ });
290
+ } else {
291
+ scope.$watch('config.series', function (newSeries, oldSeries) {
292
+ var needsRedraw = processSeries(newSeries);
293
+ if(needsRedraw) {
294
+ chart.redraw();
295
+ }
296
+ }, true);
297
+ }
298
+
299
+ scope.$watch('config.title', function (newTitle) {
300
+ chart.setTitle(newTitle, true);
301
+ }, true);
302
+
303
+ scope.$watch('config.subtitle', function (newSubtitle) {
304
+ chart.setTitle(true, newSubtitle);
305
+ }, true);
306
+
307
+ scope.$watch('config.loading', function (loading) {
308
+ if(loading) {
309
+ chart.showLoading(loading === true ? null : loading);
310
+ } else {
311
+ chart.hideLoading();
312
+ }
313
+ });
314
+ scope.$watch('config.noData', function (noData) {
315
+ if(scope.config && scope.config.loading) {
316
+ chart.showLoading(noData);
317
+ }
318
+ }, true);
319
+
320
+ scope.$watch('config.credits.enabled', function (enabled) {
321
+ if (enabled) {
322
+ chart.credits.show();
323
+ } else if (chart.credits) {
324
+ chart.credits.hide();
325
+ }
326
+ });
327
+
328
+ scope.$watch(getChartType, function (chartType, oldChartType) {
329
+ if (chartType === oldChartType) return;
330
+ initChart();
331
+ });
332
+
333
+ angular.forEach(axisNames, function(axisName) {
334
+ scope.$watch('config.' + axisName, function(newAxes, oldAxes) {
335
+ if (newAxes === oldAxes || !newAxes) {
336
+ return;
337
+ }
338
+
339
+ if (angular.isArray(newAxes)) {
340
+
341
+ for (var axisIndex = 0; axisIndex < newAxes.length; axisIndex++) {
342
+ var axis = newAxes[axisIndex];
343
+
344
+ if (axisIndex < chart[axisName].length) {
345
+ chart[axisName][axisIndex].update(axis, false);
346
+ updateZoom(chart[axisName][axisIndex], angular.copy(axis));
347
+ }
348
+
349
+ }
350
+
351
+ } else {
352
+ // update single axis
353
+ chart[axisName][0].update(newAxes, false);
354
+ updateZoom(chart[axisName][0], angular.copy(newAxes));
355
+ }
356
+
357
+ chart.redraw();
358
+ }, true);
359
+ });
360
+ scope.$watch('config.options', function (newOptions, oldOptions, scope) {
361
+ //do nothing when called on registration
362
+ if (newOptions === oldOptions) return;
363
+ initChart();
364
+ processSeries(scope.config.series);
365
+ chart.redraw();
366
+ }, true);
367
+
368
+ scope.$watch('config.size', function (newSize, oldSize) {
369
+ if(newSize === oldSize) return;
370
+ if(newSize) {
371
+ chart.setSize(newSize.width || chart.chartWidth, newSize.height || chart.chartHeight);
372
+ }
373
+ }, true);
374
+
375
+ scope.$on('highchartsng.reflow', function () {
376
+ chart.reflow();
377
+ });
378
+
379
+ scope.$on('$destroy', function() {
380
+ if (chart) {
381
+ try{
382
+ chart.destroy();
383
+ }catch(ex){
384
+ // fail silently as highcharts will throw exception if element doesn't exist
385
+ }
386
+
387
+ $timeout(function(){
388
+ element.remove();
389
+ }, 0);
390
+ }
391
+ });
392
+
393
+ }
394
+ };
395
+ }
396
+ }());