| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- def validate_rule_ranges(data: dict, path: str = "") -> (bool, str):
- """
- 递归检查配置中看起来像评分规则的列表。
- 规则:
- 1. 列表中的元素必须包含 min, max, deduction。
- 2. 第一个元素的 min 必须为 0。
- 3. 当前元素的 min 必须等于上一个元素的 max。
- """
- if isinstance(data, dict):
- for key, value in data.items():
- is_valid, error = validate_rule_ranges(value, path=f"{path}.{key}" if path else key)
- if not is_valid:
- return False, error
- elif isinstance(data, list):
- # 检查这是否是一个规则列表(通过检查第一个元素是否包含特定key)
- if len(data) > 0 and isinstance(data[0], dict) and "min" in data[0] and "max" in data[0] and "deduction" in \
- data[0]:
- current_path = path
- # 1. 检查首项 min 是否为 0
- first_min = data[0].get("min")
- if first_min != 0:
- return False, f"范围错误 [{current_path}]: 第一个区间的 min 必须为 0,当前为 {first_min}"
- # 2. 检查连续性
- prev_max = None
- for idx, rule in enumerate(data):
- current_min = rule.get("min")
- current_max = rule.get("max")
- # 处理 inf 字符串
- if current_max == "inf":
- current_max_val = float("inf")
- else:
- try:
- current_max_val = float(current_max)
- except (ValueError, TypeError):
- return False, f"数值错误 [{current_path}]: max 值 '{current_max}' 无效"
- try:
- current_min_val = float(current_min)
- except (ValueError, TypeError):
- return False, f"数值错误 [{current_path}]: min 值 '{current_min}' 无效"
- # 检查连续性 (除了第一个元素)
- if idx > 0:
- # 使用一个微小的容差处理浮点数比较,或者直接相等
- if prev_max == "inf":
- return False, f"逻辑错误 [{current_path}]: 'inf' 只能出现在最后一个区间的 max"
- prev_max_val = float(prev_max) if prev_max != "inf" else float("inf")
- if current_min_val != prev_max_val:
- return False, f"不连续错误 [{current_path}]: 第 {idx + 1} 项的 min ({current_min}) 不等于上一项的 max ({prev_max})"
- prev_max = current_max
- return True, "验证通过"
- if __name__ == '__main__':
- new_config = {
- "base_score": 10,
- "corner": {
- "rules": {
- "wear_area": [
- {
- "min": 0,
- "max": 0.05,
- "deduction": -0.1
- },
- {
- "min": 0.05,
- "max": 0.1,
- "deduction": -0.5
- },
- {
- "min": 0.1,
- "max": 0.25,
- "deduction": -1.5
- },
- {
- "min": 0.25,
- "max": 0.5,
- "deduction": -3
- },
- {
- "min": 0.5,
- "max": "inf",
- "deduction": -5
- }
- ],
- "loss_area": [
- {
- "min": 0,
- "max": 0.05,
- "deduction": -0.1
- },
- {
- "min": 0.05,
- "max": 0.1,
- "deduction": -0.5
- },
- {
- "min": 0.1,
- "max": 0.25,
- "deduction": -1.5
- },
- {
- "min": 0.25,
- "max": 0.5,
- "deduction": -3
- },
- {
- "min": 0.5,
- "max": "inf",
- "deduction": -5
- }
- ]
- },
- "front_weights": {
- "wear_area": 0.3,
- "loss_area": 0.7
- },
- "back_weights": {
- "wear_area": 0.3,
- "loss_area": 0.7
- },
- "final_weights": {
- "front": 0.7,
- "back": 0.3
- }
- },
- "edge": {
- "rules": {
- "wear_area": [
- {
- "min": 0,
- "max": 0.05,
- "deduction": -0.1
- },
- {
- "min": 0.05,
- "max": 0.1,
- "deduction": -0.5
- },
- {
- "min": 0.1,
- "max": 0.25,
- "deduction": -1.5
- },
- {
- "min": 0.25,
- "max": 0.5,
- "deduction": -3
- },
- {
- "min": 0.5,
- "max": "inf",
- "deduction": -5
- }
- ],
- "loss_area": [
- {
- "min": 0,
- "max": 0.05,
- "deduction": -0.1
- },
- {
- "min": 0.05,
- "max": 0.1,
- "deduction": -0.5
- },
- {
- "min": 0.1,
- "max": 0.25,
- "deduction": -1.5
- },
- {
- "min": 0.25,
- "max": 0.5,
- "deduction": -3
- },
- {
- "min": 0.5,
- "max": "inf",
- "deduction": -5
- }
- ]
- },
- "front_weights": {
- "wear_area": 0.4,
- "loss_area": 0.6
- },
- "back_weights": {
- "wear_area": 0.4,
- "loss_area": 0.6
- },
- "final_weights": {
- "front": 0.7,
- "back": 0.3
- }
- },
- "face": {
- "rules": {
- "wear_area": [
- {
- "min": 0,
- "max": 0.05,
- "deduction": -0.1
- },
- {
- "min": 0.05,
- "max": 0.1,
- "deduction": -0.5
- },
- {
- "min": 0.1,
- "max": 0.25,
- "deduction": -1.5
- },
- {
- "min": 0.25,
- "max": 0.5,
- "deduction": -3
- },
- {
- "min": 0.5,
- "max": "inf",
- "deduction": -5
- }
- ],
- "pit_area": [
- {
- "min": 0,
- "max": 0.05,
- "deduction": -0.1
- },
- {
- "min": 0.05,
- "max": 0.1,
- "deduction": -0.5
- },
- {
- "min": 0.1,
- "max": 0.25,
- "deduction": -1.5
- },
- {
- "min": 0.25,
- "max": 0.5,
- "deduction": -3
- },
- {
- "min": 0.5,
- "max": "inf",
- "deduction": -5
- }
- ],
- "stain_area": [
- {
- "min": 0,
- "max": 0.05,
- "deduction": -0.1
- },
- {
- "min": 0.05,
- "max": 0.1,
- "deduction": -0.5
- },
- {
- "min": 0.1,
- "max": 0.25,
- "deduction": -1.5
- },
- {
- "min": 0.25,
- "max": 0.5,
- "deduction": -3
- },
- {
- "min": 0.5,
- "max": "inf",
- "deduction": -5
- }
- ],
- "scratch_length": [
- {
- "min": 0,
- "max": 1,
- "deduction": -0.1
- },
- {
- "min": 1,
- "max": 2,
- "deduction": -0.5
- },
- {
- "min": 2,
- "max": 5,
- "deduction": -1
- },
- {
- "min": 5,
- "max": 10,
- "deduction": -2
- },
- {
- "min": 10,
- "max": 20,
- "deduction": -3
- },
- {
- "min": 20,
- "max": 50,
- "deduction": -4
- },
- {
- "min": 50,
- "max": "inf",
- "deduction": -5
- }
- ]
- },
- "coefficients": {
- "wear_area": 0.25,
- "scratch_length": 0.25,
- "dent_area": 0.25,
- "stain_area": 0.25
- },
- "final_weights": {
- "front": 0.75,
- "back": 0.25
- }
- },
- "centering": {
- "front": {
- "rules": [
- {
- "min": 0,
- "max": 52,
- "deduction": 0
- },
- {
- "min": 52,
- "max": 55,
- "deduction": -0.5
- },
- {
- "min": 55,
- "max": 60,
- "deduction": -1
- },
- {
- "min": 60,
- "max": 62.5,
- "deduction": -1.5
- },
- {
- "min": 62.5,
- "max": 65,
- "deduction": -2
- },
- {
- "min": 65,
- "max": 67.5,
- "deduction": -2.5
- },
- {
- "min": 67.5,
- "max": 70,
- "deduction": -3
- },
- {
- "min": 70,
- "max": 72.5,
- "deduction": -3.5
- },
- {
- "min": 72.5,
- "max": 75,
- "deduction": -4
- },
- {
- "min": 75,
- "max": 77.5,
- "deduction": -4.5
- },
- {
- "min": 77.5,
- "max": 80,
- "deduction": -5
- },
- {
- "min": 80,
- "max": 82.5,
- "deduction": -5.5
- },
- {
- "min": 82.5,
- "max": 85,
- "deduction": -6
- },
- {
- "min": 85,
- "max": 87.5,
- "deduction": -6.5
- },
- {
- "min": 87.5,
- "max": 90,
- "deduction": -7
- },
- {
- "min": 90,
- "max": 92.5,
- "deduction": -7.5
- },
- {
- "min": 92.5,
- "max": 95,
- "deduction": -8
- },
- {
- "min": 95,
- "max": 97.5,
- "deduction": -8.5
- },
- {
- "min": 97.5,
- "max": "inf",
- "deduction": -9
- }
- ],
- "coefficients": {
- "horizontal": 1.2,
- "vertical": 0.9
- }
- },
- "back": {
- "rules": [
- {
- "min": 0,
- "max": 60,
- "deduction": -0.5
- },
- {
- "min": 60,
- "max": 70,
- "deduction": -1
- },
- {
- "min": 70,
- "max": 75,
- "deduction": -1.5
- },
- {
- "min": 75,
- "max": 85,
- "deduction": -2
- },
- {
- "min": 85,
- "max": 95,
- "deduction": -2.5
- },
- {
- "min": 95,
- "max": "inf",
- "deduction": -3
- }
- ],
- "coefficients": {
- "horizontal": 1.2,
- "vertical": 0.9
- }
- },
- "final_weights": {
- "front": 0.75,
- "back": 0.25
- }
- },
- "card": {
- "PSA": {
- "face": 0.35,
- "corner": 0.3,
- "edge": 0.1,
- "center": 0.25
- },
- "BGS": {
- "face": 0.3,
- "corner": 0.25,
- "edge": 0.1,
- "center": 0.25
- }
- }
- }
- is_range_valid, range_reason = validate_rule_ranges(new_config)
- print(is_range_valid, range_reason)
|