Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
CSDN 技术社区
skill_tree_web_in_action
提交
1bb7d17f
S
skill_tree_web_in_action
项目概览
CSDN 技术社区
/
skill_tree_web_in_action
通知
8
Star
3
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
S
skill_tree_web_in_action
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
1bb7d17f
编写于
11月 02, 2021
作者:
CSDN-Ada助手
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
init README.txt
上级
6a1d6f4b
变更
5
显示空白变更内容
内联
并排
Showing
5 changed file
with
427 addition
and
1 deletion
+427
-1
.gitignore
.gitignore
+6
-0
README.md
README.md
+295
-1
main.py
main.py
+4
-0
requirements.txt
requirements.txt
+0
-0
src/tree.py
src/tree.py
+122
-0
未找到文件。
.gitignore
0 → 100644
浏览文件 @
1bb7d17f
.vscode
.idea
.DS_Store
__pycache__
*.pyc
*.zip
README.md
浏览文件 @
1bb7d17f
# skill_tree_toolchains
全栈工具链
## 初始化
```
pip install -r requirements.txt
```
## 目录结构说明
*
技能树
`骨架文件`
:
*
位置:
`data/tree.json`
*
说明:该文件是执行
`python main.py`
生成的,请勿人工编辑
*
技能树
`根节点`
配置文件:
*
位置:
`data/config.json`
*
说明:可编辑配置关键词等字段,其中
`node_id`
字段是生成的,请勿编辑
*
技能树
`难度节点`
:
*
位置:
`data/xxx`
,例如:
`data/1.toolchains初阶`
*
说明:
*
每个技能树有 3 个等级,目录前的序号是必要的,用来保持文件夹目录的顺序
*
每个目录下有一个
`config.json`
可配置关键词信息,其中
`node_id`
字段是生成的,请勿编辑
*
技能树
`章节点`
:
*
位置:
`data/xxx/xxx`
,例如:
`data/1.toolchains初阶/1.git`
*
说明:
*
每个技能树的每个难度等级有 n 个章节,目录前的序号是必要的,用来保持文件夹目录的顺序
*
每个目录下有一个
`config.json`
可配置关键词信息,其中
`node_id`
字段是生成的,请勿编辑
*
技能树
`知识节点`
:
*
位置:
`data/xxx/xxx/xxx`
,例如:
`data/1.toolchains初阶/1.git/3.push修改到远程创库`
*
说明:
*
每个技能树的每章有
`n`
个知识节点,目录前的序号是必要的,用来保持文件夹目录的顺序
*
每个目录下有一个
`config.json`
*
其中
`node_id`
字段是生成的,请勿编辑
*
其中
`keywords`
可配置关键字字段
*
其中
`children`
可配置该
`知识节点`
下的子树结构信息,参考后面描述
*
其中
`export`
可配置该
`知识节点`
下的导出习题信息,参考后面描述
## `知识节点` 子树信息结构
例如
`data/1.toolchains初阶/1.git/4.push修改到远程创库/config.json`
里配置对该知识节点子树信息结构:
```
json
{
//
...
"children"
:
[
//
TODO
...
],
}
```
## `知识节点` 的导出习题编辑
例如
`data/1.toolchains初阶/1.git/4.push修改到远程创库/config.json`
里配置对该知识节点导出的习题
```
json
{
//
...
"export"
:
[
//
TODO
...
]
}
```
格式说明:
*
`file`
: 指定该目录下的习题源文件
*
`variants`
: 指定习题同名的json选项配置文件,参考下一节
*
`depends`
: 如果习题依赖同目录下的其他习题源代码,则在此字段里配置依赖的其他习题源文件名
## `知识节点` 的导出习题选项配置编辑
首先,在知识节点下增加一个习题代码,例如在
`data/1.toolchains初阶/1.git/4.push修改到远程创库`
下增加一个
`HelloWord.sh`
代码:
```
console
$
git add README.txt
$
git commit
-m
"first modify README.txt"
$
git push origin master
```
其次,增加一个同名的选项配置文件
`HelloWord.json`
,目前有三种配置规则
**单行替换规则**
:
*
配置由
`one_line`
字段指定的单行替换字典
*
格式是:
`"<源字符串>"`
: [
`"<替换字符串A>"`
,
`<替换字符串B>`
,...],
*
其中每个
`"<源字符串>"`
`/`
`"<替换字符串A>"`
被生成为是一个替换选项
*
指定的配置应该能至少生成
`3+`
个替换选项
```
json
{
"one_line"
:
{
"git add"
:
[
"git put"
,
"git in"
,
"git checkout"
]
}
}
```
上面的替换规则会将代码替换成 3 个变种的代码:
```
console
// 变种代码1
$
git put README.txt
$
git commit
-m
"first modify README.txt"
$
git push origin master
```
```
console
// 变种代码2
$
git
in
README.txt
$
git commit
-m
"first modify README.txt"
$
git push origin master
```
```
html
// 变种代码3
$ git checkout README.txt
$ git commit -m "first modify README.txt"
$ git push origin master
```
这些变种代码将会作为技能树该知识点该代码选择题的选项。
**多行替换规则**
:
*
配置由
`multiline`
字段指定的多行替换数组
*
数组的每个元素是一组替换规则,会整组被替换
例如:
```
json
{
"mulitiline"
:
[{
"git add"
:
"git put"
,
"git commit -m"
:
"git commit -a"
},
{
"git commit"
:
"git base"
,
"git push"
:
"git pull"
}]
}
```
同样,该配置将支持将源代码生成2个变种代码
```
console
// 变种代码1
$
git put README.txt
$
git commit
-a
"first modify README.txt"
$
git push origin master
```
```
console
// 变种代码2, 注意第2组替换规则,包含了两行替换
$
git add README.txt
$
git base
-m
"first modify README.txt"
$
git pull origin master
```
## 预制的替换规则
*
配置由
`prepared`
字段制定的预制文件数组
*
数组每一个元素是一个预制的代码文件的路径文件名
例如:
```
json
{
"prepared"
:
[
"HelloWord.1.sh"
,
"HelloWord.2.sh"
,
"HelloWord.3.sh"
]
}
```
同样,该配置将支持将源代码生成3个变种代码
```
console
// HelloWord.1.sh
$
git put README.txt
$
git commit
-a
"first modify README.txt"
$
git push origin master
```
```
console
$
git add README.txt
$
git base
-m
"first modify README.txt"
$
git pull origin master
```
## 使用 markdown 编写习题
如前内容,我们在知识节点下增加一个习题配置,例如在
`data/1.toolchains初阶/1.git/4.push修改到远程创库`
下增加一个
`HelloWorld.json`
代码:
```
json
{
"type"
:
"code_options"
,
"author"
:
"刘鑫"
,
"source"
:
"HelloWorld.md"
,
"exercise_id"
:
"1190bb7834904da0b1f20915960714d5"
,
"notebook_enable"
:
true
}
```
其中 type 字段目前都固定是
`code_options`
。exercise_id 可以不写,处理程序会自动填补这个数据。根据具体情况写好其它字段,注意这里 source 的文件名,我们指定了一个 markdwon 文件。现在我们新建一个 HelloWorld.md 并编辑为:
````
markdown
# Hello World
以下
`Hello World`
代码中,能够正确提交代码到远程maste仓库的是:
## 答案
```
console
$
git add README.txt
$
git commit
-m
"first modify README.txt"
$
git push origin master
```
## 选项
### A
```
console
// HelloWord.1.sh
$
git put README.txt
$
git commit
-a
"first modify README.txt"
$
git push origin master
```
### B
```
console
$
git add README.txt
$
git base
-m
"first modify README.txt"
$
git pull origin master
```
````
这是一个最基本的习题结构,它包含标题、答案、选项,注意这几个一级和二级标题必须填写正确,解释器会读取这几个标题。而选项的标题会被直接忽略掉,在
最终生成的习题中不包含选项的三级标题,所以这个标题可以用来标注一些编辑信息,例如“此选项没有关闭文件连接”,“类型错误”等等。
## 增强信息
为了编写习题和生成 notebook 的需要,markdown 解释器支持两种模板能力,如果我们在答案之前,有一个名为 aop 的二级标题:
````
markdown
## aop
### before
```
console
echo "do something before"
```
### after
```
console
echo "do something after"
```
````
那么在创建notebook的时候,before 会插入到源代码前一个单元,after 则会插入到源代码后。aop 章节可以只包含 before 或 after 中的某一个,也可以两个都有。
另一些情况下,我们可能需要把各个选项中重复的代码提取出来,建立一个模板,此时可以在答案之前建立一个名为 template 的二级标题,例如:
````
markdwon
## template
```console
$ cd /root/
// 下面的 code 占位符会被替换成答案和选项代码
$ $code
```
````
注意这里的代码中,有一个
`$code`
占位符,它在管道程序处理过程中,会替换成答案和个选项内容中的代码。
在后续的数据处理流程中,markdown 会被编译为 prepared 类型的习题。
## 技能树合成
在根目录下执行
`python main.py`
会合成技能树文件,合成的技能树文件:
`data/tree.json`
*
合成过程中,会自动检查每个目录下
`config.json`
里的
`node_id`
是否存在,不存在则生成
*
合成过程中,会自动检查每个知识点目录下
`config.json`
里的
`export`
里导出的习题配置,检查是否存在
`exercise_id`
字段,如果不存在则生成
main.py
0 → 100644
浏览文件 @
1bb7d17f
from
src.tree
import
gen_tree
if
__name__
==
'__main__'
:
gen_tree
(
'data'
)
requirements.txt
0 → 100644
浏览文件 @
1bb7d17f
src/tree.py
0 → 100644
浏览文件 @
1bb7d17f
from
genericpath
import
exists
import
json
import
os
import
uuid
import
sys
import
re
def
load_json
(
p
):
with
open
(
p
,
'r'
)
as
f
:
return
json
.
loads
(
f
.
read
())
def
dump_json
(
p
,
j
,
exist_ok
=
False
,
override
=
False
):
if
os
.
path
.
exists
(
p
):
if
exist_ok
:
if
not
override
:
return
else
:
print
(
f
"
{
p
}
already exist"
)
sys
.
exit
(
0
)
with
open
(
p
,
'w'
)
as
f
:
f
.
write
(
json
.
dumps
(
j
,
indent
=
2
,
ensure_ascii
=
False
))
def
parse_no_name
(
d
):
p
=
r
'(\d+)\.(.*)'
m
=
re
.
search
(
p
,
d
)
try
:
no
=
int
(
m
.
group
(
1
))
dir_name
=
m
.
group
(
2
)
except
:
sys
.
exit
(
0
)
return
no
,
dir_name
def
gen_tree
(
data_path
):
root
=
{}
def
gen_node_id
():
return
''
.
join
(
str
(
uuid
.
uuid5
(
uuid
.
NAMESPACE_URL
,
'skill_tree'
)).
split
(
'-'
))
def
list_dir
(
p
):
v
=
os
.
listdir
(
p
)
v
.
sort
()
for
no_name
in
v
:
no_dir
=
os
.
path
.
join
(
p
,
no_name
)
if
os
.
path
.
isdir
(
no_dir
):
yield
no_dir
,
no_name
def
ensure_node_id
(
cfg_path
,
cfg
):
if
cfg
.
get
(
'node_id'
)
is
None
:
cfg
[
'node_id'
]
=
gen_node_id
()
dump_json
(
cfg_path
,
cfg
,
exist_ok
=
True
,
override
=
True
)
def
make_node
(
name
,
node_id
,
keywords
,
children
=
None
):
node
=
{}
node_children
=
children
or
[]
node
[
name
]
=
{
'node_id'
:
node_id
,
'keywords'
:
keywords
,
'children'
:
node_children
}
return
node
,
node_children
# 根节点
cfg_path
=
os
.
path
.
join
(
data_path
,
'config.json'
)
cfg
=
load_json
(
cfg_path
)
ensure_node_id
(
cfg_path
,
cfg
)
tree_node
=
{
"node_id"
:
cfg
[
'node_id'
],
"keywords"
:
cfg
[
'keywords'
],
"children"
:
[]
}
root
[
cfg
[
'tree_name'
]]
=
tree_node
# 难度节点
for
level_no_dir
,
level_no_name
in
list_dir
(
data_path
):
print
(
level_no_dir
)
no
,
level_name
=
parse_no_name
(
level_no_name
)
cfg_path
=
os
.
path
.
join
(
level_no_dir
,
'config.json'
)
cfg
=
load_json
(
cfg_path
)
ensure_node_id
(
cfg_path
,
cfg
)
level_node
,
level_node_children
=
make_node
(
level_name
,
cfg
[
'node_id'
],
cfg
[
'keywords'
])
tree_node
[
'children'
].
append
(
level_node
)
# 章节点
for
chapter_no_dir
,
chapter_no_name
in
list_dir
(
level_no_dir
):
no
,
chapter_name
=
parse_no_name
(
chapter_no_name
)
cfg_path
=
os
.
path
.
join
(
chapter_no_dir
,
'config.json'
)
ensure_node_id
(
cfg_path
,
cfg
)
cfg
=
load_json
(
cfg_path
)
chapter_node
,
chapter_node_children
=
make_node
(
chapter_name
,
cfg
[
'node_id'
],
cfg
[
'keywords'
])
level_node_children
.
append
(
chapter_node
)
# 知识点
for
section_no_dir
,
section_no_name
in
list_dir
(
chapter_no_dir
):
no
,
section_name
=
parse_no_name
(
section_no_name
)
cfg_path
=
os
.
path
.
join
(
section_no_dir
,
'config.json'
)
ensure_node_id
(
cfg_path
,
cfg
)
cfg
=
load_json
(
cfg_path
)
section_node
,
section_node_children
=
make_node
(
section_name
,
cfg
[
'node_id'
],
cfg
[
'keywords'
],
cfg
[
'children'
])
chapter_node_children
.
append
(
section_node
)
# 确保习题分配了习题ID
for
export
in
cfg
[
'export'
]:
if
export
.
get
(
'exercise_id'
)
is
None
:
export
[
'exercise_id'
]
=
gen_node_id
()
dump_json
(
cfg_path
,
cfg
,
exist_ok
=
True
,
override
=
True
)
# 保存技能树骨架
tree_path
=
os
.
path
.
join
(
data_path
,
'tree.json'
)
dump_json
(
tree_path
,
root
,
exist_ok
=
True
,
override
=
True
)
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录