@@ -6,7 +6,7 @@ var util = require('util');
6
6
var h = require ( '../helper' ) ;
7
7
var log = require ( '../log' ) ;
8
8
var Plugin = require ( '../plugin' ) ;
9
- var queue = require ( '../queue' ) ;
9
+ var Queue = require ( '../queue' ) ;
10
10
var session = require ( '../session' ) ;
11
11
12
12
// Still working in progress!
@@ -18,44 +18,48 @@ var session = require('../session');
18
18
//
19
19
// https://github.com/skygragon/leetcode-cli-plugins/blob/master/docs/lintcode.md
20
20
//
21
- var plugin = new Plugin ( 15 , 'lintcode' , '2017.08.04 ' ,
21
+ const plugin = new Plugin ( 15 , 'lintcode' , '2018.05.29 ' ,
22
22
'Plugin to talk with lintcode APIs.' ) ;
23
23
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 '
33
33
} ;
34
34
35
- var LANGS = [
35
+ // FIXME: add more langs
36
+ const LANGS = [
36
37
{ value : 'cpp' , text : 'C++' } ,
37
38
{ value : 'java' , text : 'Java' } ,
38
39
{ value : 'python' , text : 'Python' }
39
40
] ;
40
41
42
+ var spin ;
43
+
41
44
function signOpts ( opts , user ) {
42
45
opts . headers . Cookie = 'sessionid=' + user . sessionId +
43
46
';csrftoken=' + user . sessionCSRF + ';' ;
47
+ opts . headers [ 'x-csrftoken' ] = user . sessionCSRF ;
44
48
}
45
49
46
50
function makeOpts ( url ) {
47
- var opts = { } ;
48
- opts . url = url ;
49
- opts . headers = { } ;
50
-
51
+ const opts = {
52
+ url : url ,
53
+ headers : { }
54
+ } ;
51
55
if ( session . isLogin ( ) )
52
56
signOpts ( opts , session . getUser ( ) ) ;
53
57
return opts ;
54
58
}
55
59
56
60
function checkError ( e , resp , expectedStatus ) {
57
61
if ( ! e && resp && resp . statusCode !== expectedStatus ) {
58
- var code = resp . statusCode ;
62
+ const code = resp . statusCode ;
59
63
log . debug ( 'http error: ' + code ) ;
60
64
61
65
if ( code === 403 || code === 401 ) {
@@ -84,126 +88,111 @@ plugin.getProblems = function(cb) {
84
88
log . debug ( 'running lintcode.getProblems' ) ;
85
89
86
90
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 ) ;
91
98
} ) ;
92
99
} ;
93
100
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 ) ;
100
107
return cb ( e , problems ) ;
101
108
} ) ;
102
109
} ;
103
110
104
111
plugin . getPageProblems = function ( page , cb ) {
105
112
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 ) ) ;
107
114
115
+ spin . text = 'Downloading page ' + page ;
108
116
request ( opts , function ( e , resp , body ) {
109
117
e = checkError ( e , resp , 200 ) ;
110
118
if ( e ) return cb ( e ) ;
111
119
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 ,
116
128
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 ,
120
134
tags : [ ]
121
135
} ;
122
- problem . slug = $ ( a ) . attr ( 'href' ) . split ( '/' ) . pop ( ) ;
123
136
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
+ }
141
142
return problem ;
142
- } ) . get ( ) ;
143
+ } ) ;
143
144
144
- return cb ( null , problems ) ;
145
+ ctx . count = json . count ;
146
+ ctx . pages = json . maximum_page ;
147
+ return cb ( null , problems , ctx ) ;
145
148
} ) ;
146
149
} ;
147
150
148
151
plugin . getProblem = function ( problem , cb ) {
149
152
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 ) ;
151
155
156
+ const spin = h . spin ( 'Downloading ' + problem . slug ) ;
152
157
request ( opts , function ( e , resp , body ) {
158
+ spin . stop ( ) ;
153
159
e = checkError ( e , resp , 200 ) ;
154
160
if ( e ) return cb ( e ) ;
155
161
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 ;
158
164
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 ;
180
169
problem . templates = [ ] ;
181
170
182
- var doTask = function ( lang , taskDone ) {
171
+ const getLang = function ( lang , queue , cb ) {
183
172
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 ) ;
190
179
} ) ;
191
180
} ;
192
181
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 ) ) ;
196
184
} ) ;
197
185
} ;
198
186
199
187
plugin . getProblemCode = function ( problem , lang , cb ) {
200
188
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 ) ;
205
192
193
+ const spin = h . spin ( 'Downloading code for ' + lang . text ) ;
206
194
request ( opts , function ( e , resp , body ) {
195
+ spin . stop ( ) ;
207
196
e = checkError ( e , resp , 200 ) ;
208
197
if ( e ) return cb ( e ) ;
209
198
@@ -213,29 +202,29 @@ plugin.getProblemCode = function(problem, lang, cb) {
213
202
} ;
214
203
215
204
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 ;
221
208
opts . form = {
222
209
problem_id : problem . id ,
223
210
code : h . getFileData ( problem . file ) ,
224
- language : lang . text ,
225
- csrfmiddlewaretoken : session . getUser ( ) . sessionCSRF
211
+ language : lang . text
226
212
} ;
227
213
if ( isTest ) {
228
214
opts . form . input = problem . testcase ;
229
215
opts . form . is_test_submission = true ;
230
216
}
231
217
218
+ spin = h . spin ( 'Sending code to judge' ) ;
232
219
request . post ( opts , function ( e , resp , body ) {
220
+ spin . stop ( ) ;
233
221
e = checkError ( e , resp , 200 ) ;
234
222
if ( e ) return cb ( e ) ;
235
223
236
224
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!' ) ;
238
226
227
+ spin = h . spin ( 'Waiting for judge result' ) ;
239
228
verifyResult ( json . id , isTest , cb ) ;
240
229
} ) ;
241
230
}
@@ -258,6 +247,7 @@ function verifyResult(id, isTest, cb) {
258
247
}
259
248
260
249
function formatResult ( result ) {
250
+ spin . stop ( ) ;
261
251
var x = {
262
252
ok : result . status === 'Accepted' ,
263
253
type : 'Actual' ,
@@ -290,7 +280,7 @@ plugin.testProblem = function(problem, cb) {
290
280
runCode ( problem , true , function ( e , result ) {
291
281
if ( e ) return cb ( e ) ;
292
282
293
- var expected = {
283
+ const expected = {
294
284
ok : true ,
295
285
type : 'Expected' ,
296
286
answer : result . expected_answer ,
@@ -322,33 +312,28 @@ plugin.starProblem = function(problem, starred, cb) {
322
312
323
313
plugin . login = function ( user , cb ) {
324
314
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
+ } ;
328
325
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?' ) ;
345
331
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
349
335
350
- return cb ( null , user ) ;
351
- } ) ;
336
+ return cb ( null , user ) ;
352
337
} ) ;
353
338
} ;
354
339
0 commit comments