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
This commit is contained in:
115
scripts/calc_audit.py
Normal file
115
scripts/calc_audit.py
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/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())
|
||||
Reference in New Issue
Block a user