Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 8fb7747

Browse files
committedSep 4, 2015
Merge pull request #156 from appirio-tech/tom-history-graph
Tom history graph
2 parents f053b7c + ce8bedf commit 8fb7747

File tree

15 files changed

+473
-37
lines changed

15 files changed

+473
-37
lines changed
 

‎app/directives/distribution-graph/distribution-graph.directive.jade

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.graph-viewer
1+
.graph-viewer(ng-show="graphState.show == 'distribution'")
22
.distribution-graph
33

44
.info-port
@@ -8,3 +8,6 @@
88
.rating(ng-if="!displayCoders")
99
.num {{rating}}
1010
.label RATING
11+
12+
.compare
13+
button(ng-click="graphState.show = 'history'") COMPARE

‎app/directives/distribution-graph/distribution-graph.directive.js

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
},
1212
scope: {
1313
promise: '=',
14-
rating: '='
14+
rating: '=',
15+
graphState: '='
1516
},
1617
controller: ['$scope', DistributionGraphController]
1718
};
@@ -55,9 +56,11 @@
5556
'end': Infinity
5657
}
5758
];
58-
var w = 700,
59+
var w = 600,
5960
h = 400,
60-
padding = 100;
61+
padding = { top: 20, left: 100, bottom: 100, right: 0 };
62+
var totalW = w + padding.left + padding.right;
63+
var totalH = h + padding.top + padding.bottom;
6164

6265
activate();
6366

@@ -71,45 +74,48 @@
7174
removeTrailingZeroes(ranges);
7275
var xScale = d3.scale.ordinal()
7376
.domain(d3.range(ranges.length))
74-
.rangeRoundBands([padding, w], 0.05);
77+
.rangeRoundBands([padding.left, padding.left + w], 0.05);
7578
var yScale = d3.scale.linear()
7679
.domain([0, d3.max(ranges, function(range) { return range.number }) + 1])
77-
.range([h - padding, 0]);
80+
.range([totalH - padding.bottom, padding.top]);
7881
var xScale2 = d3.scale.linear()
7982
.domain([ranges[0].start,
8083
d3.max(ranges, function(range) { return range.end })])
81-
.range([padding, w]);
84+
.range([padding.left, totalW - padding.right]);
8285
var svg = d3.select('div.distribution-graph')
8386
.append('svg')
84-
.attr('width', w)
85-
.attr('height', h);
87+
.attr('width', totalW)
88+
.attr('height', totalH);
8689

8790
svg.append('rect')
88-
.attr('x', padding)
89-
.attr('y', 0)
91+
.attr('x', padding.left)
92+
.attr('y', padding.top)
9093
.attr('width', w)
91-
.attr('height', h - padding)
92-
.attr('fill', '#eeeeee')
94+
.attr('height', h)
95+
.attr('fill', '#f6f6f6')
9396

9497
svg.append('g')
9598
.attr('class', 'grid')
96-
.attr('transform', 'translate(' + padding + ',0)')
99+
.attr('transform', 'translate(' + padding.left + ',0)')
97100
.call(
98-
yAxis(5).tickSize(-w, 0, 0)
101+
yAxis(5).tickSize(-totalW, 0, 0)
99102
.tickFormat('')
100103
)
101104

102105
svg.append('g')
103106
.attr('class', 'axis')
104-
.attr('transform', 'translate(' + padding + ',0)')
107+
.attr('transform', 'translate(' + padding.left + ', 0)')
105108
.call(yAxis(5))
106109

107110

