Files
Mimikko-zeus de2d9d6f02 feat(blog): add comprehensive blog review and publishing workflow system
- Implemented calc_audit.py script for verifying arithmetic expressions in blog calculations
- Added humanizer-style-guide.md with guidelines for removing AI-sounding language from Chinese blogs
- Created openai.yaml configuration for blog review publish workflow interface
- Established publishing-preview-checklist.md for pre/post publication verification
- Developed 12-dimension review rubric for systematic article evaluation
- Built complete SKILL.md documentation covering blog writing, reviewing, and publishing workflows
- Added source-rights-policy.md for copyright and attribution guidelines
- Defined hard gates and quality standards for publication approval process
- Implemented multi-mode workflow selection based on task requirements
- Created standardized output templates for draft delivery and review reports
2026-05-12 18:54:12 +08:00

116 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Evaluate arithmetic expressions for blog calculation audits.
Input JSON format:
[
{
"id": "1",
"claim": "monthly cost is 12.50",
"expression": "0.25 * 50",
"expected": 12.5,
"tolerance": 0.000001,
"note": "unit: usd/month"
}
]
The script intentionally supports arithmetic only, not arbitrary Python code.
"""
from __future__ import annotations
import ast
import json
import math
import operator
import sys
from pathlib import Path
from typing import Any
ALLOWED_BINOPS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod,
ast.Pow: operator.pow,
}
ALLOWED_UNARYOPS = {ast.UAdd: operator.pos, ast.USub: operator.neg}
def eval_expr(expr: str) -> float:
node = ast.parse(expr, mode="eval")
def walk(n: ast.AST) -> float:
if isinstance(n, ast.Expression):
return walk(n.body)
if isinstance(n, ast.Constant) and isinstance(n.value, (int, float)):
return float(n.value)
if isinstance(n, ast.BinOp) and type(n.op) in ALLOWED_BINOPS:
return float(ALLOWED_BINOPS[type(n.op)](walk(n.left), walk(n.right)))
if isinstance(n, ast.UnaryOp) and type(n.op) in ALLOWED_UNARYOPS:
return float(ALLOWED_UNARYOPS[type(n.op)](walk(n.operand)))
raise ValueError(f"unsupported expression element: {ast.dump(n)}")
return walk(node)
def fmt(value: Any) -> str:
if value is None:
return ""
if isinstance(value, float):
if math.isfinite(value):
return f"{value:.12g}"
return str(value)
def main() -> int:
if len(sys.argv) != 2:
print("usage: calc_audit.py calculations.json", file=sys.stderr)
return 2
path = Path(sys.argv[1])
items = json.loads(path.read_text(encoding="utf-8"))
if not isinstance(items, list):
print("input must be a json list", file=sys.stderr)
return 2
rows = []
for item in items:
if not isinstance(item, dict):
raise ValueError("each item must be an object")
result = eval_expr(str(item["expression"]))
expected = item.get("expected")
tolerance = float(item.get("tolerance", 1e-9))
if expected is None:
status = "computed"
delta = None
else:
expected_float = float(expected)
delta = result - expected_float
status = "pass" if abs(delta) <= tolerance else "fail"
rows.append(
[
str(item.get("id", "")),
str(item.get("claim", "")),
str(item.get("expression", "")),
fmt(result),
fmt(expected),
fmt(delta),
status,
str(item.get("note", "")),
]
)
headers = ["id", "claim", "expression", "result", "expected", "delta", "status", "note"]
print("| " + " | ".join(headers) + " |")
print("|" + "|".join(["---"] * len(headers)) + "|")
for row in rows:
safe = [cell.replace("|", "\\|").replace("\n", " ") for cell in row]
print("| " + " | ".join(safe) + " |")
return 0
if __name__ == "__main__":
raise SystemExit(main())