tree.py 3.6 KB
Newer Older
CSDN-Ada助手's avatar
CSDN-Ada助手 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
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)