change
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from cadfs_to_cdsl.describe import describe_samples, select_description_samples
|
||||
|
||||
|
||||
SAMPLE_FS = r'''
|
||||
FeatureScript 1511;
|
||||
import(path : "onshape/std/geometry.fs", version : "1511.0");
|
||||
const mm = millimeter;
|
||||
const FACE = EntityType.FACE;
|
||||
function v(x, y){return vector(x, y);}
|
||||
function sQuery(a, b, c) {return sketchEntityQuery(a, b, c);}
|
||||
export const myFeature = defineFeature(function(context is Context, id is Id, definition is map)
|
||||
precondition{}
|
||||
{
|
||||
{
|
||||
var Q0;
|
||||
Q0=qCreatedBy(makeId("Top.planeOp"),FACE);
|
||||
var sketch = newSketch(context, id + "F0", { "sketchPlane" : qUnion([Q0])});
|
||||
skLineSegment(sketch, "E0", {"start": v(-269.41, -156.6) * mm, "end": v(-0.92, 311.62) * mm});
|
||||
skLineSegment(sketch, "E1", {"start": v(-0.92, 311.62) * mm, "end": v(270.33, -155.02) * mm});
|
||||
skLineSegment(sketch, "E2", {"start": v(270.33, -155.02) * mm, "end": v(-269.41, -156.6) * mm});
|
||||
skSolve(sketch);
|
||||
}
|
||||
{
|
||||
var Q0;
|
||||
Q0 = qSketchRegion(id + "F0", true);
|
||||
extrude(context, id + "F1", {"entities" : qUnion([Q0]), "depth" : 1828.8 * mm});
|
||||
}
|
||||
});
|
||||
'''
|
||||
|
||||
|
||||
SAMPLE_ANNOTATION = """Step 1 - Sketch
|
||||
Draw a closed triangle.
|
||||
|
||||
Step 2 - Extrude NEW
|
||||
Extrude the triangular area upward a distance of 1828.8 mm.
|
||||
"""
|
||||
|
||||
|
||||
class FailingVision:
|
||||
def describe(self, *, sample_id: str, image_path: Path, local_facts: dict[str, object]) -> dict[str, object]:
|
||||
raise RuntimeError("vision offline")
|
||||
|
||||
|
||||
class StaticVision:
|
||||
def describe(self, *, sample_id: str, image_path: Path, local_facts: dict[str, object]) -> dict[str, object]:
|
||||
return {
|
||||
"category": "视觉增强三角楔块(候选)",
|
||||
"category_confidence": 0.7,
|
||||
"candidate_names": ["视觉楔块"],
|
||||
"summary_zh": "视觉模型认为该模型是三角楔块候选件。",
|
||||
"possible_functions": ["可能用于定位。"],
|
||||
"applications": ["夹具。"],
|
||||
"structural_features": ["斜面"],
|
||||
"geometric_features": ["三角截面"],
|
||||
"keywords_zh": ["视觉识别"],
|
||||
"keywords_en": ["vision wedge"],
|
||||
"uncertainties": ["视觉语义仍需人工确认。"],
|
||||
}
|
||||
|
||||
|
||||
class DescribeTests(unittest.TestCase):
|
||||
def test_selects_only_requested_shard(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
self._write_sample(root, "0005", "00050089")
|
||||
self._write_sample(root, "0006", "00060001")
|
||||
|
||||
selected = select_description_samples(root, shard="0005")
|
||||
|
||||
self.assertEqual([sample.sample_id for sample in selected], ["00050089"])
|
||||
|
||||
def test_hybrid_falls_back_and_writes_txt_manifest(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
self._write_sample(root, "0005", "00050089")
|
||||
|
||||
records, summary = describe_samples(root, shard="0005", mode="hybrid", vision_client=FailingVision())
|
||||
|
||||
self.assertEqual(summary["sample_count"], 1)
|
||||
self.assertEqual(summary["statuses"], {"local_fallback": 1})
|
||||
txt_path = root / "description_txt/0005/00050089.txt"
|
||||
self.assertTrue(txt_path.is_file())
|
||||
text = txt_path.read_text(encoding="utf-8")
|
||||
self.assertIn("样本ID:00050089", text)
|
||||
self.assertIn("三角棱柱", text)
|
||||
self.assertIn("证据与不确定性", text)
|
||||
manifest = root / "description_txt/description_manifest.jsonl"
|
||||
rows = [json.loads(line) for line in manifest.read_text(encoding="utf-8").splitlines()]
|
||||
self.assertEqual(len(rows), 1)
|
||||
self.assertEqual(rows[0]["sample_id"], "00050089")
|
||||
self.assertEqual(rows[0]["status"], "local_fallback")
|
||||
self.assertIn("vision_failed", [item["code"] for item in records[0]["diagnostics"]])
|
||||
|
||||
def test_hybrid_uses_vision_when_available(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
self._write_sample(root, "0005", "00050089")
|
||||
|
||||
records, _summary = describe_samples(root, shard="0005", mode="hybrid", vision_client=StaticVision())
|
||||
|
||||
self.assertEqual(records[0]["status"], "described_hybrid")
|
||||
self.assertEqual(records[0]["category"], "视觉增强三角楔块(候选)")
|
||||
self.assertIn("视觉楔块", records[0]["candidate_names"])
|
||||
|
||||
def test_existing_txt_is_skipped_without_force(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
root = Path(tmp)
|
||||
self._write_sample(root, "0005", "00050089")
|
||||
first, _ = describe_samples(root, shard="0005", mode="local")
|
||||
txt_path = root / "description_txt/0005/00050089.txt"
|
||||
before = txt_path.read_text(encoding="utf-8")
|
||||
|
||||
second, _ = describe_samples(root, shard="0005", mode="local")
|
||||
|
||||
self.assertEqual(second[0]["status"], first[0]["status"])
|
||||
self.assertEqual(txt_path.read_text(encoding="utf-8"), before)
|
||||
|
||||
@staticmethod
|
||||
def _write_sample(root: Path, shard: str, sample_id: str) -> None:
|
||||
for directory, suffix, content in (
|
||||
("featurescript_rp", ".txt", SAMPLE_FS),
|
||||
("text_annotations", ".txt", SAMPLE_ANNOTATION),
|
||||
("step_abc", ".step", "ISO-10303-21;\nEND-ISO-10303-21;\n"),
|
||||
("stl_abc", ".stl", "solid sample\nendsolid sample\n"),
|
||||
):
|
||||
path = root / directory / shard / f"{sample_id}{suffix}"
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(content, encoding="utf-8")
|
||||
image = root / "multiview_images_abc" / shard / f"{sample_id}.png"
|
||||
image.parent.mkdir(parents=True, exist_ok=True)
|
||||
image.write_bytes(
|
||||
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR"
|
||||
b"\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00"
|
||||
b"\x90wS\xde\x00\x00\x00\x00IEND\xaeB`\x82"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user