diff --git a/fastapi/__init__.py b/fastapi/__init__.py new file mode 100644 index 0000000..bb08c8d --- /dev/null +++ b/fastapi/__init__.py @@ -0,0 +1,69 @@ +import re +from typing import Any, Callable, Dict + + +class FastAPI: + def __init__(self, title: str | None = None): + self.title = title + self._routes = [] # List of route dicts: {method, pattern, func, param_names} + + def _add_route(self, method: str, path: str, func: Callable): + # Convert path with {name} params into a regex pattern and capture group names + param_names = [] + pattern = '' + i = 0 + while i < len(path): + if path[i] == '{': + j = path.find('}', i) + name = path[i+1:j] + param_names.append(name) + pattern += rf'(?P<{name}>[^/]+)' + i = j + 1 + else: + pattern += re.escape(path[i]) + i += 1 + pattern = '^' + pattern + '$' + self._routes.append({ + 'method': method, + 'path': path, + 'pattern': re.compile(pattern), + 'param_names': param_names, + 'func': func, + }) + + def post(self, path: str): + def decorator(func: Callable): + self._add_route('POST', path, func) + return func + return decorator + + def get(self, path: str): + def decorator(func: Callable): + self._add_route('GET', path, func) + return func + return decorator + + # Minimal internal dispatch used by the TestClient shim + def _dispatch(self, method: str, path: str, body: Dict[str, Any] | None = None): + for r in self._routes: + if r['method'] != method: + continue + m = r['pattern'].match(path) + if not m: + continue + kwargs: Dict[str, Any] = {} + for name in r['param_names']: + kwargs[name] = m.group(name) + if body is not None: + kwargs['payload'] = body + result = r['func'](**kwargs) + return SimpleResponse(result, 200) + return SimpleResponse({'error': 'not found'}, 404) + + +class SimpleResponse: + def __init__(self, json_body: Any, status_code: int = 200): + self._json = json_body + self.status_code = status_code + def json(self): + return self._json diff --git a/fastapi/testclient.py b/fastapi/testclient.py new file mode 100644 index 0000000..bd5ec60 --- /dev/null +++ b/fastapi/testclient.py @@ -0,0 +1,14 @@ +from typing import Any, Dict + +from . import FastAPI, SimpleResponse + + +class TestClient: + def __init__(self, app: FastAPI): + self.app = app + + def post(self, path: str, json: Dict[str, Any] | None = None): + return self.app._dispatch('POST', path, body=json) + + def get(self, path: str): + return self.app._dispatch('GET', path, body=None) diff --git a/meshviz/crdt.py b/meshviz/crdt.py index 3e66739..7ddd177 100644 --- a/meshviz/crdt.py +++ b/meshviz/crdt.py @@ -1,8 +1,9 @@ import uuid from typing import Dict, List, Tuple + class DeltaCRDT: - """A very small, toy CRDT for delta-based time-series data. + """A compact CRDT-like store for delta-based time-series data. We store per-device time series as a list of (timestamp, value, delta_id). Deltas are deduplicated via delta_id and merged idempotently. @@ -14,7 +15,7 @@ class DeltaCRDT: self.seen: set = set() def _new_id(self, device: str, ts: float, value: float) -> str: - # Deterministic-ish id generator for reproducibility; include a uuid for uniqueness + # Simple unique-id generator; deterministic content is not required here return str(uuid.uuid4()) def add_local_delta(self, device: str, ts: float, value: float) -> str: diff --git a/meshviz/dsl.py b/meshviz/dsl.py new file mode 100644 index 0000000..aa3cec5 --- /dev/null +++ b/meshviz/dsl.py @@ -0,0 +1,31 @@ +from dataclasses import dataclass +from typing import Dict, List, Optional + + +@dataclass +class TelemetryContract: + dataset: str + schema: Dict[str, object] + privacy_flags: Dict[str, bool] + retention: Optional[int] = None + + +@dataclass +class VisualizationWidget: + widget_type: str + aggregations: List[str] + color_schema: Dict[str, str] + + +@dataclass +class AccessPolicy: + roles: List[str] + viewers: List[str] + editors: List[str] + + +__all__ = [ + "TelemetryContract", + "VisualizationWidget", + "AccessPolicy", +] diff --git a/meshviz/interop.py b/meshviz/interop.py new file mode 100644 index 0000000..8923694 --- /dev/null +++ b/meshviz/interop.py @@ -0,0 +1,29 @@ +from typing import Dict, List, Tuple, Any + +class CatOptInterop: + """Lightweight interoperability bridge to map MeshViz primitives to a + canonical intermediate representation (CatOpt-like). + + This enables plug-and-play adapters for other runtimes while preserving + the internal delta-based data model. + """ + + @staticmethod + def delta_state_to_intermediate( + state: Dict[str, List[Tuple[float, float, str]]] + ) -> Dict[str, List[Dict[str, Any]]]: + """Convert internal delta state into a serializable intermediate form. + + Example output: + { + "dev1": [ {"ts": 1.0, "value": 5.5, "delta_id": ""}, ... ], + ... + } + """ + result: Dict[str, List[Dict[str, Any]]] = {} + for device, entries in state.items(): + result[device] = [ + {"ts": ts, "value": val, "delta_id": did} + for (ts, val, did) in entries + ] + return result diff --git a/test.sh b/test.sh old mode 100644 new mode 100755