sw2at-ui 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }());