CadHubManage/app/core/cad_task_router.py
sladro b19ef1467a feat: Implement CAD batch processing framework with plugin callback handling
- Added configuration for file storage and software plugins in `software_config.yaml`.
- Created core components for batch processing including `CadBatchManager`, `CadTaskRouter`, and `SerialBatchExecutor`.
- Implemented plugin callback handling with `PluginCallbackRegistry` and HTTP client for task submission.
- Developed API endpoint for receiving plugin callbacks in `plugin_callbacks.py`.
- Enhanced data models for batch processing including `BatchJob`, `BatchItem`, and callback payloads.
- Introduced WebSocket support for real-time updates on batch processing status.
- Added comprehensive tests for routing, callback API, and serial executor behavior.
- Documented the implementation plan and core execution rules in `cad-batch-plan.md`.
2026-03-01 08:48:10 +08:00

57 lines
1.9 KiB
Python

from __future__ import annotations
import re
from pathlib import Path
from typing import Dict, Optional, Tuple
from app.config import software_config
class RouteNotFoundError(ValueError):
pass
class CadTaskRouter:
"""Route model file path to software_id based on file extension."""
def __init__(self, extension_to_software: Optional[Dict[str, str]] = None):
self._extension_to_software = self._normalize_mapping(
extension_to_software or software_config.get_extension_routing()
)
@staticmethod
def _normalize_mapping(mapping: Dict[str, str]) -> Dict[str, str]:
normalized: Dict[str, str] = {}
for ext, software_id in mapping.items():
if not ext:
continue
normalized_ext = ext.lower().strip()
if not normalized_ext.startswith("."):
normalized_ext = f".{normalized_ext}"
normalized[normalized_ext] = software_id
return normalized
@staticmethod
def _normalize_extension(model_path: str, configured_ext: str) -> Optional[str]:
lower_name = Path(model_path).name.lower()
if lower_name.endswith(configured_ext):
return configured_ext
# Support versioned suffixes such as .prt.1/.asm.2
version_pattern = rf"{re.escape(configured_ext)}\.\d+$"
if re.search(version_pattern, lower_name):
return configured_ext
return None
def resolve(self, model_path: str) -> Tuple[str, str]:
if not model_path:
raise RouteNotFoundError("model_path is empty")
for configured_ext in sorted(self._extension_to_software.keys(), key=len, reverse=True):
normalized_ext = self._normalize_extension(model_path, configured_ext)
if normalized_ext:
return normalized_ext, self._extension_to_software[configured_ext]
raise RouteNotFoundError(f"No software route found for model path: {model_path}")