# skill_tree_opencv

## 目录结构说明

* 技能树`骨架文件`：
    * 位置：`data/tree.json` 
    * 说明：该文件是执行 `python main.py` 生成的，请勿人工编辑
* 技能树`根节点`配置文件：
    * 位置：`data/config.json`
    * 说明：可编辑配置关键词等字段，其中 `node_id` 字段是生成的，请勿编辑
* 技能树`难度节点`：
    * 位置：`data/xxx`，例如: `data/1.OpenCV初阶`
    * 说明：
        * 每个技能树有 3 个等级，目录前的序号是必要的，用来保持文件夹目录的顺序
        * 每个目录下有一个 `config.json` 可配置关键词信息，其中 `node_id` 字段是生成的，请勿编辑
* 技能树`章节点`：
    * 位置：`data/xxx/xxx`，例如：`data/1.OpenCV初阶/1.OpenCV基础`
    * 说明：
        * 每个技能树的每个难度等级有 n 个章节，目录前的序号是必要的，用来保持文件夹目录的顺序
        * 每个目录下有一个 `config.json` 可配置关键词信息，其中 `node_id` 字段是生成的，请勿编辑
* 技能树`知识节点`：
    * 位置：`data/xxx/xxx/xxx`，例如：`data/1.OpenCV初阶/1.OpenCV基础/1.OpenCV简介`
    * 说明：
        * 每个技能树的每章有 `n` 个知识节点，目录前的序号是必要的，用来保持文件夹目录的顺序
        * 每个目录下有一个 `config.json`
            * 其中 `node_id` 字段是生成的，请勿编辑
            * 其中 `keywords` 可配置关键字字段
            * 其中 `children` 可配置该`知识节点`下的子树结构信息，参考后面描述
            * 其中 `export` 可配置该`知识节点`下的导出习题信息，参考后面描述


## `知识节点` 子树信息结构

例如 `data/1.OpenCV初阶/1.OpenCV基础/1.OpenCV简介/config.json` 里配置对该知识节点子树信息结构：
```json
{
    // ...

    "children": [
        // TODO ...
    ],
}
```



## `知识节点` 的导出习题编辑

例如 `data/1.OpenCV初阶/1.OpenCV基础/1.OpenCV简介/config.json` 里配置对该知识节点导出的习题

```json
{
    // ...
    "export": [
        "helloworld.json"
    ]
}
```

在 export 字段中，我们列出习题定义 json ，下面我们了解如何编写习题。

## `知识节点` 的导出习题选项配置编辑

目前我们支持使用 markdown 语法直接编辑习题和各选项。

如前文内容，我们在知识节点下增加习题 `helloworld`的定义文件，即在`data/1.OpenCV初阶/1.OpenCV基础/1.OpenCV简介` 目录增加一个`helloworld.json`文件：

```json
{
    "type": "code_options",
    "author": "幻灰龙",
    "source": "helloworld.md",
    "notebook_enable": true
}
```
其中 type 字段目前都固定是 `code_options`，`notebook_enable` 字段决定这个习题是否生成对应的 `notebook` 。根据具体情况写好其它字段，注意这里 `source` 的文件名，我们指定了一个 markdwon 文件。现在我们新建一个 `helloworld.md` 并编辑为：

````markdown
# Hello World

以下 `Hello World` 程序中，能够正确执行下述操作的是？
1. 读取目录下`lena`图片
2. 显示`lena`图像窗口
3. 等待用户输入任意按键后关闭窗口

## 答案

```cpp
#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
  Mat img = imread("lena.png");
  imshow("lena", img);
  waitKey(0);
  destroyAllWindows();
  return 0;
}
```

## 选项

### 没有显示图像

```cpp
#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
  Mat img = imread("lena.png");
  waitKey(0);
  destroyAllWindows();
  return 0;
}
```

### imshow参数顺序错误

```cpp
#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
  Mat img = imread("lena.png");
  imshow(img, "lena");
  waitKey(0);
  destroyAllWindows();
  return 0;
}
```

### 忘记销毁窗口

```cpp
#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
  Mat img = imread("lena.png");
  imshow("lena", img);
  waitKey(0);
  return 0;
}
```

### 没有等待用户输入按键

```cpp
#include <opencv2/opencv.hpp>
using namespace cv;

int main() {
  Mat img = imread("lena.png");
  imshow("lena", img);
  destroyAllWindows();
  return 0;
}
```
````

这是一个最基本的习题结构，它包含标题、答案、选项，注意这几个一级和二级标题必须填写正确，解释器会读取这几个标题。而选项的标题会被直接忽略掉，在
最终生成的习题中不包含选项的三级标题，所以这个标题可以用来标注一些编辑信息，例如“此选项没有关闭文件连接”，“类型错误”等等。


## 技能树合成

在根目录下执行 `python main.py` 会合成技能树文件，合成的技能树文件: `data/tree.json`
* 合成过程中，会自动检查每个目录下 `config.json` 里的 `node_id` 是否存在，不存在则生成
* 合成过程中，会自动检查每个知识点目录下 `config.json` 里的 `export` 里导出的习题配置，检查是否存在`exercise_id` 字段，如果不存在则生成
