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

Commit 2cd2a41

Browse files
committed
Module Assembly - TopCoder NodeJS Challenges RSS Output API
1 parent c82d0f2 commit 2cd2a41

25 files changed

+2547
-48
lines changed

actions/challenges.js

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,6 @@ var ALLOWABLE_SORT_COLUMN = [
8585
"submissionEndDate", "finalFixEndDate", "prize1", "currentStatus", "digitalRunPoints"
8686
];
8787

88-
/**
89-
* Represents a ListType enum
90-
*/
91-
var ListType = { ACTIVE: "ACTIVE", OPEN: "OPEN", UPCOMING: "UPCOMING", PAST: "PAST" };
92-
93-
/**
94-
* Represents a predefined list of valid list type.
95-
*/
96-
var ALLOWABLE_LIST_TYPE = [ListType.ACTIVE, ListType.OPEN, ListType.UPCOMING, ListType.PAST];
97-
9888
/**
9989
* Represents Percentage of Placement Points for digital run
10090
*/
@@ -105,24 +95,6 @@ var DR_POINT = [[1], [0.7, 0.3], [0.65, 0.25, 0.10], [0.6, 0.22, 0.1, 0.08], [0.
10595
*/
10696
var MAX_INT = 2147483647;
10797

108-
/**
109-
* The list type and registration phase status map.
110-
*/
111-
var LIST_TYPE_REGISTRATION_STATUS_MAP = {};
112-
LIST_TYPE_REGISTRATION_STATUS_MAP[ListType.ACTIVE] = [2, 3];
113-
LIST_TYPE_REGISTRATION_STATUS_MAP[ListType.OPEN] = [2];
114-
LIST_TYPE_REGISTRATION_STATUS_MAP[ListType.UPCOMING] = [1];
115-
LIST_TYPE_REGISTRATION_STATUS_MAP[ListType.PAST] = [3];
116-
117-
/**
118-
* The list type and project status map.
119-
*/
120-
var LIST_TYPE_PROJECT_STATUS_MAP = {};
121-
LIST_TYPE_PROJECT_STATUS_MAP[ListType.ACTIVE] = [1];
122-
LIST_TYPE_PROJECT_STATUS_MAP[ListType.OPEN] = [1];
123-
LIST_TYPE_PROJECT_STATUS_MAP[ListType.UPCOMING] = [2];
124-
LIST_TYPE_PROJECT_STATUS_MAP[ListType.PAST] = [4, 5, 6, 7, 8, 9, 10, 11];
125-
12698
/**
12799
* This copilot posting project type id
128100
*/
@@ -194,7 +166,7 @@ function validateInputParameter(helper, caller, challengeType, query, filter, pa
194166
helper.checkPositiveInteger(pageSize, "pageSize") ||
195167
helper.checkMaxNumber(pageSize, MAX_INT, 'pageSize') ||
196168
helper.checkMaxNumber(pageIndex, MAX_INT, 'pageIndex') ||
197-
helper.checkContains(ALLOWABLE_LIST_TYPE, type.toUpperCase(), "type") ||
169+
helper.checkContains(helper.ALLOWABLE_LIST_TYPE, type.toUpperCase(), "type") ||
198170
checkQueryParameterAndSortColumn(helper, type, query, sortColumn);
199171

200172
if (_.isDefined(query.communityId)) {
@@ -466,7 +438,7 @@ var searchChallenges = function (api, connection, dbConnectionMap, community, ne
466438

467439
sortOrder = query.sortorder || "asc";
468440
sortColumn = query.sortcolumn || DEFAULT_SORT_COLUMN;
469-
listType = (query.listtype || ListType.OPEN).toUpperCase();
441+
listType = (query.listtype || helper.ListType.OPEN).toUpperCase();
470442
pageIndex = Number(query.pageindex || 1);
471443
pageSize = Number(query.pagesize || 50);
472444

@@ -494,8 +466,8 @@ var searchChallenges = function (api, connection, dbConnectionMap, community, ne
494466
// Set the project type id
495467
sqlParams.project_type_id = challengeType.category;
496468
// Set the submission phase status id.
497-
sqlParams.registration_phase_status = LIST_TYPE_REGISTRATION_STATUS_MAP[listType];
498-
sqlParams.project_status_id = LIST_TYPE_PROJECT_STATUS_MAP[listType];
469+
sqlParams.registration_phase_status = helper.LIST_TYPE_REGISTRATION_STATUS_MAP[listType];
470+
sqlParams.project_status_id = helper.LIST_TYPE_PROJECT_STATUS_MAP[listType];
499471
sqlParams.userId = caller.userId || 0;
500472

501473
// Check the private challenge access

actions/rss.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
3+
*
4+
* @version 1.0
5+
* @author Ghost_141
6+
*/
7+
"use strict";
8+
9+
var async = require('async');
10+
var _ = require('underscore');
11+
var BadRequestError = require('../errors/BadRequestError');
12+
var UnauthorizedError = require('../errors/UnauthorizedError');
13+
var ForbiddenError = require('../errors/ForbiddenError');
14+
15+
/**
16+
* valid value for challenge type.
17+
*/
18+
var VALID_CHALLENGE_TYPE = ['develop', 'design', 'data'];
19+
20+
/**
21+
* Get the challenges RSS information.
22+
*
23+
* @param {Object} api - The api object that is used to access the global infrastructure
24+
* @param {Object} connection - The connection object for the current request
25+
* @param {Function<connection, render>} next - The callback to be called after this function is done
26+
*/
27+
function getChallengesRSS(api, connection, next) {
28+
var result, helper = api.helper,
29+
dbConnectionMap = connection.dbConnectionMap,
30+
RSSMaxLength = api.config.general.maxRSSLength,
31+
positionsRemain = RSSMaxLength,
32+
challengeType = connection.params.challengeType,
33+
listType = (connection.params.listType || helper.ListType.OPEN).toUpperCase(),
34+
copyToResult = function (queryResults) {
35+
if (positionsRemain > 0) {
36+
var i, row, res;
37+
for (i = 0; i < Math.min(positionsRemain, queryResults.length); i += 1) {
38+
row = queryResults[i];
39+
res = {
40+
challengeType: row.challenge_type.trim(),
41+
challengeName: row.challenge_name,
42+
challengeId: row.challenge_id,
43+
detailedRequirements: row.detailed_requirements || '',
44+
projectId: row.project_id
45+
};
46+
if (_.isDefined(row.software_detailed_requirements)) {
47+
res.detailedRequirements = row.software_detailed_requirements || '';
48+
}
49+
if (_.isDefined(row.studio_detailed_requirements)) {
50+
res.detailedRequirements = row.studio_detailed_requirements || '';
51+
}
52+
result.data.push(res);
53+
}
54+
positionsRemain -= queryResults.length;
55+
}
56+
};
57+
async.waterfall([
58+
function (cb) {
59+
var error;
60+
if (_.isDefined(challengeType)) {
61+
error = helper.checkContains(VALID_CHALLENGE_TYPE, challengeType.toLowerCase(), 'challengeType');
62+
}
63+
error = error || helper.checkContains(helper.VALID_LIST_TYPE, listType, 'listType');
64+
if (error) {
65+
cb(error);
66+
return;
67+
}
68+
69+
challengeType = (challengeType || 'all').toLowerCase();
70+
71+
async.parallel({
72+
design: function (cbx) {
73+
if (challengeType === 'design' || challengeType === 'all') {
74+
api.dataAccess.executeQuery('get_software_studio_challenges_rss',
75+
{
76+
page_size: RSSMaxLength,
77+
project_status_id: helper.LIST_TYPE_PROJECT_STATUS_MAP[listType],
78+
project_type_id: helper.studio.category,
79+
registration_phase_status: helper.LIST_TYPE_REGISTRATION_STATUS_MAP[listType]
80+
}, dbConnectionMap, cbx);
81+
} else {
82+
cbx();
83+
}
84+
},
85+
develop: function (cbx) {
86+
if (challengeType === 'develop' || challengeType === 'all') {
87+
api.dataAccess.executeQuery('get_software_studio_challenges_rss',
88+
{
89+
page_size: RSSMaxLength,
90+
project_status_id: helper.LIST_TYPE_PROJECT_STATUS_MAP[listType],
91+
project_type_id: helper.software.category,
92+
registration_phase_status: helper.LIST_TYPE_REGISTRATION_STATUS_MAP[listType]
93+
}, dbConnectionMap, cbx);
94+
} else {
95+
cbx();
96+
}
97+
},
98+
data: function (cbx) {
99+
if (challengeType === 'data' || challengeType === 'all') {
100+
if (listType === helper.ListType.PAST) {
101+
api.dataAccess.executeQuery('get_past_data_challenges_rss', { page_size: RSSMaxLength }, dbConnectionMap, cbx);
102+
} else if (listType === helper.ListType.OPEN || listType === helper.ListType.ACTIVE) {
103+
api.dataAccess.executeQuery('get_open_data_challenges_rss', { page_size: RSSMaxLength }, dbConnectionMap, cbx);
104+
} else {
105+
cbx();
106+
}
107+
} else {
108+
cbx();
109+
}
110+
}
111+
}, cb);
112+
},
113+
function (results, cb) {
114+
result = {
115+
data: []
116+
};
117+
_.compact([results.design, results.develop, results.data]).forEach(function (item) {
118+
copyToResult(item);
119+
});
120+
result.total = result.data.length;
121+
cb();
122+
}
123+
], function (err) {
124+
if (err) {
125+
helper.handleError(api, connection, err);
126+
} else {
127+
connection.response = result;
128+
}
129+
next(connection, true);
130+
});
131+
}
132+
133+
/**
134+
* Get Challenges RSS API.
135+
*/
136+
exports.getChallengesRSS = {
137+
name: 'getChallengesRSS',
138+
description: 'getChallengesRSS',
139+
inputs: {
140+
required: [],
141+
optional: ['listType', 'challengeType']
142+
},
143+
blockedConnectionTypes: [],
144+
outputExample: {},
145+
version: 'v2',
146+
transaction: 'read', // this action is read-only
147+
databases: ['informixoltp', 'tcs_catalog', 'topcoder_dw'],
148+
run: function (api, connection, next) {
149+
api.log('Execute getChallengesRSS#run', 'debug');
150+
getChallengesRSS(api, connection, next);
151+
}
152+
};

apiary.apib

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4994,17 +4994,17 @@ Register a new user.
49944994
{
49954995
"name":"Bad Request",
49964996
"value":"400",
4997-
"description":"This message will explain why the request is invalid or cannot be served."
4997+
"description":"challengeType should be an element of develop,design,data."
49984998
}
4999-
5000-
+ Response 404 (application/json)
4999+
5000+
+ Response 400 (application/json)
50015001
50025002
{
5003-
"name":"Not Found",
5004-
"value":"404",
5005-
"description":"This message will explain why the URI requested is invalid or the resource does not exist."
5003+
"name":"Bad Request",
5004+
"value":"400",
5005+
"description":"listType should be an element of ACTIVE,OPEN,UPCOMING,PAST."
50065006
}
5007-
5007+
50085008
+ Response 406 (application/json)
50095009
50105010
{

config.js

Lines changed: 6 additions & 3 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-
* @author vangavroche, Ghost_141, kurtrips, Sky_, isv
5-
* @version 1.12
4+
* @author vangavroche, Ghost_141, kurtrips, Sky_, isv, TCSASSEMBLER
5+
* @version 1.13
66
* changes in 1.1:
77
* - add defaultCacheLifetime parameter
88
* changes in 1.2:
@@ -30,6 +30,8 @@
3030
* - added designSubmissionsBasePath for design submissions
3131
* changes in 1.12:
3232
* - add defaultUserCacheLifetime property.
33+
* Changes in 1.13:
34+
* - add maxRSSLength.
3335
*/
3436
"use strict";
3537

@@ -89,7 +91,8 @@ config.general = {
8991
* The directory where uploaded files are stored.
9092
* It can be relative to the current directory or can be absolute
9193
*/
92-
uploadsRootDirectory: process.env.UPLOADS_ROOT_DIRECTORY || "test/test_files/dev_download_submission"
94+
uploadsRootDirectory: process.env.UPLOADS_ROOT_DIRECTORY || "test/test_files/dev_download_submission",
95+
maxRSSLength: 1000
9396
};
9497

9598
/////////////

initializers/helper.js

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
/**
77
* This module contains helper functions.
88
* @author Sky_, Ghost_141, muzehyun, kurtrips, isv
9-
* @version 1.13
10-
* changes in 1.15
9+
* @version 1.16
10+
* changes in 1.1
1111
* - add mapProperties
1212
* changes in 1.2:
1313
* - add getPercent to underscore mixin
@@ -48,6 +48,8 @@
4848
* - add method checkMember to check if the caller have at least member access leve.
4949
* changes in 1.15
5050
* - added checkUserExists function
51+
* Changes in 1.16:
52+
* - add LIST_TYPE_REGISTRATION_STATUS_MAP and VALID_LIST_TYPE.
5153
*/
5254
"use strict";
5355

@@ -293,6 +295,34 @@ var phaseName2Id = _.object(_.values(_.extend(helper.studioChallengeTypes, helpe
293295
return [item.name.toLowerCase(), item.phaseId];
294296
}));
295297

298+
/**
299+
* Represents a ListType enum
300+
*/
301+
helper.ListType = { ACTIVE: "ACTIVE", OPEN: "OPEN", UPCOMING: "UPCOMING", PAST: "PAST" };
302+
303+
/**
304+
* valid value for listType.
305+
*/
306+
helper.VALID_LIST_TYPE = [helper.ListType.ACTIVE, helper.ListType.OPEN, helper.ListType.UPCOMING, helper.ListType.PAST];
307+
308+
/**
309+
* The list type and registration phase status map.
310+
*/
311+
helper.LIST_TYPE_REGISTRATION_STATUS_MAP = {};
312+
helper.LIST_TYPE_REGISTRATION_STATUS_MAP[helper.ListType.ACTIVE] = [2, 3];
313+
helper.LIST_TYPE_REGISTRATION_STATUS_MAP[helper.ListType.OPEN] = [2];
314+
helper.LIST_TYPE_REGISTRATION_STATUS_MAP[helper.ListType.UPCOMING] = [1];
315+
helper.LIST_TYPE_REGISTRATION_STATUS_MAP[helper.ListType.PAST] = [3];
316+
317+
/**
318+
* The list type and project status map.
319+
*/
320+
helper.LIST_TYPE_PROJECT_STATUS_MAP = {};
321+
helper.LIST_TYPE_PROJECT_STATUS_MAP[helper.ListType.ACTIVE] = [1];
322+
helper.LIST_TYPE_PROJECT_STATUS_MAP[helper.ListType.OPEN] = [1];
323+
helper.LIST_TYPE_PROJECT_STATUS_MAP[helper.ListType.UPCOMING] = [2];
324+
helper.LIST_TYPE_PROJECT_STATUS_MAP[helper.ListType.PAST] = [4, 5, 6, 7, 8, 9, 10, 11];
325+
296326
/**
297327
* Checks whether given object is defined.
298328
* @param {Object}obj the obj to check.
@@ -928,9 +958,9 @@ helper.getPhaseId = function (phaseName) {
928958
*/
929959
helper.getColorStyle = function (rating) {
930960

931-
if (rating === null) {
932-
return "color: #000000";
933-
}
961+
if (rating === null) {
962+
return "color: #000000";
963+
}
934964

935965
if (rating < 0) {
936966
return "color: #FF9900"; // orange

queries/get_open_data_challenges_rss

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
SELECT
2+
FIRST @page_size@
3+
r.round_id AS challenge_id
4+
, ct.name AS challenge_name
5+
, ct.name AS detailed_requirements
6+
, 'marathon' AS challenge_type
7+
FROM
8+
round_segment rs
9+
, round r
10+
, contest ct
11+
WHERE rs.round_id = r.round_id
12+
AND ct.contest_id = r.contest_id
13+
AND rs.segment_id = 2
14+
AND r.round_type_id IN (13,15,19,22,24,25,27)
15+
AND CURRENT < rs.end_time
16+
AND CURRENT >= rs.start_time
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name" : "get_open_data_challenges_rss",
3+
"db" : "informixoltp",
4+
"sqlfile" : "get_open_data_challenges_rss"
5+
}

0 commit comments

Comments
 (0)