tree.py 6.5 KB
Newer Older
M
Mars Liu 已提交
1
import logging
M
Mars Liu 已提交
2 3 4 5 6 7 8 9 10
from genericpath import exists
import json
import os
import uuid
import sys
import re

id_set = set()

M
Mars Liu 已提交
11 12 13
logger = logging.getLogger(__name__)


M
Mars Liu 已提交
14 15 16 17 18 19 20 21 22 23 24
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:
M
Mars Liu 已提交
25
            logger.error(f"{p} already exist")
M
Mars Liu 已提交
26 27
            sys.exit(0)

M
Mars Liu 已提交
28
    with open(p, 'w+') as f:
M
Mars Liu 已提交
29 30 31
        f.write(json.dumps(j, indent=2, ensure_ascii=False))


M
Mars Liu 已提交
32 33 34 35 36 37 38 39 40
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)

M
Mars Liu 已提交
41 42 43 44 45 46 47 48 49 50 51 52
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

M
Mars Liu 已提交
53

M
Mars Liu 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
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('-'))
M
Mars Liu 已提交
73
        return "oceanbase-" + uuid.uuid4().hex
M
Mars Liu 已提交
74 75 76 77 78 79 80 81 82 83 84 85

    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

M
Mars Liu 已提交
86
        if (node.get('node_id') is None) or node.get('node_id') in id_set:
M
Mars Liu 已提交
87
            node['node_id'] = gen_node_id()
M
Mars Liu 已提交
88 89 90 91 92 93 94
            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])
M
Mars Liu 已提交
95

M
Mars Liu 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
        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])
M
Mars Liu 已提交
114

M
Mars Liu 已提交
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
        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

    # 根节点
M
Mars Liu 已提交
131
    cfg = ensure_config(data_path)
M
Mars Liu 已提交
132 133
    cfg_path = os.path.join(data_path, 'config.json')
    if ensure_node_id(cfg):
M
Mars Liu 已提交
134
        dump_json(cfg_path, cfg, exist_ok=True, override=True)
M
Mars Liu 已提交
135

M
Mars Liu 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    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')
M
Mars Liu 已提交
151
        level_cfg = ensure_config(level_no_dir)
M
Mars Liu 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164
        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')
M
Mars Liu 已提交
165
            chapter_cfg = ensure_config(chapter_no_dir)
M
Mars Liu 已提交
166 167 168
            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):
M
Mars Liu 已提交
169
                dump_json(chapter_path, chapter_cfg, exist_ok=True, override=True)
M
Mars Liu 已提交
170 171 172 173 174 175 176 177 178

            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')
M
Mars Liu 已提交
179
                sec_cfg = ensure_config(section_no_dir)
M
Mars Liu 已提交
180 181 182 183 184 185 186
                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
M
Mars Liu 已提交
187

M
Mars Liu 已提交
188 189 190 191 192
                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):
M
Mars Liu 已提交
193
                        ecfg['exercise_id'] = uuid.uuid4().hex
M
Mars Liu 已提交
194
                        dump_json(ecfg_path, ecfg, exist_ok=True, override=True)
M
Mars Liu 已提交
195

M
Mars Liu 已提交
196 197 198 199 200 201
                    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):
M
Mars Liu 已提交
202
                    dump_json(sec_path, sec_cfg, exist_ok=True, override=True)
M
Mars Liu 已提交
203

M
Mars Liu 已提交
204
                    # 保存技能树骨架
M
Mars Liu 已提交
205 206
    tree_path = os.path.join(data_path, 'tree.json')
    dump_json(tree_path, root, exist_ok=True, override=True)