Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e2cb5ae

Browse files
committed
[lintcode] update to latest API (WIP)
refs skygragon#15 skygragon#14 Signed-off-by: Eric Wang <skygragon@gmail.com>
1 parent 36aa07c commit e2cb5ae

File tree

1 file changed

+111
-126
lines changed

1 file changed

+111
-126
lines changed

plugins/lintcode.js

Lines changed: 111 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ var util = require('util');
66
var h = require('../helper');
77
var log = require('../log');
88
var Plugin = require('../plugin');
9-
var queue = require('../queue');
9+
var Queue = require('../queue');
1010
var session = require('../session');
1111

1212
// Still working in progress!
@@ -18,44 +18,48 @@ var session = require('../session');
1818
//
1919
// https://github.com/skygragon/leetcode-cli-plugins/blob/master/docs/lintcode.md
2020
//
21-
var plugin = new Plugin(15, 'lintcode', '2017.08.04',
21+
const plugin = new Plugin(15, 'lintcode', '2018.05.29',
2222
'Plugin to talk with lintcode APIs.');
2323

24-
var config = {
25-
URL_BASE: 'http://www.lintcode.com/en',
26-
URL_PROBLEMS: 'http://www.lintcode.com/en/problem?page=$page',
27-
URL_PROBLEM: 'http://www.lintcode.com/en/problem/$slug/',
28-
URL_PROBLEM_CODE: 'http://www.lintcode.com/en/problem/api/code/?problem_id=$id&language=$lang',
29-
URL_TEST: 'http://www.lintcode.com/submission/api/submit/',
30-
URL_TEST_VERIFY: 'http://www.lintcode.com/submission/api/refresh/?id=$id&waiting_time=0&is_test_submission=true',
31-
URL_SUBMIT_VERIFY: 'http://www.lintcode.com/submission/api/refresh/?id=$id&waiting_time=0',
32-
URL_LOGIN: 'http://www.lintcode.com/en/accounts/signin/'
24+
const config = {
25+
URL_PROBLEMS: 'https://www.lintcode.com/api/problems/?page=$page',
26+
URL_PROBLEM: 'https://www.lintcode.com/problem/$slug/description',
27+
URL_PROBLEM_DETAIL: 'https://www.lintcode.com/api/problems/detail/?unique_name_or_alias=$slug&_format=detail',
28+
URL_PROBLEM_CODE: 'https://www.lintcode.com/api/problems/$id/reset/?language=$lang',
29+
URL_TEST: 'https://www.lintcode.com/api/submissions/',
30+
URL_TEST_VERIFY: 'https://www.lintcode.com/api/submissions/refresh/?id=$id&is_test_submission=true',
31+
URL_SUBMIT_VERIFY: 'https://www.lintcode.com/api/submissions/refresh/?id=$id',
32+
URL_LOGIN: 'https://www.lintcode.com/api/accounts/signin/?next=%2F'
3333
};
3434

35-
var LANGS = [
35+
// FIXME: add more langs
36+
const LANGS = [
3637
{value: 'cpp', text: 'C++'},
3738
{value: 'java', text: 'Java'},
3839
{value: 'python', text: 'Python'}
3940
];
4041

42+
var spin;
43+
4144
function signOpts(opts, user) {
4245
opts.headers.Cookie = 'sessionid=' + user.sessionId +
4346
';csrftoken=' + user.sessionCSRF + ';';
47+
opts.headers['x-csrftoken'] = user.sessionCSRF;
4448
}
4549

4650
function makeOpts(url) {
47-
var opts = {};
48-
opts.url = url;
49-
opts.headers = {};
50-
51+
const opts = {
52+
url: url,
53+
headers: {}
54+
};
5155
if (session.isLogin())
5256
signOpts(opts, session.getUser());
5357
return opts;
5458
}
5559

5660
function checkError(e, resp, expectedStatus) {
5761
if (!e && resp && resp.statusCode !== expectedStatus) {
58-
var code = resp.statusCode;
62+
const code = resp.statusCode;
5963
log.debug('http error: ' + code);
6064

6165
if (code === 403 || code === 401) {
@@ -84,126 +88,111 @@ plugin.getProblems = function(cb) {
8488
log.debug('running lintcode.getProblems');
8589

8690
var problems = [];
87-
var doTask = function(page, taskDone) {
88-
plugin.getPageProblems(page, function(e, _problems) {
89-
if (!e) problems = problems.concat(_problems);
90-
return taskDone(e);
91+
const getPage = function(page, queue, cb) {
92+
plugin.getPageProblems(page, function(e, _problems, ctx) {
93+
if (!e) {
94+
problems = problems.concat(_problems);
95+
queue.tasks = _.reject(queue.tasks, x => ctx.pages > 0 && x > ctx.pages);
96+
}
97+
return cb(e);
9198
});
9299
};
93100

94-
// FIXME: remove this hardcoded range!
95-
var pages = [0, 1, 2, 3, 4];
96-
queue.run(pages, doTask, function(e) {
97-
problems = _.sortBy(problems, function(x) {
98-
return -x.id;
99-
});
101+
const pages = _.range(1, 100);
102+
const q = new Queue(pages, {}, getPage);
103+
spin = h.spin('Downloading problems');
104+
q.run(null, function(e, ctx) {
105+
spin.stop();
106+
problems = _.sortBy(problems, x => -x.id);
100107
return cb(e, problems);
101108
});
102109
};
103110

104111
plugin.getPageProblems = function(page, cb) {
105112
log.debug('running lintcode.getPageProblems: ' + page);
106-
var opts = makeOpts(config.URL_PROBLEMS.replace('$page', page));
113+
const opts = makeOpts(config.URL_PROBLEMS.replace('$page', page));
107114

115+
spin.text = 'Downloading page ' + page;
108116
request(opts, function(e, resp, body) {
109117
e = checkError(e, resp, 200);
110118
if (e) return cb(e);
111119

112-
var $ = cheerio.load(body);
113-
var problems = $('div[id=problem_list_pagination] a').map(function(i, a) {
114-
var problem = {
115-
locked: false,
120+
const ctx = {};
121+
const json = JSON.parse(body);
122+
const problems = json.problems.map(function(p, a) {
123+
const problem = {
124+
id: p.id,
125+
fid: p.id,
126+
name: p.title,
127+
slug: p.unique_name,
116128
category: 'lintcode',
117-
state: 'None',
118-
starred: false,
119-
companies: [],
129+
level: h.levelToName(p.level),
130+
locked: false,
131+
percent: p.accepted_rate,
132+
starred: p.is_favorited,
133+
companies: p.company_tags,
120134
tags: []
121135
};
122-
problem.slug = $(a).attr('href').split('/').pop();
123136
problem.link = config.URL_PROBLEM.replace('$slug', problem.slug);
124-
125-
$(a).children('span').each(function(i, span) {
126-
var text = $(span).text().trim();
127-
var type = _split($(span).attr('class'), ' ');
128-
type = type.concat(_split($(span).find('i').attr('class'), ' '));
129-
130-
if (type.indexOf('title') >= 0) {
131-
problem.id = Number(text.split('.')[0]);
132-
problem.name = text.split('.')[1].trim();
133-
} else if (type.indexOf('difficulty') >= 0) problem.level = text;
134-
else if (type.indexOf('rate') >= 0) problem.percent = parseInt(text, 10);
135-
else if (type.indexOf('fa-star') >= 0) problem.starred = true;
136-
else if (type.indexOf('fa-check') >= 0) problem.state = 'ac';
137-
else if (type.indexOf('fa-minus') >= 0) problem.state = 'notac';
138-
else if (type.indexOf('fa-briefcase') >= 0) problem.companies = _split($(span).attr('title'), ',');
139-
});
140-
137+
switch (p.user_status) {
138+
case 'Accepted': problem.state = 'ac'; break;
139+
case 'Failed': problem.state = 'notac'; break;
140+
default: problem.state = 'None';
141+
}
141142
return problem;
142-
}).get();
143+
});
143144

144-
return cb(null, problems);
145+
ctx.count = json.count;
146+
ctx.pages = json.maximum_page;
147+
return cb(null, problems, ctx);
145148
});
146149
};
147150

148151
plugin.getProblem = function(problem, cb) {
149152
log.debug('running lintcode.getProblem');
150-
var opts = makeOpts(problem.link);
153+
const link = config.URL_PROBLEM_DETAIL.replace('$slug', problem.slug);
154+
const opts = makeOpts(link);
151155

156+
const spin = h.spin('Downloading ' + problem.slug);
152157
request(opts, function(e, resp, body) {
158+
spin.stop();
153159
e = checkError(e, resp, 200);
154160
if (e) return cb(e);
155161

156-
var $ = cheerio.load(body);
157-
problem.testcase = $('textarea[id=input-testcase]').text();
162+
const json = JSON.parse(body);
163+
problem.testcase = json.testcase_sample;
158164
problem.testable = problem.testcase.length > 0;
159-
160-
var lines = [];
161-
$('div[id=description] > div').each(function(i, div) {
162-
if (i === 0) {
163-
div = $(div).find('div')[0];
164-
lines.push($(div).text().trim());
165-
return;
166-
}
167-
168-
var text = $(div).text().trim();
169-
var type = $(div).find('b').text().trim();
170-
171-
if (type === 'Tags') {
172-
problem.tags = _split(text, '\n');
173-
problem.tags.shift();
174-
} else if (type === 'Related Problems') return;
175-
else lines.push(text);
176-
});
177-
problem.desc = lines.join('\n').replace(/\n{2,}/g, '\n');
178-
problem.totalAC = '';
179-
problem.totalSubmit = '';
165+
problem.tags = json.tags.map(x => x.name);
166+
problem.desc = cheerio.load(json.description).root().text();
167+
problem.totalAC = json.total_accepted;
168+
problem.totalSubmit = json.total_submissions;
180169
problem.templates = [];
181170

182-
var doTask = function(lang, taskDone) {
171+
const getLang = function(lang, queue, cb) {
183172
plugin.getProblemCode(problem, lang, function(e, code) {
184-
if (e) return taskDone(e);
185-
186-
lang = _.clone(lang);
187-
lang.defaultCode = code;
188-
problem.templates.push(lang);
189-
return taskDone();
173+
if (!e) {
174+
lang = _.clone(lang);
175+
lang.defaultCode = code;
176+
problem.templates.push(lang);
177+
}
178+
return cb(e);
190179
});
191180
};
192181

193-
queue.run(LANGS, doTask, function(e) {
194-
return cb(e, problem);
195-
});
182+
const q = new Queue(LANGS, {}, getLang);
183+
q.run(null, e => cb(e, problem));
196184
});
197185
};
198186

199187
plugin.getProblemCode = function(problem, lang, cb) {
200188
log.debug('running lintcode.getProblemCode:' + lang.value);
201-
var url = config.URL_PROBLEM_CODE
202-
.replace('$id', problem.id)
203-
.replace('$lang', lang.text.replace(/\+/g, '%2b'));
204-
var opts = makeOpts(url);
189+
const url = config.URL_PROBLEM_CODE.replace('$id', problem.id)
190+
.replace('$lang', lang.text.replace(/\+/g, '%2B'));
191+
const opts = makeOpts(url);
205192

193+
const spin = h.spin('Downloading code for ' + lang.text);
206194
request(opts, function(e, resp, body) {
195+
spin.stop();
207196
e = checkError(e, resp, 200);
208197
if (e) return cb(e);
209198

@@ -213,29 +202,29 @@ plugin.getProblemCode = function(problem, lang, cb) {
213202
};
214203

215204
function runCode(problem, isTest, cb) {
216-
var lang = _.find(LANGS, function(x) {
217-
return x.value === h.extToLang(problem.file);
218-
});
219-
220-
var opts = makeOpts(config.URL_TEST);
205+
const lang = _.find(LANGS, x => x.value === h.extToLang(problem.file));
206+
const opts = makeOpts(config.URL_TEST);
207+
opts.headers.referer = problem.link;
221208
opts.form = {
222209
problem_id: problem.id,
223210
code: h.getFileData(problem.file),
224-
language: lang.text,
225-
csrfmiddlewaretoken: session.getUser().sessionCSRF
211+
language: lang.text
226212
};
227213
if (isTest) {
228214
opts.form.input = problem.testcase;
229215
opts.form.is_test_submission = true;
230216
}
231217

218+
spin = h.spin('Sending code to judge');
232219
request.post(opts, function(e, resp, body) {
220+
spin.stop();
233221
e = checkError(e, resp, 200);
234222
if (e) return cb(e);
235223

236224
var json = JSON.parse(body);
237-
if (!json.id || !json.success) return cb(json.message);
225+
if (!json.id) return cb('Failed to start judge!');
238226

227+
spin = h.spin('Waiting for judge result');
239228
verifyResult(json.id, isTest, cb);
240229
});
241230
}
@@ -258,6 +247,7 @@ function verifyResult(id, isTest, cb) {
258247
}
259248

260249
function formatResult(result) {
250+
spin.stop();
261251
var x = {
262252
ok: result.status === 'Accepted',
263253
type: 'Actual',
@@ -290,7 +280,7 @@ plugin.testProblem = function(problem, cb) {
290280
runCode(problem, true, function(e, result) {
291281
if (e) return cb(e);
292282

293-
var expected = {
283+
const expected = {
294284
ok: true,
295285
type: 'Expected',
296286
answer: result.expected_answer,
@@ -322,33 +312,28 @@ plugin.starProblem = function(problem, starred, cb) {
322312

323313
plugin.login = function(user, cb) {
324314
log.debug('running lintcode.login');
325-
request(config.URL_LOGIN, function(e, resp, body) {
326-
e = checkError(e, resp, 200);
327-
if (e) return cb(e);
315+
const opts = {
316+
url: config.URL_LOGIN,
317+
headers: {
318+
'x-csrftoken': null
319+
},
320+
form: {
321+
username_or_email: user.login,
322+
password: user.pass
323+
}
324+
};
328325

329-
user.loginCSRF = h.getSetCookieValue(resp, 'csrftoken');
330-
331-
var opts = {
332-
url: config.URL_LOGIN,
333-
headers: {
334-
Cookie: 'csrftoken=' + user.loginCSRF + ';'
335-
},
336-
form: {
337-
csrfmiddlewaretoken: user.loginCSRF,
338-
username_or_email: user.login,
339-
password: user.pass
340-
}
341-
};
342-
request.post(opts, function(e, resp, body) {
343-
if (e) return cb(e);
344-
if (resp.statusCode !== 302) return cb('invalid password?');
326+
const spin = h.spin('Signing in lintcode.com');
327+
request.post(opts, function(e, resp, body) {
328+
spin.stop();
329+
if (e) return cb(e);
330+
if (resp.statusCode !== 200) return cb('invalid password?');
345331

346-
user.sessionCSRF = h.getSetCookieValue(resp, 'csrftoken');
347-
user.sessionId = h.getSetCookieValue(resp, 'sessionid');
348-
user.name = user.login; // FIXME
332+
user.sessionCSRF = h.getSetCookieValue(resp, 'csrftoken');
333+
user.sessionId = h.getSetCookieValue(resp, 'sessionid');
334+
user.name = user.login; // FIXME
349335

350-
return cb(null, user);
351-
});
336+
return cb(null, user);
352337
});
353338
};
354339

0 commit comments

Comments
 (0)