提交 7d090cd8 编写于 作者: M Mars Liu

oceanbase tree init

上级
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
.idea
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
.settings/**
.project
.pydevproject
# skill_tree_java
## 初始化
```
pip install -r requirement.txt
```
## 目录结构说明
* 技能树`骨架文件`
* 位置:`data/tree.json`
* 说明:该文件是执行 `python main.py` 生成的,请勿人工编辑
* 技能树`根节点`配置文件:
* 位置:`data/config.json`
* 说明:可编辑配置关键词等字段,其中 `node_id` 字段是生成的,请勿编辑
* 技能树`难度节点`
* 位置:`data/xxx`,例如: `data/1.Java初阶`
* 说明:
* 每个技能树有 3 个等级,目录前的序号是必要的,用来保持文件夹目录的顺序
* 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑
* 技能树`章节点`
* 位置:`data/xxx/xxx`,例如:`data/1.Java初阶/1.Java概述`
* 说明:
* 每个技能树的每个难度等级有 n 个章节,目录前的序号是必要的,用来保持文件夹目录的顺序
* 每个目录下有一个 `config.json` 可配置关键词信息,其中 `node_id` 字段是生成的,请勿编辑
* 技能树`知识节点`
* 位置:`data/xxx/xxx/xxx`,例如:`data/1.Java初阶/1.Java概述/1.什么是Java`
* 说明:
* 每个技能树的每章有 `n` 个知识节点,目录前的序号是必要的,用来保持文件夹目录的顺序
* 每个目录下有一个 `config.json`
* 其中 `node_id` 字段是生成的,请勿编辑
* 其中 `keywords` 可配置关键字字段
* 其中 `children` 可配置该`知识节点`下的子树结构信息,参考后面描述
* 其中 `export` 可配置该`知识节点`下的导出习题信息,参考后面描述
## `知识节点` 子树信息结构
例如 `data/1.Java初阶/1.Java概述/1.什么是Java/config.json` 里配置对该知识节点子树信息结构:
```json
{
// ...
"children": [
// TODO ...
],
}
```
## `知识节点` 的导出习题编辑
例如 `data/1.Java初阶/1.Java概述/1.什么是Java/config.json` 里配置对该知识节点导出的习题
```json
{
// ...
"export": [
// TODO ...
]
}
```
格式说明:
* `file`: 指定该目录下的习题源文件
* `variants`: 指定习题同名的json选项配置文件,参考下一节
* `depends`: 如果习题依赖同目录下的其他习题源代码,则在此字段里配置依赖的其他习题源文件名
## `知识节点` 的导出习题选项配置编辑
目前我们支持使用 markdown 语法直接编辑习题和各选项,或者编写习题代码和替换规则,让程序动态生成选项。
我们先介绍使用替换规则的习题如何编辑,在最后介绍 markdown 方式编写习题的方法。对于 Java 技能树,我们更*推荐*
使用 markdown 方式编写习题。
### 使用替换规则编写习题
首先,在知识节点下增加一个习题代码,例如在 `data/1.Java初阶/1.Java概述/1.什么是Java` 下增加一个`HelloWorld.java`代码:
```java
public class App {
public static void main(String[] args){
System.out.println("Hello world!")
}
}
```
其次,增加一个同名的选项配置文件`HelloWorld.json`,目前有三种配置规则
#### 单行替换规则
* 配置由`one_line`字段指定的单行替换字典
* 格式是:`"<源字符串>"`: [`"<替换字符串A>"`, `<替换字符串B>`,...],
* 其中每个 `"<源字符串>"` `/` `"<替换字符串A>"` 被生成为是一个替换选项
* 指定的配置应该能至少生成 `3+` 个替换选项
```json
{
"one_line": {
"println": ["printf", "print", "fprint"]
}
}
```
上面的替换规则会将代码替换成 3 个变种的代码:
```java
public class App {
public static void main(String[] args){
System.out.print("Hello world!")
}
}
```
```java
public class App {
public static void main(String[] args){
System.out.printf("Hello world!")
}
}
```
```java
public class App {
public static void main(String[] args){
System.out.sprint("Hello world!")
}
}
```
这些变种代码将会作为技能树该知识点该代码选择题的选项。
#### 多行替换规则
* 配置由`multiline`字段指定的多行替换数组
* 数组的每个元素是一组替换规则,会整组被替换
例如:
```json
{
"mulitiline": [{
"public class": "public interface",
"main(": "Main("
},
{
"public class": "interface",
"void main": "int main"
},
{
"public static void main": "public void main"
}]
```
上面的替换规则会将代码替换成 3 个变种的代码:
```java
public interface App {
public static void Main(String[] args){
System.out.println("Hello world!")
}
}
```
```java
public interface App {
public static int main(String[] args){
System.out.println("Hello world!")
}
}
```
```java
public class App {
public void main(String[] args){
System.out.print("Hello world!")
}
}
```
这些变种代码将会作为技能树该知识点该代码选择题的选项。
#### 预制的替换规则
* 配置由 `prepared` 字段制定的预制文件数组
* 数组每一个元素是一个预制的代码文件的路径文件名
例如:
```json
{
"prepared": [
"HelloWord.1.java",
"HelloWord.2.java",
"HelloWord.3.java"]
}
```
同样,该配置将支持将源代码生成3个变种代码
```java
// HelloWord.1.java
public interface App {
default void Main(String[] args){
System.out.println("Hello world!")
}
}
```
```java
public interface App {
public static void main(String[] args){
System.out.println("Hello world!")
}
}
```
```java
class App {
void main(String[] args){
System.out.print("Hello world!")
}
}
```
### 使用 markdown 编写习题
如前内容,我们在知识节点下增加一个习题配置,例如在 `data/1.Java初阶/1.Java概述/1.什么是Java` 下增加一个`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` 程序中,能够正确输出内容的是:
## 答案
```java
public class App {
public static void main(String[] args){
System.out.println("Hello World");
}
}
```
## 选项
### A
```java
public class App {
public int main(){
System.out.printf("Hello World");
return 0;
}
}
```
### B
```java
public class App {
public static void main(String[] args){
println("Hello World");
}
}
```
### C
```java
import stdout
public class App {
public int main(){
print("Hello World\n");
return 0;
}
}
```
````
这是一个最基本的习题结构,它包含标题、答案、选项,注意这几个一级和二级标题必须填写正确,解释器会读取这几个标题。而选项的标题会被直接忽略掉,在
最终生成的习题中不包含选项的三级标题,所以这个标题可以用来标注一些编辑信息,例如“此选项没有关闭文件连接”,“类型错误”等等。
### 增强信息
为了编写习题和生成 notebook 的需要,markdown 解释器支持两种模板能力,如果我们在答案之前,有一个名为 aop 的二级标题:
````markdown
## aop
### before
```java
System.out.print("hello world");
```
### after
```java
App.main(new String[]{});
```
````
那么在创建notebook的时候,before 会插入到源代码前一个单元,after 则会插入到源代码后。aop 章节可以只包含 before 或 after 中的某一个,也可以两个都有。
另一些情况下,我们可能需要把各个选项中重复的代码提取出来,建立一个模板,此时可以在答案之前建立一个名为 template 的二级标题,例如:
````markdwon
## template
```java
public class App {
public static void main(String[] args){
// 下面的 code 占位符会被替换成答案和选项代码
$code
}
}
```
````
注意这里的代码中,有一个 `$code` 占位符,它在管道程序处理过程中,会替换成答案和个选项内容中的代码。
在后续的数据处理流程中,markdown 会被编译为 prepared 类型的习题。
## 技能树合成
`src`目录下执行 `python main.py -a tree` 会合成技能树文件,合成的技能树文件: `data/tree.json`
* 合成过程中,会自动检查每个目录下 `config.json` 里的 `node_id` 是否存在,不存在则生成
* 合成过程中,会自动检查每个知识点目录下 `config.json` 里的 `export` 里导出的习题配置,检查是否存在`exercise_id` 字段,如果不存在则生成
{
"keywords": [],
"node_id": "c-a4c730f9873b4d1c985048c5e63990cf",
"title": "安装OBD"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-e175537376e64d88840ca4d9d1e81610",
"title": "使用OBD部署"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-15525a96d7f24538a3d449ae623efd48",
"title": "问题排查"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-9c2a5df2e2c34cf3a1103cd2e2a3267b",
"title": "使用OBD"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-79d5327189b34b5a90fa74ad32b27305",
"title": "快速入门"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-cae5477828d14b448afb6660947859d0",
"title": "数据库操作"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-e675cce191c3420aa85b7c6b96580635",
"title": "表操作"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-c50584a0656e495bb7e800a8b6ceaf77",
"title": "索引操作"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-999212d4ca42491ca47f34b936399fcf",
"title": "插入数据"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-70958f7d51b54425b958cefc6ac45269",
"title": "删除数据"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-be0d2dee8d904f6cb886a7056d9e46f8",
"title": "更新数据"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-a7151df5705f4579bf5f63413385242e",
"title": "查询数据"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-3a77f96aec6e43d7ac98d4d300c02cf5",
"title": "提交事务"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-f35dcc63713745a6876ab5d4ff328dfe",
"title": "回滚事务"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-5021529f00ca4b9daa02569d4cddbb24",
"title": "基本操作"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-f599bcb975bd48f89e54e072a988bfb7",
"title": "整体架构"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-0dc6506e5e594e30990147b7265bbaa1",
"title": "数据分布"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-b9d8c4a7d8a14f398f73c521107017db",
"title": "数据模型"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-43e78261e6bb42fe92e5a5c238bf826a",
"title": "高可用"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-c2c32929d7c0478d8f93884568b5c13d",
"title": "事务管理"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-5a0baf4394b84466aca8c703366843cc",
"title": "存储架构"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-b3ea14fb38134f089c8583d1fd427cf6",
"title": "SQL引擎"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-517b0507b9994e7aab49c3af40479af4",
"title": "备份与恢复"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-cb9ef6b2ddcc4a76a8031f32c4e10ad0",
"title": "整体概览"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-9304eceab88a41fca82a970dbafc92d8",
"title": "OceanBase初阶"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-a917487b75574a68bb956d210da1170f",
"title": "数据库基础组件"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-226ffd9c22e9419b912ba9a9ee965307",
"title": "数据库管理工具"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-d5165a59206347949d45b8f95b7400f2",
"title": "数据库基础管理"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-88118ffc8e1741c6aa44dd1be3606e03",
"title": "数据库对象管理"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-52d13d48fb1b4ac4a2ed73cf1f3afd09",
"title": "数据库分布和链路管理"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-539923037c6a46b5b1ad94f6059a01fd",
"title": "事务管理"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-3d5e9c3efc2447cfabf92042ec2f1e8b",
"title": "用户权限管理"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-6b15f53d05dc48ae8ba5459334e14533",
"title": "数据高可用"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-1aa1f8af64dc43fca95163e01ced58b1",
"title": "管理员指南"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-18974cc7c0b7434fa77fc3d5e7e6eeba",
"title": "事务保存点"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-f167b583bd154a468f7748da99330693",
"title": "事务超时"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-c13e52bd07294f7ea94a025436658e18",
"title": "创建和管理表"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-b1093113f6a349a096f9259fce155d11",
"title": "创建和管理分区表"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-6624753925a54634aa9f995024130db5",
"title": "创建和管理表组"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-242155c0caf846b1b0862c969da53f21",
"title": "创建和管理视图"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-cdc5f596c1d64103a3c139e470a0df1b",
"title": "迁移和同步"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-43de7039f5b64679b681eb361c0aaee7",
"title": "数据库集群"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-8ef5ac8069114f5f87f50a856cdde5eb",
"title": "OceanBase租户"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-f7ca41020acd4ba7a1bfa7c7fe1db5e2",
"title": "MySQL租户"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-e605cdbd6dd14f5a9cb86038397613c1",
"title": "MySQL客户端"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-fc2b0b1d033047b3827889e112d8e9c4",
"title": "OceanBase客户端"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-21f2d54deae642e2b98a5f678ccdff9d",
"title": "结构化查询语言"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-cb4507b2ad80426597c38f904d7630f1",
"title": "Java连接驱动"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-89c1e279cee04b109e40a05eea4ee800",
"title": "实例数据库"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-92dff9fcbf0240aba97d1ff9af97ba99",
"title": "开发者指南"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-c716d8ca9e63427fb8165472b61d90ad",
"title": "表查询"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-15c6b14ca7624e528c1a4ca5df8c1980",
"title": "INSERT"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-213bc6f5a4ec449fa3d4efced8403bd6",
"title": "UPDATE"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-c1c3b2a58aa04d97b6f06481239b2487",
"title": "DELETE"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-ffdef42f321b4e9da804e50d24033d35",
"title": "REPLACE"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-5be80fbafdab400cb2581495c19b8c46",
"title": "提交事务"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-183a734c8d064b1695f1c3ff296df02a",
"title": "回滚事务"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-e4d079949fa6404a9ab2ab30f7c5edc6",
"title": "OceanBase中阶"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-72e40f4c5b704d8ebbc55333a92bcd10",
"title": "ob-admin"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-f850d34c90be4ad6b4440a1ccb9ec252",
"title": "ob-agent"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-c3953f56cc4d4cbdbfaebe899163c399",
"title": "liboblog"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-acfdf149d041465cbb137b9ee1113a68",
"title": "oblogproxy"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-4ffc00c8e57a49958fe0a21c406b2e8a",
"title": "oblogmsg"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-fda3afb7b20542b4b0581f640f862863",
"title": "生态工具"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-bcddfe107fa94f2db475cf3ba8b6cf6c",
"title": "通用系统参数配置"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-3dd02f73d75443829f8d110943321243",
"title": "默认生产参数配置"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-850a30b6277547dfa1d34bfa4ca0d557",
"title": "最小资源参数配置"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-a6c41eab49bf494cad102016646a29e0",
"title": "最佳性能参数配置"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-f61d0f9a558c468dafb9027b2e42a9b1",
"title": "最佳实践"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-5065d39c76f847429b8e39af10b226f8",
"title": "子查询"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-03c93d710d484e928753c100b08a3b5c",
"title": "连接查询"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-efc83dcbe5cb4e64828391fef26b6869",
"title": "数据分析"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-cf0df1911b794d7abb27edca7465c608",
"title": "SQL请求执行顺序"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-eee60b66d39147a6ae393334b3ca26b1",
"title": "函数"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-5dbadfb2e60b49e9b6fe796ff0813733",
"title": "SQL执行计划"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-1d075127ad8b44aba2086be1d5ecd97a",
"title": "分布式执行计划"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-9bff77915066428eb36907fa8cae457e",
"title": "优化"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-2fda0435c133420dbc4c048ab571405f",
"title": "编程和查询"
}
\ No newline at end of file
{
"keywords": [],
"node_id": "c-59a8c215a192470388e31341ad679c47",
"title": "OceanBase高阶"
}
\ No newline at end of file
{
"tree_name": "OceanBase",
"keywords": [],
"node_id": "569d5e11c4fc5de7844053d9a733c5e8",
"title": "C"
}
\ No newline at end of file
{
"OceanBase": {
"node_id": "569d5e11c4fc5de7844053d9a733c5e8",
"keywords": [],
"children": [
{
"OceanBase初阶": {
"node_id": "c-9304eceab88a41fca82a970dbafc92d8",
"keywords": [],
"children": [
{
"快速入门": {
"node_id": "c-79d5327189b34b5a90fa74ad32b27305",
"keywords": [],
"children": [
{
"安装OBD": {
"node_id": "c-a4c730f9873b4d1c985048c5e63990cf",
"keywords": [],
"children": []
}
},
{
"使用OBD部署": {
"node_id": "c-e175537376e64d88840ca4d9d1e81610",
"keywords": [],
"children": []
}
},
{
"问题排查": {
"node_id": "c-15525a96d7f24538a3d449ae623efd48",
"keywords": [],
"children": []
}
},
{
"使用OBD": {
"node_id": "c-9c2a5df2e2c34cf3a1103cd2e2a3267b",
"keywords": [],
"children": []
}
}
]
}
},
{
"基本操作": {
"node_id": "c-5021529f00ca4b9daa02569d4cddbb24",
"keywords": [],
"children": [
{
"数据库操作": {
"node_id": "c-cae5477828d14b448afb6660947859d0",
"keywords": [],
"children": []
}
},
{
"表操作": {
"node_id": "c-e675cce191c3420aa85b7c6b96580635",
"keywords": [],
"children": []
}
},
{
"索引操作": {
"node_id": "c-c50584a0656e495bb7e800a8b6ceaf77",
"keywords": [],
"children": []
}
},
{
"插入数据": {
"node_id": "c-999212d4ca42491ca47f34b936399fcf",
"keywords": [],
"children": []
}
},
{
"删除数据": {
"node_id": "c-70958f7d51b54425b958cefc6ac45269",
"keywords": [],
"children": []
}
},
{
"更新数据": {
"node_id": "c-be0d2dee8d904f6cb886a7056d9e46f8",
"keywords": [],
"children": []
}
},
{
"查询数据": {
"node_id": "c-a7151df5705f4579bf5f63413385242e",
"keywords": [],
"children": []
}
},
{
"提交事务": {
"node_id": "c-3a77f96aec6e43d7ac98d4d300c02cf5",
"keywords": [],
"children": []
}
},
{
"回滚事务": {
"node_id": "c-f35dcc63713745a6876ab5d4ff328dfe",
"keywords": [],
"children": []
}
}
]
}
},
{
"整体概览": {
"node_id": "c-cb9ef6b2ddcc4a76a8031f32c4e10ad0",
"keywords": [],
"children": [
{
"整体架构": {
"node_id": "c-f599bcb975bd48f89e54e072a988bfb7",
"keywords": [],
"children": []
}
},
{
"数据分布": {
"node_id": "c-0dc6506e5e594e30990147b7265bbaa1",
"keywords": [],
"children": []
}
},
{
"数据模型": {
"node_id": "c-b9d8c4a7d8a14f398f73c521107017db",
"keywords": [],
"children": []
}
},
{
"高可用": {
"node_id": "c-43e78261e6bb42fe92e5a5c238bf826a",
"keywords": [],
"children": []
}
},
{
"事务管理": {
"node_id": "c-c2c32929d7c0478d8f93884568b5c13d",
"keywords": [],
"children": []
}
},
{
"存储架构": {
"node_id": "c-5a0baf4394b84466aca8c703366843cc",
"keywords": [],
"children": []
}
},
{
"SQL引擎": {
"node_id": "c-b3ea14fb38134f089c8583d1fd427cf6",
"keywords": [],
"children": []
}
},
{
"备份与恢复": {
"node_id": "c-517b0507b9994e7aab49c3af40479af4",
"keywords": [],
"children": []
}
}
]
}
}
]
}
},
{
"OceanBase中阶": {
"node_id": "c-e4d079949fa6404a9ab2ab30f7c5edc6",
"keywords": [],
"children": [
{
"管理员指南": {
"node_id": "c-1aa1f8af64dc43fca95163e01ced58b1",
"keywords": [],
"children": [
{
"数据库基础组件": {
"node_id": "c-a917487b75574a68bb956d210da1170f",
"keywords": [],
"children": []
}
},
{
"数据库管理工具": {
"node_id": "c-226ffd9c22e9419b912ba9a9ee965307",
"keywords": [],
"children": []
}
},
{
"数据库基础管理": {
"node_id": "c-d5165a59206347949d45b8f95b7400f2",
"keywords": [],
"children": []
}
},
{
"数据库对象管理": {
"node_id": "c-88118ffc8e1741c6aa44dd1be3606e03",
"keywords": [],
"children": []
}
},
{
"数据库分布和链路管理": {
"node_id": "c-52d13d48fb1b4ac4a2ed73cf1f3afd09",
"keywords": [],
"children": []
}
},
{
"事务管理": {
"node_id": "c-539923037c6a46b5b1ad94f6059a01fd",
"keywords": [],
"children": []
}
},
{
"用户权限管理": {
"node_id": "c-3d5e9c3efc2447cfabf92042ec2f1e8b",
"keywords": [],
"children": []
}
},
{
"数据高可用": {
"node_id": "c-6b15f53d05dc48ae8ba5459334e14533",
"keywords": [],
"children": []
}
}
]
}
},
{
"事务保存点": {
"node_id": "c-18974cc7c0b7434fa77fc3d5e7e6eeba",
"keywords": [],
"children": []
}
},
{
"事务超时": {
"node_id": "c-f167b583bd154a468f7748da99330693",
"keywords": [],
"children": []
}
},
{
"创建和管理表": {
"node_id": "c-c13e52bd07294f7ea94a025436658e18",
"keywords": [],
"children": []
}
},
{
"创建和管理分区表": {
"node_id": "c-b1093113f6a349a096f9259fce155d11",
"keywords": [],
"children": []
}
},
{
"创建和管理表组": {
"node_id": "c-6624753925a54634aa9f995024130db5",
"keywords": [],
"children": []
}
},
{
"创建和管理视图": {
"node_id": "c-242155c0caf846b1b0862c969da53f21",
"keywords": [],
"children": []
}
},
{
"迁移和同步": {
"node_id": "c-cdc5f596c1d64103a3c139e470a0df1b",
"keywords": [],
"children": []
}
},
{
"开发者指南": {
"node_id": "c-92dff9fcbf0240aba97d1ff9af97ba99",
"keywords": [],
"children": [
{
"数据库集群": {
"node_id": "c-43de7039f5b64679b681eb361c0aaee7",
"keywords": [],
"children": []
}
},
{
"OceanBase租户": {
"node_id": "c-8ef5ac8069114f5f87f50a856cdde5eb",
"keywords": [],
"children": []
}
},
{
"MySQL租户": {
"node_id": "c-f7ca41020acd4ba7a1bfa7c7fe1db5e2",
"keywords": [],
"children": []
}
},
{
"MySQL客户端": {
"node_id": "c-e605cdbd6dd14f5a9cb86038397613c1",
"keywords": [],
"children": []
}
},
{
"OceanBase客户端": {
"node_id": "c-fc2b0b1d033047b3827889e112d8e9c4",
"keywords": [],
"children": []
}
},
{
"结构化查询语言": {
"node_id": "c-21f2d54deae642e2b98a5f678ccdff9d",
"keywords": [],
"children": []
}
},
{
"Java连接驱动": {
"node_id": "c-cb4507b2ad80426597c38f904d7630f1",
"keywords": [],
"children": []
}
},
{
"实例数据库": {
"node_id": "c-89c1e279cee04b109e40a05eea4ee800",
"keywords": [],
"children": []
}
}
]
}
},
{
"表查询": {
"node_id": "c-c716d8ca9e63427fb8165472b61d90ad",
"keywords": [],
"children": []
}
},
{
"INSERT": {
"node_id": "c-15c6b14ca7624e528c1a4ca5df8c1980",
"keywords": [],
"children": []
}
},
{
"UPDATE": {
"node_id": "c-213bc6f5a4ec449fa3d4efced8403bd6",
"keywords": [],
"children": []
}
},
{
"DELETE": {
"node_id": "c-c1c3b2a58aa04d97b6f06481239b2487",
"keywords": [],
"children": []
}
},
{
"REPLACE": {
"node_id": "c-ffdef42f321b4e9da804e50d24033d35",
"keywords": [],
"children": []
}
},
{
"提交事务": {
"node_id": "c-5be80fbafdab400cb2581495c19b8c46",
"keywords": [],
"children": []
}
},
{
"回滚事务": {
"node_id": "c-183a734c8d064b1695f1c3ff296df02a",
"keywords": [],
"children": []
}
}
]
}
},
{
"OceanBase高阶": {
"node_id": "c-59a8c215a192470388e31341ad679c47",
"keywords": [],
"children": [
{
"生态工具": {
"node_id": "c-fda3afb7b20542b4b0581f640f862863",
"keywords": [],
"children": [
{
"ob-admin": {
"node_id": "c-72e40f4c5b704d8ebbc55333a92bcd10",
"keywords": [],
"children": []
}
},
{
"ob-agent": {
"node_id": "c-f850d34c90be4ad6b4440a1ccb9ec252",
"keywords": [],
"children": []
}
},
{
"liboblog": {
"node_id": "c-c3953f56cc4d4cbdbfaebe899163c399",
"keywords": [],
"children": []
}
},
{
"oblogproxy": {
"node_id": "c-acfdf149d041465cbb137b9ee1113a68",
"keywords": [],
"children": []
}
},
{
"oblogmsg": {
"node_id": "c-4ffc00c8e57a49958fe0a21c406b2e8a",
"keywords": [],
"children": []
}
}
]
}
},
{
"最佳实践": {
"node_id": "c-f61d0f9a558c468dafb9027b2e42a9b1",
"keywords": [],
"children": [
{
"通用系统参数配置": {
"node_id": "c-bcddfe107fa94f2db475cf3ba8b6cf6c",
"keywords": [],
"children": []
}
},
{
"默认生产参数配置": {
"node_id": "c-3dd02f73d75443829f8d110943321243",
"keywords": [],
"children": []
}
},
{
"最小资源参数配置": {
"node_id": "c-850a30b6277547dfa1d34bfa4ca0d557",
"keywords": [],
"children": []
}
},
{
"最佳性能参数配置": {
"node_id": "c-a6c41eab49bf494cad102016646a29e0",
"keywords": [],
"children": []
}
}
]
}
},
{
"编程和查询": {
"node_id": "c-2fda0435c133420dbc4c048ab571405f",
"keywords": [],
"children": [
{
"子查询": {
"node_id": "c-5065d39c76f847429b8e39af10b226f8",
"keywords": [],
"children": []
}
},
{
"连接查询": {
"node_id": "c-03c93d710d484e928753c100b08a3b5c",
"keywords": [],
"children": []
}
},
{
"数据分析": {
"node_id": "c-efc83dcbe5cb4e64828391fef26b6869",
"keywords": [],
"children": []
}
},
{
"SQL请求执行顺序": {
"node_id": "c-cf0df1911b794d7abb27edca7465c608",
"keywords": [],
"children": []
}
},
{
"函数": {
"node_id": "c-eee60b66d39147a6ae393334b3ca26b1",
"keywords": [],
"children": []
}
},
{
"SQL执行计划": {
"node_id": "c-5dbadfb2e60b49e9b6fe796ff0813733",
"keywords": [],
"children": []
}
},
{
"分布式执行计划": {
"node_id": "c-1d075127ad8b44aba2086be1d5ecd97a",
"keywords": [],
"children": []
}
},
{
"优化": {
"node_id": "c-9bff77915066428eb36907fa8cae457e",
"keywords": [],
"children": []
}
}
]
}
}
]
}
}
]
}
}
\ No newline at end of file
# -*- coding: UTF-8 -*-
import importlib
def dispatch(config, options, actions, targets):
''' 分发命令行 action '''
action_len = len(actions)
if action_len < 2:
return
index = 1
next = targets
action = actions[index]
print(f"[命令路由中..]: {actions[0]}")
while action_len >= index:
if type(next) == type({}):
if index == action_len:
if next.get('run') != None:
print(f"[命令路由执行]:", '->'.join(actions))
next['run']()
break
action = actions[index]
if next.get(action) != None:
print(f"[命令路由中..]: {action}")
next = next[action]
index += 1
else:
print("[命令路由错误]: 未找到支持的命令行路由:", '->'.join(actions))
index += 1
else:
print(f"[命令路由执行]:", '->'.join(actions))
next()
index += 1
break
def dispatch_runner(config, options, actions, targets):
''' 分发命令行 action '''
action_len = len(actions)
if action_len < 2:
return
def load_and_run(target):
modules = target.split('.')
class_pos = len(modules)-2
path_pos = len(modules)-1
if class_pos >= 0 and modules[class_pos][0].isupper():
constructor = modules[class_pos]
runner = modules[path_pos]
module_path = '.'.join(modules[:class_pos])
importlib.import_module(module_path).__getattribute__(
constructor)(config, options).__getattribute__(runner)()
else:
runner = modules[path_pos]
module_path = '.'.join(modules[:path_pos])
importlib.import_module(module_path).__getattribute__(
runner)(config, options)
index = 1
next = targets
while action_len >= index:
if type(next) == type({}):
if index == action_len:
if next.get('run') != None:
load_and_run(next['run'])
break
action = actions[index]
if next.get(action) != None:
next = next[action]
index += 1
else:
load_and_run(next)
index += 1
break
# -*- coding: UTF-8 -*-
import os
import logging
import platform
from logging.handlers import RotatingFileHandler
def is_osx():
p = platform.platform()
return p.find('macOS') >= 0 or p.find('Darwin') >= 0
def get_root_log_dir(config, options):
action = 'default'
if options.action:
action = options.action
if is_osx():
return '/tmp/csdn/ai/{}'.format(action)
else:
is_dev = (options.cluster is None) or (options.cluster == 'dev')
if is_dev:
return '../log/csdn/ai/{}'.format(action)
else:
return '/var/csdn/csdn/ai/{}'.format(action)
class TruncatedFileHandler(RotatingFileHandler):
'''
日志文件按固定大小自动分割
'''
def __init__(self, filename, mode='a', maxBytes=0, encoding=None, delay=0):
super(TruncatedFileHandler, self).__init__(
filename, mode, maxBytes, 0, encoding, delay)
def doRollover(self):
"""Truncate the file"""
if self.stream:
self.stream.close()
dfn = self.baseFilename + ".1"
if os.path.exists(dfn):
os.remove(dfn)
os.rename(self.baseFilename, dfn)
os.remove(dfn)
self.mode = 'w'
self.stream = self._open()
def init_log(config, options):
# 创建日志目录
root_log_dir = get_root_log_dir(config, options)
os.makedirs(root_log_dir, exist_ok=True)
print('root_log_dir:', root_log_dir)
# 文件日志控制器
log_filename = root_log_dir+'/app.log'
file_handler = TruncatedFileHandler(log_filename, "w", 10*1024)
# 控制台日志控制器
console_handler = logging.StreamHandler()
# 日志配置
logging.basicConfig(
# 日志格式
format="[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d at %(funcName)s]: %(message)s",
# 日期格式
datefmt='%Y-%m-%d %H:%M:%S',
# 日志级别
level=logging.INFO,
# 输出目标,日志文件+控制台
handlers=[
file_handler,
console_handler
]
)
# -*- coding: UTF-8 -*-
import os
import json
from lib.apollo import ApolloClient
def load_apollo_config(options):
'''
阿波罗配置加载
===
* 如果是 `pro` 环境,则使用线上配置
* 否则,使用内网配置
'''
config_url = None
if options.cluster == 'pro':
config_url = 'http://pro.config.csdn.net:8080'
elif options.cluster == 'fat':
config_url = 'http://fat.config.csdn.net:8080'
elif options.cluster == 'uat':
config_url = 'http://uat.config.csdn.net:8080'
else:
config_url = 'http://dev.config.csdn.net:8080'
client = ApolloClient(
app_id="949",
cluster="default",
config_url=config_url,
start_hot_update=False
)
config = client.get_value("csdn-ai", namespace="application")
return json.loads(config)
def load_config(options, args):
'''
配置加载
===
* 如果本地 config/config 目录下存在配置,则使用本地配置文件
* 如果本地 config/config 目录下不存在配置,
* 默认使用 阿波罗配置中心 cluster=dev 配置
* 如果指定 --cluster,则使用指定 cluster 的阿波罗配置中心的配置
'''
profile_path = "config/config/{}.json".format(options.profile)
config = None
if options.cluster:
config = load_apollo_config(options)
else:
if os.path.exists(profile_path):
with open(profile_path, "r") as f:
config = json.loads(f.read())
else:
# try:
# options.cluster = 'dev'
# config = load_apollo_config(options)
# except:
config = {}
return config
# -*- coding: UTF-8 -*-
from common.logger import init_log
from config.config import load_config
from options import parse_options, show_help
from tree import gen_tree
from os import walk
import os.path
def test(config, options, actions):
import test as test
test.dispatch(config, options, actions)
def readall(path):
with open(path) as f:
return f.read()
def write(path, data):
with open(path, "w") as f:
f.write(data)
def makeup():
for dir, sub_dirs, files in walk("../data"):
for fname in files:
_, ext = os.path.splitext(fname)
if ext == ".md":
full_path = os.path.join(dir, fname)
content = readall(full_path)
lines = content.split("\n")
new_lines = []
flag = False
in_block = False
for line in lines:
new_line = line
if line.startswith("```"):
if in_block:
in_block = False
else:
in_block = True
if line[3:] != "":
continue
else:
new_line = line + 'c'
flag = True
new_lines.append(new_line)
if flag:
content = "\n".join(new_lines)
write(full_path, content)
def tree(config, options, actions):
import test as test
gen_tree("../data")
def run(options):
# 操作入口
if options.action is not None:
actions = options.action.split('.')
if len(actions) == 0:
return
print('@init config...')
config = load_config(options, args)
print('')
print('@init log...')
init_log(config, options)
print('')
print('@dispatch action:{}...'.format(options.action))
root_action = actions[0]
next = actions[1:]
dispatch = {
'test': lambda: test(config, options, next),
'tree': lambda: tree(config, options, next),
'makeup': lambda: makeup()
}
dispatch[root_action]()
else:
show_help()
if __name__ == "__main__":
[options, args] = parse_options()
run(options)
from optparse import OptionParser
def parse_common_options(parser):
'''
## 公共选项
* -t 或者 --tag_id : 某个操作限制到指定tag_id的数据范围
* --reset: 重置选项
* --tag_name: 标签名字
* --model: 模型名字
* --log: 日志级别
* --train: 训练
* --port: 端口
* --show_config: 显示配置
* --count: 指定数量
* --query: 自定义查询
'''
parser.add_option(
"-t", "--tag_id",
dest="tag_id",
help="tag_id",
metavar="TAG_ID"
)
parser.add_option(
"--reset",
dest="reset",
help="reset",
action="store_true",
metavar="RESET"
)
parser.add_option(
"--tag_name",
dest="tag_name",
help="tag_name",
metavar="TAG_NAME"
)
parser.add_option(
"--model",
dest="model",
help="model",
metavar="MODEL"
)
parser.add_option(
"--log",
dest="log",
help="log",
metavar="LOG"
)
parser.add_option(
"--train",
dest="train",
help="train",
action="store_true",
metavar="TRAIN"
)
parser.add_option(
"--port",
dest="port",
help="port",
metavar="PORT"
)
parser.add_option(
"--server",
dest="server",
help="server",
metavar="SERVER"
)
parser.add_option(
"--show_config",
dest="show_config",
help="show_config",
metavar="SHOW_CONFIG"
)
parser.add_option(
"--count",
dest="count",
help="count",
metavar="COUNT"
)
parser.add_option(
"--query",
dest="query",
help="query",
metavar="QUERY"
)
def parse_profile_options(parser):
'''
## 环境配置选项
* -p 或 --profile 指定配置环境,可选的有 `dev`, `fat`, `pre`, `pro`
* 如果本地 config/config 目录下存在配置,则使用本地配置文件
* 如果本地 config/config 目录下不存在配置,
* 默认使用 阿波罗配置中心 cluster=dev 配置
* 如果指定 --cluster,则使用指定 cluster 的阿波罗配置中心的配置
'''
parser.add_option(
"-p", "--profile",
dest="profile",
help="profile",
default='pro',
metavar="PROFILE"
)
parser.add_option(
"--cluster",
dest="cluster",
help="cluster",
metavar="REMOTE"
)
def parse_action_options(parser):
'''
## 操作选项
* -a 或 --action 指定了操作目标,多级目标用点号分割,例如:
* -a dataset.build.tag.all
* -a server.ask
* -a test.code
* 参考[README](./README.md)
'''
parser.add_option(
"-a", "--action",
dest="action",
help="action",
metavar="ACTION"
)
def parse_test_options(parser):
'''
## 测试选项
执行 -a test.xx 测试时默认执行冒烟测试,下面的选项改变行为
* --label 指定执行测试并生成待标注数据
* --count 指定标注上限
'''
parser.add_option(
"--label",
dest="label",
help="label",
action="store_true",
metavar="LABEL"
)
def parse_db_options(parser):
'''
## 数据库 migrate 选项
* --message 传入一个消息变量,db.migrate action 接收此参数
* --revision 传入版本参数,db.upgrade, db.downgrade, db.stamp, db.show, db.edit 接受此参数
'''
parser.add_option(
"--message",
dest="message",
help="message",
metavar="MESSAGE"
)
parser.add_option(
"--revision",
dest="revision",
help="revision",
metavar="REVISION"
)
def parse_options():
parser = OptionParser()
parse_common_options(parser)
parse_profile_options(parser)
parse_action_options(parser)
parse_test_options(parser)
parse_db_options(parser)
(options, args) = parser.parse_args()
return [options, args]
def show_help():
'''
命令行选项说明:
==
'''
help = '\n'.join([
show_help.__doc__,
parse_common_options.__doc__,
parse_profile_options.__doc__,
parse_action_options.__doc__
])
print(help)
from genericpath import exists
import json
import os
import uuid
import sys
import re
id_set = set()
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 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_tree(data_path):
root = {}
def gen_node_id():
# return ''.join(str(uuid.uuid5(uuid.NAMESPACE_URL, 'skill_tree')).split('-'))
return "c-" + uuid.uuid4().hex
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_id_helper(node):
flag = False
if (node.get('node_id') is None) or node.get('node_id') in id_set:
node['node_id'] = gen_node_id()
flag = True
id_set.add(node['node_id'])
if 'children' in node:
for c in node["children"]:
flag = flag or ensure_id_helper(list(c.values())[0])
return flag
def ensure_node_id(cfg):
return ensure_id_helper(cfg)
def ensure_title_helper(node, cfg_path, title=""):
flag = False
if node.get('title') is None:
if cfg_path:
node['title'] = re.sub("^[0-9]{1,3}\.", "", os.path.split(os.path.dirname(cfg_path))[-1])
else:
node['title'] = title
flag = True
if 'children' in node:
for c in node["children"]:
flag = flag or ensure_title_helper(list(c.values())[0], None, list(c.keys())[0])
return flag
def ensure_title(cfg, cfg_path):
return ensure_title_helper(cfg, cfg_path)
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)
if ensure_node_id(cfg):
dump_json(cfg_path, cfg)
if ensure_title(cfg, cfg_path):
cfg["title"] = "C"
dump_json(cfg_path, cfg, exist_ok=True, override=True)
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)
level_path = os.path.join(level_no_dir, 'config.json')
level_cfg = load_json(level_path)
if ensure_node_id(level_cfg) or check_export(level_no_dir, level_cfg):
dump_json(level_path, level_cfg, exist_ok=True, override=True)
if ensure_title(level_cfg, level_path):
dump_json(level_path, level_cfg, exist_ok=True, override=True)
level_node, level_node_children = make_node(
level_name, level_cfg['node_id'], level_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)
chapter_path = os.path.join(chapter_no_dir, 'config.json')
chapter_cfg = load_json(chapter_path)
if ensure_node_id(chapter_cfg) or check_export(chapter_no_dir, chapter_cfg):
dump_json(chapter_path, chapter_cfg, exist_ok=True, override=True)
if ensure_title(chapter_cfg, chapter_path):
dump_json(chapter_path, chapter_cfg, exist_ok=True, override=True)
chapter_node, chapter_node_children = make_node(
chapter_name, chapter_cfg['node_id'], chapter_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)
sec_path = os.path.join(section_no_dir, 'config.json')
sec_cfg = load_json(sec_path)
flag = ensure_node_id(sec_cfg) or check_export(section_no_dir, sec_cfg)
section_node, section_node_children = make_node(
section_name, sec_cfg['node_id'], sec_cfg['keywords'], sec_cfg.get('children', []))
chapter_node_children.append(section_node)
# 确保习题分配了习题ID
for export in sec_cfg.get("export", []):
ecfg_path = os.path.join(section_no_dir, export)
ecfg = load_json(ecfg_path)
if (ecfg.get('exercise_id') is None) or (ecfg.get('exercise_id') in id_set):
ecfg['exercise_id'] = uuid.uuid4().hex
dump_json(ecfg_path, ecfg, exist_ok=True, override=True)
id_set.add(ecfg['exercise_id'])
if flag:
dump_json(sec_path, sec_cfg, exist_ok=True, override=True)
if ensure_title(sec_cfg, sec_path):
dump_json(sec_path, sec_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.
先完成此消息的编辑!
想要评论请 注册