diff --git a/lib/cache.js b/lib/cache.js index 42efad0b..dc10d52e 100644 --- a/lib/cache.js +++ b/lib/cache.js @@ -9,6 +9,12 @@ cache.init = function() { file.mkdir(file.cacheDir()); }; +cache.deleteAll = function () { + cache.list().forEach(value => { + cache.del(value.name); + }) +}; + cache.get = function(k) { const fullpath = file.cacheFile(k); if (!file.exist(fullpath)) return null; diff --git a/lib/commands/list.js b/lib/commands/list.js index 963284a3..ea0b0a0c 100644 --- a/lib/commands/list.js +++ b/lib/commands/list.js @@ -28,6 +28,12 @@ const cmd = { default: false, describe: 'Show extra details: category, companies, tags.' }) + .option('T', { + alias: 'dontTranslate', + type: 'boolean', + default: false, + describe: 'Set to true to disable endpoint\'s translation', + }) .positional('keyword', { type: 'string', default: '', diff --git a/lib/commands/show.js b/lib/commands/show.js index 56b9341e..93f643e2 100644 --- a/lib/commands/show.js +++ b/lib/commands/show.js @@ -57,6 +57,12 @@ const cmd = { default: false, describe: 'Show extra question details in source code' }) + .option('T', { + alias: 'dontTranslate', + type: 'boolean', + default: false, + describe: 'Set to true to disable endpoint\'s translation', + }) .positional('keyword', { type: 'string', default: '', @@ -175,7 +181,7 @@ cmd.handler = function(argv) { session.argv = argv; if (argv.keyword.length > 0) { // show specific one - core.getProblem(argv.keyword, function(e, problem) { + core.getProblem(argv.keyword, !argv.dontTranslate, function(e, problem) { if (e) return log.fail(e); showProblem(problem, argv); }); @@ -194,7 +200,7 @@ cmd.handler = function(argv) { if (problems.length === 0) return log.fail('Problem not found!'); const problem = _.sample(problems); - core.getProblem(problem, function(e, problem) { + core.getProblem(problem, !argv.dontTranslate, function(e, problem) { if (e) return log.fail(e); showProblem(problem, argv); }); diff --git a/lib/commands/star.js b/lib/commands/star.js index 3660432b..6a396dc9 100644 --- a/lib/commands/star.js +++ b/lib/commands/star.js @@ -29,7 +29,8 @@ const cmd = { cmd.handler = function(argv) { session.argv = argv; - core.getProblem(argv.keyword, function(e, problem) { + // translation doesn't affect question lookup + core.getProblem(argv.keyword, true, function(e, problem) { if (e) return log.fail(e); core.starProblem(problem, !argv.delete, function(e, starred) { diff --git a/lib/commands/submission.js b/lib/commands/submission.js index de0449a3..547e3061 100644 --- a/lib/commands/submission.js +++ b/lib/commands/submission.js @@ -42,6 +42,12 @@ const cmd = { default: false, describe: 'Show extra question details in submission code' }) + .option('T', { + alias: 'dontTranslate', + type: 'boolean', + default: false, + describe: 'Set to true to disable endpoint\'s translation', + }) .positional('keyword', { type: 'string', default: '', @@ -69,7 +75,7 @@ function doTask(problem, queue, cb) { if (argv.extra) { // have to get problem details, e.g. problem description. - core.getProblem(problem.fid, function(e, problem) { + core.getProblem(problem.fid, !argv.dontTranslate, function(e, problem) { if (e) return cb(e); exportSubmission(problem, argv, onTaskDone); }); @@ -135,7 +141,7 @@ cmd.handler = function(argv) { if (!argv.keyword) return log.fail('missing keyword?'); - core.getProblem(argv.keyword, function(e, problem) { + core.getProblem(argv.keyword, !argv.dontTranslate, function(e, problem) { if (e) return log.fail(e); q.addTask(problem).run(); }); diff --git a/lib/commands/submit.js b/lib/commands/submit.js index 2209249b..c550849b 100644 --- a/lib/commands/submit.js +++ b/lib/commands/submit.js @@ -49,7 +49,8 @@ cmd.handler = function(argv) { const meta = file.meta(argv.filename); - core.getProblem(meta.id, function(e, problem) { + // translation doesn't affect problem lookup + core.getProblem(meta.id, true, function(e, problem) { if (e) return log.fail(e); problem.file = argv.filename; diff --git a/lib/commands/test.js b/lib/commands/test.js index 609a18fd..787d7b57 100644 --- a/lib/commands/test.js +++ b/lib/commands/test.js @@ -60,7 +60,7 @@ function runTest(argv) { const meta = file.meta(argv.filename); - core.getProblem(meta.id, function(e, problem) { + core.getProblem(meta.id, true, function(e, problem) { if (e) return log.fail(e); if (!problem.testable) diff --git a/lib/core.js b/lib/core.js index 5d194e21..c9df6322 100644 --- a/lib/core.js +++ b/lib/core.js @@ -61,7 +61,7 @@ const QUERY_HANDLERS = { }; core.filterProblems = function(opts, cb) { - this.getProblems(function(e, problems) { + this.getProblems(!opts.dontTranslate, function(e, problems) { if (e) return cb(e); for (let q of (opts.query || '').split('')) { @@ -82,11 +82,11 @@ core.filterProblems = function(opts, cb) { }); }; -core.getProblem = function(keyword, cb) { +core.getProblem = function(keyword, needTranslation, cb) { if (keyword.id) - return core.next.getProblem(keyword, cb); + return core.next.getProblem(keyword, needTranslation, cb); - this.getProblems(function(e, problems) { + this.getProblems(needTranslation, function(e, problems) { if (e) return cb(e); keyword = Number(keyword) || keyword; @@ -95,7 +95,7 @@ core.getProblem = function(keyword, cb) { return x.fid + '' === keyword + '' || x.fid + '' === metaFid + '' || x.name === keyword || x.slug === keyword; }); if (!problem) return cb('Problem not found!'); - core.next.getProblem(problem, cb); + core.next.getProblem(problem, needTranslation, cb); }); }; diff --git a/lib/helper.js b/lib/helper.js index d256103e..9c5ff43f 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -52,11 +52,12 @@ const LANGS = [ const h = {}; h.KEYS = { - user: '../user', - stat: '../stat', - plugins: '../../plugins', - problems: 'problems', - problem: p => p.fid + '.' + p.slug + '.' + p.category + user: '../user', + stat: '../stat', + plugins: '../../plugins', + problems: 'problems', + translation: 'translationConfig', + problem: p => p.fid + '.' + p.slug + '.' + p.category }; h.prettyState = function(state) { diff --git a/lib/plugins/cache.js b/lib/plugins/cache.js index c224a27e..d6316861 100644 --- a/lib/plugins/cache.js +++ b/lib/plugins/cache.js @@ -9,14 +9,28 @@ var session = require('../session'); const plugin = new Plugin(50, 'cache', '', 'Plugin to provide local cache.'); -plugin.getProblems = function(cb) { +// this function will clear all caches if needTranslation is different than stored +// it will also store the new needTranslation into cache automatically +function clearCacheIfTchanged(needTranslation) { + const translationConfig = cache.get(h.KEYS.translation); + if (!translationConfig || translationConfig['useEndpointTranslation'] != needTranslation) { + // cache doesn't have the key => old cache version, need to update + // or cache does have the key but it contains a different value + cache.deleteAll(); + cache.set(h.KEYS.translation, { useEndpointTranslation: needTranslation }); + log.debug('cache cleared: -T option changed'); + } +} + +plugin.getProblems = function (needTranslation, cb) { + clearCacheIfTchanged(needTranslation); const problems = cache.get(h.KEYS.problems); if (problems) { log.debug('cache hit: problems.json'); return cb(null, problems); } - plugin.next.getProblems(function(e, problems) { + plugin.next.getProblems(needTranslation, function(e, problems) { if (e) return cb(e); cache.set(h.KEYS.problems, problems); @@ -24,7 +38,8 @@ plugin.getProblems = function(cb) { }); }; -plugin.getProblem = function(problem, cb) { +plugin.getProblem = function (problem, needTranslation, cb) { + clearCacheIfTchanged(needTranslation); const k = h.KEYS.problem(problem); const _problem = cache.get(k); if (_problem) { @@ -42,7 +57,7 @@ plugin.getProblem = function(problem, cb) { } } - plugin.next.getProblem(problem, function(e, _problem) { + plugin.next.getProblem(problem, needTranslation, function(e, _problem) { if (e) return cb(e); plugin.saveProblem(_problem); diff --git a/lib/plugins/company.js b/lib/plugins/company.js index 7959363b..68cd2ac8 100644 --- a/lib/plugins/company.js +++ b/lib/plugins/company.js @@ -1511,8 +1511,8 @@ var TAGS = { '1148': ['math'] }; -plugin.getProblems = function(cb) { - plugin.next.getProblems(function(e, problems) { +plugin.getProblems = function(needTranslation, cb) { + plugin.next.getProblems(needTranslation, function(e, problems) { if (e) return cb(e); problems.forEach(function(problem) { diff --git a/lib/plugins/leetcode.cn.js b/lib/plugins/leetcode.cn.js index 60e620eb..77639c7d 100644 --- a/lib/plugins/leetcode.cn.js +++ b/lib/plugins/leetcode.cn.js @@ -70,21 +70,29 @@ function checkError(e, resp, expectedStatus) { return e; } -plugin.getProblems = function(cb) { - plugin.next.getProblems(function(e, problems) { +// overloading getProblems here to make sure everything related +// to listing out problems can have a chance to be translated. +// NOTE: Details of the problem is translated inside leetcode.js +plugin.getProblems = function (needTranslation, cb) { + plugin.next.getProblems(needTranslation, function(e, problems) { if (e) return cb(e); - plugin.getProblemsTitle(function(e, titles) { - if (e) return cb(e); + if (needTranslation) { + // only translate titles of the list if user requested + plugin.getProblemsTitle(function (e, titles) { + if (e) return cb(e); - problems.forEach(function(problem) { - const title = titles[problem.id]; - if (title) - problem.name = title; - }); + problems.forEach(function (problem) { + const title = titles[problem.id]; + if (title) + problem.name = title; + }); + return cb(null, problems); + }); + } else { return cb(null, problems); - }); + } }); }; diff --git a/lib/plugins/leetcode.js b/lib/plugins/leetcode.js index c66d7cfc..9e810146 100644 --- a/lib/plugins/leetcode.js +++ b/lib/plugins/leetcode.js @@ -54,7 +54,7 @@ plugin.init = function() { config.app = 'leetcode'; }; -plugin.getProblems = function(cb) { +plugin.getProblems = function (needTranslation, cb) { log.debug('running leetcode.getProblems'); let problems = []; const getCategory = function(category, queue, cb) { @@ -117,7 +117,7 @@ plugin.getCategoryProblems = function(category, cb) { }); }; -plugin.getProblem = function(problem, cb) { +plugin.getProblem = function(problem, needTranslation, cb) { log.debug('running leetcode.getProblem'); const user = session.getUser(); if (problem.locked && !user.paid) return cb('failed to load locked problem!'); @@ -161,7 +161,7 @@ plugin.getProblem = function(problem, cb) { problem.likes = q.likes; problem.dislikes = q.dislikes; - problem.desc = q.translatedContent ? q.translatedContent : q.content; + problem.desc = (q.translatedContent && needTranslation) ? q.translatedContent : q.content; problem.templates = JSON.parse(q.codeDefinition); problem.testcase = q.sampleTestCase; diff --git a/lib/plugins/solution.discuss.js b/lib/plugins/solution.discuss.js index a161aa50..d5f07c1f 100644 --- a/lib/plugins/solution.discuss.js +++ b/lib/plugins/solution.discuss.js @@ -71,8 +71,8 @@ function getSolution(problem, lang, cb) { }); } -plugin.getProblem = function(problem, cb) { - plugin.next.getProblem(problem, function(e, problem) { +plugin.getProblem = function(problem, needTranslation, cb) { + plugin.next.getProblem(problem, needTranslation, function(e, problem) { if (e || !session.argv.solution) return cb(e, problem); var lang = session.argv.lang; diff --git a/test/plugins/test_cache.js b/test/plugins/test_cache.js index 4480b192..0009c317 100644 --- a/test/plugins/test_cache.js +++ b/test/plugins/test_cache.js @@ -19,6 +19,7 @@ describe('plugin:cache', function() { {id: 0, fid: 0, name: 'name0', slug: 'slug0', starred: false, desc: '
', likes: '1', dislikes: '1', category: 'algorithms'}, {id: 1, fid: 1, name: 'name1', slug: 'slug1', starred: true, desc: '', likes: '1', dislikes: '1', category: 'algorithms'} ]; + const TRANSLATION_CONFIGS = { useEndpointTranslation: false }; const PROBLEM = {id: 0, fid: 0, slug: 'slug0', category: 'algorithms'}; before(function() { @@ -51,8 +52,9 @@ describe('plugin:cache', function() { describe('#getProblems', function() { it('should getProblems w/ cache ok', function(done) { cache.set('problems', PROBLEMS); + cache.set(h.KEYS.translation, TRANSLATION_CONFIGS); - plugin.getProblems(function(e, problems) { + plugin.getProblems(false, function(e, problems) { assert.equal(e, null); assert.deepEqual(problems, PROBLEMS); done(); @@ -61,9 +63,9 @@ describe('plugin:cache', function() { it('should getProblems w/o cache ok', function(done) { cache.del('problems'); - next.getProblems = cb => cb(null, PROBLEMS); + next.getProblems = (needT, cb) => cb(null, PROBLEMS); - plugin.getProblems(function(e, problems) { + plugin.getProblems(false, function(e, problems) { assert.equal(e, null); assert.deepEqual(problems, PROBLEMS); done(); @@ -72,9 +74,9 @@ describe('plugin:cache', function() { it('should getProblems w/o cache fail if client error', function(done) { cache.del('problems'); - next.getProblems = cb => cb('client getProblems error'); + next.getProblems = (needT, cb) => cb('client getProblems error'); - plugin.getProblems(function(e, problems) { + plugin.getProblems(false, function(e, problems) { assert.equal(e, 'client getProblems error'); done(); }); @@ -84,9 +86,10 @@ describe('plugin:cache', function() { describe('#getProblem', function() { it('should getProblem w/ cache ok', function(done) { cache.set('problems', PROBLEMS); + cache.set(h.KEYS.translation, TRANSLATION_CONFIGS); cache.set('0.slug0.algorithms', PROBLEMS[0]); - plugin.getProblem(_.clone(PROBLEM), function(e, problem) { + plugin.getProblem(_.clone(PROBLEM), false, function(e, problem) { assert.equal(e, null); assert.deepEqual(problem, PROBLEMS[0]); done(); @@ -96,9 +99,9 @@ describe('plugin:cache', function() { it('should getProblem w/o cache ok', function(done) { cache.set('problems', PROBLEMS); cache.del('0.slug0.algorithms'); - next.getProblem = (problem, cb) => cb(null, PROBLEMS[0]); + next.getProblem = (problem, needT, cb) => cb(null, PROBLEMS[0]); - plugin.getProblem(_.clone(PROBLEM), function(e, problem) { + plugin.getProblem(_.clone(PROBLEM), false, function(e, problem) { assert.equal(e, null); assert.deepEqual(problem, PROBLEMS[0]); done(); @@ -108,9 +111,9 @@ describe('plugin:cache', function() { it('should getProblem fail if client error', function(done) { cache.set('problems', PROBLEMS); cache.del('0.slug0.algorithms'); - next.getProblem = (problem, cb) => cb('client getProblem error'); + next.getProblem = (problem, needT, cb) => cb('client getProblem error'); - plugin.getProblem(_.clone(PROBLEM), function(e, problem) { + plugin.getProblem(_.clone(PROBLEM), false, function(e, problem) { assert.equal(e, 'client getProblem error'); done(); }); @@ -135,12 +138,13 @@ describe('plugin:cache', function() { describe('#updateProblem', function() { it('should updateProblem ok', function(done) { cache.set('problems', PROBLEMS); + cache.set(h.KEYS.translation, TRANSLATION_CONFIGS); const kv = {value: 'value00'}; const ret = plugin.updateProblem(PROBLEMS[0], kv); assert.equal(ret, true); - plugin.getProblems(function(e, problems) { + plugin.getProblems(false, function(e, problems) { assert.equal(e, null); assert.deepEqual(problems, [ {id: 0, fid: 0, name: 'name0', slug: 'slug0', value: 'value00', starred: false, desc: '', likes: '1', dislikes: '1', category: 'algorithms'}, diff --git a/test/plugins/test_leetcode.js b/test/plugins/test_leetcode.js index 39a1431e..79887df7 100644 --- a/test/plugins/test_leetcode.js +++ b/test/plugins/test_leetcode.js @@ -125,7 +125,7 @@ describe('plugin:leetcode', function() { .get('/api/problems/concurrency/') .replyWithFile(200, './test/mock/problems.json.20160911'); - plugin.getProblems(function(e, problems) { + plugin.getProblems(false, function(e, problems) { assert.equal(e, null); assert.equal(problems.length, 377 * 4); done(); @@ -149,7 +149,7 @@ describe('plugin:leetcode', function() { .get('/api/problems/concurrency/') .replyWithFile(200, './test/mock/problems.json.20160911'); - plugin.getProblems(function(e, problems) { + plugin.getProblems(false, function(e, problems) { assert.equal(e.message, 'unknown error'); done(); }); @@ -192,7 +192,7 @@ describe('plugin:leetcode', function() { .post('/graphql') .replyWithFile(200, './test/mock/find-the-difference.json.20171216'); - plugin.getProblem(PROBLEM, function(e, problem) { + plugin.getProblem(PROBLEM, false, function(e, problem) { assert.equal(e, null); assert.equal(problem.totalAC, '89.7K'); assert.equal(problem.totalSubmit, '175.7K'); @@ -367,7 +367,7 @@ describe('plugin:leetcode', function() { it('should fail if no permission for locked', function(done) { PROBLEM.locked = true; - plugin.getProblem(PROBLEM, function(e, problem) { + plugin.getProblem(PROBLEM, false, function(e, problem) { assert.equal(e, 'failed to load locked problem!'); done(); }); @@ -376,7 +376,7 @@ describe('plugin:leetcode', function() { it('should fail if session expired', function(done) { nock('https://leetcode.com').post('/graphql').reply(403); - plugin.getProblem(PROBLEM, function(e, problem) { + plugin.getProblem(PROBLEM, false, function(e, problem) { assert.equal(e, session.errors.EXPIRED); done(); }); @@ -385,7 +385,7 @@ describe('plugin:leetcode', function() { it('should fail if http error', function(done) { nock('https://leetcode.com').post('/graphql').reply(500); - plugin.getProblem(PROBLEM, function(e, problem) { + plugin.getProblem(PROBLEM, false, function(e, problem) { assert.deepEqual(e, {msg: 'http error', statusCode: 500}); done(); }); @@ -394,7 +394,7 @@ describe('plugin:leetcode', function() { it('should fail if unknown error', function(done) { nock('https://leetcode.com').post('/graphql').replyWithError('unknown error!'); - plugin.getProblem(PROBLEM, function(e, problem) { + plugin.getProblem(PROBLEM, false, function(e, problem) { assert.equal(e.message, 'unknown error!'); done(); }); diff --git a/test/test_core.js b/test/test_core.js index 195f736a..1c689f52 100644 --- a/test/test_core.js +++ b/test/test_core.js @@ -40,8 +40,8 @@ describe('core', function() { beforeEach(function() { next = {}; - next.getProblems = cb => cb(null, PROBLEMS); - next.getProblem = (p, cb) => cb(null, p); + next.getProblems = (needTrans, cb) => cb(null, PROBLEMS); + next.getProblem = (p, needTrans, cb) => cb(null, p); core = rewire('../lib/core'); core.setNext(next); @@ -68,7 +68,7 @@ describe('core', function() { let n = cases.length; for (let x of cases) { - core.filterProblems({query: x[0]}, function(e, problems) { + core.filterProblems({query: x[0], dontTranslate: false}, function(e, problems) { assert.notExists(e); assert.equal(problems.length, x[1].length); @@ -90,7 +90,7 @@ describe('core', function() { let n = cases.length; for (let x of cases) { - core.filterProblems({tag: x[0]}, function(e, problems) { + core.filterProblems({ tag: x[0], dontTranslate: false}, function(e, problems) { assert.notExists(e); assert.equal(problems.length, x[1].length); @@ -102,7 +102,7 @@ describe('core', function() { }); it('should fail if getProblems error', function(done) { - next.getProblems = cb => cb('getProblems error'); + next.getProblems = (needT, cb) => cb('getProblems error'); core.filterProblems({}, function(e) { assert.equal(e, 'getProblems error'); done(); @@ -340,8 +340,9 @@ describe('core', function() { }); // #exportProblem describe('#getProblem', function() { - it('should get by id ok', function(done) { - core.getProblem(0, function(e, problem) { + it('should get by id ok', function (done) { + // set needTranslate to false here because it's not used anyways + core.getProblem(0, false, function(e, problem) { assert.notExists(e); assert.deepEqual(problem, PROBLEMS[0]); done(); @@ -349,7 +350,7 @@ describe('core', function() { }); it('should get by key ok', function(done) { - core.getProblem('slug0', function(e, problem) { + core.getProblem('slug0', false, function(e, problem) { assert.notExists(e); assert.deepEqual(problem, PROBLEMS[0]); done(); @@ -357,23 +358,23 @@ describe('core', function() { }); it('should fail if not found', function(done) { - core.getProblem(3, function(e, problem) { + core.getProblem(3, false, function(e, problem) { assert.equal(e, 'Problem not found!'); done(); }); }); it('should fail if client error', function(done) { - next.getProblem = (problem, cb) => cb('client getProblem error'); + next.getProblem = (problem, needT, cb) => cb('client getProblem error'); - core.getProblem(0, function(e, problem) { + core.getProblem(0, false, function(e, problem) { assert.equal(e, 'client getProblem error'); done(); }); }); it('should ok if problem is already there', function(done) { - core.getProblem(PROBLEMS[1], function(e, problem) { + core.getProblem(PROBLEMS[1], false, function(e, problem) { assert.notExists(e); assert.deepEqual(problem, PROBLEMS[1]); done(); @@ -381,9 +382,9 @@ describe('core', function() { }); it('should fail if getProblems error', function(done) { - next.getProblems = cb => cb('getProblems error'); + next.getProblems = (needT, cb) => cb('getProblems error'); - core.getProblem(0, function(e, problem) { + core.getProblem(0, false, function(e, problem) { assert.equal(e, 'getProblems error'); done(); });