111+
function logr(x) {
112+
console.log(x); return x;
113+
}
108114
svg.append('line')
109115
.attr('x1', xScale2($scope.rating))
110-
.attr('y1', h - padding)
116+
.attr('y1', totalH - padding.bottom)
111117
.attr('x2', xScale2($scope.rating))
112-
.attr('y2', 0)
118+
.attr('y2', padding.top)
113119
.attr('class', 'my-rating')
114120
.attr('stroke-width', 2)
115121
.attr('stroke', ratingToColor($scope.colors, $scope.rating));
@@ -127,7 +133,7 @@
127133
})
128134
.attr('width', xScale.rangeBand())
129135
.attr('height', function(d) {
130-
return h - padding - yScale(d.number);
136+
return totalH - padding.bottom - yScale(d.number);
131137
})
132138
.attr('fill', function(d) {
133139
return ratingToColor($scope.colors, d.start);
@@ -153,20 +159,20 @@
153159
.attr('class', 'xaxis')
154160
.attr('x1', function(d, i) {
155161
if (i === 0) {
156-
return padding;
162+
return padding.left;
157163
} else {
158164
return xScale(i) - .5;
159165
}
160166
})
161167
.attr('x2', function(d, i) {
162168
if (i === ranges.length - 1) {
163-
return w;
169+
return totalW - padding.right;
164170
} else {
165171
return xScale(i) + xScale.rangeBand() + .5;
166172
}
167173
})
168-
.attr('y1', h - padding + .5)
169-
.attr('y2', h - padding + .5)
174+
.attr('y1', h + padding.top + .5)
175+
.attr('y2', h + padding.top + .5)
170176
.attr('stroke', function(d) {
171177
return ratingToColor($scope.colors, d.start);
172178
})
@@ -185,7 +191,7 @@
185191

186192
svg.append('g')
187193
.attr('class', 'axis')
188-
.attr('transform', 'translate(0,' + (h - padding) + ')')
194+
.attr('transform', 'translate(0,' + (h + padding.top) + ')')
189195
.call(xAxis)
190196
.selectAll('text')
191197
.attr('x', 9)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.history-graph-container(ng-show="graphState.show == 'history'")
2+
.history-graph
3+
4+
.info-port
5+
.rating
6+
.num {{historyRating || rating}}
7+
.label RATING
8+
.history-info(ng-if="historyRating")
9+
| Attained on {{historyDate}} in challenge "{{historyChallenge}}"
10+
.lower
11+
button(ng-click="graphState.show = 'distribution'") COMPARE
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
(function() {
2+
'use strict';
3+
4+
angular.module('tcUIComponents').directive('historyGraph', historyGraph);
5+
6+
function historyGraph() {
7+
return {
8+
restrict: 'E',
9+
templateUrl: function(elem, attrs) {
10+
return 'directives/history-graph/history-graph.directive.html';
11+
},
12+
scope: {
13+
promise: '=',
14+
rating: '=',
15+
graphState: '='
16+
},
17+
controller: ['$scope', HistoryGraphController]
18+
};
19+
}
20+
21+
function HistoryGraphController($scope) {
22+
$scope.colors = [
23+
// grey
24+
{
25+
'color': '#7f7f7f',
26+
'darkerColor': '#656565',
27+
'start': 0,
28+
'end': 899
29+
},
30+
// green
31+
{
32+
'color': '#99cc09',
33+
'darkerColor': '#7aa307',
34+
'start': 900,
35+
'end': 1199
36+
},
37+
// blue
38+
{
39+
'color': '#09affe',
40+
'darkerColor': '#078ccb',
41+
'start': 1200,
42+
'end': 1499
43+
},
44+
// yellow
45+
{
46+
'color': '#f39426',
47+
'darkerColor': '#c2761e',
48+
'start': 1500,
49+
'end': 2199
50+
},
51+
// red
52+
{
53+
'color': '#fe0866',
54+
'darkerColor': '#cb0651',
55+
'start': 2200,
56+
'end': Infinity
57+
}
58+
];
59+
var w = 600,
60+
h = 400,
61+
padding = { top: 20, right: 5, bottom: 30, left: 50 };
62+
63+
var totalH = h + padding.top + padding.bottom;
64+
var totalW = w + padding.left + padding.right;
65+
66+
activate();
67+
68+
function activate() {
69+
$scope.promise.then(function(history) {
70+
71+
var parseDate = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ").parse;
72+
history.forEach(function(d) {
73+
d.ratingDate = parseDate(d.ratingDate);
74+
});
75+
76+
var x = d3.time.scale()
77+
.range([padding.left + 5, w + padding.left - 5])
78+
.domain(d3.extent(history, function(d) { return d.ratingDate; }));
79+
80+
var y = d3.scale.linear()
81+
.range([h + padding.top - 5, padding.top + 5])
82+
.domain(d3.extent(history, function(d) { return d.newRating; }));
83+
84+
85+
function yAxis(ticks) {
86+
return d3.svg.axis()
87+
.scale(y)
88+
.ticks(ticks || 10)
89+
.orient("left");
90+
}
91+
92+
function xAxis(ticks) {
93+
return d3.svg.axis()
94+
.scale(x)
95+
.ticks(ticks || 10)
96+
.orient("bottom");
97+
}
98+
99+
var line = d3.svg.line()
100+
.interpolate('cardinal')
101+
.x(function(d) { return x(d.ratingDate); })
102+
.y(function(d) { return y(d.newRating); })
103+
104+
105+
var svg = d3.select('.history-graph').append('svg')
106+
.attr('width', w + padding.left + padding.right)
107+
.attr('height', h + padding.top + padding.bottom)
108+
109+
110+
svg.append('rect')
111+
.attr('x', padding.left)
112+
.attr('y', padding.top)
113+
.attr('width', w)
114+
.attr('height', h)
115+
116+
117+
118+
svg.append('g')
119+
.attr('class', 'x axis')
120+
.attr('transform', 'translate(0,' + (h + padding.top) +')')
121+
.call(xAxis());
122+
123+
svg.selectAll('g.x.axis .tick text')
124+
.attr('font-size', function(d) {
125+
return moment(d).format('MM') == '01' ? 12 : 10;
126+
});
127+
128+
129+
svg.append('g')
130+
.attr('class', 'y axis')
131+
.attr('transform', 'translate(' + padding.left + ')')
132+
.call(yAxis().tickFormat(function(d) { return parseInt(d) })
133+
)
134+
135+
svg.append('g')
136+
.attr('class', 'grid')
137+
.attr('transform', 'translate(0, ' + (h + padding.top) + ')')
138+
.call(
139+
xAxis(Math.round(w / 35)).tickSize(-h, 0, 0)
140+
.tickFormat('')
141+
)
142+
143+
svg.append('g')
144+
.attr('class', 'grid')
145+
.attr('transform', 'translate(' + padding.left + ',0)')
146+
.call(
147+
yAxis(Math.round(h / 30)).tickSize(-w, 0, 0)
148+
.tickFormat('')
149+
)
150+
151+
152+
svg.append('path')
153+
.datum(history)
154+
.attr('class', 'line')
155+
.attr('d', line)
156+
157+
158+
// FIXME !!!
159+
// svg.append('g')
160+
// .selectAll('line')
161+
// .data($scope.colors)
162+
// .enter()
163+
// .append('line')
164+
// .attr('x1', totalW - 3)
165+
// .attr('x2', totalW - 3)
166+
// .attr('y1', function(d) {
167+
// return processRatingStripePoint(y(d.start));
168+
// })
169+
// .attr('y2', function(d) {
170+
// return processRatingStripePoint(y(d.end));
171+
// })
172+
// .attr('stroke', function(d) {
173+
// return d.color;
174+
// })
175+
// .attr('stroke-width', 3)
176+
177+
function processRatingStripePoint(y) {
178+
console.log('y:' + y)
179+
if (y < padding.top) {
180+
return padding.top;
181+
} else if (y > totalH - padding.bottom) {
182+
return padding.bottom;
183+
} else {
184+
return y;
185+
}
186+
}
187+
188+
svg.selectAll('circle')
189+
.data(history)
190+
.enter()
191+
.append('circle')
192+
.attr('cx', function(d) {
193+
return x(d.ratingDate);
194+
})
195+
.attr('cy', function(d) {
196+
return y(d.newRating);
197+
})
198+
.attr('fill', function(d) {
199+
return ratingToColor($scope.colors, d.newRating);
200+
})
201+
.on('mouseover', function(d) {
202+
$scope.historyRating = d.newRating;
203+
$scope.historyDate = moment(d.ratingDate).format('YYYY-MM-DD');
204+
$scope.historyChallenge = d.challengeName;
205+
$scope.$digest();
206+
d3.select(this)
207+
.attr('r', 4.0)
208+
.attr('stroke', 'black')
209+
.attr('stroke-width', '.5px');
210+
})
211+
.on('mouseout', function(d) {
212+
$scope.historyRating = undefined;
213+
$scope.$digest();
214+
d3.select(this)
215+
.attr('r', 3.0)
216+
.attr('stroke', 'none')
217+
.attr('stroke-width', '0px');
218+
})
219+
.attr('r', 3.0)
220+
221+
});
222+
223+
}
224+
225+
function ratingToColor(colors, rating) {
226+
colors = colors.filter(function(color) {
227+
return rating >= color.start && rating <= color.end;
228+
});
229+
return colors[0] && colors[0].color || 'black';
230+
}
231+
232+
function ratingToDarkerColor(colors, rating) {
233+
colors = colors.filter(function(color) {
234+
return rating >= color.start && rating <= color.end;
235+
});
236+
return colors[0] && colors[0].darkerColor || 'black';
237+
}
238+
239+
}
240+
241+
})();

‎app/index.jade

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ html
5555
link(rel="stylesheet", href="assets/css/directives/tc-paginator.css")
5656
link(rel="stylesheet", href="assets/css/directives/srm-tile.css")
5757
link(rel="stylesheet", href="assets/css/directives/profile-widget.css")
58+
link(rel="stylesheet", href="assets/css/directives/history-graph.css")
5859
link(rel="stylesheet", href="assets/css/directives/distribution-graph.css")
5960
link(rel="stylesheet", href="assets/css/directives/challenge-tiles.css")
6061
link(rel="stylesheet", href="assets/css/directives/badge-tooltip.css")
@@ -131,6 +132,7 @@ html
131132
script(src="directives/challenge-tiles/challenge-tile.directive.js")
132133
script(src="directives/distribution-graph/distribution-graph.directive.js")
133134
script(src="directives/focus-on.directive.js")
135+
script(src="directives/history-graph/history-graph.directive.js")
134136
script(src="directives/on-file-change.directive.js")
135137
script(src="directives/profile-widget/profile-widget.directive.js")
136138
script(src="directives/right-placeholder.directive.js")

‎app/profile/subtrack/develop/develop.jade

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444

4545
.stats(ng-show="vm.viewing == 'stats' && profileVm.status.stats === 'ready'")
4646
.graphs
47-
distribution-graph(promise="vm.distributionPromise", rating="vm.typeStats.rank.rating")
47+
distribution-graph(promise="vm.distributionPromise", rating="vm.typeStats.rank.rating", graph-state="vm.graphState")
48+
49+
history-graph(promise="vm.historyPromise", rating="vm.typeStats.rank.rating", graph-state="vm.graphState")
4850

4951
h2.detailed Detailed Stats
5052

‎app/profile/subtrack/subtrack.controller.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
function ProfileSubtrackController($scope, ProfileService, $q, $stateParams, ChallengeService, CONSTANTS, $state, $window) {
1010
var vm = this;
11+
vm.graphState = { show: 'history' };
1112
vm.subTrack = decodeURIComponent($stateParams.subTrack || '') || '';
1213
vm.track = $stateParams.track;
1314
vm.viewing = 'challenges';
@@ -39,6 +40,14 @@
3940
vm.distributionPromise.then(function(data) {
4041
vm.distribution = data.distribution;
4142
});
43+
var historyDeferred = $q.defer();
44+
vm.historyPromise = historyDeferred.promise;
45+
ProfileService.getHistoryStats(profileVm.profile.handle).then(function(data) {
46+
if (data.handle) {
47+
vm.history = ProfileService.getChallengeTypeStats(data, vm.track, vm.subTrack).history;
48+
historyDeferred.resolve(vm.history);
49+
}
50+
});
4251
profileVm.statsPromise.then(function(data) {
4352
vm.typeStats = ProfileService.getChallengeTypeStats(
4453
profileVm.stats,

‎app/profile/subtrack/subtrack.spec.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ describe('SubTrack Controller', function() {
66
var mockStats = mockData.getMockStats();
77
var mockSkills = mockData.getMockSkills();
88
var mockChallenges = mockData.getMockiOSChallenges();
9+
var mockHistory = mockData.getMockHistory();
910
var apiUrl;
1011
var track = 'develop', subTrack = 'development';
1112
var profileScope, scope;
@@ -57,6 +58,10 @@ describe('SubTrack Controller', function() {
5758
.when('GET', apiUrl + '/members/rakesh/stats/')
5859
.respond(200, {result: {content: mockStats}});
5960
// mock skills
61+
$httpBackend
62+
.when('GET', apiUrl + '/members/albertwang/stats/history/')
63+
.respond(200, mockHistory);
64+
// mock history
6065
$httpBackend
6166
.when('GET', apiUrl + '/members/rakesh/skills/')
6267
.respond(200, {result: {content: mockSkills}});
@@ -67,6 +72,13 @@ describe('SubTrack Controller', function() {
6772
deferred.resolve(resp);
6873
return deferred.promise;
6974
});
75+
76+
sinon.stub(profileService, 'getHistoryStats', function(handle) {
77+
var deferred = $q.defer();
78+
var resp = {};
79+
deferred.resolve(resp);
80+
return deferred.promise;
81+
});
7082
});
7183

7284
afterEach(function() {

‎app/services/profile.service.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
getUserFinancials: getUserFinancials,
1818
getUserStats: getUserStats,
1919
getDistributionStats: getDistributionStats,
20+
getHistoryStats: getHistoryStats,
2021
// auxiliary functions for profile
2122
getRanks: getRanks,
2223
getChallengeTypeStats: getChallengeTypeStats,
@@ -62,6 +63,10 @@
6263
});
6364
}
6465

66+
function getHistoryStats(handle) {
67+
return restangular.one('members', handle).one('stats').one('history').get();
68+
}
69+
6570
function getRanks(stats) {
6671
if (!stats) {
6772
return [];

‎app/settings/update-password/update-password.spec.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ describe('Update Password Controller', function() {
1212
};
1313

1414
vm = $controller('UpdatePasswordController', {
15+
userData: {
16+
handle: 'foo',
17+
email: 'bar'
18+
},
1519
UserService: {
1620
getUserIdentity: function() {
1721
return {

‎app/skill-picker/skill-picker.spec.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ describe('Skill Picker Controller', function() {
33
var vm;
44

55
beforeEach(function() {
6-
bard.appModule('tc.settings');
6+
bard.appModule('tc.skill-picker');
77
bard.inject(this, '$controller', '$rootScope', '$q');
88

99
vm = $controller('SkillPickerController', {
10-
userData: userData
1110
});
1211
});
1312

@@ -17,8 +16,4 @@ describe('Skill Picker Controller', function() {
1716
expect(vm).to.exist;
1817
});
1918

20-
describe('user data', function() {
21-
22-
});
23-
2419
});

‎assets/css/directives/distribution-graph.scss

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@
5252
}
5353
.info-port {
5454
margin-left: 2px;
55-
background: #eeeeee;
56-
height: 300px;
55+
margin-top: 20px;
56+
background: #f6f6f6;
57+
height: 400px;
5758
width: 250px;
5859
display: flex;
5960
flex-direction: column;
@@ -80,6 +81,11 @@
8081
.label {
8182
color: white;
8283
}
84+
margin-bottom: 20px;
85+
}
86+
87+
.compare {
88+
8389
}
8490
}
8591
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
.history-graph-container {
2+
display: flex;
3+
flex-direction: row;
4+
.history-graph {
5+
6+
margin-left: 40px;
7+
8+
.axis path,
9+
.axis line {
10+
fill: none;
11+
stroke: none;
12+
shape-rendering: crispEdges;
13+
}
14+
15+
.x.axis path {
16+
display: none;
17+
}
18+
19+
rect {
20+
fill: #f6f6f6;
21+
}
22+
23+
.line {
24+
fill: none;
25+
stroke: #c5c5c5;
26+
stroke-width: 2.3px;
27+
}
28+
29+
.grid .tick {
30+
stroke: white;
31+
opacity: 0.7;
32+
}
33+
34+
.y.axis .tick text {
35+
font-size: 10px;
36+
}
37+
38+
39+
}
40+
.info-port {
41+
background: #f6f6f6;
42+
margin-top: 20px;
43+
height: 400px;
44+
width: 250px;
45+
display: flex;
46+
flex-direction: column;
47+
justify-content: center;
48+
align-items: center;
49+
margin-right: 100px;
50+
.rating {
51+
width: 130px;
52+
height: 130px;
53+
background: #09affe;
54+
-moz-border-radius: 65px;
55+
-webkit-border-radius: 65px;
56+
border-radius: 65px;
57+
display: flex;
58+
flex-direction: column;
59+
justify-content: center;
60+
align-items: center;
61+
62+
.num {
63+
color: white;
64+
font-size: 25px;
65+
font-weight: bold;
66+
}
67+
.label {
68+
color: white;
69+
}
70+
margin-bottom: 20px;
71+
}
72+
.history-info {
73+
margin-bottom: 20px;
74+
padding: 0 5px;
75+
text-align: center;
76+
}
77+
.lower {
78+
}
79+
}
80+
}

‎assets/css/profile/subtrack.scss

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,12 @@
1717
}
1818
}
1919
.develop, .design {
20-
20+
2121
.stats {
2222
.graphs {
2323
display: flex;
2424
flex-direction: row;
2525
justify-content: center;
26-
img {
27-
width: 700px;
28-
}
2926
}
3027
h2.detailed {
3128
margin: 50px auto 25px auto;

‎tests/test-helpers/mock-data.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ var mockData = (function() {
1414
getMockUserProfile: getMockUserProfile,
1515
getMockMarathons: getMockMarathons,
1616
getMockProfile: getMockProfile,
17+
getMockHistory: getMockHistory,
1718
getMockStats: getMockStats,
1819
getMockSkills: getMockSkills,
1920
getMockSRMs: getMockSRMs,
@@ -1291,6 +1292,68 @@ var mockData = (function() {
12911292
};
12921293
}
12931294

1295+
function getMockHistory() {
1296+
return {
1297+
"id": "-306aafb8:14f65e30765:-8000",
1298+
"result": {
1299+
"success": true,
1300+
"status": 200,
1301+
"metadata": null,
1302+
"content": {
1303+
"updatedAt": null,
1304+
"createdAt": null,
1305+
"createdBy": null,
1306+
"updatedBy": null,
1307+
"userId": 151743,
1308+
"handle": "Ghostar",
1309+
"DEVELOP": {
1310+
"subTracks": [
1311+
{
1312+
"id": 112,
1313+
"name": "DESIGN",
1314+
"history": [
1315+
{
1316+
"challengeId": 30009817,
1317+
"challengeName": "Disney Marine Munch - Flash Game Module Architecture",
1318+
"ratingDate": "2010-02-28T13:00:00.000Z",
1319+
"newRating": 1172
1320+
}
1321+
]
1322+
},
1323+
{
1324+
"id": 125,
1325+
"name": "ASSEMBLY_COMPETITION",
1326+
"history": [
1327+
{
1328+
"challengeId": 30007872,
1329+
"challengeName": "Best Buy Blackberry Web Application Wrapper Assembly",
1330+
"ratingDate": "2009-10-18T23:30:00.000Z",
1331+
"newRating": 1515
1332+
},
1333+
{
1334+
"challengeId": 30008010,
1335+
"challengeName": "Best Buy Android Web Application Wrapper Assembly",
1336+
"ratingDate": "2009-10-24T01:00:00.000Z",
1337+
"newRating": 1566
1338+
}
1339+
]
1340+
}
1341+
]
1342+
},
1343+
"DATA_SCIENCE": {
1344+
"SRM": {
1345+
"history": []
1346+
},
1347+
"MARATHON_MATCH": {
1348+
"history": []
1349+
}
1350+
}
1351+
}
1352+
},
1353+
"version": "v3"
1354+
}
1355+
}
1356+
12941357
function getMockStats() {
12951358
return {
12961359
"updatedAt": null,

0 commit comments

Comments
 (0)
This repository has been archived.