@@ -6,7 +6,7 @@ var util = require('util');
66var h = require ( '../helper' ) ;
77var log = require ( '../log' ) ;
88var Plugin = require ( '../plugin' ) ;
9- var queue = require ( '../queue' ) ;
9+ var Queue = require ( '../queue' ) ;
1010var 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+
4144function signOpts ( opts , user ) {
4245 opts . headers . Cookie = 'sessionid=' + user . sessionId +
4346 ';csrftoken=' + user . sessionCSRF + ';' ;
47+ opts . headers [ 'x-csrftoken' ] = user . sessionCSRF ;
4448}
4549
4650function 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
5660function 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
104111plugin . 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
148151plugin . 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
199187plugin . 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
215204function 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
260249function 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
323313plugin . 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