提交 bc3b4676 编写于 作者: M Mars Liu

project init

上级 7f4d755f
# skill_tree_pg
PostgreSQL 技能树
\ No newline at end of file
本项目是 [CSDN 技能树](https://codechina.csdn.net/explore/topics/skill_tree) 的 PostgreSQL 专项。
本仓库面向学习者,以树状结构管理 PostgreSQL 技能的知识点。
为了操作方便,在仓库中有一组微型的 Python 工具脚本,用于加工和维护技能树,一般情况下不用关注。对于项目贡献者,
主要维护的是技能树的目录结构和相关习题
## 目录结构说明
* 技能树`骨架文件`
* 位置:`data/tree.json`
* 说明:该文件是执行 `python main.py` 生成的,请勿人工编辑
* 技能树`根节点`配置文件:
* 位置:`data/config.json`
* 说明:可编辑配置关键词等字段,其中 `node_id` 字段是生成的,请勿编辑
* 技能树`难度节点`
* 位置:`data/xxx`,例如: `data/1.PostgreSQL初阶`
* 说明:
* 每个技能树有 3 个等级,目录前的序号是必要的,用来保持文件夹目录的顺序
* 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑
* 技能树`章节点`
* 位置:`data/xxx/xxx`,例如:`data/1.PostgreSQL初阶/1.快速入门`
* 说明:
* 每个技能树的每个难度等级有 n 个章节,目录前的序号是必要的,用来保持文件夹目录的顺序
* 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑
* 技能树`知识节点`
* 位置:`data/xxx/xxx/xxx`,例如:`data/1.PostgreSQL初阶/1.快速入门/1.安装OBD`
* 说明:
* 每个技能树的每章有 `n` 个知识节点,目录前的序号是必要的,用来保持文件夹目录的顺序
* 每个目录下有一个 `config.json`
* 其中 `node_id` 字段是生成的,请勿编辑
* 其中 `keywords` 可配置关键字字段
* 其中 `children` 可配置该`知识节点`下的子树结构信息,参考后面描述
* 其中 `export` 可配置该`知识节点`下的导出习题信息,参考后面描述
## `知识节点` 子树信息结构
举例,如果在 `data/1.PostgreSQL初阶/7.查询数据/config.json` 里配置对该知识节点子树信息结构:
```json
{
"children": [
"输入输出",
"访问工具",
"查询的基本要素"
]
}
```
在后续的信息加工过程中,这些内容就会关联到相关的节点。
通常情况下,我们只需要维护固定深度的目录结构,大部分知识点不涉及 children 的维护。
## `知识节点` 的导出习题编辑
例如 `data/1.PostgreSQL初阶/7.查询数据/config.json` 里配置对该知识节点导出的习题
```json
{
"export": [
"hello.json"
]
}
```
每个文件名,指向对应的习题定义 json 。
## `知识节点` 的导出习题选项配置编辑
首先,我们添加前文中 export 指定的习题配置,例如在 `data/1.PostgreSQL初阶/7.查询数据` 下增加一个`hello.json`代码:
```json
{
"type": "code_options",
"author": "刘鑫",
"source": "hello.md",
"exercise_id":"1190bb7834904da0b1f20915960714d5",
"notebook_enable": false
}
```
其中 type 字段目前都固定是 `code_options`。exercise_id 可以不写,处理程序会自动填补这个数据。notebook_enable对于 PG 技能树总是false。
根据具体情况写好其它 字段,注意这里 source 的文件名,我们指定了一个 markdwon 文件。现在我们新建一个 HelloWorld.md 并编辑为:
````markdown
# Hello World
以下 `Hello World` 程序中,能够正确输出`hello`的是:
## 答案
```sql
select 'hello';
```
## 选项
### A
```sql
print 'hello';
```
### B
```sql
echo 'hello';
```
### C
```sql
log 'hello';
```
````
这是一个最基本的习题结构,它包含标题、答案、选项,注意这几个一级和二级标题必须填写正确,解释器会读取这几个标题。而选项的标题会被直接忽略掉,在最终生成的习题中不包含选项的三级标题,所以这个标题可以用来标注一些编辑信息,例如“此选项没有关闭文件连接”,“类型错误”等等。
## 技能树合成
`src`目录下执行 `python main.py -a tree` 会合成技能树文件,合成的技能树文件: `data/tree.json`
* 合成过程中,会自动检查每个目录下 `config.json` 里的 `node_id` 是否存在,不存在则生成
* 合成过程中,会自动检查每个知识点目录下 `config.json` 里的 `export` 里导出的习题配置,检查是否存在`exercise_id` 字段,如果不存在则生成
{
"node_id": "pg-6920a4f2ba864459bd8883a7a48fa238",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-40374311856c4ad3b76fff16ecbe9dce",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-b710bd5c63fb4651bebfdd9b3fdb17a5",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-940afa6d71af44f689011ded8fd91a98",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-586916fe52e245efb399ae862378f87f",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-c252ff4b0e7f4163837c9b6f81678505",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-3ac4a2bf31af423fb9e2ec9fe30024c5",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-dca8da7637f94ded91a1871daa51746e",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-b0f366abe12e41b0a9672d317e556662",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-1eae8a80615f436081e71b36d89fb0aa",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-ebef9fceb3684b119a9ba221a81f304b",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-7e2e52dbddb44d5bad6deea6c5e57198",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-6089aef54c4c4ce7bc85b4dfa14f44cd",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-806d3976e8764d4f8e26c197c53d45a8",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-f371b91ef63a4ce08f80b92bc8580196",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-cb942198b3a84d5398d73d558a241543",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-eeb777ca54434480a06bfe7fc5e7d3ca",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-a0cb78d68d814f5e935b41922b88e085",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-d2b6e840cfad44a2aa357cfee56d437d",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-100e256ddb2e43ea8d68558e73522819",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-3a87b0dbead643e58f1ff1d93acd80cc",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-458d5d590ea64188847b45d8e6343f83",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-55fd213f919d411c9b572241c4bb7807",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-69d3a1c646004e1da497fed2af57806c",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-6beb084543204f4f9d87930c0a6c5707",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-e1cd59819dd74a0bacc707f7863aad53",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-eb4048b7dfd3469f8049330ba78427b5",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-0378bcd60ccd4dfebcfead92abbdd673",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-c99d8673d47444988c37f6dd3abb1e63",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-c360138dad92431594130596c5c91394",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-58e924e93b564a24abb1e6b9cdfbc094",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-7eff3dcf68644c23bd89f2a2abe4fac6",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-20a433f6ae65429f9b1a7f17c751ac01",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-328b09c201b6414d83228970591910af",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-ec4bbb39e76949b48b13f04cc8c16532",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-3bbe12c0a19d4cb7a61dc0a20624ed89",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-69ad784e39c044d7a115cfd124e49ef1",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-6dbbb44b41d347b58075a9155dfb8356",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-532f8f84ea74493a9581e6bf720ebf78",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-f80410c703044bfea61b2f3a4fbc5705",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-2e691ed3a847424eb887b40aca750c4e",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-6ce3520f7a67494a90378e7b194f8720",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-2d547e778fc4453b84feb0a0c6341348",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-79fe660f35f4469481db38509343c2b7",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-e4b00736ab4c483785c022a0a464f30a",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-0e280b203b524a28ab8587aac125a865",
"keywords": [],
"children": [],
"export": []
}
\ No newline at end of file
{
"node_id": "pg-9e66230caa834cb99a2390c7f71fb582",
"keywords": []
}
\ No newline at end of file
{
"node_id": "pg-2f46da8212cf41ad9c0d2a5bf1330adc",
"keywords": []
}
\ No newline at end of file
{
"tree_name": "pg",
"keywords": [],
"node_id": "pg-4544619de2a54841930282c700ba92b8"
}
\ No newline at end of file
{
"pg": {
"node_id": "pg-4544619de2a54841930282c700ba92b8",
"keywords": [],
"children": [
{
"PostgreSQL初阶": {
"node_id": "pg-6089aef54c4c4ce7bc85b4dfa14f44cd",
"keywords": [],
"children": [
{
"PostgreSQL基本概念": {
"node_id": "pg-b710bd5c63fb4651bebfdd9b3fdb17a5",
"keywords": [],
"children": [
{
"关系型数据库": {
"node_id": "pg-6920a4f2ba864459bd8883a7a48fa238",
"keywords": [],
"children": []
}
},
{
"服务器和客户端": {
"node_id": "pg-40374311856c4ad3b76fff16ecbe9dce",
"keywords": [],
"children": []
}
}
]
}
},
{
"PostgreSQL的安装": {
"node_id": "pg-3ac4a2bf31af423fb9e2ec9fe30024c5",
"keywords": [],
"children": [
{
"安装PostgreSQL": {
"node_id": "pg-940afa6d71af44f689011ded8fd91a98",
"keywords": [],
"children": []
}
},
{
"登录PostgreSQL": {
"node_id": "pg-586916fe52e245efb399ae862378f87f",
"keywords": [],
"children": []
}
},
{
"使用PostgreSQL": {
"node_id": "pg-c252ff4b0e7f4163837c9b6f81678505",
"keywords": [],
"children": []
}
}
]
}
},
{
"查询数据": {
"node_id": "pg-7e2e52dbddb44d5bad6deea6c5e57198",
"keywords": [],
"children": [
{
"SQL基本语法": {
"node_id": "pg-dca8da7637f94ded91a1871daa51746e",
"keywords": [],
"children": []
}
},
{
"SQL、DML和DDL": {
"node_id": "pg-b0f366abe12e41b0a9672d317e556662",
"keywords": [],
"children": []
}
},
{
"PostgreSQL的数据类型": {
"node_id": "pg-1eae8a80615f436081e71b36d89fb0aa",
"keywords": [],
"children": []
}
},
{
"函数调用": {
"node_id": "pg-ebef9fceb3684b119a9ba221a81f304b",
"keywords": [],
"children": []
}
}
]
}
}
]
}
},
{
"PostgreSQL中阶": {
"node_id": "pg-c360138dad92431594130596c5c91394",
"keywords": [],
"children": [
{
"PostgreSQL数据库的基本结构": {
"node_id": "pg-100e256ddb2e43ea8d68558e73522819",
"keywords": [],
"children": [
{
"表": {
"node_id": "pg-806d3976e8764d4f8e26c197c53d45a8",
"keywords": [],
"children": []
}
},
{
"函数": {
"node_id": "pg-f371b91ef63a4ce08f80b92bc8580196",
"keywords": [],
"children": []
}
},
{
"用户和权限": {
"node_id": "pg-cb942198b3a84d5398d73d558a241543",
"keywords": [],
"children": []
}
},
{
"索引和约束": {
"node_id": "pg-eeb777ca54434480a06bfe7fc5e7d3ca",
"keywords": [],
"children": []
}
},
{
" 触发器": {
"node_id": "pg-a0cb78d68d814f5e935b41922b88e085",
"keywords": [],
"children": []
}
},
{
"数据库扩展": {
"node_id": "pg-d2b6e840cfad44a2aa357cfee56d437d",
"keywords": [],
"children": []
}
}
]
}
},
{
"服务端编程": {
"node_id": "pg-c99d8673d47444988c37f6dd3abb1e63",
"keywords": [],
"children": [
{
"聚合和分组": {
"node_id": "pg-3a87b0dbead643e58f1ff1d93acd80cc",
"keywords": [],
"children": []
}
},
{
"连接查询": {
"node_id": "pg-458d5d590ea64188847b45d8e6343f83",
"keywords": [],
"children": []
}
},
{
"子查询": {
"node_id": "pg-55fd213f919d411c9b572241c4bb7807",
"keywords": [],
"children": []
}
},
{
"分页查询": {
"node_id": "pg-69d3a1c646004e1da497fed2af57806c",
"keywords": [],
"children": []
}
},
{
"CTE": {
"node_id": "pg-6beb084543204f4f9d87930c0a6c5707",
"keywords": [],
"children": []
}
},
{
"DML": {
"node_id": "pg-e1cd59819dd74a0bacc707f7863aad53",
"keywords": [],
"children": []
}
},
{
"过程化编程": {
"node_id": "pg-eb4048b7dfd3469f8049330ba78427b5",
"keywords": [],
"children": []
}
},
{
"DDL": {
"node_id": "pg-0378bcd60ccd4dfebcfead92abbdd673",
"keywords": [],
"children": []
}
}
]
}
}
]
}
},
{
"PostgreSQL高阶": {
"node_id": "pg-2f46da8212cf41ad9c0d2a5bf1330adc",
"keywords": [],
"children": [
{
"集群和复制": {
"node_id": "pg-328b09c201b6414d83228970591910af",
"keywords": [],
"children": [
{
"standby": {
"node_id": "pg-58e924e93b564a24abb1e6b9cdfbc094",
"keywords": [],
"children": []
}
},
{
"流式复制集群": {
"node_id": "pg-7eff3dcf68644c23bd89f2a2abe4fac6",
"keywords": [],
"children": []
}
},
{
"fdw": {
"node_id": "pg-20a433f6ae65429f9b1a7f17c751ac01",
"keywords": [],
"children": []
}
}
]
}
},
{
"高级数据类型": {
"node_id": "pg-f80410c703044bfea61b2f3a4fbc5705",
"keywords": [],
"children": [
{
"自定义类型": {
"node_id": "pg-ec4bbb39e76949b48b13f04cc8c16532",
"keywords": [],
"children": []
}
},
{
"几何类型和GIS": {
"node_id": "pg-3bbe12c0a19d4cb7a61dc0a20624ed89",
"keywords": [],
"children": []
}
},
{
"JSON和JSONB": {
"node_id": "pg-69ad784e39c044d7a115cfd124e49ef1",
"keywords": [],
"children": []
}
},
{
"函数": {
"node_id": "pg-6dbbb44b41d347b58075a9155dfb8356",
"keywords": [],
"children": []
}
},
{
"视图": {
"node_id": "pg-532f8f84ea74493a9581e6bf720ebf78",
"keywords": [],
"children": []
}
}
]
}
},
{
"SQL高级技巧": {
"node_id": "pg-9e66230caa834cb99a2390c7f71fb582",
"keywords": [],
"children": [
{
"递归查询": {
"node_id": "pg-2e691ed3a847424eb887b40aca750c4e",
"keywords": [],
"children": []
}
},
{
"写入和冲突": {
"node_id": "pg-6ce3520f7a67494a90378e7b194f8720",
"keywords": [],
"children": []
}
},
{
"事务": {
"node_id": "pg-2d547e778fc4453b84feb0a0c6341348",
"keywords": [],
"children": []
}
},
{
"索引和优化": {
"node_id": "pg-79fe660f35f4469481db38509343c2b7",
"keywords": [],
"children": []
}
},
{
"表函数": {
"node_id": "pg-e4b00736ab4c483785c022a0a464f30a",
"keywords": [],
"children": []
}
},
{
"结果集类型": {
"node_id": "pg-0e280b203b524a28ab8587aac125a865",
"keywords": [],
"children": []
}
}
]
}
}
]
}
}
]
}
}
\ No newline at end of file
from src.tree import TreeWalker
if __name__ == '__main__':
walker = TreeWalker("data", "pg", "PostgreSQL")
walker.walk()
import logging
from genericpath import exists
import json
import os
import uuid
import sys
import re
id_set = set()
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
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:
logger.error(f"{p} already exist")
sys.exit(0)
with open(p, 'w+') as f:
f.write(json.dumps(j, indent=2, ensure_ascii=False))
def ensure_config(path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
node = {"keywords": []}
dump_json(config_path, node, exist_ok=True, override=False)
return node
else:
return load_json(config_path)
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 check_export(base, cfg):
flag = False
exports = []
for export in cfg.get('export', []):
ecfg_path = os.path.join(base, export)
if os.path.exists(ecfg_path):
exports.append(export)
else:
flag = True
if flag:
cfg["export"] = exports
return flag
def gen_node_id():
return "oceanbase-" + uuid.uuid4().hex
class TreeWalker:
def __init__(self, root, tree_name, title=None, log=None):
self.name = tree_name
self.root = root
self.title = tree_name if title is None else title
self.tree = {}
self.logger = logger if log is None else log
def walk(self):
root = self.load_root()
root_node = {
"node_id": root["node_id"],
"keywords": root["keywords"],
"children": []
}
self.tree[root["tree_name"]] = root_node
self.load_levels(root_node)
self.load_chapters(self.root, root_node)
for index, level in enumerate(root_node["children"]):
level_title = list(level.keys())[0]
level_node = list(level.values())[0]
level_path = os.path.join(self.root, f"{index+1}.{level_title}")
self.load_chapters(level_path, level_node)
for index, chapter in enumerate(level_node["children"]):
chapter_title = list(chapter.keys())[0]
chapter_node = list(chapter.values())[0]
chapter_path = os.path.join(level_path, f"{index+1}.{chapter_title}")
self.load_sections(chapter_path, chapter_node)
for index, section_node in enumerate(chapter_node["children"]):
section_title = list(section_node.keys())[0]
full_path = os.path.join(chapter_path, f"{index}.{section_title}")
if os.path.isdir(full_path):
self.ensure_exercises(full_path)
tree_path = os.path.join(self.root, "tree.json")
dump_json(tree_path, self.tree, exist_ok=True, override=True)
return self.tree
def load_levels(self, root_node):
levels = []
for level in os.listdir(self.root):
if not os.path.isdir(level):
continue
level_path = os.path.join(self.root, level)
num, config = self.load_level_node(level_path)
levels.append((num, config))
levels = self.resort_children(self.root, levels)
root_node["children"] = [item[1] for item in levels]
return root_node
def load_level_node(self, level_path):
config = self.ensure_level_config(level_path)
num, name = self.extract_node_env(level_path)
result = {
name: {
"node_id": config["node_id"],
"keywords": config["keywords"],
"children": [],
}
}
return num, result
def load_chapters(self, base, level_node):
chapters = []
for name in os.listdir(base):
full_name = os.path.join(base, name)
if os.path.isdir(full_name):
num, chapter = self.load_chapter_node(full_name)
chapters.append((num, chapter))
chapters = self.resort_children(base, chapters)
level_node["children"] = [item[1] for item in chapters]
return level_node
def load_sections(self, base, chapter_node):
sections = []
for name in os.listdir(base):
full_name = os.path.join(base, name)
if os.path.isdir(full_name):
num, section = self.load_section_node(full_name)
sections.append((num, section))
sections = self.resort_children(base, sections)
chapter_node["children"] = [item[1] for item in sections]
return chapter_node
def resort_children(self, base, children):
children.sort(key=lambda item: item[0])
for index, [number, element] in enumerate(children):
title = list(element.keys())[0]
origin = os.path.join(base, f"{number}.{title}")
posted = os.path.join(base, f"{index+1}.{title}")
if origin != posted:
self.logger.info(f"rename [{origin}] to [{posted}]")
os.rename(origin, posted)
return children
def ensure_chapters(self):
for subdir in os.listdir(self.root):
self.ensure_level_config(subdir)
def load_root(self):
config_path = os.path.join(self.root, "config.json")
if not os.path.exists(config_path):
config = {
"tree_name": self.name,
"keywords": [],
"node_id": self.gen_node_id(),
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, result, exist_ok=True, override=True)
return config
def ensure_level_config(self, path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
config = {
"node_id": self.gen_node_id()
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, config, exist_ok=True, override=True)
return config
def ensure_chapter_config(self, path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
config = {
"node_id": self.gen_node_id(),
"keywords": []
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, config, exist_ok=True, override=True)
return config
def ensure_section_config(self, path):
config_path = os.path.join(path, "config.json")
if not os.path.exists(config_path):
config = {
"node_id": self.gen_node_id(),
"keywords": [],
"children":[],
"export":[]
}
dump_json(config_path, config, exist_ok=True, override=True)
else:
config = load_json(config_path)
flag, result = self.ensure_node_id(config)
if flag:
dump_json(config_path, config, exist_ok=True, override=True)
return config
def ensure_node_id(self, config):
if "node_id" not in config:
config["node_id"] = self.gen_node_id()
return True, config
else:
return False, config
def gen_node_id(self):
return f"{self.name}-{uuid.uuid4().hex}"
def extract_node_env(self, path):
try:
_, dir = os.path.split(path)
self.logger.info(path)
number, title = dir.split(".", 1)
return int(number), title
except Exception as error:
self.logger.error(f"目录 [{path}] 解析失败,结构不合法,可能是缺少序号")
sys.exit(1)
def load_chapter_node(self, full_name):
config = self.ensure_chapter_config(full_name)
num, name = self.extract_node_env(full_name)
result = {
name: {
"node_id": config["node_id"],
"keywords": config["keywords"],
"children": [],
}
}
return num, result
def load_section_node(self, full_name):
config = self.ensure_section_config(full_name)
num, name = self.extract_node_env(full_name)
result = {
name: {
"node_id": config["node_id"],
"keywords": config["keywords"],
"children": config.get("children", [])
}
}
# if "children" in config:
# result["children"] = config["children"]
return num, result
def ensure_exercises(self, section_path):
config = self.ensure_section_config(section_path)
for e in config.get("export", []):
full_name = os.path.join(section_path, e)
exercise = load_json(full_name)
if "exercise_id" not in exercise:
exercise["exercise_id"] = uuid.uuid4().hex
dump_json(full_name, exercise)
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册