Skip to content

Commit 088f4af

Browse files
authored
Merge pull request #36 from appirio-tech/dev
merge dev to master for release
2 parents 74f304a + 0459341 commit 088f4af

File tree

3 files changed

+120
-73
lines changed

3 files changed

+120
-73
lines changed

src/app/filters.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,25 @@ angular.module('supportAdminApp')
1717
return 'https://connect.topcoder.com//projects/' + id + '/timeline';
1818
};
1919
})
20+
21+
// The resulting link will point to the public member profile,
22+
// use 'tcPrivateProfileLink' instead to link to the private profile.
2023
.filter('tcProfileLink', function () {
2124
return function (id) {
2225
if (id && id !== 'unassigned')
2326
return 'http://www.topcoder.com/tc?module=MemberProfile&cr=' + id;
27+
return 'javascript:;';
28+
};
29+
})
2430

31+
.filter('tcPrivateProfileLink', function () {
32+
return function (handle) {
33+
if (handle)
34+
return 'http://www.topcoder.com/tc?module=LegacyReport&t=profile&ha=' + handle;
2535
return 'javascript:;';
2636
};
2737
})
38+
2839
.filter('tcDirectLink', function () {
2940
return function (id) {
3041
return 'https://www.topcoder.com/direct/projectOverview?formData.projectId=' + id;

src/app/work/project.list.controller.js

Lines changed: 107 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -70,80 +70,116 @@ module.controller('ProjectListCtrl', [
7070
search: vm.formSearch.search,
7171
status: vm.formSearch.status === ALL ? '' : vm.formSearch.status
7272
}).then(
73+
74+
// For the loaded project objects this function does:
75+
// 1. Queries user email and handles for ids of all projects' copilots
76+
// and owners. The queries are send in batches of size equal to
77+
// MAX_BATCH_SIZE ids per query.
78+
// 2. If 'vm.formSearch.filterByHandleOrEmail' is true, it filters
79+
// loaded project object by handle and emails;
80+
// 3. Redraws the table.
81+
7382
function (projects) {
7483

75-
// Generates the query to search for members with 'usedId' specified
76-
// in 'ownerId' and 'copilotId' field of each project we have found.
77-
// The 'isNaN' check is necessary as 'copilotId' may be equal to
78-
// "unassigned", or some other string values.
79-
var query = projects.reduce(function(query, project) {
80-
if (!isNaN(project.ownerId))
81-
query += 'userId:' + project.ownerId + ' OR ';
82-
if (!isNaN(project.copilotId))
83-
query += 'userId:' + project.copilotId + ' OR ';
84-
return query;
85-
}, '').slice(0,-4);
86-
87-
vm.allProjects = projects;
88-
$scope.projects = vm.allProjects;
89-
90-
// Resolution of User IDs in the found project objects.
91-
membersService.search({
92-
search: query
93-
}).then(
94-
function (users) {
95-
96-
// Uses the found 'users' array to add 'copilotHandle' and
97-
// 'ownerHandle' field to each project object.
98-
var map = users.reduce(function(map, user) {
99-
map[user.userId] = user;
100-
return map;
101-
}, {});
102-
projects.forEach(function(project) {
103-
project.copilot = map[project.copilotId];
104-
project.owner = map[project.ownerId];
105-
});
84+
var MAX_BATCH_SIZE = 100;
10685

107-
// If asked, filters the projects by owner's handle and
108-
// and email (remaining projects will contain
109-
// 'vm.formSearch.handleOrEmail' as a substring
110-
// of their owner's handle or email).
111-
if (vm.filterByHandleOrEmail) {
112-
vm.filterByHandleOrEmail = false;
113-
var key = vm.formSearch.handleOrEmail;
114-
projects = projects.filter(function(project) {
115-
var res = false;
116-
res |= project.owner && project.owner.handle &&
117-
project.owner.handle.includes(key);
118-
res |= project.owner && project.owner.email &&
119-
project.owner.email.includes(key);
120-
res |= project.copilot && project.copilot.handle &&
121-
project.copilot.handle.includes(key);
122-
res |= project.copilot && project.copilot.email &&
123-
project.copilot.email.includes(key);
124-
return res;
125-
});
126-
vm.allProjects = projects;
127-
$scope.projects = projects;
128-
}
129-
$timeout(function () {
130-
vm.formSearch.isLoading = false;
131-
$('.table').trigger('footable_redraw');
132-
}, 500);
86+
// Collects copilot and owner's ids from the loaded project objects.
87+
// The resulting 'queue' array will hold unique user ids to be
88+
// be queried from the MembersService in several batches, and
89+
// the 'users' dictionary will be prepared to collect queiried data.
90+
var queue = [], collect = function (users, userId) {
91+
if (!isNaN(userId) && !users[userId]) {
92+
users[userId] = { email: '', handle: ''};
93+
queue.push(userId);
94+
}
95+
};
96+
var users = projects.reduce(function(users, project) {
97+
98+
// Ids should be trimmed, as in the production data some of them
99+
// may include spaces before and after, which breaks matching of
100+
// ids width handles.
101+
if (project.copilotId) {
102+
project.copilotId = project.copilotId.trim();
103+
}
104+
if (project.ownerId) {
105+
project.ownerId = project.ownerId.trim();
106+
}
133107

134-
},
135-
function (error) {
108+
collect(users, project.copilotId);
109+
collect(users, project.ownerId);
110+
return users;
111+
}, {});
136112

137-
// Even in case of failure with the request to the Member
138-
// Service we still should update the page as the project's
139-
// data have been loaded (but not the user handles).
140-
141-
$timeout(function () {
142-
vm.formSearch.isLoading = false;
143-
$('.table').trigger('footable_redraw');
144-
}, 500);
113+
// Now we send queries to MembersService in batches.
114+
// The MAX_BATCH_SIZE constant sets the maximal batch size
115+
// (i.e. the number of ids in a single query).
116+
var pos = 0, queryNextBatch = function() {
117+
var query = '';
118+
var end = Math.min(pos + MAX_BATCH_SIZE, queue.length);
119+
for (var i = pos; i < end; ++i) {
120+
query += 'userId:' + queue[i] + ' OR ';
121+
}
122+
membersService.search({
123+
search: query.slice(0, -4)
124+
}).then(
125+
function (userObjects) {
126+
pos += MAX_BATCH_SIZE;
127+
userObjects.forEach(function(user) {
128+
users[user.userId] = {
129+
email: user.email,
130+
handle: user.handle
131+
};
132+
});
133+
if (pos < queue.length) {
134+
queryNextBatch();
135+
} else {
136+
finalize();
137+
}
138+
},
139+
function (error) {
140+
finalize();
141+
}
142+
);
143+
};
144+
145+
// Once all necessary data have been queried, this function
146+
// will be used to attach the queried user data to the loaded
147+
// project object, and to redraw the table.
148+
var finalize = function() {
149+
150+
projects.forEach(function(project) {
151+
project.copilot = users[project.copilotId];
152+
project.owner = users[project.ownerId];
153+
});
154+
155+
// If asked, filters the projects by owner's handle and
156+
// and email (remaining projects will contain
157+
// 'vm.formSearch.handleOrEmail' as a substring
158+
// of their owner's handle or email).
159+
if (vm.formSearch.filterByHandleOrEmail) {
160+
vm.formSearch.filterByHandleOrEmail = false;
161+
var key = vm.formSearch.handleOrEmail;
162+
projects = projects.filter(function(project) {
163+
var res = false;
164+
res |= project.owner && project.owner.handle &&
165+
project.owner.handle.includes(key);
166+
res |= project.owner && project.owner.email &&
167+
project.owner.email.includes(key);
168+
res |= project.copilot && project.copilot.handle &&
169+
project.copilot.handle.includes(key);
170+
res |= project.copilot && project.copilot.email &&
171+
project.copilot.email.includes(key);
172+
return res;
173+
});
145174
}
146-
);
175+
176+
vm.allProjects = projects;
177+
$scope.projects = projects;
178+
vm.formSearch.isLoading = false;
179+
$('.table').trigger('footable_redraw');
180+
};
181+
182+
queryNextBatch();
147183
},
148184
function (error) {
149185
// error handling
@@ -166,9 +202,9 @@ module.controller('ProjectListCtrl', [
166202
* with an additionally activated 'handle or e-mail' filter.
167203
*/
168204
vm.findByHandleOrEmail = function() {
169-
vm.filterByHandleOrEmail = true;
170-
vm.search = '';
171-
vm.status = ALL;
205+
vm.formSearch.filterByHandleOrEmail = true;
206+
vm.formSearch.search = '';
207+
vm.formSearch.status = ALL;
172208
vm.findProjects();
173209
};
174210

src/app/work/projects.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ <h2>Projects</h2>
137137
<td>{{proj.id}}</td>
138138
<td>{{proj.projectType }}</td>
139139
<td>
140-
<a ng-href="{{ proj.ownerId | tcProfileLink }}">
140+
<a ng-href="{{ proj.owner.handle | tcPrivateProfileLink }}">
141141
<!-- In case the owner's member object has not been found,
142142
we show his ID as the fallback. -->
143143
{{proj.owner && proj.owner.handle ?
144144
proj.owner.handle : proj.ownerId}}
145145
</a>
146146
</td>
147147
<td>
148-
<a data-ng-if="proj.copilotId && proj.copilotId !== 'unassigned'" ng-href="{{ proj.copilotId | tcProfileLink}}">
148+
<a data-ng-if="proj.copilotId && proj.copilotId !== 'unassigned'" ng-href="{{ proj.copilot.handle | tcPrivateProfileLink}}">
149149
<!-- In case the copilot's member object has not been
150150
found, we show his ID as the fallback. -->
151151
{{proj.copilot && proj.copilot.handle ?

0 commit comments

Comments
 (0)