Last active
June 23, 2022 13:15
-
-
Save Aadv1k/bfa9f874345c6204e4871e66b2f6d99b to your computer and use it in GitHub Desktop.
program to fetch data from leetcode and generate tests + starter code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
/* | |
* Author: Aadv1k<[email protected]> | |
* License: GNU Public License | |
* Source: https://gist.github.com/Aadv1k/bfa9f874345c6204e4871e66b2f6d99b | |
* Install: npx https://gist.github.com/Aadv1k/bfa9f874345c6204e4871e66b2f6d99b | |
*/ | |
const axios = require("axios"); | |
const cheerio = require("cheerio"); | |
const { writeFileSync, existsSync, mkdirSync } = require("fs"); | |
const path = require("path"); | |
async function getQuestion(title) { | |
const payload = { | |
query: | |
"query getQuestionDetail($titleSlug: String!) { isCurrentUserAuthenticated question(titleSlug: $titleSlug) { questionId questionFrontendId questionTitle translatedTitle questionTitleSlug content translatedContent difficulty stats allowDiscuss contributors { username profileUrl __typename } similarQuestions mysqlSchemas randomQuestionUrl sessionId categoryTitle submitUrl interpretUrl codeDefinition sampleTestCase enableTestMode metaData enableRunCode enableSubmit judgerAvailable infoVerified envInfo urlManager article questionDetailUrl libraryUrl adminUrl companyTags { name slug translatedName __typename } companyTagStats topicTags { name slug translatedName __typename } __typename } interviewed { interviewedUrl companies { id name slug __typename } timeOptions { id name __typename } stageOptions { id name __typename } __typename } subscribeUrl isPremium loginUrl}", | |
variables: { titleSlug: title }, | |
}; | |
const url = "https://leetcode.com/graphql"; | |
const headers = { "content-type": "application/json" }; | |
let res = await axios.post(url, payload, headers); | |
let que = res.data["data"]["question"]; | |
let content = cheerio.load(que.content); | |
return { | |
id: que.questionId, | |
title: que.questionTitle, | |
titleUrl: que.questionTitleSlug, | |
content: content.text(), | |
codeDefinition: que.codeDefinition, | |
input: content("pre") | |
.text() | |
.split("\n") | |
.filter((e) => e.trim().includes("Input")) | |
.map((e) => e.split(":")[1].trim()) | |
.map((e) => e.replace(/"/g, "'")), | |
output: content("pre") | |
.text() | |
.split("\n") | |
.filter((e) => e.trim().includes("Output")) | |
.map((e) => e.split(":")[1].trim()) | |
.map((e) => e.replace(/"/g, "'")), | |
stats: que.stats, | |
}; | |
} | |
const ARGSIZE = 4; | |
const RED = "\x1b[31m"; | |
const GREEN = "\x1b[32m"; | |
const CLREND = "\x1b[0m"; | |
const args = process.argv; | |
if (args.length < ARGSIZE) { | |
console.log( | |
"Not enough arguments! \nUSAGE: ./leetcode.js <leetcode-problem-in-dash-case> <target-folder>" | |
); | |
process.exit(1); | |
// process.exitCode = 1; | |
// ^- This doesn't work the way I want it to. | |
} | |
let parsedArgs = args | |
.slice(2, 3) | |
.map((e) => e[0].toLowerCase() + e.slice(1)) | |
.join("-"); | |
let srcPath = path.join(process.cwd(), args[3]); | |
getQuestion(parsedArgs) | |
.then((data) => { | |
let probName = `problem${data["id"]}-${data["titleUrl"]}`; | |
console.log(GREEN + `[SUCCESS] Found leetcode ${probName}` + CLREND); | |
if (!existsSync(srcPath)) { | |
console.error( | |
RED + `[WARN] Folder ${srcPath} not found, creating new folder` + CLREND | |
); | |
mkdirSync(srcPath); | |
} | |
writeFileSync(path.join(srcPath, `${probName}.txt`), data["content"]); | |
let probs = data["input"].map((e, i) => [e, data["output"][i]]); | |
let camelName = data["titleUrl"] | |
.split("-") | |
.map((elem, i) => { | |
if (i === 0) { | |
return elem; | |
} | |
return `${elem[0].toUpperCase()}${elem.slice(1)}`; | |
}) | |
.join(""); | |
/*******************************************/ | |
console.log(GREEN + "[SUCCESS] Writing source..." + CLREND); | |
// Generate source | |
let sourceFileContent = JSON.parse(data["codeDefinition"]).find( | |
(e) => e["value"] === "javascript" | |
); | |
writeFileSync( | |
path.join(srcPath, `${camelName}.js`), | |
sourceFileContent["defaultCode"] | |
); | |
/*******************************************/ | |
/*******************************************/ | |
console.log(GREEN + `[SUCCESS] Writing tests...` + CLREND); | |
// Generate tests | |
const testArgs = probs.map((e) => [ | |
e[0] | |
.split("=") | |
.map((e, i) => { | |
if (i === 1) { | |
let tar = e.trim().split(" ").shift(); | |
return tar.slice(0, tar.length - 1); | |
} | |
return e.trim(); | |
}) | |
.filter((_, i) => i !== 0), | |
e[1], | |
]); | |
let testFileTests = testArgs | |
.map((prob) => { | |
let inp = prob[0].join(", "); | |
let out = prob[1]; | |
return ` test("${inp} -> ${out}", () => {\n expect(res(${inp})).toEqual(${out}); \n });`; | |
}) | |
.join("\n\n"); | |
let testFileContent = `const res = require('./${camelName}');\n\ndescribe('Leetcode problem#${data["id"]}: ${data["title"]}', () => {\n${testFileTests}\n});`; | |
// Write tests to the file | |
writeFileSync(path.join(srcPath, `${camelName}.test.js`), testFileContent); | |
/*******************************************/ | |
}) | |
.catch((err) => console.error(err)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "leetcode.js", | |
"version": "1.0.0", | |
"author": "Aadvik <[email protected]>", | |
"license": "GPL-3.0-or-later", | |
"dependencies": { | |
"axios": "^0.27.2", | |
"cheerio": "^1.0.0-rc.11" | |
}, | |
"bin": "./leetcode.js" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment