# skill_tree_python Python 技能树开放编辑仓库 ## 初始化 ``` pip install -r requirement.txt ``` ## 目录结构说明 * 技能树`骨架文件`: * 位置:`data/tree.json` * 说明:该文件是执行 `python main.py` 生成的,请勿人工编辑 * 技能树`根节点`配置文件: * 位置:`data/config.json` * 说明:可编辑配置关键词等字段,其中 `node_id` 字段是生成的,请勿编辑 * 技能树`难度节点`: * 位置:`data/xxx`,例如: `data/1.python初阶` * 说明: * 每个技能树有 3 个等级,目录前的序号是必要的,用来保持文件夹目录的顺序 * 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑 * 技能树`章节点`: * 位置:`data/xxx/xxx`,例如:`data/1.python初阶/1.预备知识` * 说明: * 每个技能树的每个难度等级有 n 个章节,目录前的序号是必要的,用来保持文件夹目录的顺序 * 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑 * 技能树`知识节点`: * 位置:`data/xxx/xxx`,例如:`data/1.python初阶/1.预备知识` * 说明: * 每个技能树的每章有 n 个知识节点,目录前的序号是必要的,用来保持文件夹目录的顺序 * 每个目录下有一个 `config.json` * 其中 `node_id` 字段是生成的,请勿编辑 * 其中 `keywords` 可配置关键字字段 * 其中 `children` 可配置该`知识节点`下的子树结构信息,参考后面描述 * 其中 `export` 可配置该`知识节点`下的导出习题信息,参考后面描述 ## `知识节点` 子树信息结构 例如 `data/1.python初阶/1.预备知识/1.Python简介/config.json` 里配置对该知识节点子树信息结构: ```json { // ... "children": [ { "Python都能做什么": { "keywords": [], "children": [], "node_id": "python-4-0" } }, { "python起源和发展": { "keywords": [], "children": [], "node_id": "python-insert-2" } }, { "python语言特点": { "keywords": [], "children": [], "node_id": "python-insert-3" } } ], } ``` ## `知识节点` 的习题配置规则(一) 例如 `data/1.python初阶/1.预备知识/1.Python简介/config.json` 里配置对该知识节点导出的习题 ```json { // ... "export": [ "helloworld.json" // ... ] } ``` 其中`"helloworld.json"`是习题的配置文件,文件如下: ```json { "exercise_id": 33, "source": "helloworld.py", "depends": [], "multiline": [ { "str1 = \"Hello,\"": "", "str2 = \"World!\"": "str = \"Hello,World!\"", "print('str1'+'str2')": "print(str)" }, { "str1 = \"Hello,\"": "", "str2 = \"World!\"": "", "print('str1'+'str2')": "print(\"Hello\" +\",\" +\"World!\")" }, { "print('str1'+'str2')": "print(str1+str2)" } ] } ``` 其中: * `"exercise_id"` 是脚本填充的字段,编辑的时候不用填写 * `"source"` 指定了习题的来源文件 * `"depends"`: 指定了习题依赖的同知识点下的其他源文件 * `"multiline"`: 是习题选项替换的规则之一 #### 习题源代码 `"helloworld.py"`的源代码如下: ```python # -*- coding: UTF-8 -*- # 作者:幻灰龙 # 标题:Hello World # 描述:输出 "Hello,World!" 字符串,找出错的那项。 if __name__ == '__main__': str1 = "Hello," str2 = "World!" print('str1'+'str2') ``` 源代码的头部注释固定格式,配置`作者`,`标题`,`描述`,其中描述可以是MarkDown格式 #### 习题源代码替换规则 对于习题代码 `"helloworld.py"` 的代码变种替换规则配置在`"helloworld.json"` 里面。有两种替换规则: **单行替换规则**: * 配置由`one_line`字段指定的单行替换字典 * 格式是:`"<源字符串>"`: [`"<替换字符串A>"`, `<替换字符串B>`,...], * 其中每个 `"<源字符串>"` `/` `"<替换字符串A>"` 被生成为是一个替换选项 * 指定的配置应该能至少生成 `3+` 个替换选项 ```json { "one_line": { "printf": ["print"], "return 0;": ["return 0"], "(\"Hello,Wrold!\")": [" \"Hello,Wrold!\""] } } ``` 上面的替换规则会将代码替换成 3 个变种的代码: ```c // 变种代码1 #include int main(int argc, char** argv){ print("Hello,Wrold!"); return 0; } ``` ```c // 变种代码2 #include int main(int argc, char** argv){ print("Hello,Wrold!"); return 0 } ``` ```c // 变种代码3 #include int main(int argc, char** argv){ print "Hello,Wrold!"; return 0 } ``` 这些变种代码将会作为技能树该知识点该代码选择题的选项。 **多行替换规则**: * 配置由`multiline`字段指定的多行替换数组 * 数组的每个元素是一组替换规则,会整组被替换 例如: ```json { "multiline": [ { "printf": "print" }, { "int main(int argc, char** argv){" : "int main(char** argv){", "return 0;" : "return 0", }, { "#include ": "" } ] } ``` 同样,该配置将支持将源代码生成3个变种代码 ```c // 变种代码1 #include int main(int argc, char** argv){ print("Hello,Wrold!"); return 0; } ``` ```c // 变种代码2, 注意第2组替换规则,包含了两行替换 #include int main(char** argv){ print("Hello,Wrold!"); return 0 } ``` ```c // 变种代码3 int main(int argc, char** argv){ print("Hello,Wrold!"); return 0; } ``` ## `知识节点` 的习题配置规则(二) 另外一种更便利的配置习题的方式是直接基于 MarkDown 格式配置。还是以`"helloworld.json"` 为例子展开描述。 此时,`"helloworld.json"` 简化为: ```json { "type": "code_options", "author": "幻灰龙", "source": "helloworld.md", } ``` 其中 type 字段目前都固定是 `code_options`。根据具体情况写好其它字段,注意这里 `source` 的文件名,我们指定了一个 Markdown 文件。现在我们新建一个 `helloworld.md` 并编辑为: ````markdown # Hello World 输出 "Hello,World!" 字符串,找出错的那项。 ## 答案 ```python if __name__ == '__main__': str1 = "Hello," str2 = "World!" print('str1'+'str2') ``` ## 选项 ### 两字符串变量拼接 ```python if __name__ == '__main__': str1 = "Hello," str2 = "World!" print(str1+str2) ``` ### 两字符串字面量拼接 ```python if __name__ == '__main__': print("Hello,"+"World!") ``` ### 两字符串字面量拼接成字符串变量 ```python if __name__ == '__main__': str = "Hello," + "World!" print(str) ``` ```` 这是一个最基本的习题结构,它包含标题、答案、选项,注意这几个一级和二级标题必须填写正确,解释器会读取这几个标题。而选项的标题会被直接忽略掉,在 最终生成的习题中不包含选项的三级标题,所以这个标题可以用来标注一些编辑信息,例如“此选项没有关闭文件连接”,“类型错误”等等。 ## 习题配置规则的选择 我们介绍了两种习题编辑规则,目前Python技能树这里大量使用了规则(一),这是历史编辑遗留。新增习题建议使用规则(二),更简洁方便。 ## 技能树合成 在根目录下执行 `python main.py` 会合成技能树文件,合成的技能树文件: `data/tree.json` * 合成过程中,会自动检查每个目录下 `config.json` 里的 `node_id` 是否存在,不存在则生成 * 合成过程中,会自动检查每个知识点目录下 `config.json` 里的 `export` 里导出的习题配置,检查是否存在`exercise_id` 字段,如果不存在则生成