From 2fcd4f9b2883b8bd5bcc52e267b6ea1b9421c272 Mon Sep 17 00:00:00 2001 From: lucifer Date: Sun, 2 May 2021 18:55:47 +0800 Subject: [PATCH] =?UTF-8?q?Revert=20"feat:=20=E6=96=B0=E5=A2=9Emd=E8=BD=AC?= =?UTF-8?q?json=E8=84=9A=E6=9C=AC"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +- routes/dailyProblem.js | 56 +++++++++++- utils/transforMd2json.js | 192 --------------------------------------- 3 files changed, 53 insertions(+), 199 deletions(-) delete mode 100644 utils/transforMd2json.js diff --git a/package.json b/package.json index 9fd43069f4..b3e55a4976 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,10 @@ "start": "NODE_ENV=production node bin/www", "dev": "NODE_ENV=development ./node_modules/.bin/nodemon bin/www", "prd": "pm2 start bin/www", - "test": "echo \"Error: no test specified\" && exit 1", - "md2json": "node ./utils/transforMd2json.js" + "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { "@koa/cors": "^3.1.0", - "commonmark": "^0.29.3", "debug": "^4.1.1", "koa": "^2.7.0", "koa-bodyparser": "^4.2.1", diff --git a/routes/dailyProblem.js b/routes/dailyProblem.js index 4ff4b71874..09e476a961 100644 --- a/routes/dailyProblem.js +++ b/routes/dailyProblem.js @@ -1,6 +1,5 @@ const router = require("koa-router")(); const solutions = require("../static/solution/solutions.json"); -const problems = require("../static/problem/problem.json"); const { decrypt } = require("../utils/crypto"); const { success, fail } = require("../utils/request"); @@ -18,9 +17,58 @@ router.get("/api/v1/daily-problem", async (ctx) => { // 3. 根据 Day 几 计算出具体返回哪一个题目 // !!注意: 如果用户指定的时间大于今天,则返回”题目不存在,仅支持查询历史每日一题“ - const day = getDay(ctx.query.date || new Date().getTime()); // 用户指定的实际 - if (day in problems) { - ctx.body = success(problems[day]); + const date = getDay(ctx.query.date || new Date().getTime()); // 用户指定的实际 + if (date === 2) { + ctx.body = success({ + day: 2, + title: "821. 字符的最短距离", + link: "https://leetcode-cn.com/problems/plus-one", + tags: ["基础篇", "数组"], // 目前所有 README 都是没有的。因此如果没有的话,你可以先不返回,有的话就返回。后面我慢慢补 + pres: ["数组的遍历(正向遍历和反向遍历)"], + description: ` +给定一个字符串 S 和一个字符 C。返回一个代表字符串 S 中每个字符到字符串 S 中的字符 C 的最短距离的数组。 + +示例 1: + +输入: S = "loveleetcode", C = 'e' +输出: [3, 2, 1, 0, 1, 0, 0, 1, 2, 2, 1, 0] +说明: + +- 字符串 S 的长度范围为 [1, 10000]。 +- C 是一个单字符,且保证是字符串 S 里的字符。 +- S 和 C 中的所有字母均为小写字母。 + + `, + }); + } else if (date <= 1) { + ctx.body = success({ + day: 1, + title: "66. 加一", + whys: [ + "1. 由于是大家第一次打卡,因此出一个简单题。虽然是简单题,但是如果将加 1 改为加任意的数字,那么就变成了一个非常常见的面试题", + ], + link: "https://leetcode-cn.com/problems/plus-one", + tags: ["基础篇", "数组"], // 目前所有 README 都是没有的。因此如果没有的话,你可以先不返回,有的话就返回。后面我慢慢补 + pres: ["数组的遍历(正向遍历和反向遍历)"], + description: ` +给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。 + +最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 + +你可以假设除了整数 0 之外,这个整数不会以零开头。 + +示例 1: + +输入: [1,2,3] +输出: [1,2,4] +解释: 输入数组表示数字 123。 +示例 2: + +输入: [4,3,2,1] +输出: [4,3,2,2] +解释: 输入数组表示数字 4321。 + `, + }); } else { ctx.body = fail({ message: "当前暂时没有每日一题,请联系当前讲师进行处理~", diff --git a/utils/transforMd2json.js b/utils/transforMd2json.js deleted file mode 100644 index f665a6ec9e..0000000000 --- a/utils/transforMd2json.js +++ /dev/null @@ -1,192 +0,0 @@ -const path = require("path") -const fs = require('fs') -const commonmark = require('commonmark'); -// md文件存放的路径 -const inputDirPath = path.resolve(__dirname, '../static/md') - -// json文件输出的路径 -const outputDirPath = path.resolve(__dirname, '../static') -const problemJson = {}; -const solutionJson = {}; -const { encrypt } = require("./crypto.js"); - -// 需要放到题目描述里的标题 -// 因为部分题解不规范,标题名不能使用完全等于而应该使用include -const problemTitleDimWordArr = ['入选', '地址', '描述', '前置', '公司'] -// 最终生成的题解的key,与上面的模糊匹配的词一一对应 -const problemTitleWordMap = ['why', 'link', 'description', 'pres', 'company'] - - -// 递归读取某一目录下的所有md文件 -function recursionAllMdFile (dir, cb) { - const files = fs.readdirSync(dir); - files.forEach((fileName) => { - var fullPath = path.join(dir, fileName); - const childFile = fs.statSync(fullPath); - if (childFile.isDirectory()) { - recursionAllMdFile(path.join(dir, fileName), cb); //递归读取文件 - } else { - let fildData = fs.readFileSync(fullPath).toString(); - cb(fullPath, fildData) - } - }); -} - -// 预先对文件内容进行处理 -// 1. 根据文件名过滤掉非题解的md -// 2. 去除精选题解 -function preprocessFile(fullPath, fileData){ - let fileName = path.basename(fullPath).trim().toLowerCase() - if( !/d[0-9]+.*\.md$/.test(fileName) || fileName.includes('selec')){ - return - } - transformFileToJSon(fullPath, fileData) -} - -// 通过遍历ast节点树找到type为text节点的值 -// isDeep 为false在找到第一个文案时就中止 -// isDeep 为true在找到下一个heading节点时中止 -function getAstNodeVal(walker, isDeep = false) { - if(!walker.current) return null - let result = [], - now = walker.current; - // 如果当前就是head节点那就将指针向后值一下 - if(now.type === 'heading') now = walker.next() - do { - if(!now.entering) continue - let { node = {} } = now - if(node.type === 'heading') break - if (['text', 'code_block'].includes(node.type)) { - result.push(node.literal || node.info); - if(isDeep === false) break - } - }while((now = walker.next())) - if(!result.length) return null - return result.length > 1 ? result : result[0] -} - -// 当前标题是否属于题目描述的内容, 不属于返回-1, 属于则返回模糊匹配词组中的索引值 -function findIndexProblemDimWorld (title) { - // 把括号内的内容删掉 - // 避免类似这样的标题: 题目地址(239. xxx) - // 括号内的内容与关键字重复导致误判 - title = title.replace(/(\(.*\))/,'') - return problemTitleDimWordArr.findIndex(item => title.includes(item)) -} - -// 获取文件某行之后的所有内容(包含该行) -function getFileDataAfterLine (fullPath, lineNum) { - try { - const data = fs.readFileSync(fullPath, 'UTF-8'); - const lines = data.split(/\r?\n/); - return lines.slice(lineNum - 1) - } catch (err) { - console.error(err); - } -} - -// 写入json对象 -function writeToJsonObject (fullPath, problemData, soluteContentStartLine){ - // 将题目相关的内容写入json - problemData = formateProblemValue(problemData) - problemJson[problemData.day] = problemData - // 将题解相关的内容写入json - let solutionFileData = getFileDataAfterLine(fullPath, soluteContentStartLine) - solutionJson[problemData.day] = solutionFileData -} - -// 格式化题目的相关数据 -function formateProblemValue (data) { - return Object.assign({ - day: 1, - title: "当前暂时没有对应的数据,请联系当前讲师进行处理~", - link: "当前暂时没有对应的数据,请联系当前讲师进行处理~", - // tags: [], // 目前所有 README 都是没有的。因此如果没有的话,你可以先不返回,有的话就返回。后面我慢慢补 - pres: ["当前暂时没有对应的数据,请联系当前讲师进行处理~"], - description: "当前暂时没有对应的数据,请联系当前讲师进行处理~", - company: "暂无" - }, data) -} - -// 将某个md文件解析为 题解与题目介绍 -function transformFileToJSon(fullPath, fileData){ - // 根据题解名获取这是第几天的题解和题目title - let fileName = path.basename(fullPath).trim().toLowerCase() - let problemData = { - day: +fileName.match(/d([0-9]+)/)[1], - // title: fileName.split('.').slice(1, -1).join('.') - } - let walker = new commonmark.Parser().parse(fileData.toString()).walker(); - let nowNode = walker.next(), nextNode - while (nowNode) { - // 当前讲义的基本格式为标题紧跟着是对应的内容, - // 所以碰到 heading 类型的节点时,因此将ast的节点按heading进行分割 - if(nowNode.node.type === 'heading'){ - // 这里做下兼容处理,有部分md有一级标题,碰到就直接忽视,当前指针迭代到下一个head - if(nowNode.node.level === 1){ - nowNode = walker.next() - continue - } - - let key = getAstNodeVal(walker) - // 如果不是题目相关的标题,代表从这一行开始就是题解的内容了 - // 结束ast循环,将该行即该行之下的内容全部截取,就是题解的md内容 - if(findIndexProblemDimWorld(key) === -1) break - // 如果是 题目地址(821. xxx) 的形式,则在这里取一下括号内的内容做title,没有就显示为空 - if(/题目地址.*[\((].*?([0-9]+\..*)[\))]/.test(key)){ - problemData.title = key.match(/题目地址.*[\((].*?([0-9]+\..*)[\))]/)[1] - } - key = problemTitleWordMap[findIndexProblemDimWorld(key)] - - nextNode = walker.next(); - while(walker.entering === false){ - nextNode = walker.next(); - } - if(!nextNode) break - let nextNodeVal = getAstNodeVal(walker, true) - problemData[key] = nextNodeVal.length > 1 ? nextNodeVal : nextNodeVal[0] - nowNode = nextNode - } else { - nowNode = walker.next() - } - } - // 这一行(包括本行)之下的内容为题解, - let hasSourceNode = walker.current - while(!Array.isArray(hasSourceNode.sourcepos) && hasSourceNode){ - hasSourceNode = hasSourceNode.parent - } - let soluteContentStartLine = hasSourceNode ? hasSourceNode.sourcepos[0][0] : 1; - // 将该文件解析出的内容写入json对象 - writeToJsonObject(fullPath, problemData, soluteContentStartLine) - // 将该文件解析出的内容写入json文件 - // writeFile(fullPath, problemData, soluteContentStartLine) -} - -function run(){ - recursionAllMdFile(inputDirPath, preprocessFile) - if (!fs.existsSync(outputDirPath)) { - fs.mkdirSync(outputDirPath); - } - - // 将题目相关的内容写入json - fs.writeFile(path.resolve(outputDirPath, `problem/problem.json`), JSON.stringify(problemJson, null, 4), function (err) { - if (err) console.log(`problem.json写入失败`, err); - }) - - // 将题解相关的内容写入json - Object.keys(solutionJson).forEach((key) => { - let content = encrypt(solutionJson[key].join('\n')) - solutionJson[key] = { - content - } - }); - fs.writeFile(path.resolve(outputDirPath, `solution/solutions.json`), JSON.stringify(solutionJson, null, 4), function (err) { - if (err) console.log(`加密前的solution.json写入失败`, err); - }) - return { - problemJson, - solutionJson - } -} - -run()