chore: import existing codebase as baseline
Verify / verify (push) Failing after 1m20s

This commit is contained in:
Jayway Liu
2026-09-26 01:12:10 +08:00
commit 0ac0c4eb56
77 changed files with 14155 additions and 0 deletions
+246
View File
@@ -0,0 +1,246 @@
import json
from decimal import Decimal
from io import BytesIO
from unittest.mock import patch
import pytest
from django.contrib.auth.models import User
from openpyxl import load_workbook
from finance.exporting import build_formula_workbook
from finance.formula_columns import validate_column
from finance.formula_views import design_column
from finance.models import Dataset, Fact, Membership, Organization
pytestmark = pytest.mark.django_db
@pytest.fixture
def formula_data():
org = Organization.objects.create(name="Formula tests")
user = User.objects.create_user("formula", password="Formula-TestPassword991!")
member = Membership.objects.create(user=user, organization=org, role="admin")
mapping = {
"sheet": "CSV",
"columns": [
{"index": 0, "name": "Period", "role": "period"},
{"index": 1, "name": "Department", "role": "dimension", "key": "department"},
{"index": 2, "name": "Revenue", "role": "revenue"},
{"index": 3, "name": "Cost", "role": "cost"},
],
"roi_formula": "profit_over_cost",
}
ds = Dataset.objects.create(
organization=org,
created_by=user,
name="formula.csv",
sha256="b" * 64,
status="ready",
mapping=mapping,
periods=["2025-08", "2026-08"],
)
for period, revenue, cost in (("2025-08", 80, 30), ("2026-08", 100, 40)):
Fact.objects.create(
dataset=ds,
period=period,
dimensions={"department": "儿科"},
revenue=Decimal(revenue),
cost=Decimal(cost),
gross=Decimal(revenue - cost),
customer=f"{period}-patient",
source_row=1 if period == "2025-08" else 2,
raw=[period, "儿科", revenue, cost],
)
return ds, member
def custom_column():
return {
"name": "收入成本差",
"meaning": "实际收入减成本",
"scope": "按所选分组及本期、基期计算",
"missing": "结果不可用(#N/A);零分母不可用",
"expression": {
"operator": "subtract",
"left": {"metric": "revenue"},
"right": {"metric": "cost"},
},
}
def test_formula_workbook_exposes_custom_metric_as_recalculable_column(formula_data):
ds, member = formula_data
output = BytesIO()
build_formula_workbook(
ds,
member,
{
"start": "2026-08",
"end": "2026-08",
"comparison": "yoy",
"group": "department",
"custom_columns": [custom_column()],
},
output,
)
workbook = load_workbook(BytesIO(output.getvalue()), data_only=False)
assert workbook["计算明细"]["M2"].value == '=IF(H2="direct",F2,IF(H2="lookup",D2*G2,""))'
assert workbook["计算明细"]["I2"].data_type == "n"
summary = workbook["分析汇总"]
metric_row = next(
row for row in range(6, summary.max_row + 1) if summary.cell(row, 1).value == "收入成本差"
)
formula = summary.cell(metric_row, 3).value
assert formula.startswith("=IF(OR(ISNA(C")
assert "IFERROR((C" in formula and "NA())" in formula
assert "自定义指标" in workbook.sheetnames
custom = workbook["自定义指标"]
assert custom["B1"].value == "收入成本差(本期)"
assert custom["B2"].value == f"='分析汇总'!C{metric_row}"
assert "含义:实际收入减成本" in workbook["使用说明"]["B12"].value
assert "范围:按所选分组及本期、基期计算" in workbook["使用说明"]["B12"].value
def test_custom_expression_rejects_formula_text_and_unknown_metrics():
from finance.formula_columns import metrics_used, to_excel
expression = {
"operator": "divide",
"left": {"metric": "revenue"},
"right": {
"operator": "add",
"left": {"metric": "cost"},
"right": {"number": 2.5},
},
}
validate_column({**custom_column(), "expression": expression})
assert metrics_used(expression) == {"revenue", "cost"}
assert to_excel(expression, {"revenue": "C6", "cost": "D6"}) == "(C6/(D6+2.5))"
column = custom_column()
column["expression"] = {"metric": "__import__('os')"}
with pytest.raises(ValueError, match="不支持的指标"):
validate_column(column)
column = custom_column()
column["expression"] = {"formula": "=HYPERLINK(...)"}
with pytest.raises(ValueError, match="不支持的字段或运算"):
validate_column(column)
def test_design_column_preserves_clarification_branch_and_full_conversation(formula_data, settings):
ds, _ = formula_data
settings.AI_ENABLED = True
settings.OPENAI_API_KEY = "test-key"
settings.OPENAI_MODEL = "test-model"
clarification = {
"status": "completed",
"output": [
{
"content": [
{
"type": "output_text",
"text": '{"complete":false,"questions":["请说明零分母如何处理。"],"column":null}',
}
]
}
],
}
with patch("finance.formula_views.httpx.Client") as http:
http.return_value.__enter__.return_value.post.return_value = type(
"Response",
(),
{"raise_for_status": lambda self: None, "json": lambda self: clarification},
)()
incomplete = design_column(
[{"role": "user", "content": "新增利润率列"}],
ds,
{"current": ["2026-08"], "baseline": ["2025-08"], "filters": {}, "group": "department"},
)
assert incomplete == {
"complete": False,
"questions": ["请说明零分母如何处理。"],
"column": None,
}
class Response:
def raise_for_status(self):
pass
def json(self):
return {
"status": "completed",
"output": [
{
"content": [
{
"type": "output_text",
"text": '{"complete":false,"questions":["零分母如何处理?"],"column":null}',
}
]
}
],
}
messages = [
{"role": "user", "content": "增加收入成本差列"},
{"role": "assistant", "content": "请说明计算方式和缺失值处理。"},
{"role": "user", "content": "收入减成本,缺失时不可用"},
]
with patch("finance.formula_views.httpx.Client") as client:
client.return_value.__enter__.return_value.post.return_value = Response()
result = design_column(
messages,
ds,
{"current": ["2026-08"], "baseline": ["2025-08"], "filters": {}, "group": "department"},
)
assert result == {"complete": False, "questions": ["零分母如何处理?"], "column": None}
payload = client.return_value.__enter__.return_value.post.call_args.kwargs["json"]
assert payload["input"][1:] == messages
def test_design_column_returns_only_validated_formula_definition(formula_data, settings):
ds, _ = formula_data
settings.AI_ENABLED = True
settings.OPENAI_API_KEY = "test-key"
settings.OPENAI_MODEL = "test-model"
definition = custom_column()
response = type(
"Response",
(),
{
"raise_for_status": lambda self: None,
"json": lambda self: {
"status": "completed",
"output": [
{
"content": [
{
"type": "output_text",
"text": json.dumps(
{"complete": True, "questions": [], "column": definition},
ensure_ascii=False,
),
}
]
}
],
},
},
)()
with patch("finance.formula_views.httpx.Client") as http:
http.return_value.__enter__.return_value.post.return_value = response
result = design_column(
[
{"role": "user", "content": "增加收入成本差列"},
{"role": "assistant", "content": "请补充计算口径"},
{"role": "user", "content": "收入减成本,按分组选期间计算"},
],
ds,
{"current": ["2026-08"], "baseline": ["2025-08"], "filters": {}, "group": "department"},
)
assert result == {"complete": True, "questions": [], "column": definition}
payload = http.return_value.__enter__.return_value.post.call_args.kwargs["json"]
assert payload["store"] is False
assert payload["text"]["format"]["strict"] is True