from fastapi import APIRouter, HTTPException from pydantic import BaseModel, validator from app.services.speckle_service import SpeckleService from specklepy.objects import Base from datetime import datetime from typing import List, Any import json class GenericPayload(BaseModel): payload: str @validator("payload") def validate_payload(cls, value): # Parse the JSON string to ensure it's valid try: parsed = json.loads(value) # You can add additional validation here if needed except json.JSONDecodeError: raise ValueError("payload must be a valid JSON string") return value def get_data(self): return json.loads(self.payload) router = APIRouter() def extract_value(obj: Any, key: str) -> Any: """Recursively search for the value of a given key in nested objects""" print(f"Extracting value for key: {key} from object: {obj}") if isinstance(obj, dict): for k, v in obj.items(): print(f"Checking key: {k}, value: {v}") if k == key: print(f"Found key: {k}, returning value: {v}") return v elif isinstance(v, (dict, list)): result = extract_value(v, key) if result is not None: return result elif isinstance(obj, list): for item in obj: result = extract_value(item, key) if result is not None: return result return None @router.post("/createDistanceMatrix") async def create_distance_matrix(payload: GenericPayload): try: data = payload.get_data() # Extract necessary fields from the payload speckle_input_graph = extract_value(data, 'speckleInput_Graph') speckle_input_buildings = extract_value(data, 'speckleInput_Buildings') speckle_output_distance_matrix = extract_value(data, 'speckleOutput_DistanceMatrix') token = extract_value(data, 'token') modal_mode = extract_value(data, 'modal_mode') # Step 1: Create the client client = SpeckleService.create_client(base_url="https://speckle.xyz", token=token) # Step 2: Parse the Speckle URLs to get the components graph_speckle_url_info = SpeckleService.parse_speckle_url(speckle_input_graph) graph_stream_id = graph_speckle_url_info["streamID"] graph_branch_name = graph_speckle_url_info["branchName"] buildings_speckle_url_info = SpeckleService.parse_speckle_url(speckle_input_buildings) buildings_stream_id = buildings_speckle_url_info["streamID"] buildings_branch_name = buildings_speckle_url_info["branchName"] output_speckle_url_info = SpeckleService.parse_speckle_url(speckle_output_distance_matrix) output_stream_id = output_speckle_url_info["streamID"] output_branch_name = output_speckle_url_info["branchName"] # Step 3: Fetch the Speckle branches graph_data, graph_commit_id = SpeckleService.get_speckle_stream(graph_stream_id, graph_branch_name, client) buildings_data, buildings_commit_id = SpeckleService.get_speckle_stream(buildings_stream_id, buildings_branch_name, client) # Step 4: Generate metadata metadata = SpeckleService.generate_metadata([speckle_input_graph, speckle_input_buildings], token) # Step 5: Create a dummy Speckle object dummy_object = Base() dummy_object["IWasCreatedBy"] = "createDistanceMatrix" dummy_object["ModalMode"] = modal_mode dummy_object["MetaData"] = metadata # Step 6: Update the Speckle stream new_commit_id = SpeckleService.update_speckle_stream(output_stream_id, output_branch_name, client, dummy_object) # Step 7: Create the report using metadata current_time = datetime.utcnow().isoformat() sources = [ { "streamID": graph_stream_id, "branchName": graph_branch_name, "commitID": graph_commit_id, "time": current_time }, { "streamID": buildings_stream_id, "branchName": buildings_branch_name, "commitID": buildings_commit_id, "time": current_time } ] targets = [ { "streamID": output_stream_id, "branchName": output_branch_name, "commitID": new_commit_id, "time": current_time } ] report = { "method": "createDistanceMatrix", "sources": sources, "targets": targets } return report except Exception as e: raise HTTPException(status_code=500, detail=str(e))