139 lines
3.9 KiB
Python
139 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
pbl_appcodes.codes_data —— codes.json 装载与数量契约常量
|
||
|
||
唯一事实源:modules/pbl_appcodes/pbl_appcodes/codes.json
|
||
(设计文档:projects/pbls/docs/01-design/appcodes.md)
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
CODES_PATH = os.path.join(HERE, 'codes.json')
|
||
|
||
# ---- 数量契约(硬编码常量,self_check 据此校验实际数据)----
|
||
GROUP_COUNT = 8 # MAIN_GROUPS(6) + EXTRA_GROUPS(2)
|
||
ITEM_COUNT = 74
|
||
DIALECT = 'mariadb'
|
||
MAIN_GROUP_COUNT = 6
|
||
EXTRA_GROUP_COUNT = 2
|
||
|
||
# 主组(6):蓝图类型 / 蓝图状态 / 质量等级 / 校验维度 / 子对象类型 / Agent 工具
|
||
MAIN_GROUP_CODES = (
|
||
'pbl_blueprint_type',
|
||
'pbl_blueprint_status',
|
||
'pbl_quality_level',
|
||
'pbl_validation_dim',
|
||
'pbl_subobject_type',
|
||
'pbl_agent_tool',
|
||
)
|
||
# 扩展组(2):证据类型 / 量规等级
|
||
EXTRA_GROUP_CODES = (
|
||
'pbl_evidence_type',
|
||
'pbl_assessment_rubric_level',
|
||
)
|
||
|
||
_CACHE = {'meta': None, 'groups': None, 'path': None, 'mtime': None}
|
||
|
||
|
||
def load_codes(path=None, force=False):
|
||
"""
|
||
装载 codes.json,返回 (meta, groups)。
|
||
带 mtime 缓存:文件未变不重复解析(应用启动后高频调用)。
|
||
"""
|
||
path = path or CODES_PATH
|
||
try:
|
||
mtime = os.path.getmtime(path)
|
||
except OSError:
|
||
mtime = None
|
||
|
||
if (not force and _CACHE['groups'] is not None
|
||
and _CACHE['path'] == path and _CACHE['mtime'] == mtime):
|
||
return _CACHE['meta'], _CACHE['groups']
|
||
|
||
with open(path, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
|
||
if isinstance(data, list):
|
||
meta, groups = {}, data
|
||
else:
|
||
meta = data.get('_meta') or {}
|
||
groups = None
|
||
for key in ('groups', 'codes', 'appcodes', 'data', 'items'):
|
||
val = data.get(key)
|
||
if isinstance(val, list) and val:
|
||
groups = val
|
||
break
|
||
if groups is None:
|
||
groups = []
|
||
|
||
_CACHE.update({'meta': meta, 'groups': groups, 'path': path, 'mtime': mtime})
|
||
return meta, groups
|
||
|
||
|
||
def _reload():
|
||
return load_codes(force=True)
|
||
|
||
|
||
CODES_META, ALL_GROUPS = load_codes()
|
||
|
||
# 主组 / 扩展组切片(按 code 归属划分,顺序保持 codes.json 原序)
|
||
MAIN_GROUPS = [g for g in ALL_GROUPS if g.get('code') in MAIN_GROUP_CODES]
|
||
EXTRA_GROUPS = [g for g in ALL_GROUPS if g.get('code') in EXTRA_GROUP_CODES]
|
||
|
||
|
||
def group_items(group):
|
||
"""取组内项数组(兼容 items/children/codes/options 键名)"""
|
||
if not isinstance(group, dict):
|
||
return []
|
||
for key in ('items', 'children', 'codes', 'options', 'values', 'entries'):
|
||
val = group.get(key)
|
||
if isinstance(val, list):
|
||
return val
|
||
return []
|
||
|
||
|
||
def iter_items(groups=None):
|
||
"""迭代 (group_code, group_name, item) 三元组"""
|
||
groups = ALL_GROUPS if groups is None else groups
|
||
for g in groups:
|
||
gcode = g.get('code') or ''
|
||
gname = g.get('name') or ''
|
||
for it in group_items(g):
|
||
yield gcode, gname, it
|
||
|
||
|
||
def group_codes(groups=None):
|
||
"""组编码有序列表"""
|
||
groups = ALL_GROUPS if groups is None else groups
|
||
return [g.get('code') for g in groups]
|
||
|
||
|
||
def all_item_codes(group_code=None):
|
||
"""项编码列表(可按组过滤)"""
|
||
out = []
|
||
for gcode, _gname, it in iter_items():
|
||
if group_code is None or gcode == group_code:
|
||
out.append(it.get('code'))
|
||
return out
|
||
|
||
|
||
def find_group(group_code):
|
||
"""按组编码取组 dict,未找到返回 None"""
|
||
for g in ALL_GROUPS:
|
||
if g.get('code') == group_code:
|
||
return g
|
||
return None
|
||
|
||
|
||
def counts():
|
||
"""实测数量:{'groups': n, 'items': m, 'main': x, 'extra': y}"""
|
||
return {
|
||
'groups': len(ALL_GROUPS),
|
||
'items': sum(len(group_items(g)) for g in ALL_GROUPS),
|
||
'main': len(MAIN_GROUPS),
|
||
'extra': len(EXTRA_GROUPS),
|
||
'dialect': (CODES_META or {}).get('dialect'),
|
||
}
|