Expose product length calculation through MCP
This commit is contained in:
@@ -63,6 +63,27 @@ class CalculationApiTests(unittest.TestCase):
|
||||
self.assertEqual(result["state"]["request"]["roll_length_m"], 80.0)
|
||||
self.assertEqual(result["calculation"]["effective_roll_length_m"], 80.0)
|
||||
|
||||
def test_product_length_endpoint_uses_the_domain_calculation(self):
|
||||
response = self.client.post(
|
||||
"/api/calculations/product-length",
|
||||
json={
|
||||
"target_roll_diameter_mm": 600,
|
||||
"core_diameter_mm": 140,
|
||||
"thickness_mm": 4.311611,
|
||||
"thickness_stddev_mm": 0.249999,
|
||||
"width_m": 6.0,
|
||||
"area_weight_g_m2": 495.395833,
|
||||
},
|
||||
headers=self.headers,
|
||||
)
|
||||
result = response.get_json()
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(result["status"], "success")
|
||||
self.assertAlmostEqual(
|
||||
result["calculation"]["average_product_length_m"], 62.0068774341
|
||||
)
|
||||
|
||||
def test_transport_endpoint_delegates_to_shared_domain_service(self):
|
||||
payload = {
|
||||
"length_m": 10,
|
||||
|
||||
@@ -8,12 +8,14 @@ import mcp_server
|
||||
from roll_calculation import (
|
||||
ArticleRepository,
|
||||
calculate_material_weight,
|
||||
calculate_product_length,
|
||||
calculate_roll,
|
||||
get_article,
|
||||
)
|
||||
from rollcalc_mcp_tools import (
|
||||
analyze_transport_capacity_result,
|
||||
calculate_material_weight_result,
|
||||
calculate_product_length_result,
|
||||
calculate_roll_diameter_result,
|
||||
get_article_result,
|
||||
search_articles_result,
|
||||
@@ -87,6 +89,23 @@ class McpAdapterTests(unittest.TestCase):
|
||||
self.assertEqual(result["weight_scope"], "material_only")
|
||||
self.assertAlmostEqual(result["material_weight_kg"], 1455.0239028)
|
||||
|
||||
def test_product_length_adapter_delegates_with_article_218500_values(self):
|
||||
arguments = {
|
||||
"target_roll_diameter_mm": 600.0,
|
||||
"core_diameter_mm": 140.0,
|
||||
"thickness_mm": 4.311611,
|
||||
"thickness_stddev_mm": 0.249999,
|
||||
"width_m": 6.0,
|
||||
"area_weight_g_m2": 495.395833,
|
||||
}
|
||||
|
||||
result = calculate_product_length_result(**arguments)
|
||||
|
||||
self.assertEqual(result, calculate_product_length(**arguments))
|
||||
self.assertAlmostEqual(
|
||||
result["calculation"]["average_product_length_m"], 62.0068774341
|
||||
)
|
||||
|
||||
def test_calculate_roll_diameter_delegates_for_normal_weighted_and_invalid_cases(self):
|
||||
for arguments in (
|
||||
{
|
||||
@@ -178,6 +197,7 @@ class McpAdapterTests(unittest.TestCase):
|
||||
"get_article",
|
||||
"search_articles",
|
||||
"calculate_material_weight",
|
||||
"calculate_product_length",
|
||||
"calculate_roll_diameter",
|
||||
"analyze_transport_capacity",
|
||||
],
|
||||
@@ -188,6 +208,7 @@ class McpAdapterTests(unittest.TestCase):
|
||||
tools = instance._tool_manager._tools
|
||||
search_tool = tools["search_articles"]
|
||||
material_weight_tool = tools["calculate_material_weight"]
|
||||
product_length_tool = tools["calculate_product_length"]
|
||||
roll_schema = tools["calculate_roll_diameter"].parameters
|
||||
transport_tool = tools["analyze_transport_capacity"]
|
||||
transport_schema = transport_tool.parameters
|
||||
@@ -202,6 +223,19 @@ class McpAdapterTests(unittest.TestCase):
|
||||
["roll_length_m", "width_m", "area_weight_g_m2"],
|
||||
)
|
||||
self.assertNotIn("core_diameter_mm", material_weight_schema["properties"])
|
||||
product_length_schema = product_length_tool.parameters
|
||||
self.assertEqual(
|
||||
product_length_schema["required"],
|
||||
[
|
||||
"target_roll_diameter_mm",
|
||||
"core_diameter_mm",
|
||||
"thickness_mm",
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
product_length_schema["properties"]["target_roll_diameter_mm"]["type"],
|
||||
"number",
|
||||
)
|
||||
|
||||
self.assertNotIn("include_roll_weight", roll_schema["properties"])
|
||||
preset_schema = transport_schema["properties"]["transport_preset"]
|
||||
@@ -294,6 +328,16 @@ class McpAdapterTests(unittest.TestCase):
|
||||
self.assertIn("do not infer or default one", roll_description)
|
||||
self.assertIn("call calculate_material_weight instead", roll_description)
|
||||
|
||||
product_length_description = product_length_tool.description
|
||||
for text in (
|
||||
"required product/roll length for a target outer roll diameter",
|
||||
"do not infer a missing core diameter, thickness, width, area weight, or article",
|
||||
"minimum, average, and maximum length ranges",
|
||||
"material-weight ranges",
|
||||
"use calculate_roll_diameter for the reverse direction",
|
||||
):
|
||||
self.assertIn(text, product_length_description)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -9,6 +9,7 @@ from roll_calculation import (
|
||||
_canonical_name,
|
||||
article_production_site,
|
||||
calculate_material_weight,
|
||||
calculate_product_length,
|
||||
calculate_roll,
|
||||
get_article,
|
||||
modify_calculation,
|
||||
@@ -212,6 +213,37 @@ class RollCalculationTests(unittest.TestCase):
|
||||
item["field"] for item in result["invalid"]
|
||||
])
|
||||
|
||||
def test_product_length_characterizes_article_218500_with_ranges_and_weight(self):
|
||||
result = calculate_product_length(
|
||||
target_roll_diameter_mm=600,
|
||||
core_diameter_mm=140,
|
||||
thickness_mm=4.311611,
|
||||
thickness_stddev_mm=0.249999,
|
||||
width_m=6.0,
|
||||
area_weight_g_m2=495.395833,
|
||||
)
|
||||
|
||||
self.assertEqual(result["status"], "success")
|
||||
calculation = result["calculation"]
|
||||
self.assertAlmostEqual(calculation["minimum_product_length_m"], 55.5634372661)
|
||||
self.assertAlmostEqual(calculation["average_product_length_m"], 62.0068774341)
|
||||
self.assertAlmostEqual(calculation["maximum_product_length_m"], 70.1407868061)
|
||||
self.assertAlmostEqual(
|
||||
calculation["material_weights_kg"]["average_kg"], 184.3076921892
|
||||
)
|
||||
|
||||
def test_product_length_rejects_target_diameter_not_larger_than_core(self):
|
||||
result = calculate_product_length(
|
||||
target_roll_diameter_mm=140,
|
||||
core_diameter_mm=140,
|
||||
thickness_mm=4,
|
||||
)
|
||||
|
||||
self.assertEqual(result["status"], "invalid_parameter")
|
||||
self.assertEqual(
|
||||
result["invalid"][0]["field"], "target_roll_diameter_mm"
|
||||
)
|
||||
|
||||
def test_diameter_only_calculation_does_not_require_width(self):
|
||||
result = self.calculate_fixture(
|
||||
{
|
||||
|
||||
@@ -17,6 +17,11 @@ class TemplatePdfIntegrationTests(unittest.TestCase):
|
||||
self.assertIn("fetch('/api/calculations/roll'", self.template)
|
||||
self.assertIn("calculation.average_diameter_mm", self.template)
|
||||
self.assertIn("calculation.effective_roll_length_m", self.template)
|
||||
|
||||
def test_product_length_ui_uses_the_shared_domain_endpoint(self):
|
||||
self.assertIn("fetch('/api/calculations/product-length'", self.template)
|
||||
self.assertIn("calculation.average_product_length_m", self.template)
|
||||
self.assertNotIn("const L_calc = (Math.PI / (4 * t))", self.template)
|
||||
self.assertNotIn("function calculateDiameterResult", self.template)
|
||||
|
||||
def test_optional_weight_requires_both_weight_inputs(self):
|
||||
|
||||
Reference in New Issue
Block a user