Skip to content
This repository was archived by the owner on Jan 23, 2025. It is now read-only.

Commit 0a6679a

Browse files
committed
Merge pull request #170 from cloudspokes/james-users-api-data
support optional param for user profile API with granular data retrie
2 parents f96456d + d797f1b commit 0a6679a

16 files changed

+484
-148
lines changed

actions/memberStatistics.js

Lines changed: 90 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
22
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
33
*
4-
* @version 1.14
5-
* @author Sky_, Ghost_141, muzehyun, hesibo, isv, LazyChild
4+
* @version 1.15
5+
* @author Sky_, Ghost_141, muzehyun, hesibo, isv, LazyChild, jamestc
66
* changes in 1.1:
77
* - implement marathon statistics
88
* changes in 1.2:
@@ -36,6 +36,8 @@
3636
* changes in 1.14
3737
* - added my profile api
3838
* - modified public profile api(basic user profile api), only return public information
39+
* changes in 1.15
40+
* - enabled granular data access in getBasicUserProfile via optional query param
3941
*/
4042
"use strict";
4143
var async = require('async');
@@ -118,6 +120,24 @@ function getBasicUserProfile(api, handle, privateInfoEligibility, dbConnectionMa
118120
},
119121
result;
120122

123+
var loadData;
124+
// check for an optional data query string param than enables loading a subset of data
125+
var requestedData = connection.rawConnection.parsedURL.query.data;
126+
if (_.isDefined(requestedData)) {
127+
// NOTE: an empty value is acceptable and indicates only basic data is returned
128+
loadData = {};
129+
if (requestedData) {
130+
// data is comma delimited string of requested data
131+
var parts = requestedData.split(',');
132+
_.each(parts, function (part) {
133+
loadData[part] = true;
134+
});
135+
}
136+
api.log("Requested data param found: " + requestedData, "debug");
137+
} else {
138+
loadData = {earnings:true, ratings:true, achievements:true, address:true, email:true}; // load all data by default
139+
}
140+
121141
async.waterfall([
122142
function (cb) {
123143
if (privateInfoEligibility) {
@@ -139,103 +159,95 @@ function getBasicUserProfile(api, handle, privateInfoEligibility, dbConnectionMa
139159
};
140160
async.parallel({
141161
basic: execQuery('basic'),
142-
earning: execQuery('overall_earning'),
143-
ratingSummary: execQuery('rating_summary'),
144-
achievements: execQuery('achievements'),
145-
privateInfo: privateInfoEligibility ? execQuery('private') : function (cbx) { cbx(); },
146-
emails: privateInfoEligibility ? execQuery('private_email') : function (cbx) { cbx(); }
162+
earning: loadData.earnings ? execQuery('overall_earning') : function(cbx) { cbx(); },
163+
ratingSummary: loadData.ratings ? execQuery('rating_summary') : function(cbx) { cbx(); },
164+
achievements: loadData.achievements ? execQuery('achievements') : function(cbx) { cbx(); },
165+
privateInfo: loadData.address && privateInfoEligibility ? execQuery('private') : function (cbx) { cbx(); },
166+
emails: loadData.email && privateInfoEligibility ? execQuery('private_email') : function (cbx) { cbx(); }
147167
}, cb);
148168
}, function (results, cb) {
149-
var basic = results.basic[0], earning = results.earning[0], ratingSummary = results.ratingSummary,
150-
achievements = results.achievements, privateInfo,
151-
mapRatingSummary = function (ratings) {
152-
var ret = [];
153-
ratings.forEach(function (item) {
154-
ret.push({
155-
name: helper.getPhaseName(item.phase_id),
156-
rating: item.rating,
157-
colorStyle: helper.getColorStyle(item.rating)
158-
});
169+
var basic = results.basic[0];
170+
171+
result = {
172+
handle: basic.handle,
173+
country: basic.country,
174+
memberSince: basic.member_since,
175+
quote: basic.quote,
176+
photoLink: basic.photo_link || ''
177+
};
178+
179+
if (loadData.earnings && _.isDefined(basic.show_earnings) && basic.show_earnings !== 'hide') {
180+
result.overallEarning = results.earning[0].overall_earning;
181+
}
182+
183+
if (loadData.ratings) {
184+
var ratingSummary = [];
185+
results.ratingSummary.forEach(function (item) {
186+
ratingSummary.push({
187+
name: helper.getPhaseName(item.phase_id),
188+
rating: item.rating,
189+
colorStyle: helper.getColorStyle(item.rating)
159190
});
160-
return ret;
161-
},
162-
mapAchievements = function (achievements) {
163-
var ret = [], achieveItem;
164-
achievements.forEach(function (item) {
165-
achieveItem = {
166-
date: item.achievement_date,
167-
description: item.description
168-
};
169-
ret.push(achieveItem);
191+
});
192+
result.ratingSummary = ratingSummary;
193+
}
194+
195+
if (loadData.achievements) {
196+
var achievements = [];
197+
results.achievements.forEach(function (item) {
198+
achievements.push({
199+
date: item.achievement_date,
200+
description: item.description
170201
});
171-
return ret;
172-
},
173-
mapEmails = function (emails) {
174-
var ret = [];
175-
emails.forEach(function (item) {
176-
ret.push({
177-
email: item.email,
178-
type: item.type,
179-
status: item.status
180-
});
202+
});
203+
// TODO: why is this capitalized?
204+
result.Achievements = achievements;
205+
}
206+
207+
if (privateInfoEligibility && loadData.email) {
208+
var emails = [];
209+
results.emails.forEach(function (item) {
210+
emails.push({
211+
email: item.email,
212+
type: item.type,
213+
status: item.status
181214
});
182-
return ret;
183-
},
184-
appendIfNotEmpty = function (str) {
215+
});
216+
result.emails = emails;
217+
}
218+
219+
if (privateInfoEligibility && loadData.address && results.privateInfo && results.privateInfo[0]) {
220+
var appendIfNotEmpty = function (str) {
185221
var ret = '';
186222
if (str && str.length > 0) {
187223
ret += ', ' + str;
188224
}
189225
return ret;
190-
},
191-
getAddressString = function (privateInfo) {
192-
var address = privateInfo.address1;
193-
if (!address) { return undefined; } // if address1 is undefined, there is no address.
226+
};
227+
228+
var privateInfo = results.privateInfo[0];
229+
230+
result.name = privateInfo.first_name + ' ' + privateInfo.last_name;
231+
result.age = privateInfo.age;
232+
result.gender = privateInfo.gender;
233+
result.shirtSize = privateInfo.shirt_size;
194234

235+
var address = privateInfo.address1;
236+
// if address1 is undefined, there is no address.
237+
if (address) {
195238
address += appendIfNotEmpty(privateInfo.address2);
196239
address += appendIfNotEmpty(privateInfo.address3);
197240
address += ', ' + privateInfo.city;
198241
address += appendIfNotEmpty(privateInfo.state);
199242
address += ', ' + privateInfo.zip + ', ' + privateInfo.country;
200-
return address;
201-
};
202-
result = {
203-
handle: basic.handle,
204-
country: basic.country,
205-
memberSince: basic.member_since,
206-
overallEarning: earning.overall_earning,
207-
quote: basic.quote,
208-
photoLink: basic.photo_link || '',
209-
isCopilot: {
210-
value: basic.is_copilot,
211-
software: basic.is_software_copilot,
212-
studio: basic.is_studio_copilot
213-
},
214-
isPM: basic.is_pm,
215-
216-
ratingSummary: mapRatingSummary(ratingSummary),
217-
Achievements: mapAchievements(achievements)
218-
};
219-
220-
if (!_.isDefined(basic.show_earnings) || basic.show_earnings === 'hide') {
221-
delete result.overallEarning;
243+
result.address = address;
244+
}
222245
}
223246

224247
if (result.isPM) {
225248
delete result.ratingSummary;
226249
}
227250

228-
if (privateInfoEligibility) {
229-
result.emails = mapEmails(results.emails);
230-
if (results.privateInfo && results.privateInfo[0]) {
231-
privateInfo = results.privateInfo[0];
232-
result.name = privateInfo.first_name + ' ' + privateInfo.last_name;
233-
result.address = getAddressString(privateInfo);
234-
result.age = privateInfo.age;
235-
result.gender = privateInfo.gender;
236-
result.shirtSize = privateInfo.shirt_size;
237-
}
238-
}
239251
cb();
240252
}
241253
], function (err) {
@@ -1094,4 +1106,4 @@ exports.getMyProfile = {
10941106
api.helper.handleNoConnection(api, connection, next);
10951107
}
10961108
}
1097-
};
1109+
};

config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ config.tasks = {
193193
// ['high,low'] is one worker working 2 queues
194194
queues: ['default'],
195195
// how long to sleep between jobs / scheduler checks
196-
timeout: 5000,
196+
timeout: process.env.TASK_TIMEOUT || 5000,
197197
// What redis server should we connect to for tasks / delayed jobs?
198198
redis: config.redis
199199
};

queries/get_user_basic_profile_basic

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,18 @@ SELECT
33
, c.country_name AS country
44
, co.member_since
55
, co.quote
6-
, (CASE WHEN r.rating < 0
7-
THEN 't'
8-
ELSE 'f'
9-
END)::boolean AS is_pm
10-
, (CASE WHEN cp.user_id>0
11-
THEN 't'
12-
ELSE 'f'
13-
END)::boolean AS is_copilot
14-
, NVL(cp.is_software_copilot, 'f') AS is_software_copilot
15-
, NVL(cp.is_studio_copilot, 'f') AS is_studio_copilot
166
, p.path || i.file_name AS photo_link
177
, up.value AS show_earnings
188
FROM user u
199
, coder co
20-
, OUTER algo_rating r
2110
, OUTER country c
22-
, OUTER tcs_catalog:copilot_profile AS cp
2311
, OUTER(coder_image_xref cix
2412
, image i
2513
, path p)
2614
, OUTER user_preference up
2715
WHERE u.handle_lower = LOWER('@handle@')
2816
AND u.status = 'A'
29-
AND u.user_id = cp.user_id
3017
AND u.user_id = co.coder_id
31-
AND u.user_id = r.coder_id
32-
AND r.algo_rating_type_id = 1
3318
AND u.user_id = cix.coder_id
3419
AND c.country_code = co.comp_country_code
3520
AND cix.display_flag = 1

test/test.basicUserProfile.js

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,11 @@ describe('Get Basic User Profile API', function () {
104104
var body = res.body, expected = require(name);
105105
delete body.serverInformation;
106106
delete body.requesterInformation;
107-
body.Achievements.forEach(function (item) {
108-
delete item.date;
109-
});
107+
if (body.Achievements) {
108+
body.Achievements.forEach(function (item) {
109+
delete item.date;
110+
});
111+
}
110112
assert.deepEqual(body, expected);
111113
cb();
112114
});
@@ -120,6 +122,51 @@ describe('Get Basic User Profile API', function () {
120122
assertBasicUserProfile('/v2/users/heffan', './test_files/expected_basic_user_profile_heffan', done);
121123
});
122124

125+
/**
126+
* Test /v2/users/heffan?data=
127+
* Should return success results for heffan without earnings, ratings and achievements.
128+
*/
129+
it('should return success results for heffan without earning, ratings and achievements', function (done) {
130+
assertBasicUserProfile('/v2/users/heffan?data=', './test_files/expected_basic_user_profile_heffan_no_data', done);
131+
});
132+
133+
/**
134+
* Test /v2/users/heffan?data=achievements
135+
* Should return success results for heffan with just achievements
136+
*/
137+
it('should return success results for heffan with just achievements', function (done) {
138+
assertBasicUserProfile('/v2/users/heffan?data=achievements', './test_files/expected_basic_user_profile_heffan_ach', done);
139+
});
140+
141+
/**
142+
* Test /v2/users/heffan?data=rating
143+
* Should return success results for heffan with just rating summary
144+
*/
145+
it('should return success results for heffan with just ratings', function (done) {
146+
assertBasicUserProfile('/v2/users/heffan?data=ratings', './test_files/expected_basic_user_profile_heffan_ratings', done);
147+
});
148+
149+
/**
150+
* Test /v2/users/heffan?data=earnings,rating
151+
* should show overallEarning and rating fields in the response.
152+
*/
153+
it('should show earnings and rating', function (done) {
154+
async.waterfall([
155+
function (cb) {
156+
testHelper.runSqlFile(SQL_DIR + 'informixoltp__update_show_earning', 'informixoltp', cb);
157+
},
158+
function (cb) {
159+
assertBasicUserProfile('/v2/users/heffan?data=earnings,ratings', './test_files/expected_basic_user_profile_heffan_earning_ratings', cb);
160+
}
161+
], function (err) {
162+
if (err) {
163+
done(err);
164+
return;
165+
}
166+
done();
167+
});
168+
});
169+
123170
/**
124171
* Test /v2/users/super.
125172
* Should return success results for super.
@@ -132,6 +179,7 @@ describe('Get Basic User Profile API', function () {
132179
* Test /v2/users/heffan.
133180
* The heffan is now be upgraded to software copilot. The isCopilot.software should be true now.
134181
*/
182+
/* isCopilot removed
135183
it('should be a software copilot', function (done) {
136184
async.waterfall([
137185
function (cb) {
@@ -161,11 +209,13 @@ describe('Get Basic User Profile API', function () {
161209
done();
162210
});
163211
});
212+
*/
164213

165214
/**
166215
* Test /v2/users/heffan.
167216
* heffan has been upgraded to studio copilot. The isCopilot.studio should be true now.
168217
*/
218+
/* isCopilot removed
169219
it('should be a studio copilot', function (done) {
170220
async.waterfall([
171221
function (cb) {
@@ -195,11 +245,13 @@ describe('Get Basic User Profile API', function () {
195245
done();
196246
});
197247
});
248+
*/
198249

199250
/**
200251
* Test /v2/users/heffan.
201252
* heffan is PM now. So the rating summary data should not be showed.
202253
*/
254+
/* isPM removed
203255
it('should show no rating summary data', function (done) {
204256
async.waterfall([
205257
function (cb) {
@@ -216,6 +268,8 @@ describe('Get Basic User Profile API', function () {
216268
done();
217269
});
218270
});
271+
*/
272+
219273

220274
/**
221275
* Test /v2/users/heffan

test/test_files/expected_basic_user_profile_heffan.json

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@
44
"memberSince": "2001-05-09T01:04:42.000Z",
55
"quote": "Acknowledge and move on.",
66
"photoLink": "/i/m/test_image.png",
7-
"isCopilot": {
8-
"value": true,
9-
"software": false,
10-
"studio": false
11-
},
12-
"isPM": false,
137
"ratingSummary": [
148
{
159
"name": "Design",

0 commit comments

Comments
 (0)