From 466e60c5538c346d2f2ff5314188daeda399d67f Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Thu, 14 Nov 2019 10:24:22 +0100 Subject: [PATCH 01/20] Update commands and add file open dialog to import command - (Task #5) - Fixed naming in the dialogs - Switched command positions in InitGui - Added file open dialog with JSON filter to import command --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/InitGui.py | 4 ++-- .../commands/command_export.py | 10 ++++---- .../commands/command_import.py | 23 +++++++++++++++---- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/VirtualSatelliteCAD/InitGui.py b/VirtualSatelliteCAD/InitGui.py index ff4ccea..9dc35be 100644 --- a/VirtualSatelliteCAD/InitGui.py +++ b/VirtualSatelliteCAD/InitGui.py @@ -45,10 +45,10 @@ def Initialize(self): import commands.command_export # NOQA @UnusedImport from commands.command_definitions import COMMAND_ID_EXPORT_2_VIRTUAL_SATELLITE from commands.command_definitions import COMMAND_ID_IMPORT_2_FREECAD - self.appendToolbar('VirtualSatelliteMod', [COMMAND_ID_EXPORT_2_VIRTUAL_SATELLITE]) - self.appendMenu('VirtualSatelliteMod', [COMMAND_ID_EXPORT_2_VIRTUAL_SATELLITE]) self.appendToolbar('VirtualSatelliteMod', [COMMAND_ID_IMPORT_2_FREECAD]) self.appendMenu('VirtualSatelliteMod', [COMMAND_ID_IMPORT_2_FREECAD]) + self.appendToolbar('VirtualSatelliteMod', [COMMAND_ID_EXPORT_2_VIRTUAL_SATELLITE]) + self.appendMenu('VirtualSatelliteMod', [COMMAND_ID_EXPORT_2_VIRTUAL_SATELLITE]) def GetClassName(self): # Required method by FreeCAD framework diff --git a/VirtualSatelliteCAD/commands/command_export.py b/VirtualSatelliteCAD/commands/command_export.py index 24ccddd..5f94ffd 100644 --- a/VirtualSatelliteCAD/commands/command_export.py +++ b/VirtualSatelliteCAD/commands/command_export.py @@ -26,21 +26,21 @@ import FreeCAD import FreeCADGui -from module.environment import Environment, ICON_IMPORT +from module.environment import Environment, ICON_EXPORT from commands.command_definitions import COMMAND_ID_EXPORT_2_VIRTUAL_SATELLITE class CommandExport: def Activated(self): - FreeCAD.Console.PrintMessage("Calling the importer\n") + FreeCAD.Console.PrintMessage("Calling the exporter\n") def IsActive(self): return True def GetResources(self): - return {'Pixmap': Environment().get_icon_path(ICON_IMPORT), - 'MenuText': 'Import from Virtual Satellite', - 'ToolTip': 'Open the dialog for the Virtual Satellite json import.'} + return {'Pixmap': Environment().get_icon_path(ICON_EXPORT), + 'MenuText': 'Export from Virtual Satellite', + 'ToolTip': 'Open the dialog for the Virtual Satellite json export.'} FreeCADGui.addCommand(COMMAND_ID_EXPORT_2_VIRTUAL_SATELLITE, CommandExport()) # @UndefinedVariable diff --git a/VirtualSatelliteCAD/commands/command_import.py b/VirtualSatelliteCAD/commands/command_import.py index 1dc2713..08bab25 100644 --- a/VirtualSatelliteCAD/commands/command_import.py +++ b/VirtualSatelliteCAD/commands/command_import.py @@ -28,19 +28,32 @@ import FreeCADGui from module.environment import Environment, ICON_IMPORT from commands.command_definitions import COMMAND_ID_IMPORT_2_FREECAD +from PySide2.QtWidgets import QFileDialog -class CommandExport: +class CommandImport: def Activated(self): - FreeCAD.Console.PrintMessage("Calling the json_io\n") + FreeCAD.Console.PrintMessage("Calling the importer\n") + + path = FreeCAD.ConfigGet("UserAppData") + + # call pyqt dialog: returns (filename, filter) + filename = QFileDialog.getOpenFileName( + None, # ui parent + "Open JSON file", # dialog caption + path, + "JSON(*.json)")[0] # filter + + if filename != '': + FreeCAD.Console.PrintMessage(f"Successful read file '{filename}'\n") def IsActive(self): return True def GetResources(self): return {'Pixmap': Environment().get_icon_path(ICON_IMPORT), - 'MenuText': 'Export to Virtual Satellite', - 'ToolTip': 'Open the dialog for the Virtual Satellite json export.'} + 'MenuText': 'Import to Virtual Satellite', + 'ToolTip': 'Open the dialog for the Virtual Satellite json import.'} -FreeCADGui.addCommand(COMMAND_ID_IMPORT_2_FREECAD, CommandExport()) # @UndefinedVariable +FreeCADGui.addCommand(COMMAND_ID_IMPORT_2_FREECAD, CommandImport()) # @UndefinedVariable From 4e002993662fb8cb20687a0ca9d63d0c3ffc5231 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Thu, 14 Nov 2019 11:32:21 +0100 Subject: [PATCH 02/20] Add import functionality to CommandImport - (Task #5) Use the JsonImporter in the CommandImport --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/commands/command_import.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/VirtualSatelliteCAD/commands/command_import.py b/VirtualSatelliteCAD/commands/command_import.py index 08bab25..7ebf365 100644 --- a/VirtualSatelliteCAD/commands/command_import.py +++ b/VirtualSatelliteCAD/commands/command_import.py @@ -29,6 +29,11 @@ from module.environment import Environment, ICON_IMPORT from commands.command_definitions import COMMAND_ID_IMPORT_2_FREECAD from PySide2.QtWidgets import QFileDialog +import json +from json_io.json_importer import JsonImporter + +from json_io.json_definitions import get_part_name_uuid +from freecad.active_document import FREECAD_FILE_EXTENSION class CommandImport: @@ -47,6 +52,18 @@ def Activated(self): if filename != '': FreeCAD.Console.PrintMessage(f"Successful read file '{filename}'\n") + with open(filename, 'r') as f: + json_object = json.load(f) + + # TODO: where do we save the created FCstd files? AppData? + # maybe create an subdir in AppData for VirSat stds? + json_importer = JsonImporter(path) + json_importer.create_or_update_part(json_object) + + # TODO: return the path (or at least the name) of the std in create_or_update_part? + test_file_name = path + get_part_name_uuid(json_object) + FREECAD_FILE_EXTENSION + FreeCAD.open(test_file_name) + def IsActive(self): return True From b2ef16c2949bc974499685eb27b2839fbfaa43e8 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Thu, 14 Nov 2019 13:30:25 +0100 Subject: [PATCH 03/20] Refactor importer and add fixture - (Task #5) - Add full import function in JsonImporter - Use that function in CommandImport - Add test function for full import - Add test fixture json file --- Task #5: Implement Import Functionality --- .../Resources/Tests/VisCube2.json | 158 ++++++++++++++++++ .../commands/command_import.py | 13 +- VirtualSatelliteCAD/json_io/json_importer.py | 25 +++ .../test/json_io/test_json_importer.py | 10 ++ 4 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 VirtualSatelliteCAD/Resources/Tests/VisCube2.json diff --git a/VirtualSatelliteCAD/Resources/Tests/VisCube2.json b/VirtualSatelliteCAD/Resources/Tests/VisCube2.json new file mode 100644 index 0000000..bf4ca17 --- /dev/null +++ b/VirtualSatelliteCAD/Resources/Tests/VisCube2.json @@ -0,0 +1,158 @@ +{ + "Products": { + "children": [{ + "posX": 0.0, + "posY": 0.0, + "posZ": 1.0, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Top", + "uuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d56", + "partUuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d56", + "partName": "Top" + }, { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.0, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Bottom", + "uuid": "61db0622-6fef-4f12-932d-a00fdb9d0848", + "partUuid": "00f430a6-6311-4a33-961b-41ded4cf57d5", + "partName": "Plate" + }, { + "posX": 0.5, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 1.5707963267948966, + "name": "Front", + "uuid": "e6af9d3f-8ad6-4488-b3d0-d35549be9a1e", + "partUuid": "e6af9d3f-8ad6-4488-b3d0-d35549be9a1e", + "partName": "Front" + }, { + "posX": -0.5, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 1.5707963267948966, + "name": "Back", + "uuid": "a3c9c547-8fd3-40d5-97a1-a3f9a3a9c337", + "partUuid": "a3c9c547-8fd3-40d5-97a1-a3f9a3a9c337", + "partName": "Back" + }, { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [{ + "posX": 0.0, + "posY": 0.5, + "posZ": 0.0, + "rotX": 1.5707963267948966, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Left", + "uuid": "615985c0-73fd-48db-8f8b-e11b7cbb2ee8", + "partUuid": "615985c0-73fd-48db-8f8b-e11b7cbb2ee8", + "partName": "Left" + }, { + "posX": 0.0, + "posY": -0.5, + "posZ": 0.0, + "rotX": 1.5707963267948966, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Right", + "uuid": "882a0b35-7da8-4555-903d-fd6b5cbec392", + "partUuid": "882a0b35-7da8-4555-903d-fd6b5cbec392", + "partName": "Right" + } + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BeamStructure", + "uuid": "2afb23c9-f458-4bdb-a4e7-fc863364644f", + "partUuid": "2afb23c9-f458-4bdb-a4e7-fc863364644f", + "partName": "BeamStructure" + } + ], + "name": "SpaceCube", + "uuid": "a3533e02-125c-4066-bffe-d046d8d8342a" + }, + "Parts": [{ + "color": 16744448, + "shape": "CYLINDER", + "name": "BeamStructure", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "2afb23c9-f458-4bdb-a4e7-fc863364644f", + "lengthZ": 1.0 + }, { + "color": 8388608, + "shape": "BOX", + "name": "Right", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "882a0b35-7da8-4555-903d-fd6b5cbec392", + "lengthZ": 0.02 + }, { + "color": 32832, + "shape": "BOX", + "name": "Front", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "e6af9d3f-8ad6-4488-b3d0-d35549be9a1e", + "lengthZ": 0.02 + }, { + "color": 16711680, + "shape": "BOX", + "name": "Left", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "615985c0-73fd-48db-8f8b-e11b7cbb2ee8", + "lengthZ": 0.02 + }, { + "color": 65280, + "shape": "BOX", + "name": "Plate", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "00f430a6-6311-4a33-961b-41ded4cf57d5", + "lengthZ": 0.02 + }, { + "color": 16776960, + "shape": "BOX", + "name": "Back", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "a3c9c547-8fd3-40d5-97a1-a3f9a3a9c337", + "lengthZ": 0.02 + }, { + "color": 32768, + "shape": "BOX", + "name": "Top", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d56", + "lengthZ": 0.02 + } + ] +} \ No newline at end of file diff --git a/VirtualSatelliteCAD/commands/command_import.py b/VirtualSatelliteCAD/commands/command_import.py index 7ebf365..ddf90eb 100644 --- a/VirtualSatelliteCAD/commands/command_import.py +++ b/VirtualSatelliteCAD/commands/command_import.py @@ -29,12 +29,8 @@ from module.environment import Environment, ICON_IMPORT from commands.command_definitions import COMMAND_ID_IMPORT_2_FREECAD from PySide2.QtWidgets import QFileDialog -import json from json_io.json_importer import JsonImporter -from json_io.json_definitions import get_part_name_uuid -from freecad.active_document import FREECAD_FILE_EXTENSION - class CommandImport: def Activated(self): @@ -52,17 +48,10 @@ def Activated(self): if filename != '': FreeCAD.Console.PrintMessage(f"Successful read file '{filename}'\n") - with open(filename, 'r') as f: - json_object = json.load(f) - # TODO: where do we save the created FCstd files? AppData? # maybe create an subdir in AppData for VirSat stds? json_importer = JsonImporter(path) - json_importer.create_or_update_part(json_object) - - # TODO: return the path (or at least the name) of the std in create_or_update_part? - test_file_name = path + get_part_name_uuid(json_object) + FREECAD_FILE_EXTENSION - FreeCAD.open(test_file_name) + json_importer.fullImport(filename) def IsActive(self): return True diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index 281f94e..94d1876 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -30,6 +30,9 @@ from json_io.parts.json_part_factory import JsonPartFactory from json_io.json_definitions import get_part_name_uuid +import json +from freecad.active_document import FREECAD_FILE_EXTENSION + App = FreeCAD Gui = FreeCADGui Log = FreeCAD.Console.PrintLog @@ -50,6 +53,9 @@ def create_or_update_part(self, json_object): Log("Creating or Updating a part...\n") json_part = JsonPartFactory().create_from_json(json_object) + part_file_name = "" + + # for: create each part if json_part is not None: # Use the name to create the part document # should be careful in case the name already exists. @@ -65,3 +71,22 @@ def create_or_update_part(self, json_object): Log("Saved part to file: " + part_file_name + "\n") else: Log("Visualization shape is most likely NONE, therefore no file is created\n") + + # json assembly with json product object + # goal: instead of a part open the product assembly + + return part_file_name + + def full_import(self, filepath): + ''' + ''' + + with open(filepath, 'r') as f: + json_object = json.load(f) + + json_parts = json_object['Parts'] + part_file_name = self.create_or_update_part(json_parts[0]) + + # TODO: return the path (or at least the name) of the std in create_or_update_part? + test_file_name = self.working_output_directory + part_file_name + FREECAD_FILE_EXTENSION + FreeCAD.open(test_file_name) diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 469a5ba..5baa868 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -278,3 +278,13 @@ def test_create_part_change_shape(self): self.assertIsNone(App.ActiveDocument.getObject("Geometry"), "Removed previous object") self.assertIsNotNone(App.ActiveDocument.getObject("Cone"), "Got correct object") App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") + + def test_full_import(self): + + json_test_resource_path = Environment.get_test_resource_path("VisCube2.json") + json_importer = JsonImporter(self._WORKING_DIRECTORY) + json_importer.full_import(json_test_resource_path) + + # Check the file got created + # test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION + # self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") From f721858d704340f1024043626c41ccac50cc13e9 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Tue, 19 Nov 2019 10:16:03 +0100 Subject: [PATCH 04/20] Call JsonProductAssembly in full_import function- (Task #5) - Add JsonProductAssembly call - Add json product tests --- Task #5: Implement Import Functionality --- .../commands/command_import.py | 2 +- VirtualSatelliteCAD/json_io/json_importer.py | 25 +++++++++-- .../test/json_io/test_json_importer.py | 42 +++++++++++++++++-- 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/VirtualSatelliteCAD/commands/command_import.py b/VirtualSatelliteCAD/commands/command_import.py index ddf90eb..d9237ff 100644 --- a/VirtualSatelliteCAD/commands/command_import.py +++ b/VirtualSatelliteCAD/commands/command_import.py @@ -51,7 +51,7 @@ def Activated(self): # TODO: where do we save the created FCstd files? AppData? # maybe create an subdir in AppData for VirSat stds? json_importer = JsonImporter(path) - json_importer.fullImport(filename) + json_importer.full_import(filename) def IsActive(self): return True diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index 94d1876..a65a573 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -28,10 +28,11 @@ import FreeCADGui from freecad.active_document import ActiveDocument from json_io.parts.json_part_factory import JsonPartFactory +from json_io.products.json_product_assembly import JsonProductAssembly from json_io.json_definitions import get_part_name_uuid import json -from freecad.active_document import FREECAD_FILE_EXTENSION +# from freecad.active_document import FREECAD_FILE_EXTENSION App = FreeCAD Gui = FreeCADGui @@ -85,8 +86,24 @@ def full_import(self, filepath): json_object = json.load(f) json_parts = json_object['Parts'] - part_file_name = self.create_or_update_part(json_parts[0]) + + part_file_names = [] + # for: create each part + for part in json_parts: + part_file_names.append(self.create_or_update_part(part)) + + # json assembly with json product object + # goal: instead of a part open the product assembly + active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document("ProductAssemblyRootPart") + json_product = JsonProductAssembly().parse_from_json(json_object['Products']) + # active_document.save_as("ProductAssemblyRootPart") + + json_product.write_to_freecad(active_document) + + active_document.save_as("ProductAssemblyRootPart") # TODO: return the path (or at least the name) of the std in create_or_update_part? - test_file_name = self.working_output_directory + part_file_name + FREECAD_FILE_EXTENSION - FreeCAD.open(test_file_name) + # test_file_name = self.working_output_directory + part_file_name + FREECAD_FILE_EXTENSION + # FreeCAD.open(test_file_name) + + return part_file_names, json_product, active_document diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 5baa868..89faddb 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -283,8 +283,42 @@ def test_full_import(self): json_test_resource_path = Environment.get_test_resource_path("VisCube2.json") json_importer = JsonImporter(self._WORKING_DIRECTORY) - json_importer.full_import(json_test_resource_path) + part_file_names, json_product, active_document = json_importer.full_import(json_test_resource_path) - # Check the file got created - # test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION - # self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") + # ========================= + # Check parts + + # Check that the right number of parts was found + self.assertEqual(len(part_file_names), 7, "Found 7 files") + + # Check each part + for part_file_name in part_file_names: + test_file_name = self._WORKING_DIRECTORY + part_file_name + FREECAD_FILE_EXTENSION + # print(f"{test_file_name}\n") + + # Check the file got created + self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") + + # ========================= + # Check product + + # Check that the right number of children and root objects got created + self.assertEquals(len(json_product.children), 5, "correct amount of children") + self.assertEquals(len(active_document.app_active_document.RootObjects), 10, "Found correct amount of root objects 5 objects plus 5 sheets") + + # Check the product root + product_part_name = json_product.get_unique_name() + product_object = active_document.app_active_document.getObjectsByLabel(product_part_name) + self.assertIsNotNone(product_object, "Found an object under the given part name") + + # Get the names of all children of the product + product_child_part_names = [child.get_unique_name() for child in json_product.children] + + # Check that for each child a file exists + for product_child_part_name in product_child_part_names: + product_object = active_document.app_active_document.getObjectsByLabel(product_child_part_name) + self.assertIsNotNone(product_object, "Found an object under the given part name") + + # TODO: Check children of children? they seem to not be created atm + + # TODO: Check double import of the cube: manually double importing creates a wrong vis atm From 7ea25ff058a2a55178d8598a97902e4b47b6d519 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 25 Nov 2019 10:05:21 +0100 Subject: [PATCH 05/20] Add inheritance to JsonProductChild - (Task #5) Also: - Add test data in test_json_data - Add test case with that data in test_json_product_assembly - Update test_full_import --- Task #5: Implement Import Functionality --- .../json_io/products/json_product_child.py | 30 +++++++++++++ .../products/test_json_product_assembly.py | 17 ++++++- .../test/json_io/test_json_data.py | 44 +++++++++++++++++++ .../test/json_io/test_json_importer.py | 24 +++++----- 4 files changed, 103 insertions(+), 12 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index 56583ad..aae2a91 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -26,6 +26,7 @@ from json_io.products.json_product import AJsonProduct from json_io.json_definitions import _get_combined_name_uuid +from json_io.json_definitions import JSON_ELEMNT_CHILDREN class JsonProductChild(AJsonProduct): @@ -40,3 +41,32 @@ def get_part_unique_name(self): return _get_combined_name_uuid(self.name, self.uuid) else: return _get_combined_name_uuid(self.part_name, self.part_uuid) + + def parse_from_json(self, json_object): + + super().parse_from_json(json_object) + if self.has_children: + # Get all children from the json and try to parse them + # into JsonProductChild objects + json_object_children = list(json_object[JSON_ELEMNT_CHILDREN]) + + self.children = [] + for json_object_child in json_object_children: + + json_product_child = JsonProductChild().parse_from_json(json_object_child) + self.children.append(json_product_child) + + return self + + def write_to_freecad(self, active_document): + # This assembly may refer to a part as well + # hence if there is a partUuid and if there is a part name, than + # it should be written to the FreeCAD document as well. + if self.is_part_reference(): + super().write_to_freecad(active_document) + + # And now write the children, they decide on their own if they reference + # part or a product + if self.has_children: + for child in self.children: + child.write_to_freecad(active_document) diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py index 4fcee99..8983ceb 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py @@ -32,7 +32,7 @@ from json_io.products.json_product_assembly import JsonProductAssembly from freecad.active_document import ActiveDocument from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN,\ - TEST_JSON_PRODUCT_WITHOUT_CHILDREN + TEST_JSON_PRODUCT_WITHOUT_CHILDREN, TEST_JSON_PRODUCT_WITH_CHILD_WITH_CHILD App = FreeCAD @@ -79,6 +79,21 @@ def test_parse_with_children(self): self.assertEqual(json_product_child_1.name, "BasePlateBottom2", "Parsed correct child") self.assertEqual(json_product_child_2.name, "BasePlateTop", "Parsed correct child") + def test_parse_with_child_with_child(self): + # Same json as in test_parse_with_children, but BasePlateTop is now child of BasePlateBottom2 + + json_object = json.loads(TEST_JSON_PRODUCT_WITH_CHILD_WITH_CHILD) + json_product = JsonProductAssembly().parse_from_json(json_object) + + # Check for the children + self.assertEquals(len(json_product.children), 1, "Parsed one children") + + json_product_child_1 = json_product.children[0] + self.assertEqual(json_product_child_1.name, "BasePlateBottom2", "Parsed correct child") + + json_product_child_2 = json_product_child_1.children[0] + self.assertEqual(json_product_child_2.name, "BasePlateTop", "Parsed correct child") + def test_parse_with_no_children(self): json_data = TEST_JSON_PRODUCT_WITHOUT_CHILDREN diff --git a/VirtualSatelliteCAD/test/json_io/test_json_data.py b/VirtualSatelliteCAD/test/json_io/test_json_data.py index 22c4375..07ca0bb 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_data.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_data.py @@ -136,6 +136,50 @@ } """ +TEST_JSON_PRODUCT_WITH_CHILD_WITH_CHILD = """{ + "name": "BasePlateBottom1", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate", + "posX": 10.0, + "posY": 15.0, + "posZ": 20.0, + "rotX": 0.349, + "rotY": 0.698, + "rotZ": 1.046, + "children": [ + { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.0, + "rotX": 0.0, + "children": [ + { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [ + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BasePlateTop", + "uuid": "a199e3bd-3bc1-426d-8321-e9bd829339b3", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" + } + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BasePlateBottom2", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" + } + ] + } + """ + TEST_JSON_PRODUCT_WITHOUT_CHILDREN = """{ "name": "BasePlateBottom", "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 89faddb..7f1c3b5 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -303,22 +303,24 @@ def test_full_import(self): # Check product # Check that the right number of children and root objects got created - self.assertEquals(len(json_product.children), 5, "correct amount of children") - self.assertEquals(len(active_document.app_active_document.RootObjects), 10, "Found correct amount of root objects 5 objects plus 5 sheets") + self.assertEquals(len(json_product.children), 5, "Correct amount of children") + self.assertEquals(len(active_document.app_active_document.RootObjects), 14, "Found correct amount of root objects 7 plus 7 sheets") # Check the product root product_part_name = json_product.get_unique_name() product_object = active_document.app_active_document.getObjectsByLabel(product_part_name) self.assertIsNotNone(product_object, "Found an object under the given part name") - # Get the names of all children of the product - product_child_part_names = [child.get_unique_name() for child in json_product.children] - # Check that for each child a file exists - for product_child_part_name in product_child_part_names: - product_object = active_document.app_active_document.getObjectsByLabel(product_child_part_name) + for child in json_product.children: + product_object = active_document.app_active_document.getObjectsByLabel(child.get_unique_name()) self.assertIsNotNone(product_object, "Found an object under the given part name") - - # TODO: Check children of children? they seem to not be created atm - - # TODO: Check double import of the cube: manually double importing creates a wrong vis atm + if(child.name == "BeamStructure"): + # Check that two sub children are found + self.assertEquals(len(child.children), 2, "Correct amount of children") + for subchild in child.children: + product_object = active_document.app_active_document.getObjectsByLabel(subchild.get_unique_name()) + self.assertIsNotNone(product_object, "Found an object under the given part name") + + # TODO: Propagate coordinate values from parent to child + # TODO: Check double import of the cube: manually double importing creates a wrong vis atm From 51af564219793042f1a5e4a55bbace6023a8e53d Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 25 Nov 2019 14:11:36 +0100 Subject: [PATCH 06/20] Add functionality for importing a JSON twice - (Task #5) Importing twice should result in the same output as importing once. json_importer: Create FreeCAD filename from JSON filename test_json_importer: Add testcase for importing twice json_product: - Add function to compare AJsonProducts - Only create a part if it doesn't exist already - Clear the placement when reimporting --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/json_io/json_importer.py | 17 +++---- .../json_io/products/json_product.py | 47 ++++++++++++++++--- .../test/json_io/test_json_importer.py | 47 ++++++++++++++++++- 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index a65a573..e403c51 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -32,6 +32,7 @@ from json_io.json_definitions import get_part_name_uuid import json +import os # from freecad.active_document import FREECAD_FILE_EXTENSION App = FreeCAD @@ -44,7 +45,7 @@ class JsonImporter(object): ''' - classdocs + TODO: classdocs ''' def __init__(self, working_ouput_directory): @@ -56,7 +57,6 @@ def create_or_update_part(self, json_object): part_file_name = "" - # for: create each part if json_part is not None: # Use the name to create the part document # should be careful in case the name already exists. @@ -73,13 +73,11 @@ def create_or_update_part(self, json_object): else: Log("Visualization shape is most likely NONE, therefore no file is created\n") - # json assembly with json product object - # goal: instead of a part open the product assembly - return part_file_name def full_import(self, filepath): ''' + Import a whole json file's products and parts into a FreeCAD document ''' with open(filepath, 'r') as f: @@ -88,19 +86,18 @@ def full_import(self, filepath): json_parts = json_object['Parts'] part_file_names = [] - # for: create each part for part in json_parts: part_file_names.append(self.create_or_update_part(part)) + reduced_name = os.path.split(filepath)[1][:-5] + # json assembly with json product object - # goal: instead of a part open the product assembly - active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document("ProductAssemblyRootPart") + active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(reduced_name) json_product = JsonProductAssembly().parse_from_json(json_object['Products']) - # active_document.save_as("ProductAssemblyRootPart") json_product.write_to_freecad(active_document) - active_document.save_as("ProductAssemblyRootPart") + active_document.save_as(reduced_name) # TODO: return the path (or at least the name) of the std in create_or_update_part? # test_file_name = self.working_output_directory + part_file_name + FREECAD_FILE_EXTENSION diff --git a/VirtualSatelliteCAD/json_io/products/json_product.py b/VirtualSatelliteCAD/json_io/products/json_product.py index b456e9c..1dfb3a1 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product.py +++ b/VirtualSatelliteCAD/json_io/products/json_product.py @@ -32,6 +32,7 @@ from json_io.json_spread_sheet import JsonSpreadSheet from A2plus.a2p_importpart import importPartFromFile from freecad.active_document import VECTOR_X, VECTOR_Y, VECTOR_Z, VECTOR_ZERO +from FreeCAD import Placement class AJsonProduct(): @@ -58,6 +59,9 @@ def __init__(self): self.rot_y = 0.0 self.rot_z = 0.0 + self.name = None + self.uuid = None + def _parse_name_and_uuid_from_json(self, json_object): self.name = str(json_object[JSON_ELEMENT_NAME]) self.uuid = str(json_object[JSON_ELEMENT_UUID]).replace("-", "_") @@ -99,14 +103,24 @@ def _create_or_update_freecad_part(self, active_document): the assembly. E.g. A BasePlate will be added as BasePlateBottom to the assembly. In case the object already exists, nothing special will happen. ''' + # TODO: overwrite in case it already exists? import_part_file_name = self.get_part_unique_name() import_part_name_in_product = self.get_unique_name() import_part_full_path = active_document.get_file_full_path(import_part_file_name) - imported_product_part = importPartFromFile( - active_document.app_active_document, - import_part_full_path) - imported_product_part.Label = import_part_name_in_product - + import_part_ref = active_document.app_active_document.getObjectsByLabel(import_part_name_in_product) + + # If the part doesn't exists (the returned list is not empty) update it + if import_part_ref: + # TODO: Update it + print(f"Found existing part '{import_part_name_in_product}'") + # Else create it + else: + imported_product_part = importPartFromFile( + active_document.app_active_document, + import_part_full_path) + imported_product_part.Label = import_part_name_in_product + + # TODO: remove? def _set_freecad_name_and_color(self, active_document): pass @@ -122,7 +136,9 @@ def _set_freecad_position_and_rotation(self, active_document): vector_rotation_y = active_document.app.Rotation(VECTOR_Y, self.rot_y) vector_rotation_z = active_document.app.Rotation(VECTOR_Z, self.rot_z) - placement = product_part.Placement + # TODO: using the original placement causes misbehavior when importing the same json twice + # placement = product_part.Placement + placement = Placement() placement_translation = active_document.app.Placement( vector_translation, @@ -178,3 +194,22 @@ def is_part_reference(self): has_part_name = hasattr(self, "part_name") return has_part_uuid and has_part_name + + def hasEqualValues(self, other): + """ + Compares values with another AJsonProduct + """ + if(isinstance(other, AJsonProduct)): + return ( + self.pos_x == other.pos_x and + self.pos_y == other.pos_y and + self.pos_z == other.pos_z and + + self.rot_x == other.rot_x and + self.rot_y == other.rot_y and + self.rot_z == other.rot_z and + + self.name == other.name and + self.uuid == other.uuid) + + return NotImplemented diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 7f1c3b5..076377f 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -314,7 +314,9 @@ def test_full_import(self): # Check that for each child a file exists for child in json_product.children: product_object = active_document.app_active_document.getObjectsByLabel(child.get_unique_name()) - self.assertIsNotNone(product_object, "Found an object under the given part name") + # An empty list in python gets asserted to true + # TODO: this was assertNotNone, which returned true all the time, so xcheck if this mistake was made somewhere else + self.assertTrue(product_object, "Found an object under the given part name") if(child.name == "BeamStructure"): # Check that two sub children are found self.assertEquals(len(child.children), 2, "Correct amount of children") @@ -322,5 +324,46 @@ def test_full_import(self): product_object = active_document.app_active_document.getObjectsByLabel(subchild.get_unique_name()) self.assertIsNotNone(product_object, "Found an object under the given part name") - # TODO: Propagate coordinate values from parent to child + # TODO: Check: Should it propagate coordinate values from parent to child or is there a bug in virsat export? + + def test_full_import_again(self): + """ + importing the same file again should not result in changes + """ + pass # TODO: Check double import of the cube: manually double importing creates a wrong vis atm + json_test_resource_path = Environment.get_test_resource_path("VisCube2.json") + json_importer = JsonImporter(self._WORKING_DIRECTORY) + + # ========================= + # First import + part_file_names, json_product, active_document = json_importer.full_import(json_test_resource_path) + + # Check that the right number of parts was found + self.assertEqual(len(part_file_names), 7, "Found 7 files") + + # Check that the right number of children and root objects got created + self.assertEqual(len(json_product.children), 5, "Correct amount of children") + self.assertEqual(len(active_document.app_active_document.RootObjects), 14, "Found correct amount of root objects 7 plus 7 sheets") + + # ========================= + # Second import + part_file_names2, json_product2, active_document2 = json_importer.full_import(json_test_resource_path) + + # Check that the right number of parts was found + self.assertEqual(len(part_file_names2), 7, "Found 7 files") + + # Check that the right number of children and root objects got created + self.assertEqual(len(json_product2.children), 5, "Correct amount of children") + self.assertEqual(len(active_document2.app_active_document.RootObjects), 14, "Found correct amount of root objects 7 plus 7 sheets") + + # ========================= + # Check equality + self.assertEquals(part_file_names, part_file_names2) + + for i, child1 in enumerate(json_product.children): + child2 = json_product2.children[i] + child1.hasEqualValues(child2) + + def test_full_import_again_with_changes(self): + pass From 205ea9c341bbd64f2f63bf2a4fe6d5ac5acc4879 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 25 Nov 2019 15:50:40 +0100 Subject: [PATCH 07/20] Add position and rotation propagation - (Task #5) Add a simple solution in json_product_child to propagate values from parent to children and also test this behavior. The current method results in children only having an absolute position (and rotation) instead of a relative one to their parent. This should be reconsidered with usability and the export functionality in mind. --- Task #5: Implement Import Functionality --- .../json_io/products/json_product.py | 4 +++- .../json_io/products/json_product_assembly.py | 1 + .../json_io/products/json_product_child.py | 23 +++++++++++++++++++ .../test/json_io/test_json_importer.py | 10 ++++++-- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/products/json_product.py b/VirtualSatelliteCAD/json_io/products/json_product.py index 1dfb3a1..5b2cf56 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product.py +++ b/VirtualSatelliteCAD/json_io/products/json_product.py @@ -129,6 +129,8 @@ def _set_freecad_position_and_rotation(self, active_document): product_part = active_document.app_active_document.getObjectsByLabel(product_part_name)[0] + # TODO: check if the same bug as below applies + # TODO: Add testcases in test_product # First translate than rotate around X, Y and Z vector_translation = active_document.app.Vector(self.pos_x, self.pos_y, self.pos_z) vector_rotation_zero = active_document.app.Rotation(VECTOR_ZERO, 0) @@ -195,7 +197,7 @@ def is_part_reference(self): return has_part_uuid and has_part_name - def hasEqualValues(self, other): + def has_equal_values(self, other): """ Compares values with another AJsonProduct """ diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py index 426d984..af8a55c 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py @@ -70,6 +70,7 @@ class without implementation. Additionally this method starts parsing self.children = [] for json_object_child in json_object_children: json_product_child = JsonProductChild().parse_from_json(json_object_child) + json_product_child.propagate_pos_and_rot_from_parent(self) self.children.append(json_product_child) # Don't hand back an assembly if there are no children diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index aae2a91..735ed68 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -54,6 +54,7 @@ def parse_from_json(self, json_object): for json_object_child in json_object_children: json_product_child = JsonProductChild().parse_from_json(json_object_child) + json_product_child.propagate_pos_and_rot_from_parent(self) self.children.append(json_product_child) return self @@ -70,3 +71,25 @@ def write_to_freecad(self, active_document): if self.has_children: for child in self.children: child.write_to_freecad(active_document) + + def propagate_pos_and_rot_from_parent(self, parent): + """ + TODO: + This function propagates position and rotation parameters from the parent: + Doing this the pos and rot of an object become absolute not relative, which could result + in overhead when parsing back. + The question occurs how we handle this problems: + - Does a product hold its relative and absolute position? + - if yes how do we keep them in sync + - ideal would be to only show relative positions to the FreeCAD user and compute them + to absolute positions internally + - is that possible with freecad? it seems to only accept absolute positions and no inheritance + - constraints??? + """ + self.pos_x += parent.pos_x + self.pos_y += parent.pos_y + self.pos_z += parent.pos_z + + self.rot_x += parent.rot_x + self.rot_y += parent.rot_y + self.rot_z += parent.rot_z diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 076377f..c38a866 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -324,7 +324,9 @@ def test_full_import(self): product_object = active_document.app_active_document.getObjectsByLabel(subchild.get_unique_name()) self.assertIsNotNone(product_object, "Found an object under the given part name") - # TODO: Check: Should it propagate coordinate values from parent to child or is there a bug in virsat export? + # Check propagation + # poz_z of -500 should be propagated from "BeamStructure" + self.assertEqual(subchild.pos_z, 500.0, "Z position got propagated correctly") def test_full_import_again(self): """ @@ -363,7 +365,11 @@ def test_full_import_again(self): for i, child1 in enumerate(json_product.children): child2 = json_product2.children[i] - child1.hasEqualValues(child2) + child1.has_equal_values(child2) def test_full_import_again_with_changes(self): + """ + If two files with the same name get imported all elements of the second file should overwrite the elements of the first file, + but elements existing in the first file (but not in the second file) won't be changed + """ pass From 05bc713d4debda05aa56bf7c56195dbdf9651cf3 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 25 Nov 2019 17:57:56 +0100 Subject: [PATCH 08/20] Add functionality for full import twice with changes - (Task #5) Because of CRUD assume that a change from Virtual Satellite side has always newer information, so we can simply clear the first imported file and use the newer file. For that: - Add a function to clear a file in active_document - Call that function in full_import - Get FreeCAD document from ProductRoot instead of filename (so different versions can easily be saved in different files) - Add new test fixture --- Task #5: Implement Import Functionality --- .../Resources/Tests/VisCube2_update.json | 31 +++++++++++++++ .../freecad/active_document.py | 8 ++++ VirtualSatelliteCAD/json_io/json_importer.py | 18 ++++----- .../test/json_io/test_json_importer.py | 39 +++++++++++++++---- 4 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 VirtualSatelliteCAD/Resources/Tests/VisCube2_update.json diff --git a/VirtualSatelliteCAD/Resources/Tests/VisCube2_update.json b/VirtualSatelliteCAD/Resources/Tests/VisCube2_update.json new file mode 100644 index 0000000..082262e --- /dev/null +++ b/VirtualSatelliteCAD/Resources/Tests/VisCube2_update.json @@ -0,0 +1,31 @@ +{ + "Products": { + "children": [{ + "posX": 0.0, + "posY": 0.0, + "posZ": 1.0, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Top", + "uuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d56", + "partUuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d57", + "partName": "Top2" + } + ], + "name": "SpaceCube", + "uuid": "a3533e02-125c-4066-bffe-d046d8d8342a" + }, + "Parts": [{ + "color": 32768, + "shape": "BOX", + "name": "Top2", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d57", + "lengthZ": 0.02 + } + ] +} \ No newline at end of file diff --git a/VirtualSatelliteCAD/freecad/active_document.py b/VirtualSatelliteCAD/freecad/active_document.py index a49d343..1cf324b 100644 --- a/VirtualSatelliteCAD/freecad/active_document.py +++ b/VirtualSatelliteCAD/freecad/active_document.py @@ -70,6 +70,14 @@ def open_set_and_get_document(self, file_name_without_extension): return self + def clear_if_open_document(self, file_name_without_extension): + documents = list(App.listDocuments().keys()) + + if documents.count(file_name_without_extension) != 0: + Log('Delete and recreate new FreeCAD file...\n') + App.closeDocument(file_name_without_extension) + App.newDocument(file_name_without_extension) + def set_active_documents(self, file_name_without_extension): App.setActiveDocument(file_name_without_extension) diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index e403c51..fbf6750 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -32,8 +32,6 @@ from json_io.json_definitions import get_part_name_uuid import json -import os -# from freecad.active_document import FREECAD_FILE_EXTENSION App = FreeCAD Gui = FreeCADGui @@ -89,18 +87,18 @@ def full_import(self, filepath): for part in json_parts: part_file_names.append(self.create_or_update_part(part)) - reduced_name = os.path.split(filepath)[1][:-5] - # json assembly with json product object - active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(reduced_name) json_product = JsonProductAssembly().parse_from_json(json_object['Products']) + freecad_name = json_product.name - json_product.write_to_freecad(active_document) + # if there is a root document with the same name open already: + # assume that all changes of the current import are valid (CRUD) + # so clear the document + ActiveDocument(self.working_output_directory).clear_if_open_document(freecad_name) - active_document.save_as(reduced_name) + active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(freecad_name) + json_product.write_to_freecad(active_document) - # TODO: return the path (or at least the name) of the std in create_or_update_part? - # test_file_name = self.working_output_directory + part_file_name + FREECAD_FILE_EXTENSION - # FreeCAD.open(test_file_name) + active_document.save_as(freecad_name) return part_file_names, json_product, active_document diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index c38a866..d53b8d5 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -306,11 +306,6 @@ def test_full_import(self): self.assertEquals(len(json_product.children), 5, "Correct amount of children") self.assertEquals(len(active_document.app_active_document.RootObjects), 14, "Found correct amount of root objects 7 plus 7 sheets") - # Check the product root - product_part_name = json_product.get_unique_name() - product_object = active_document.app_active_document.getObjectsByLabel(product_part_name) - self.assertIsNotNone(product_object, "Found an object under the given part name") - # Check that for each child a file exists for child in json_product.children: product_object = active_document.app_active_document.getObjectsByLabel(child.get_unique_name()) @@ -369,7 +364,35 @@ def test_full_import_again(self): def test_full_import_again_with_changes(self): """ - If two files with the same name get imported all elements of the second file should overwrite the elements of the first file, - but elements existing in the first file (but not in the second file) won't be changed + If two files with the same name get imported: we assume that the VirSat side used CRUD, that means: + - new parts/products could be created, so add them + - old parts/products could be replaced/updated, so use the new information + - old parts/products could be deleted, so delete all not updated files + -> this means instead of merging, simply the information of the old files get replaced be the newer ones """ - pass + + json_importer = JsonImporter(self._WORKING_DIRECTORY) + + # ========================= + # First import + json_test_resource_path = Environment.get_test_resource_path("VisCube2.json") + part_file_names, json_product, active_document = json_importer.full_import(json_test_resource_path) + + # Check that the right number of parts was found + self.assertEqual(len(part_file_names), 7, "Found 7 files") + + # Check that the right number of children and root objects got created + self.assertEqual(len(json_product.children), 5, "Correct amount of children") + self.assertEqual(len(active_document.app_active_document.RootObjects), 14, "Found correct amount of root objects 7 plus 7 sheets") + + # ========================= + # Second import + json_test_resource_path2 = Environment.get_test_resource_path("VisCube2_update.json") + part_file_names2, json_product2, active_document2 = json_importer.full_import(json_test_resource_path2) + + # Check that the right number of parts was found + self.assertEqual(len(part_file_names2), 1, "Found 1 files") + + # Check that the right number of children and root objects got created + self.assertEquals(len(json_product2.children), 1, "Correct amount of children") + self.assertEquals(len(active_document2.app_active_document.RootObjects), 2, "Found correct amount of root objects 1 plus 1 sheets") From f7a0f2771963a274a6aa0ce6c9b063d9f8357a5c Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Wed, 27 Nov 2019 10:52:11 +0100 Subject: [PATCH 09/20] Resolve TODOs - (Task #5) - Add a module directory in FreeCAD Appdata: - Create directory in Init - Provide it with Environment - Use it in CommandImport - Update documentation and Log Messages - Update part update in json_product: Now deletes and recreates existing parts --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/Init.py | 9 +++++-- .../commands/command_import.py | 11 +++----- VirtualSatelliteCAD/json_io/json_importer.py | 20 ++++++++++---- .../json_io/products/json_product.py | 27 ++++++------------- .../json_io/products/json_product_child.py | 8 ------ VirtualSatelliteCAD/module/environment.py | 9 ++++++- .../test/json_io/test_json_importer.py | 12 ++++----- 7 files changed, 48 insertions(+), 48 deletions(-) diff --git a/VirtualSatelliteCAD/Init.py b/VirtualSatelliteCAD/Init.py index 1e61532..fda83da 100644 --- a/VirtualSatelliteCAD/Init.py +++ b/VirtualSatelliteCAD/Init.py @@ -24,9 +24,10 @@ # SPDX-License-Identifier: LGPL-3.0-or-later # import FreeCAD -from os.path import isdir +import os import sys +APPDATA_DIR = os.path.join(FreeCAD.ConfigGet("UserAppData"), "Mod", "VirtualSatelliteCAD") # FreeCAD seems to load modules differently once they are stored in the User Home directory. # We try to load the whole folder if it exists @@ -37,7 +38,7 @@ Log("See if the directory " + freecad_user_mod + "exists...") -if isdir(freecad_user_mod): +if os.path.isdir(freecad_user_mod): Log("Directory Exists... Check if it is already on the path...") if (freecad_user_mod in sys.path): Log("Directory is already on the path...") @@ -45,5 +46,9 @@ Log("Directory will be appended to system path...") sys.path.append(freecad_user_mod) +# Create an appdata directory +if not os.path.isdir(APPDATA_DIR): + os.mkdir(APPDATA_DIR) + # Finally register the unit test for being executed with all other FreeCAD tests FreeCAD.__unit_test__ += ["TestVirtualSatelliteApp"] diff --git a/VirtualSatelliteCAD/commands/command_import.py b/VirtualSatelliteCAD/commands/command_import.py index d9237ff..7846929 100644 --- a/VirtualSatelliteCAD/commands/command_import.py +++ b/VirtualSatelliteCAD/commands/command_import.py @@ -30,27 +30,24 @@ from commands.command_definitions import COMMAND_ID_IMPORT_2_FREECAD from PySide2.QtWidgets import QFileDialog from json_io.json_importer import JsonImporter +import os class CommandImport: def Activated(self): FreeCAD.Console.PrintMessage("Calling the importer\n") - path = FreeCAD.ConfigGet("UserAppData") - # call pyqt dialog: returns (filename, filter) filename = QFileDialog.getOpenFileName( None, # ui parent "Open JSON file", # dialog caption - path, + Environment.get_appdata_module_path(), "JSON(*.json)")[0] # filter if filename != '': - FreeCAD.Console.PrintMessage(f"Successful read file '{filename}'\n") + FreeCAD.Console.PrintMessage(f"Selected file '{filename}'\n") - # TODO: where do we save the created FCstd files? AppData? - # maybe create an subdir in AppData for VirSat stds? - json_importer = JsonImporter(path) + json_importer = JsonImporter(Environment.get_appdata_module_path() + os.sep) json_importer.full_import(filename) def IsActive(self): diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index fbf6750..298cda8 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -43,11 +43,11 @@ class JsonImporter(object): ''' - TODO: classdocs + Provides functionality to import a JSON created by Virtual Satellite into FreeCAD ''' - def __init__(self, working_ouput_directory): - self.working_output_directory = working_ouput_directory + def __init__(self, working_output_directory): + self.working_output_directory = working_output_directory def create_or_update_part(self, json_object): Log("Creating or Updating a part...\n") @@ -77,9 +77,15 @@ def full_import(self, filepath): ''' Import a whole json file's products and parts into a FreeCAD document ''' + Log(f"Importing JSON file '{filepath}'\n") with open(filepath, 'r') as f: - json_object = json.load(f) + try: + json_object = json.load(f) + except ValueError as error: + Log(f"ERROR: Invalid JSON found: '{error}'\n") + Log("Please provide a valid JSON\n") + return json_parts = json_object['Parts'] @@ -89,9 +95,11 @@ def full_import(self, filepath): # json assembly with json product object json_product = JsonProductAssembly().parse_from_json(json_object['Products']) + + # name the freecad document after the root product freecad_name = json_product.name - # if there is a root document with the same name open already: + # If there is a root document with the same name open already: # assume that all changes of the current import are valid (CRUD) # so clear the document ActiveDocument(self.working_output_directory).clear_if_open_document(freecad_name) @@ -101,4 +109,6 @@ def full_import(self, filepath): active_document.save_as(freecad_name) + Log(f"Import successful\n") + return part_file_names, json_product, active_document diff --git a/VirtualSatelliteCAD/json_io/products/json_product.py b/VirtualSatelliteCAD/json_io/products/json_product.py index 5b2cf56..a5a2c72 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product.py +++ b/VirtualSatelliteCAD/json_io/products/json_product.py @@ -101,36 +101,27 @@ def _create_or_update_freecad_part(self, active_document): This method imports the part referenced by the product. The referenced part will be placed under the product part name into the assembly. E.g. A BasePlate will be added as BasePlateBottom to the - assembly. In case the object already exists, nothing special will happen. + assembly. In case the object already exists, it will be recreated. ''' - # TODO: overwrite in case it already exists? import_part_file_name = self.get_part_unique_name() import_part_name_in_product = self.get_unique_name() import_part_full_path = active_document.get_file_full_path(import_part_file_name) import_part_ref = active_document.app_active_document.getObjectsByLabel(import_part_name_in_product) - # If the part doesn't exists (the returned list is not empty) update it + # If the part doesn't exists (the returned list is not empty) update (delete and recreate) it if import_part_ref: - # TODO: Update it - print(f"Found existing part '{import_part_name_in_product}'") - # Else create it - else: - imported_product_part = importPartFromFile( - active_document.app_active_document, - import_part_full_path) - imported_product_part.Label = import_part_name_in_product - - # TODO: remove? - def _set_freecad_name_and_color(self, active_document): - pass + active_document.app_active_document.removeObject(import_part_ref[0].Name) + + imported_product_part = importPartFromFile( + active_document.app_active_document, + import_part_full_path) + imported_product_part.Label = import_part_name_in_product def _set_freecad_position_and_rotation(self, active_document): product_part_name = self.get_unique_name() product_part = active_document.app_active_document.getObjectsByLabel(product_part_name)[0] - # TODO: check if the same bug as below applies - # TODO: Add testcases in test_product # First translate than rotate around X, Y and Z vector_translation = active_document.app.Vector(self.pos_x, self.pos_y, self.pos_z) vector_rotation_zero = active_document.app.Rotation(VECTOR_ZERO, 0) @@ -138,8 +129,6 @@ def _set_freecad_position_and_rotation(self, active_document): vector_rotation_y = active_document.app.Rotation(VECTOR_Y, self.rot_y) vector_rotation_z = active_document.app.Rotation(VECTOR_Z, self.rot_z) - # TODO: using the original placement causes misbehavior when importing the same json twice - # placement = product_part.Placement placement = Placement() placement_translation = active_document.app.Placement( diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index 735ed68..6c449e6 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -74,17 +74,9 @@ def write_to_freecad(self, active_document): def propagate_pos_and_rot_from_parent(self, parent): """ - TODO: This function propagates position and rotation parameters from the parent: Doing this the pos and rot of an object become absolute not relative, which could result in overhead when parsing back. - The question occurs how we handle this problems: - - Does a product hold its relative and absolute position? - - if yes how do we keep them in sync - - ideal would be to only show relative positions to the FreeCAD user and compute them - to absolute positions internally - - is that possible with freecad? it seems to only accept absolute positions and no inheritance - - constraints??? """ self.pos_x += parent.pos_x self.pos_y += parent.pos_y diff --git a/VirtualSatelliteCAD/module/environment.py b/VirtualSatelliteCAD/module/environment.py index dadf9b7..e8e078d 100644 --- a/VirtualSatelliteCAD/module/environment.py +++ b/VirtualSatelliteCAD/module/environment.py @@ -39,7 +39,7 @@ class Environment: ''' This class helps to understand the environment where the module is executed in. - E.g. knwoing which is the directory of the module and so on + E.g. knowing which is the directory of the module and so on ''' @classmethod @@ -90,3 +90,10 @@ def get_test_resource_path(cls, test_resource_name): ''' path = os.path.join(cls.get_tests_resource_path(), test_resource_name) return path + + @classmethod + def get_appdata_module_path(cls): + ''' + This method hands back the module path of the local Appdata directory. + ''' + return Init.APPDATA_DIR diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index d53b8d5..4645000 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -280,6 +280,9 @@ def test_create_part_change_shape(self): App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") def test_full_import(self): + """ + Full JSON import test + """ json_test_resource_path = Environment.get_test_resource_path("VisCube2.json") json_importer = JsonImporter(self._WORKING_DIRECTORY) @@ -294,7 +297,6 @@ def test_full_import(self): # Check each part for part_file_name in part_file_names: test_file_name = self._WORKING_DIRECTORY + part_file_name + FREECAD_FILE_EXTENSION - # print(f"{test_file_name}\n") # Check the file got created self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") @@ -310,7 +312,6 @@ def test_full_import(self): for child in json_product.children: product_object = active_document.app_active_document.getObjectsByLabel(child.get_unique_name()) # An empty list in python gets asserted to true - # TODO: this was assertNotNone, which returned true all the time, so xcheck if this mistake was made somewhere else self.assertTrue(product_object, "Found an object under the given part name") if(child.name == "BeamStructure"): # Check that two sub children are found @@ -325,10 +326,9 @@ def test_full_import(self): def test_full_import_again(self): """ - importing the same file again should not result in changes + Importing the same file again should not result in changes """ - pass - # TODO: Check double import of the cube: manually double importing creates a wrong vis atm + json_test_resource_path = Environment.get_test_resource_path("VisCube2.json") json_importer = JsonImporter(self._WORKING_DIRECTORY) @@ -368,7 +368,7 @@ def test_full_import_again_with_changes(self): - new parts/products could be created, so add them - old parts/products could be replaced/updated, so use the new information - old parts/products could be deleted, so delete all not updated files - -> this means instead of merging, simply the information of the old files get replaced be the newer ones + -> this means instead of merging, simply the information of the old files get replaced by the newer one """ json_importer = JsonImporter(self._WORKING_DIRECTORY) From 76ebada1ae7063305f5b03de57b37d542f705576 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Wed, 27 Nov 2019 11:22:46 +0100 Subject: [PATCH 10/20] Fix missing Mod directory in AppData - (Task #5) Also add the Mod directory --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/Init.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/VirtualSatelliteCAD/Init.py b/VirtualSatelliteCAD/Init.py index fda83da..f4074cb 100644 --- a/VirtualSatelliteCAD/Init.py +++ b/VirtualSatelliteCAD/Init.py @@ -27,7 +27,8 @@ import os import sys -APPDATA_DIR = os.path.join(FreeCAD.ConfigGet("UserAppData"), "Mod", "VirtualSatelliteCAD") +MOD_DIR = os.path.join(FreeCAD.ConfigGet("UserAppData"), "Mod") +APPDATA_DIR = os.path.join(MOD_DIR, "VirtualSatelliteCAD") # FreeCAD seems to load modules differently once they are stored in the User Home directory. # We try to load the whole folder if it exists @@ -47,6 +48,8 @@ sys.path.append(freecad_user_mod) # Create an appdata directory +if not os.path.isdir(MOD_DIR): + os.mkdir(MOD_DIR) if not os.path.isdir(APPDATA_DIR): os.mkdir(APPDATA_DIR) From abbaab2bab0222748dea664c5281a4d4c0cc06f3 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Thu, 28 Nov 2019 16:10:29 +0100 Subject: [PATCH 11/20] Disable full import test cases and add TODOs - (Task #5) --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/json_io/json_importer.py | 2 + .../json_io/products/json_product.py | 4 +- .../json_io/products/json_product_assembly.py | 2 +- .../json_io/products/json_product_child.py | 89 ++++++++++--------- .../products/test_json_product_assembly.py | 30 ++++++- .../test/json_io/test_json_data.py | 6 +- .../test/json_io/test_json_importer.py | 4 + 7 files changed, 88 insertions(+), 49 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index 298cda8..5ae8848 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -73,12 +73,14 @@ def create_or_update_part(self, json_object): return part_file_name + # TODO: give json def full_import(self, filepath): ''' Import a whole json file's products and parts into a FreeCAD document ''' Log(f"Importing JSON file '{filepath}'\n") + # TODO: refactor in command with open(filepath, 'r') as f: try: json_object = json.load(f) diff --git a/VirtualSatelliteCAD/json_io/products/json_product.py b/VirtualSatelliteCAD/json_io/products/json_product.py index a5a2c72..1f7ffcf 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product.py +++ b/VirtualSatelliteCAD/json_io/products/json_product.py @@ -108,6 +108,8 @@ def _create_or_update_freecad_part(self, active_document): import_part_full_path = active_document.get_file_full_path(import_part_file_name) import_part_ref = active_document.app_active_document.getObjectsByLabel(import_part_name_in_product) + print(f"Called with '{import_part_name_in_product}'") + # TODO: # If the part doesn't exists (the returned list is not empty) update (delete and recreate) it if import_part_ref: active_document.app_active_document.removeObject(import_part_ref[0].Name) @@ -129,7 +131,7 @@ def _set_freecad_position_and_rotation(self, active_document): vector_rotation_y = active_document.app.Rotation(VECTOR_Y, self.rot_y) vector_rotation_z = active_document.app.Rotation(VECTOR_Z, self.rot_z) - placement = Placement() + placement = product_part.Placement # Placement() placement_translation = active_document.app.Placement( vector_translation, diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py index af8a55c..e1d1635 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py @@ -70,7 +70,7 @@ class without implementation. Additionally this method starts parsing self.children = [] for json_object_child in json_object_children: json_product_child = JsonProductChild().parse_from_json(json_object_child) - json_product_child.propagate_pos_and_rot_from_parent(self) + # json_product_child.propagate_pos_and_rot_from_parent(self) self.children.append(json_product_child) # Don't hand back an assembly if there are no children diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index 6c449e6..a759a8b 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -26,7 +26,7 @@ from json_io.products.json_product import AJsonProduct from json_io.json_definitions import _get_combined_name_uuid -from json_io.json_definitions import JSON_ELEMNT_CHILDREN +# from json_io.json_definitions import JSON_ELEMNT_CHILDREN class JsonProductChild(AJsonProduct): @@ -42,46 +42,49 @@ def get_part_unique_name(self): else: return _get_combined_name_uuid(self.part_name, self.part_uuid) - def parse_from_json(self, json_object): +# def parse_from_json(self, json_object): +# +# super().parse_from_json(json_object) +# +# if self.has_children: +# # Get all children from the json and try to parse them +# # into JsonProductChild objects +# json_object_children = list(json_object[JSON_ELEMNT_CHILDREN]) +# +# self.children = [] +# for json_object_child in json_object_children: +# +# # TODO: child to check it has children? -> differ +# +# json_product_child = JsonProductChild().parse_from_json(json_object_child) +# # json_product_child.propagate_pos_and_rot_from_parent(self) +# self.children.append(json_product_child) +# +# return self +# +# def write_to_freecad(self, active_document): +# # This assembly may refer to a part as well +# # hence if there is a partUuid and if there is a part name, than +# # it should be written to the FreeCAD document as well. +# if self.is_part_reference(): +# super().write_to_freecad(active_document) +# +# # And now write the children, they decide on their own if they reference +# # part or a product +# if self.has_children: +# for child in self.children: +# child.write_to_freecad(active_document) - super().parse_from_json(json_object) - if self.has_children: - # Get all children from the json and try to parse them - # into JsonProductChild objects - json_object_children = list(json_object[JSON_ELEMNT_CHILDREN]) - - self.children = [] - for json_object_child in json_object_children: - - json_product_child = JsonProductChild().parse_from_json(json_object_child) - json_product_child.propagate_pos_and_rot_from_parent(self) - self.children.append(json_product_child) - - return self - - def write_to_freecad(self, active_document): - # This assembly may refer to a part as well - # hence if there is a partUuid and if there is a part name, than - # it should be written to the FreeCAD document as well. - if self.is_part_reference(): - super().write_to_freecad(active_document) - - # And now write the children, they decide on their own if they reference - # part or a product - if self.has_children: - for child in self.children: - child.write_to_freecad(active_document) - - def propagate_pos_and_rot_from_parent(self, parent): - """ - This function propagates position and rotation parameters from the parent: - Doing this the pos and rot of an object become absolute not relative, which could result - in overhead when parsing back. - """ - self.pos_x += parent.pos_x - self.pos_y += parent.pos_y - self.pos_z += parent.pos_z - - self.rot_x += parent.rot_x - self.rot_y += parent.rot_y - self.rot_z += parent.rot_z +# def propagate_pos_and_rot_from_parent(self, parent): +# """ +# This function propagates position and rotation parameters from the parent: +# Doing this the pos and rot of an object become absolute not relative, which could result +# in overhead when parsing back. +# """ +# self.pos_x += parent.pos_x +# self.pos_y += parent.pos_y +# self.pos_z += parent.pos_z +# +# self.rot_x += parent.rot_x +# self.rot_y += parent.rot_y +# self.rot_z += parent.rot_z diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py index 8983ceb..c4ce5ed 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py @@ -62,6 +62,7 @@ def test_parse_with_children(self): self.assertEqual(json_product.part_uuid, "3d3708fd_5c6c_4af9_b710_d68778466084", "Property is correctly set") # Properties have to be 0 since an assembly itself has no position and orientation + # TODO: check self.assertEqual(json_product.pos_x, 0, "Property is correctly set") self.assertEqual(json_product.pos_y, 0, "Property is correctly set") self.assertEqual(json_product.pos_z, 0, "Property is correctly set") @@ -79,6 +80,23 @@ def test_parse_with_children(self): self.assertEqual(json_product_child_1.name, "BasePlateBottom2", "Parsed correct child") self.assertEqual(json_product_child_2.name, "BasePlateTop", "Parsed correct child") + # Check that position and rotation from parent are propagated correctly + self.assertEqual(json_product_child_1.pos_x, 0, "Property is correctly set") + self.assertEqual(json_product_child_1.pos_y, 0, "Property is correctly set") + self.assertEqual(json_product_child_1.pos_z, 0, "Property is correctly set") + + self.assertEqual(json_product_child_1.rot_x, 0.0, "Property is correctly set") + self.assertEqual(json_product_child_1.rot_y, 0.0, "Property is correctly set") + self.assertEqual(json_product_child_1.rot_z, 0.0, "Property is correctly set") + + self.assertEqual(json_product_child_2.pos_x, 0, "Property is correctly set") + self.assertEqual(json_product_child_2.pos_y, 0, "Property is correctly set") + self.assertEqual(json_product_child_2.pos_z, 0.5, "Property is correctly set") + + self.assertEqual(json_product_child_2.rot_x, 0.0, "Property is correctly set") + self.assertEqual(json_product_child_2.rot_y, 0.0, "Property is correctly set") + self.assertEqual(json_product_child_2.rot_z, 0.0, "Property is correctly set") + def test_parse_with_child_with_child(self): # Same json as in test_parse_with_children, but BasePlateTop is now child of BasePlateBottom2 @@ -108,7 +126,7 @@ def test_create_part_product_assembly_with_root_part(self): active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("ProductAssemblyRootPart") json_object = json.loads(self.json_data) json_product = JsonProductAssembly().parse_from_json(json_object) - active_document.save_as("ProductAssemblyRootPart") + # active_document.save_as("ProductAssemblyRootPart") json_product.write_to_freecad(active_document) @@ -130,3 +148,13 @@ def test_create_part_product_assembly_with_root_part(self): product_child2_part_name = json_product.children[1].get_unique_name() product_object = active_document.app_active_document.getObjectsByLabel(product_child2_part_name)[0] self.assertIsNotNone(product_object, "Found an object under the given part name") + + + # TODO: + # test same json as above but with subassembvly as assemblychid + + #TODO: + # test same as above, first create subassembly then with that the assembly + + #TODO: + # test same as above, but with automated tree traverser \ No newline at end of file diff --git a/VirtualSatelliteCAD/test/json_io/test_json_data.py b/VirtualSatelliteCAD/test/json_io/test_json_data.py index 07ca0bb..06bb677 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_data.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_data.py @@ -97,9 +97,9 @@ "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", "partName": "BasePlate", - "posX": 10.0, - "posY": 15.0, - "posZ": 20.0, + "posX": 1.0, + "posY": 2.0, + "posZ": 3.0, "rotX": 0.349, "rotY": 0.698, "rotZ": 1.046, diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 4645000..85f2afe 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -35,6 +35,7 @@ from freecad.active_document import FREECAD_FILE_EXTENSION from module.environment import Environment from json_io.json_definitions import JSON_ELEMENT_STL_PATH +import unittest App = FreeCAD Gui = FreeCADGui @@ -279,6 +280,7 @@ def test_create_part_change_shape(self): self.assertIsNotNone(App.ActiveDocument.getObject("Cone"), "Got correct object") App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") + @unittest.SkipTest def test_full_import(self): """ Full JSON import test @@ -324,6 +326,7 @@ def test_full_import(self): # poz_z of -500 should be propagated from "BeamStructure" self.assertEqual(subchild.pos_z, 500.0, "Z position got propagated correctly") + @unittest.SkipTest def test_full_import_again(self): """ Importing the same file again should not result in changes @@ -362,6 +365,7 @@ def test_full_import_again(self): child2 = json_product2.children[i] child1.has_equal_values(child2) + @unittest.SkipTest def test_full_import_again_with_changes(self): """ If two files with the same name get imported: we assume that the VirSat side used CRUD, that means: From 5e0384774b25d8b829c2b738bb7e34ca51d5b279 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Thu, 28 Nov 2019 17:32:57 +0100 Subject: [PATCH 12/20] Add test_json_product_assembly test cases - (Task #5) Add: - test_create_part_product_subassembly_with_root_part - test_create_part_product_assembly_and_subassembly_with_root_part - test_create_part_product_assembly_with_root_part_with_traverser (empty test) --- Task #5: Implement Import Functionality --- .../json_io/json_definitions.py | 3 + .../products/test_json_product_assembly.py | 77 +++++++++++++------ .../test/json_io/test_json_data.py | 52 ++++++++----- 3 files changed, 88 insertions(+), 44 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/json_definitions.py b/VirtualSatelliteCAD/json_io/json_definitions.py index baf6832..d23c4f7 100644 --- a/VirtualSatelliteCAD/json_io/json_definitions.py +++ b/VirtualSatelliteCAD/json_io/json_definitions.py @@ -30,6 +30,9 @@ RAD_TO_DEG = 180.0 / math.pi +JSON_PARTS = "Parts" +JSON_PRODUCTS = "Products" + JSON_ELEMENT_COLOR = "color" JSON_ELEMENT_SHAPE = "shape" diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py index c4ce5ed..bfc7617 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py @@ -32,7 +32,7 @@ from json_io.products.json_product_assembly import JsonProductAssembly from freecad.active_document import ActiveDocument from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN,\ - TEST_JSON_PRODUCT_WITHOUT_CHILDREN, TEST_JSON_PRODUCT_WITH_CHILD_WITH_CHILD + TEST_JSON_PRODUCT_WITHOUT_CHILDREN, TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD App = FreeCAD @@ -91,27 +91,12 @@ def test_parse_with_children(self): self.assertEqual(json_product_child_2.pos_x, 0, "Property is correctly set") self.assertEqual(json_product_child_2.pos_y, 0, "Property is correctly set") - self.assertEqual(json_product_child_2.pos_z, 0.5, "Property is correctly set") + self.assertEqual(json_product_child_2.pos_z, 500.0, "Property is correctly set") self.assertEqual(json_product_child_2.rot_x, 0.0, "Property is correctly set") self.assertEqual(json_product_child_2.rot_y, 0.0, "Property is correctly set") self.assertEqual(json_product_child_2.rot_z, 0.0, "Property is correctly set") - def test_parse_with_child_with_child(self): - # Same json as in test_parse_with_children, but BasePlateTop is now child of BasePlateBottom2 - - json_object = json.loads(TEST_JSON_PRODUCT_WITH_CHILD_WITH_CHILD) - json_product = JsonProductAssembly().parse_from_json(json_object) - - # Check for the children - self.assertEquals(len(json_product.children), 1, "Parsed one children") - - json_product_child_1 = json_product.children[0] - self.assertEqual(json_product_child_1.name, "BasePlateBottom2", "Parsed correct child") - - json_product_child_2 = json_product_child_1.children[0] - self.assertEqual(json_product_child_2.name, "BasePlateTop", "Parsed correct child") - def test_parse_with_no_children(self): json_data = TEST_JSON_PRODUCT_WITHOUT_CHILDREN @@ -149,12 +134,54 @@ def test_create_part_product_assembly_with_root_part(self): product_object = active_document.app_active_document.getObjectsByLabel(product_child2_part_name)[0] self.assertIsNotNone(product_object, "Found an object under the given part name") + def test_create_part_product_subassembly_with_root_part(self): + json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD + self.create_Test_Part() + + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("ProductSubassemblyRootPart") + + json_object = json.loads(json_data) + + subassembly = json_object["children"][0] + json_product = JsonProductAssembly().parse_from_json(subassembly) + + json_product.write_to_freecad(active_document) + active_document.save_as("ProductSubassemblyRootPart") + + # find the object by its label there should be two objects identifiable in the current export + self.assertEquals(len(json_product.children), 1, "correct amount of children") + self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") + + product_part_name = json_product.get_unique_name() + product_object = active_document.app_active_document.getObjectsByLabel(product_part_name)[0] + self.assertIsNotNone(product_object, "Found an object under the given part name") + + product_child1_part_name = json_product.children[0].get_unique_name() + product_object = active_document.app_active_document.getObjectsByLabel(product_child1_part_name)[0] + self.assertIsNotNone(product_object, "Found an object under the given part name") + + # TODO: + def test_create_part_product_assembly_and_subassembly_with_root_part(self): + json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD + self.create_Test_Part() + + json_object = json.loads(json_data) + + subassembly = json_object["children"][0] + + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + + json_product = JsonProductAssembly().parse_from_json(subassembly) + json_product.write_to_freecad(active_document) + active_document.save_as("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + + # TODO: this assembly now has to use the other assembly + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("ProductAssemblyAndSubassemblyRootPart") + + json_product = JsonProductAssembly().parse_from_json(json_object) + json_product.write_to_freecad(active_document) + active_document.save_as("ProductAssemblyAndSubassemblyRootPart") - # TODO: - # test same json as above but with subassembvly as assemblychid - - #TODO: - # test same as above, first create subassembly then with that the assembly - - #TODO: - # test same as above, but with automated tree traverser \ No newline at end of file + # TODO: + def test_create_part_product_assembly_with_root_part_with_traverser(self): + pass diff --git a/VirtualSatelliteCAD/test/json_io/test_json_data.py b/VirtualSatelliteCAD/test/json_io/test_json_data.py index 06bb677..046648d 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_data.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_data.py @@ -136,14 +136,14 @@ } """ -TEST_JSON_PRODUCT_WITH_CHILD_WITH_CHILD = """{ +TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD = """{ "name": "BasePlateBottom1", "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", "partName": "BasePlate", - "posX": 10.0, - "posY": 15.0, - "posZ": 20.0, + "posX": 1.0, + "posY": 2.0, + "posZ": 3.0, "rotX": 0.349, "rotY": 0.698, "rotZ": 1.046, @@ -154,27 +154,41 @@ "posZ": 0.0, "rotX": 0.0, "children": [ - { - "posX": 0.0, - "posY": 0.0, - "posZ": 0.5, - "rotX": 0.0, - "children": [ - ], - "rotZ": 0.0, - "rotY": 0.0, - "name": "BasePlateTop", - "uuid": "a199e3bd-3bc1-426d-8321-e9bd829339b3", - "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", - "partName": "BasePlate" - } + { + "posX": 0.5, + "posY": 0.5, + "posZ": 0.5, + "rotX": 0.0, + "children": [ + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BasePlateBottom3", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45485", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" + } ], - "rotZ": 0.0, + "rotZ": 0.3490659, "rotY": 0.0, "name": "BasePlateBottom2", "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", "partName": "BasePlate" + }, + { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [ + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BasePlateTop", + "uuid": "a199e3bd-3bc1-426d-8321-e9bd829339b3", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" } ] } From c3c7cb96d03f9e61c3da01a5763d360f5e5c3cf0 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 2 Dec 2019 11:38:19 +0100 Subject: [PATCH 13/20] Add JsonProductAssemblyTreeTraverser - (Task #5) --- Task #5: Implement Import Functionality --- .../json_product_assembly_tree_traverser.py | 90 +++++++++++++++++++ .../products/test_json_product_assembly.py | 34 +++++-- 2 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py new file mode 100644 index 0000000..63e57c1 --- /dev/null +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +# +# Virtual Satellite 4 - FreeCAD module +# +# Copyright (C) 2019 by +# +# DLR (German Aerospace Center), +# Software for Space Systems and interactive Visualization +# Braunschweig, Germany +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +from json_io.products.json_product_assembly import JsonProductAssembly +from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME +from freecad.active_document import ActiveDocument + + +class JsonProductAssemblyTreeTraverser(object): + ''' + classdocs + ''' + + def __init__(self): + ''' + Constructor + ''' + pass + + def traverse(self, json_object, depth=0, lst_of_depths=[]): + """ + recursive traverse the tree and create list of depths (length(list) = max_depth): + containing a list of found assembly (NOT child) nodes at that depth for each depth + """ + + # TODO: change condition? + if json_object != "": + print(f"found {json_object[JSON_ELEMENT_NAME]} at {depth}") + + # TODO: test condition + if(JSON_ELEMNT_CHILDREN in json_object and json_object[JSON_ELEMNT_CHILDREN] != []): + # only append assemblys that have children + # print(len(lst_of_depths), depth) + if(len(lst_of_depths) < depth+1): + lst_of_depths.append([]) + print(f"added depth {depth} to lst_of_depths") + print(f"found assembly{json_object[JSON_ELEMENT_NAME]} at {depth}") + + lst_of_depths[depth].append(json_object) + + depth += 1 + + # recursive call on all children + for child in json_object[JSON_ELEMNT_CHILDREN]: + self.traverse(child, depth) + + return lst_of_depths + + def parse_from_json(self, lst_of_depths, cwd): + json_product = None + + for depth in reversed(lst_of_depths): + for assembly in depth: + json_product = JsonProductAssembly().parse_from_json(assembly) + + # TODO: replace cwd + active_document = ActiveDocument(cwd).open_set_and_get_document(json_product.get_unique_name()) + + # print(assembly) + # print(json_product) + json_product.write_to_freecad(active_document) + active_document.save_as(json_product.get_unique_name()) + + return json_product + + def traverse_and_parse_from_json(self, json_object, cwd): + lst_of_depths = self.traverse(json_object) + return self.parse_from_json(lst_of_depths, cwd) diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py index bfc7617..bfad71f 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py @@ -33,7 +33,10 @@ from freecad.active_document import ActiveDocument from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN,\ TEST_JSON_PRODUCT_WITHOUT_CHILDREN, TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD - +from json_io.json_definitions import JSON_ELEMNT_CHILDREN +from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser +import glob +import os App = FreeCAD Gui = FreeCADGui @@ -48,8 +51,14 @@ def setUpClass(cls): cls.setUpDirectory("ProductAssembly/") cls._WORKING_DIRECTORY = cls.getDirectoryFullPath() + def clearWorkingDirectory(self): + filelist = glob.glob(os.path.join(self._WORKING_DIRECTORY, "*")) + for f in filelist: + os.remove(f) + def tearDown(self): super().tearDown() + self.clearWorkingDirectory() def test_parse_with_children(self): json_object = json.loads(self.json_data) @@ -142,7 +151,7 @@ def test_create_part_product_subassembly_with_root_part(self): json_object = json.loads(json_data) - subassembly = json_object["children"][0] + subassembly = json_object[JSON_ELEMNT_CHILDREN][0] json_product = JsonProductAssembly().parse_from_json(subassembly) json_product.write_to_freecad(active_document) @@ -160,14 +169,14 @@ def test_create_part_product_subassembly_with_root_part(self): product_object = active_document.app_active_document.getObjectsByLabel(product_child1_part_name)[0] self.assertIsNotNone(product_object, "Found an object under the given part name") - # TODO: + # TODO: move following tests into seperate file for tree_traverser test? def test_create_part_product_assembly_and_subassembly_with_root_part(self): json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD self.create_Test_Part() json_object = json.loads(json_data) - subassembly = json_object["children"][0] + subassembly = json_object[JSON_ELEMNT_CHILDREN][0] active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") @@ -175,13 +184,26 @@ def test_create_part_product_assembly_and_subassembly_with_root_part(self): json_product.write_to_freecad(active_document) active_document.save_as("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") - # TODO: this assembly now has to use the other assembly active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("ProductAssemblyAndSubassemblyRootPart") json_product = JsonProductAssembly().parse_from_json(json_object) json_product.write_to_freecad(active_document) active_document.save_as("ProductAssemblyAndSubassemblyRootPart") + # TODO: assigns + # TODO: def test_create_part_product_assembly_with_root_part_with_traverser(self): - pass + json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD + self.create_Test_Part() + + json_object = json.loads(json_data) + + traverser = JsonProductAssemblyTreeTraverser() + lst_of_depths = traverser.traverse(json_object) + + print(len(lst_of_depths)) + print(lst_of_depths[0]) + print(lst_of_depths[1]) + + traverser.parse_from_json(lst_of_depths, self._WORKING_DIRECTORY) From 90b28ba633f13376d1d3e8d91f898d1dcba8f954 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 2 Dec 2019 14:16:42 +0100 Subject: [PATCH 14/20] Add test_json_product_assemby_tree_traverser - (Task #5) - Add first test cases - Add the test class to TestVirtualSatelliteApp - Update traverser - Update product assembly test --- Task #5: Implement Import Functionality --- .../TestVirtualSatelliteApp.py | 1 + .../json_product_assembly_tree_traverser.py | 42 +++++---- .../products/test_json_product_assembly.py | 34 +------ ...st_json_product_assembly_tree_traverser.py | 91 +++++++++++++++++++ 4 files changed, 118 insertions(+), 50 deletions(-) create mode 100644 VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py diff --git a/VirtualSatelliteCAD/TestVirtualSatelliteApp.py b/VirtualSatelliteCAD/TestVirtualSatelliteApp.py index 8ebab03..abf8191 100644 --- a/VirtualSatelliteCAD/TestVirtualSatelliteApp.py +++ b/VirtualSatelliteCAD/TestVirtualSatelliteApp.py @@ -36,4 +36,5 @@ from test.json_io.products.test_json_product import TestJsonProduct # NOQA from test.json_io.products.test_json_product_assembly import TestJsonProductAssembly # NOQA from test.json_io.products.test_json_product_child import TestJsonProductChild # NOQA +from test.json_io.products.test_json_product_assembly_tree_traverser import TestJsonProductAssemblyTreeTraverser # NOQA from test.freecad.test_actice_document import TestActiveDocument # NOQA diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py index 63e57c1..d4afd07 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py @@ -33,13 +33,14 @@ class JsonProductAssemblyTreeTraverser(object): classdocs ''' - def __init__(self): + def __init__(self, working_output_directory): ''' Constructor ''' - pass + self._lst_of_depths = [] + self.working_output_directory = working_output_directory - def traverse(self, json_object, depth=0, lst_of_depths=[]): + def traverse(self, json_object, depth=0): # , _lst_of_depths=[]): """ recursive traverse the tree and create list of depths (length(list) = max_depth): containing a list of found assembly (NOT child) nodes at that depth for each depth @@ -47,36 +48,37 @@ def traverse(self, json_object, depth=0, lst_of_depths=[]): # TODO: change condition? if json_object != "": - print(f"found {json_object[JSON_ELEMENT_NAME]} at {depth}") # TODO: test condition if(JSON_ELEMNT_CHILDREN in json_object and json_object[JSON_ELEMNT_CHILDREN] != []): # only append assemblys that have children - # print(len(lst_of_depths), depth) - if(len(lst_of_depths) < depth+1): - lst_of_depths.append([]) - print(f"added depth {depth} to lst_of_depths") - print(f"found assembly{json_object[JSON_ELEMENT_NAME]} at {depth}") + # print(len(_lst_of_depths), depth) + if(len(self._lst_of_depths) < depth + 1): + self._lst_of_depths.append([]) + print(f"Added depth {depth} to _lst_of_depths") + print(f"Found assembly '{json_object[JSON_ELEMENT_NAME]}' at {depth}") - lst_of_depths[depth].append(json_object) + self._lst_of_depths[depth].append(json_object) - depth += 1 + # depth += 1 # recursive call on all children for child in json_object[JSON_ELEMNT_CHILDREN]: - self.traverse(child, depth) + self.traverse(child, depth+1) - return lst_of_depths + # if depth == 0: + # return self._lst_of_depths + # return None - def parse_from_json(self, lst_of_depths, cwd): + def parse_from_json(self): json_product = None - for depth in reversed(lst_of_depths): + for depth in reversed(self._lst_of_depths): for assembly in depth: + print(f"Parsing '{assembly[JSON_ELEMENT_NAME]}'") json_product = JsonProductAssembly().parse_from_json(assembly) - # TODO: replace cwd - active_document = ActiveDocument(cwd).open_set_and_get_document(json_product.get_unique_name()) + active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_unique_name()) # print(assembly) # print(json_product) @@ -85,6 +87,6 @@ def parse_from_json(self, lst_of_depths, cwd): return json_product - def traverse_and_parse_from_json(self, json_object, cwd): - lst_of_depths = self.traverse(json_object) - return self.parse_from_json(lst_of_depths, cwd) + def traverse_and_parse_from_json(self, json_object): + self.traverse(json_object) + return self.parse_from_json() diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py index bfad71f..c950499 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py @@ -34,9 +34,6 @@ from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN,\ TEST_JSON_PRODUCT_WITHOUT_CHILDREN, TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD from json_io.json_definitions import JSON_ELEMNT_CHILDREN -from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser -import glob -import os App = FreeCAD Gui = FreeCADGui @@ -51,14 +48,8 @@ def setUpClass(cls): cls.setUpDirectory("ProductAssembly/") cls._WORKING_DIRECTORY = cls.getDirectoryFullPath() - def clearWorkingDirectory(self): - filelist = glob.glob(os.path.join(self._WORKING_DIRECTORY, "*")) - for f in filelist: - os.remove(f) - def tearDown(self): super().tearDown() - self.clearWorkingDirectory() def test_parse_with_children(self): json_object = json.loads(self.json_data) @@ -120,7 +111,6 @@ def test_create_part_product_assembly_with_root_part(self): active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("ProductAssemblyRootPart") json_object = json.loads(self.json_data) json_product = JsonProductAssembly().parse_from_json(json_object) - # active_document.save_as("ProductAssemblyRootPart") json_product.write_to_freecad(active_document) @@ -157,7 +147,6 @@ def test_create_part_product_subassembly_with_root_part(self): json_product.write_to_freecad(active_document) active_document.save_as("ProductSubassemblyRootPart") - # find the object by its label there should be two objects identifiable in the current export self.assertEquals(len(json_product.children), 1, "correct amount of children") self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") @@ -169,8 +158,7 @@ def test_create_part_product_subassembly_with_root_part(self): product_object = active_document.app_active_document.getObjectsByLabel(product_child1_part_name)[0] self.assertIsNotNone(product_object, "Found an object under the given part name") - # TODO: move following tests into seperate file for tree_traverser test? - def test_create_part_product_assembly_and_subassembly_with_root_part(self): + def test_create_part_product_assembly_and_subassembly_with_root_part_manual(self): json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD self.create_Test_Part() @@ -184,26 +172,12 @@ def test_create_part_product_assembly_and_subassembly_with_root_part(self): json_product.write_to_freecad(active_document) active_document.save_as("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("ProductAssemblyAndSubassemblyRootPart") json_product = JsonProductAssembly().parse_from_json(json_object) json_product.write_to_freecad(active_document) active_document.save_as("ProductAssemblyAndSubassemblyRootPart") - # TODO: assigns - - # TODO: - def test_create_part_product_assembly_with_root_part_with_traverser(self): - json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD - self.create_Test_Part() - - json_object = json.loads(json_data) - - traverser = JsonProductAssemblyTreeTraverser() - lst_of_depths = traverser.traverse(json_object) - - print(len(lst_of_depths)) - print(lst_of_depths[0]) - print(lst_of_depths[1]) - - traverser.parse_from_json(lst_of_depths, self._WORKING_DIRECTORY) + self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py new file mode 100644 index 0000000..aed10d9 --- /dev/null +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py @@ -0,0 +1,91 @@ +# +# DLR (German Aerospace Center), +# Software for Space Systems and interactive Visualization +# Braunschweig, Germany +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# +from test.test_setup import AWorkingDirectoryTest +from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD, TEST_JSON_PRODUCT_WITHOUT_CHILDREN +import json +from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser +from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME +from json_io.products.json_product_assembly import JsonProductAssembly +from freecad.active_document import ActiveDocument + + +class TestJsonProductAssemblyTreeTraverser(AWorkingDirectoryTest): + + @classmethod + def setUpClass(cls): + cls.setUpDirectory("ProductAssemblyTreeTraverser/") + cls._WORKING_DIRECTORY = cls.getDirectoryFullPath() + + def tearDown(self): + super().tearDown() + + def test_traverse_json(self): + json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD + self.create_Test_Part() + + json_object = json.loads(json_data) + + traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) + traverser.traverse(json_object) + lst_of_depths = traverser._lst_of_depths + + self.assertEqual(len(lst_of_depths), 2, "Found the right amount of 2 depths") + self.assertEqual(len(lst_of_depths[0]), 1, "Found the right amount of 1 assembly at depth 0") + self.assertEqual(len(lst_of_depths[1]), 1, "Found the right amount of 1 assembly at depth 1") + self.assertEqual(lst_of_depths[0][0][JSON_ELEMENT_NAME], "BasePlateBottom1", "Found the right element at depth 0") + self.assertEqual(lst_of_depths[1][0][JSON_ELEMENT_NAME], "BasePlateBottom2", "Found the right element at depth 1") + + def test_parse_json_from_tree_without_traversing(self): + + traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) + self.assertIsNone(traverser.parse_from_json(), "Parsing no read in json object will result in 'None'") + + def test_traverse_and_parse_json_tree(self): + json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD + self.create_Test_Part() + + json_object = json.loads(json_data) + + traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) + traverser.traverse(json_object) + lst_of_depths = traverser._lst_of_depths + + self.assertEqual(len(lst_of_depths), 2, "Found the right amount of 2 assemblies") + + traverser.parse_from_json() + + # this should have similar results to test_create_part_product_assembly_and_subassembly_with_root_part_manual in TestJsonProductAssembly + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") + + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") + self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") + + def test_traverse_and_parse_json_tree_rootassembly_without_children(self): + json_data = TEST_JSON_PRODUCT_WITHOUT_CHILDREN + self.create_Test_Part() + + json_object = json.loads(json_data) + + traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) + traverser.traverse_and_parse_from_json(json_object) + + self.assertIsNone(traverser.parse_from_json()) From e7af1935ac5701dcc3c21eff98694ce32ecab34c Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 2 Dec 2019 14:31:47 +0100 Subject: [PATCH 15/20] Cleanup traverser - (Task #5) --- Task #5: Implement Import Functionality --- .../json_io/products/json_product.py | 4 +- .../json_product_assembly_tree_traverser.py | 56 ++++++++----------- .../json_io/products/json_product_child.py | 18 +++--- ...st_json_product_assembly_tree_traverser.py | 3 +- 4 files changed, 36 insertions(+), 45 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/products/json_product.py b/VirtualSatelliteCAD/json_io/products/json_product.py index 1f7ffcf..1b81a92 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product.py +++ b/VirtualSatelliteCAD/json_io/products/json_product.py @@ -32,7 +32,7 @@ from json_io.json_spread_sheet import JsonSpreadSheet from A2plus.a2p_importpart import importPartFromFile from freecad.active_document import VECTOR_X, VECTOR_Y, VECTOR_Z, VECTOR_ZERO -from FreeCAD import Placement +# from FreeCAD import Placement class AJsonProduct(): @@ -108,7 +108,7 @@ def _create_or_update_freecad_part(self, active_document): import_part_full_path = active_document.get_file_full_path(import_part_file_name) import_part_ref = active_document.app_active_document.getObjectsByLabel(import_part_name_in_product) - print(f"Called with '{import_part_name_in_product}'") + # print(f"Called with '{import_part_name_in_product}'") # TODO: # If the part doesn't exists (the returned list is not empty) update (delete and recreate) it if import_part_ref: diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py index d4afd07..35a13f0 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py @@ -26,62 +26,54 @@ from json_io.products.json_product_assembly import JsonProductAssembly from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME from freecad.active_document import ActiveDocument +import FreeCAD +Log = FreeCAD.Console.PrintLog class JsonProductAssemblyTreeTraverser(object): ''' - classdocs + This class provides functionality to traverse a product tree to parse the product assemblies in the right order ''' def __init__(self, working_output_directory): - ''' - Constructor - ''' self._lst_of_depths = [] self.working_output_directory = working_output_directory - def traverse(self, json_object, depth=0): # , _lst_of_depths=[]): + def traverse(self, json_object, depth=0): """ - recursive traverse the tree and create list of depths (length(list) = max_depth): - containing a list of found assembly (NOT child) nodes at that depth for each depth + Recursive traverse the tree and create a list containing the found depths, + that for each depth contains a list of found assembly (NOT child) nodes at that depth """ - # TODO: change condition? - if json_object != "": + # only look for products that have children + if(JSON_ELEMNT_CHILDREN in json_object and json_object[JSON_ELEMNT_CHILDREN] != []): - # TODO: test condition - if(JSON_ELEMNT_CHILDREN in json_object and json_object[JSON_ELEMNT_CHILDREN] != []): - # only append assemblys that have children - # print(len(_lst_of_depths), depth) - if(len(self._lst_of_depths) < depth + 1): - self._lst_of_depths.append([]) - print(f"Added depth {depth} to _lst_of_depths") - print(f"Found assembly '{json_object[JSON_ELEMENT_NAME]}' at {depth}") + # if the current depth has no list in the _lst_of_depths, add it + if(len(self._lst_of_depths) < depth + 1): + self._lst_of_depths.append([]) + Log(f"Added depth {depth} to _lst_of_depths") - self._lst_of_depths[depth].append(json_object) + # append found assembly to the list + self._lst_of_depths[depth].append(json_object) + Log(f"Found assembly '{json_object[JSON_ELEMENT_NAME]}' at {depth}") - # depth += 1 - - # recursive call on all children - for child in json_object[JSON_ELEMNT_CHILDREN]: - self.traverse(child, depth+1) - - # if depth == 0: - # return self._lst_of_depths - # return None + # recursive call on all children + for child in json_object[JSON_ELEMNT_CHILDREN]: + self.traverse(child, depth+1) def parse_from_json(self): + """ + Iterate through the list created by traversing the tree in reverse and parse the found product assemblies + """ json_product = None + # parse in reverse order for depth in reversed(self._lst_of_depths): for assembly in depth: - print(f"Parsing '{assembly[JSON_ELEMENT_NAME]}'") - json_product = JsonProductAssembly().parse_from_json(assembly) + Log(f"Parsing '{assembly[JSON_ELEMENT_NAME]}'") + json_product = JsonProductAssembly().parse_from_json(assembly) active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_unique_name()) - - # print(assembly) - # print(json_product) json_product.write_to_freecad(active_document) active_document.save_as(json_product.get_unique_name()) diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index a759a8b..1843311 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -43,32 +43,32 @@ def get_part_unique_name(self): return _get_combined_name_uuid(self.part_name, self.part_uuid) # def parse_from_json(self, json_object): -# +# # super().parse_from_json(json_object) -# +# # if self.has_children: # # Get all children from the json and try to parse them # # into JsonProductChild objects # json_object_children = list(json_object[JSON_ELEMNT_CHILDREN]) -# +# # self.children = [] # for json_object_child in json_object_children: -# +# # # TODO: child to check it has children? -> differ -# +# # json_product_child = JsonProductChild().parse_from_json(json_object_child) # # json_product_child.propagate_pos_and_rot_from_parent(self) # self.children.append(json_product_child) -# +# # return self -# +# # def write_to_freecad(self, active_document): # # This assembly may refer to a part as well # # hence if there is a partUuid and if there is a part name, than # # it should be written to the FreeCAD document as well. # if self.is_part_reference(): # super().write_to_freecad(active_document) -# +# # # And now write the children, they decide on their own if they reference # # part or a product # if self.has_children: @@ -84,7 +84,7 @@ def get_part_unique_name(self): # self.pos_x += parent.pos_x # self.pos_y += parent.pos_y # self.pos_z += parent.pos_z -# +# # self.rot_x += parent.rot_x # self.rot_y += parent.rot_y # self.rot_z += parent.rot_z diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py index aed10d9..ba812a5 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py @@ -22,8 +22,7 @@ from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD, TEST_JSON_PRODUCT_WITHOUT_CHILDREN import json from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser -from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME -from json_io.products.json_product_assembly import JsonProductAssembly +from json_io.json_definitions import JSON_ELEMENT_NAME from freecad.active_document import ActiveDocument From 694b1dd250f70475dfe491a3ec0addc52dc469bd Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Mon, 2 Dec 2019 18:13:49 +0100 Subject: [PATCH 16/20] Use traverser in importer and add test cases- (Task #5) Try to use the traverser in the importer and reactivating the full_import test case showed problems with assemblies having the same name and uuid as their part. To further investigate that, test cases were added in the traverser. --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/json_io/json_importer.py | 24 +- .../json_io/json_spread_sheet.py | 20 +- .../json_product_assembly_tree_traverser.py | 16 +- ...st_json_product_assembly_tree_traverser.py | 83 +++++- .../test/json_io/test_json_data.py | 260 ++++++++++++++++++ .../test/json_io/test_json_importer.py | 22 +- 6 files changed, 373 insertions(+), 52 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index 5ae8848..04bc645 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -28,10 +28,11 @@ import FreeCADGui from freecad.active_document import ActiveDocument from json_io.parts.json_part_factory import JsonPartFactory -from json_io.products.json_product_assembly import JsonProductAssembly -from json_io.json_definitions import get_part_name_uuid +from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser +from json_io.json_definitions import get_part_name_uuid, JSON_PRODUCTS, JSON_PARTS import json +from freecad import active_document App = FreeCAD Gui = FreeCADGui @@ -89,27 +90,14 @@ def full_import(self, filepath): Log("Please provide a valid JSON\n") return - json_parts = json_object['Parts'] + json_parts = json_object[JSON_PARTS] part_file_names = [] for part in json_parts: part_file_names.append(self.create_or_update_part(part)) - # json assembly with json product object - json_product = JsonProductAssembly().parse_from_json(json_object['Products']) - - # name the freecad document after the root product - freecad_name = json_product.name - - # If there is a root document with the same name open already: - # assume that all changes of the current import are valid (CRUD) - # so clear the document - ActiveDocument(self.working_output_directory).clear_if_open_document(freecad_name) - - active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(freecad_name) - json_product.write_to_freecad(active_document) - - active_document.save_as(freecad_name) + traverser = JsonProductAssemblyTreeTraverser(self.working_output_directory) + json_product, active_document = traverser.traverse_and_parse_from_json(json_object[JSON_PRODUCTS]) Log(f"Import successful\n") diff --git a/VirtualSatelliteCAD/json_io/json_spread_sheet.py b/VirtualSatelliteCAD/json_io/json_spread_sheet.py index 600630d..fe7626e 100644 --- a/VirtualSatelliteCAD/json_io/json_spread_sheet.py +++ b/VirtualSatelliteCAD/json_io/json_spread_sheet.py @@ -67,14 +67,18 @@ def write_to_freecad(self, active_document): sheet_line = FREECAD_PART_SHEET_ATTRIBUTE_START_LINE for json_part_attribute_name in list(self._json_part_or_product.attributes.keys()): - json_part_attribute_value = str(getattr(self._json_part_or_product, json_part_attribute_name)) - json_part_attribute_unit = self._json_part_or_product.attributes[json_part_attribute_name] - - sheet.set("A" + str(sheet_line), json_part_attribute_name) - sheet.set("B" + str(sheet_line), json_part_attribute_value) - sheet.set("C" + str(sheet_line), json_part_attribute_unit) - - sheet_line += 1 + # TODO: added this try catch because some children would not have part_name and attribute (because they are assemblies) + try: + json_part_attribute_value = str(getattr(self._json_part_or_product, json_part_attribute_name)) + json_part_attribute_unit = self._json_part_or_product.attributes[json_part_attribute_name] + + sheet.set("A" + str(sheet_line), json_part_attribute_name) + sheet.set("B" + str(sheet_line), json_part_attribute_value) + sheet.set("C" + str(sheet_line), json_part_attribute_unit) + + sheet_line += 1 + except AttributeError as e: + print(e) # Recompute the sheet, so that all properties are correctly written # if not recomputed accessing the properties will result in none objects diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py index 35a13f0..a79d01b 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py @@ -51,11 +51,11 @@ def traverse(self, json_object, depth=0): # if the current depth has no list in the _lst_of_depths, add it if(len(self._lst_of_depths) < depth + 1): self._lst_of_depths.append([]) - Log(f"Added depth {depth} to _lst_of_depths") + Log(f"Added depth {depth} to _lst_of_depths\n") # append found assembly to the list self._lst_of_depths[depth].append(json_object) - Log(f"Found assembly '{json_object[JSON_ELEMENT_NAME]}' at {depth}") + Log(f"Found assembly '{json_object[JSON_ELEMENT_NAME]}' at {depth}\n") # recursive call on all children for child in json_object[JSON_ELEMNT_CHILDREN]: @@ -65,19 +65,23 @@ def parse_from_json(self): """ Iterate through the list created by traversing the tree in reverse and parse the found product assemblies """ - json_product = None + json_product, active_document = None, None # parse in reverse order for depth in reversed(self._lst_of_depths): for assembly in depth: - Log(f"Parsing '{assembly[JSON_ELEMENT_NAME]}'") + Log(f"Parsing '{assembly[JSON_ELEMENT_NAME]}'\n") json_product = JsonProductAssembly().parse_from_json(assembly) active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_unique_name()) json_product.write_to_freecad(active_document) - active_document.save_as(json_product.get_unique_name()) + active_document.save_and_close_active_document(json_product.get_unique_name()) # + "_assembly") - return json_product + # the last json_product is the root of the assembly, open it again for the UI + if(json_product is not None): + active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_unique_name()) + + return json_product, active_document def traverse_and_parse_from_json(self, json_object): self.traverse(json_object) diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py index ba812a5..f6715db 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py @@ -19,11 +19,16 @@ # SPDX-License-Identifier: LGPL-3.0-or-later # from test.test_setup import AWorkingDirectoryTest -from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD, TEST_JSON_PRODUCT_WITHOUT_CHILDREN +from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD, TEST_JSON_PRODUCT_WITHOUT_CHILDREN, \ + TEST_JSON_FULL_VISCUBE, TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD_SUBASSEMBLY_IS_NO_PART, \ + TEST_JSON_PRODUCT_SUBASSEMBLY_WITH_SAME_PART import json from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser from json_io.json_definitions import JSON_ELEMENT_NAME from freecad.active_document import ActiveDocument +import unittest +import glob +import os class TestJsonProductAssemblyTreeTraverser(AWorkingDirectoryTest): @@ -36,6 +41,11 @@ def setUpClass(cls): def tearDown(self): super().tearDown() + def clearWorkingDirectory(self): + filelist = glob.glob(os.path.join(self._WORKING_DIRECTORY, "*")) + for f in filelist: + os.remove(f) + def test_traverse_json(self): json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD self.create_Test_Part() @@ -55,7 +65,7 @@ def test_traverse_json(self): def test_parse_json_from_tree_without_traversing(self): traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) - self.assertIsNone(traverser.parse_from_json(), "Parsing no read in json object will result in 'None'") + self.assertIsNone(traverser.parse_from_json()[0], "Parsing no read in json object will result in 'None'") def test_traverse_and_parse_json_tree(self): json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD @@ -78,6 +88,73 @@ def test_traverse_and_parse_json_tree(self): active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") + def test_traverse_and_parse_json_tree_subassembly_no_part(self): + self.clearWorkingDirectory() + json_data = TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD_SUBASSEMBLY_IS_NO_PART + self.create_Test_Part() + + json_object = json.loads(json_data) + + traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) + traverser.traverse(json_object) + lst_of_depths = traverser._lst_of_depths + + self.assertEqual(len(lst_of_depths), 2, "Found the right amount of 2 assemblies") + + traverser.parse_from_json() + + # in this test case the product assembly "BasePlateBottom2" only has a child and not a part reference + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + self.assertEquals(len(active_document.app_active_document.RootObjects), 2, "Found correct amount of root objects 1 objects plus 1 sheets") + + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") + self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") + + def test_traverse_and_parse_json_tree_subassembly_same_part(self): + self.clearWorkingDirectory() + json_data = TEST_JSON_PRODUCT_SUBASSEMBLY_WITH_SAME_PART + self.create_Test_Part() + + json_object = json.loads(json_data) + + traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) + traverser.traverse(json_object) + lst_of_depths = traverser._lst_of_depths + + self.assertEqual(len(lst_of_depths), 2, "Found the right amount of 2 assemblies") + + traverser.parse_from_json() + + # in this test case the product assembly "BasePlateBottom2" only has a child and not a part reference + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlate_3d3708fd_5c6c_4af9_b710_d68778466084") + self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") + + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") + self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") + + # TODO remove/adapt + @unittest.SkipTest + def test_traverse_and_parse_json_tree3(self): + json_data = TEST_JSON_FULL_VISCUBE + self.create_Test_Part() + + json_object = json.loads(json_data) + + traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) + traverser.traverse(json_object) + lst_of_depths = traverser._lst_of_depths + + self.assertEqual(len(lst_of_depths), 2, "Found the right amount of 2 assemblies") + + traverser.parse_from_json() + + # this should have similar results to test_create_part_product_assembly_and_subassembly_with_root_part_manual in TestJsonProductAssembly + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BeamStructure_2afb23c9_f458_4bdb_a4e7_fc863364644f") + self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") + + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("SpaceCube_a3533e02_125c_4066_bffe_d046d8d8342a") + self.assertEquals(len(active_document.app_active_document.RootObjects), 10, "Found correct amount of root objects 5 objects plus 5 sheets") + def test_traverse_and_parse_json_tree_rootassembly_without_children(self): json_data = TEST_JSON_PRODUCT_WITHOUT_CHILDREN self.create_Test_Part() @@ -87,4 +164,4 @@ def test_traverse_and_parse_json_tree_rootassembly_without_children(self): traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) traverser.traverse_and_parse_from_json(json_object) - self.assertIsNone(traverser.parse_from_json()) + self.assertIsNone(traverser.parse_from_json()[0], "Parsing a json object without children") diff --git a/VirtualSatelliteCAD/test/json_io/test_json_data.py b/VirtualSatelliteCAD/test/json_io/test_json_data.py index 046648d..15e9637 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_data.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_data.py @@ -194,6 +194,107 @@ } """ +TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD_SUBASSEMBLY_IS_NO_PART = """{ + "name": "BasePlateBottom1", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate", + "posX": 1.0, + "posY": 2.0, + "posZ": 3.0, + "rotX": 0.349, + "rotY": 0.698, + "rotZ": 1.046, + "children": [ + { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.0, + "rotX": 0.0, + "children": [ + { + "posX": 0.5, + "posY": 0.5, + "posZ": 0.5, + "rotX": 0.0, + "children": [ + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BasePlateBottom3", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45485", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" + } + ], + "rotZ": 0.3490659, + "rotY": 0.0, + "name": "BasePlateBottom2", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484" + }, + { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [ + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BasePlateTop", + "uuid": "a199e3bd-3bc1-426d-8321-e9bd829339b3", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" + } + ] + } + """ + +TEST_JSON_PRODUCT_SUBASSEMBLY_WITH_SAME_PART = """{ + "name": "BasePlateBottom1", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate", + "posX": 1.0, + "posY": 2.0, + "posZ": 3.0, + "rotX": 0.349, + "rotY": 0.698, + "rotZ": 1.046, + "children": [ + { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.0, + "rotX": 0.0, + "children": [ + { + "posX": 0.5, + "posY": 0.5, + "posZ": 0.5, + "rotX": 0.0, + "children": [ + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BasePlateBottom3", + "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45485", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" + } + ], + "rotZ": 0.3490659, + "rotY": 0.0, + "name": "BasePlate", + "uuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partUuid": "3d3708fd-5c6c-4af9-b710-d68778466084", + "partName": "BasePlate" + } + ] + } + """ + + TEST_JSON_PRODUCT_WITHOUT_CHILDREN = """{ "name": "BasePlateBottom", "uuid": "e8794f3d-86ec-44c5-9618-8b7170c45484", @@ -239,3 +340,162 @@ ] } """ +TEST_JSON_FULL_VISCUBE = """{ + "Products": { + "children": [{ + "posX": 0.0, + "posY": 0.0, + "posZ": 1.0, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Top", + "uuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d56", + "partUuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d56", + "partName": "Top" + }, { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.0, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Bottom", + "uuid": "61db0622-6fef-4f12-932d-a00fdb9d0848", + "partUuid": "00f430a6-6311-4a33-961b-41ded4cf57d5", + "partName": "Plate" + }, { + "posX": 0.5, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 1.5707963267948966, + "name": "Front", + "uuid": "e6af9d3f-8ad6-4488-b3d0-d35549be9a1e", + "partUuid": "e6af9d3f-8ad6-4488-b3d0-d35549be9a1e", + "partName": "Front" + }, { + "posX": -0.5, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [], + "rotZ": 0.0, + "rotY": 1.5707963267948966, + "name": "Back", + "uuid": "a3c9c547-8fd3-40d5-97a1-a3f9a3a9c337", + "partUuid": "a3c9c547-8fd3-40d5-97a1-a3f9a3a9c337", + "partName": "Back" + }, { + "posX": 0.0, + "posY": 0.0, + "posZ": 0.5, + "rotX": 0.0, + "children": [{ + "posX": 0.0, + "posY": 0.5, + "posZ": 0.0, + "rotX": 1.5707963267948966, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Left", + "uuid": "615985c0-73fd-48db-8f8b-e11b7cbb2ee8", + "partUuid": "615985c0-73fd-48db-8f8b-e11b7cbb2ee8", + "partName": "Left" + }, { + "posX": 0.0, + "posY": -0.5, + "posZ": 0.0, + "rotX": 1.5707963267948966, + "children": [], + "rotZ": 0.0, + "rotY": 0.0, + "name": "Right", + "uuid": "882a0b35-7da8-4555-903d-fd6b5cbec392", + "partUuid": "882a0b35-7da8-4555-903d-fd6b5cbec392", + "partName": "Right" + } + ], + "rotZ": 0.0, + "rotY": 0.0, + "name": "BeamStructure", + "uuid": "2afb23c9-f458-4bdb-a4e7-fc863364644f", + "partUuid": "2afb23c9-f458-4bdb-a4e7-fc863364644f", + "partName": "BeamStructure" + } + ], + "name": "SpaceCube", + "uuid": "a3533e02-125c-4066-bffe-d046d8d8342a" + }, + "Parts": [{ + "color": 16744448, + "shape": "CYLINDER", + "name": "BeamStructure", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "2afb23c9-f458-4bdb-a4e7-fc863364644f", + "lengthZ": 1.0 + }, { + "color": 8388608, + "shape": "BOX", + "name": "Right", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "882a0b35-7da8-4555-903d-fd6b5cbec392", + "lengthZ": 0.02 + }, { + "color": 32832, + "shape": "BOX", + "name": "Front", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "e6af9d3f-8ad6-4488-b3d0-d35549be9a1e", + "lengthZ": 0.02 + }, { + "color": 16711680, + "shape": "BOX", + "name": "Left", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "615985c0-73fd-48db-8f8b-e11b7cbb2ee8", + "lengthZ": 0.02 + }, { + "color": 65280, + "shape": "BOX", + "name": "Plate", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "00f430a6-6311-4a33-961b-41ded4cf57d5", + "lengthZ": 0.02 + }, { + "color": 16776960, + "shape": "BOX", + "name": "Back", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "a3c9c547-8fd3-40d5-97a1-a3f9a3a9c337", + "lengthZ": 0.02 + }, { + "color": 32768, + "shape": "BOX", + "name": "Top", + "lengthY": 1.0, + "lengthX": 1.0, + "radius": 0.05, + "uuid": "cc14e2c7-9d7e-4cf2-8d6d-9b8cf5e96d56", + "lengthZ": 0.02 + } + ] + } + """ diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 85f2afe..efeb5fa 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -36,6 +36,7 @@ from module.environment import Environment from json_io.json_definitions import JSON_ELEMENT_STL_PATH import unittest +from freecad.active_document import ActiveDocument App = FreeCAD Gui = FreeCADGui @@ -308,23 +309,10 @@ def test_full_import(self): # Check that the right number of children and root objects got created self.assertEquals(len(json_product.children), 5, "Correct amount of children") - self.assertEquals(len(active_document.app_active_document.RootObjects), 14, "Found correct amount of root objects 7 plus 7 sheets") - - # Check that for each child a file exists - for child in json_product.children: - product_object = active_document.app_active_document.getObjectsByLabel(child.get_unique_name()) - # An empty list in python gets asserted to true - self.assertTrue(product_object, "Found an object under the given part name") - if(child.name == "BeamStructure"): - # Check that two sub children are found - self.assertEquals(len(child.children), 2, "Correct amount of children") - for subchild in child.children: - product_object = active_document.app_active_document.getObjectsByLabel(subchild.get_unique_name()) - self.assertIsNotNone(product_object, "Found an object under the given part name") - - # Check propagation - # poz_z of -500 should be propagated from "BeamStructure" - self.assertEqual(subchild.pos_z, 500.0, "Z position got propagated correctly") + self.assertEquals(len(active_document.app_active_document.RootObjects), 10, "Found correct amount of root objects 5 plus 5 sheets") + + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BeamStructure_2afb23c9_f458_4bdb_a4e7_fc863364644f") + self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") @unittest.SkipTest def test_full_import_again(self): From a7ce01599b23ae7f8064dc48d15d03a2a5434743 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Tue, 3 Dec 2019 14:41:39 +0100 Subject: [PATCH 17/20] Add identifiers for product assembly and part files - (Task #5) These solve the problem of having a part and an assembly with the same name and uuid. - Add the identifiers to json_definitions - Update the names in part and product files and corresponding test files --- Task #5: Implement Import Functionality --- .../json_io/json_definitions.py | 3 +++ .../json_io/parts/json_part.py | 4 +-- .../json_io/products/json_product.py | 5 ++-- .../json_io/products/json_product_assembly.py | 6 ++++- .../json_product_assembly_tree_traverser.py | 6 ++--- .../json_io/products/json_product_child.py | 6 ++--- .../test/json_io/parts/test_json_part.py | 3 ++- .../json_io/products/test_json_product.py | 3 ++- .../products/test_json_product_assembly.py | 7 +++--- ...st_json_product_assembly_tree_traverser.py | 25 ++++++++++++------- .../products/test_json_product_child.py | 8 +++--- 11 files changed, 47 insertions(+), 29 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/json_definitions.py b/VirtualSatelliteCAD/json_io/json_definitions.py index d23c4f7..2eca28b 100644 --- a/VirtualSatelliteCAD/json_io/json_definitions.py +++ b/VirtualSatelliteCAD/json_io/json_definitions.py @@ -67,6 +67,9 @@ JSON_ELEMNT_CHILDREN = "children" +PART_IDENTIFIER = "part_" +PRODUCT_IDENTIFIER = "assembly_" + def _get_combined_name_uuid(name, uuid): return str(name + "_" + uuid.replace("-", "_")) diff --git a/VirtualSatelliteCAD/json_io/parts/json_part.py b/VirtualSatelliteCAD/json_io/parts/json_part.py index d0305e2..70192e6 100644 --- a/VirtualSatelliteCAD/json_io/parts/json_part.py +++ b/VirtualSatelliteCAD/json_io/parts/json_part.py @@ -27,7 +27,7 @@ from json_io.json_definitions import JSON_ELEMENT_NAME, JSON_ELEMENT_SHAPE,\ JSON_ELEMENT_UUID, JSON_ELEMENT_LENGTH_X, JSON_ELEMENT_LENGTH_Y,\ JSON_ELEMENT_LENGTH_Z, JSON_ELEMENT_RADIUS, JSON_ELEMENT_COLOR, M_TO_MM,\ - _get_combined_name_uuid + _get_combined_name_uuid, PART_IDENTIFIER from json_io.json_spread_sheet import JsonSpreadSheet @@ -138,4 +138,4 @@ def get_shape_type(self): return shape_type def get_unique_name(self): - return _get_combined_name_uuid(self.name, self.uuid) + return PART_IDENTIFIER + _get_combined_name_uuid(self.name, self.uuid) diff --git a/VirtualSatelliteCAD/json_io/products/json_product.py b/VirtualSatelliteCAD/json_io/products/json_product.py index 1b81a92..0ace4f1 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product.py +++ b/VirtualSatelliteCAD/json_io/products/json_product.py @@ -28,11 +28,10 @@ JSON_ELEMENT_POS_Y, JSON_ELEMENT_POS_X,\ JSON_ELEMENT_POS_Z, JSON_ELEMENT_ROT_X, JSON_ELEMENT_ROT_Y,\ JSON_ELEMENT_ROT_Z, JSON_ELEMENT_PART_UUID, JSON_ELEMENT_PART_NAME, M_TO_MM,\ - RAD_TO_DEG, _get_combined_name_uuid, JSON_ELEMNT_CHILDREN + RAD_TO_DEG, _get_combined_name_uuid, JSON_ELEMNT_CHILDREN, PART_IDENTIFIER from json_io.json_spread_sheet import JsonSpreadSheet from A2plus.a2p_importpart import importPartFromFile from freecad.active_document import VECTOR_X, VECTOR_Y, VECTOR_Z, VECTOR_ZERO -# from FreeCAD import Placement class AJsonProduct(): @@ -176,7 +175,7 @@ def get_part_unique_name(self): ''' Returns the unique name of the referenced part ''' - return _get_combined_name_uuid(self.part_name, self.part_uuid) + return PART_IDENTIFIER + _get_combined_name_uuid(self.part_name, self.part_uuid) def is_part_reference(self): ''' diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py index e1d1635..d4b3008 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py @@ -25,7 +25,7 @@ # from json_io.products.json_product import AJsonProduct -from json_io.json_definitions import JSON_ELEMNT_CHILDREN +from json_io.json_definitions import JSON_ELEMNT_CHILDREN, PRODUCT_IDENTIFIER, _get_combined_name_uuid from json_io.products.json_product_child import JsonProductChild @@ -90,3 +90,7 @@ def write_to_freecad(self, active_document): # part or a product for child in self.children: child.write_to_freecad(active_document) + + def get_product_unique_name(self): + return PRODUCT_IDENTIFIER + _get_combined_name_uuid(self.name, self.uuid) + diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py index a79d01b..33c4a77 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py @@ -24,7 +24,7 @@ # SPDX-License-Identifier: LGPL-3.0-or-later from json_io.products.json_product_assembly import JsonProductAssembly -from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME +from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME, PRODUCT_IDENTIFIER from freecad.active_document import ActiveDocument import FreeCAD Log = FreeCAD.Console.PrintLog @@ -73,9 +73,9 @@ def parse_from_json(self): Log(f"Parsing '{assembly[JSON_ELEMENT_NAME]}'\n") json_product = JsonProductAssembly().parse_from_json(assembly) - active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_unique_name()) + active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_product_unique_name()) json_product.write_to_freecad(active_document) - active_document.save_and_close_active_document(json_product.get_unique_name()) # + "_assembly") + active_document.save_and_close_active_document(json_product.get_product_unique_name()) # the last json_product is the root of the assembly, open it again for the UI if(json_product is not None): diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index 1843311..6f5acdc 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -26,7 +26,7 @@ from json_io.products.json_product import AJsonProduct from json_io.json_definitions import _get_combined_name_uuid -# from json_io.json_definitions import JSON_ELEMNT_CHILDREN +from json_io.json_definitions import PART_IDENTIFIER, PRODUCT_IDENTIFIER class JsonProductChild(AJsonProduct): @@ -38,9 +38,9 @@ def get_part_unique_name(self): In this case the file name of the product has to be returned ''' if self.has_children: - return _get_combined_name_uuid(self.name, self.uuid) + return PRODUCT_IDENTIFIER + _get_combined_name_uuid(self.name, self.uuid) else: - return _get_combined_name_uuid(self.part_name, self.part_uuid) + return PART_IDENTIFIER + _get_combined_name_uuid(self.part_name, self.part_uuid) # def parse_from_json(self, json_object): # diff --git a/VirtualSatelliteCAD/test/json_io/parts/test_json_part.py b/VirtualSatelliteCAD/test/json_io/parts/test_json_part.py index 7ebe2bc..e5d2335 100644 --- a/VirtualSatelliteCAD/test/json_io/parts/test_json_part.py +++ b/VirtualSatelliteCAD/test/json_io/parts/test_json_part.py @@ -32,6 +32,7 @@ import FreeCAD import FreeCADGui from test.json_io.test_json_data import TEST_JSON_PART_BOX +from json_io.json_definitions import PART_IDENTIFIER App = FreeCAD Gui = FreeCADGui @@ -105,4 +106,4 @@ def test_get_part_unique_name(self): json_part = AJsonPart() json_part.parse_from_json(json_object) - self.assertEquals(json_part.get_unique_name(), "Beam_6201a731_d703_43f8_ab37_6a0581dfe022", "Correct unique name") + self.assertEquals(json_part.get_unique_name(), PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022", "Correct unique name") diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product.py index ef880d1..4f3961b 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product.py @@ -31,6 +31,7 @@ import FreeCADGui from json_io.products.json_product import AJsonProduct from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITHOUT_CHILDREN +from json_io.json_definitions import PART_IDENTIFIER App = FreeCAD @@ -76,7 +77,7 @@ def test_get_unique_names(self): json_product = AJsonProduct().parse_from_json(json_object) self.assertEquals(json_product.get_unique_name(), "BasePlateBottom_e8794f3d_86ec_44c5_9618_8b7170c45484", "Correct unique name") - self.assertEquals(json_product.get_part_unique_name(), "BasePlate_3d3708fd_5c6c_4af9_b710_d68778466084", "Correct unique name") + self.assertEquals(json_product.get_part_unique_name(), PART_IDENTIFIER + "BasePlate_3d3708fd_5c6c_4af9_b710_d68778466084", "Correct unique name") def test_is_part_reference(self): json_data = """{ diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py index c950499..063e8f1 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py @@ -33,7 +33,7 @@ from freecad.active_document import ActiveDocument from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN,\ TEST_JSON_PRODUCT_WITHOUT_CHILDREN, TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD -from json_io.json_definitions import JSON_ELEMNT_CHILDREN +from json_io.json_definitions import JSON_ELEMNT_CHILDREN, PRODUCT_IDENTIFIER App = FreeCAD Gui = FreeCADGui @@ -166,11 +166,12 @@ def test_create_part_product_assembly_and_subassembly_with_root_part_manual(self subassembly = json_object[JSON_ELEMNT_CHILDREN][0] - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") json_product = JsonProductAssembly().parse_from_json(subassembly) json_product.write_to_freecad(active_document) - active_document.save_as("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + active_document.save_as(PRODUCT_IDENTIFIER + "BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py index f6715db..7c597e0 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py @@ -24,7 +24,7 @@ TEST_JSON_PRODUCT_SUBASSEMBLY_WITH_SAME_PART import json from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser -from json_io.json_definitions import JSON_ELEMENT_NAME +from json_io.json_definitions import JSON_ELEMENT_NAME, PRODUCT_IDENTIFIER from freecad.active_document import ActiveDocument import unittest import glob @@ -82,10 +82,12 @@ def test_traverse_and_parse_json_tree(self): traverser.parse_from_json() # this should have similar results to test_create_part_product_assembly_and_subassembly_with_root_part_manual in TestJsonProductAssembly - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") def test_traverse_and_parse_json_tree_subassembly_no_part(self): @@ -104,10 +106,12 @@ def test_traverse_and_parse_json_tree_subassembly_no_part(self): traverser.parse_from_json() # in this test case the product assembly "BasePlateBottom2" only has a child and not a part reference - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BasePlateBottom2_e8794f3d_86ec_44c5_9618_8b7170c45484") self.assertEquals(len(active_document.app_active_document.RootObjects), 2, "Found correct amount of root objects 1 objects plus 1 sheets") - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") def test_traverse_and_parse_json_tree_subassembly_same_part(self): @@ -125,12 +129,15 @@ def test_traverse_and_parse_json_tree_subassembly_same_part(self): traverser.parse_from_json() - # in this test case the product assembly "BasePlateBottom2" only has a child and not a part reference - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlate_3d3708fd_5c6c_4af9_b710_d68778466084") + # in this test case the product assembly "BasePlate" refers a part "BasePlate" with the same name and uuid + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BasePlate_3d3708fd_5c6c_4af9_b710_d68778466084") self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") - self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") + # the root assembly should only have a part and the product assembly "BasePlate" + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") + self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") # TODO remove/adapt @unittest.SkipTest diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_child.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_child.py index 7be5aed..2f2dbc4 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_child.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_child.py @@ -31,7 +31,7 @@ import FreeCADGui from json_io.products.json_product_child import JsonProductChild from freecad.active_document import ActiveDocument -from json_io.json_definitions import get_product_name_uuid +from json_io.json_definitions import get_product_name_uuid, PART_IDENTIFIER, PRODUCT_IDENTIFIER from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITHOUT_CHILDREN,\ TEST_JSON_PRODUCT_WITH_ONE_CHILD @@ -73,14 +73,16 @@ def test_parse(self): self.assertAlmostEqual(json_product.rot_z, 60, 5, "Property is correctly set") self.assertFalse(json_product.has_children, "Current product has no children") - self.assertEquals(json_product.get_part_unique_name(), "BasePlate_3d3708fd_5c6c_4af9_b710_d68778466084", "No children thus references the part") + self.assertEquals(json_product.get_part_unique_name(), + PART_IDENTIFIER + "BasePlate_3d3708fd_5c6c_4af9_b710_d68778466084", "No children thus references the part") def test_parse_with_children(self): json_object = json.loads(self.json_data_with_child) json_product = JsonProductChild().parse_from_json(json_object) self.assertTrue(json_product.has_children, "Current product has children") - self.assertEquals(json_product.get_part_unique_name(), "BasePlateBottom_e8794f3d_86ec_44c5_9618_8b7170c45484", "No children thus references the part") + self.assertEquals(json_product.get_part_unique_name(), + PRODUCT_IDENTIFIER + "BasePlateBottom_e8794f3d_86ec_44c5_9618_8b7170c45484", "No children thus references the part") def test_create_part_product_child(self): self.create_Test_Part() From 043b84c5a784e3caa91cc49bb8b2b5c160154de0 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Tue, 3 Dec 2019 15:20:48 +0100 Subject: [PATCH 18/20] Reactivate full_import test case and update importer - (Task #5) --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/json_io/json_importer.py | 4 +-- .../json_product_assembly_tree_traverser.py | 4 +-- .../test/json_io/test_json_importer.py | 31 +++++++++---------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index 04bc645..837ce61 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -29,7 +29,7 @@ from freecad.active_document import ActiveDocument from json_io.parts.json_part_factory import JsonPartFactory from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser -from json_io.json_definitions import get_part_name_uuid, JSON_PRODUCTS, JSON_PARTS +from json_io.json_definitions import get_part_name_uuid, JSON_PRODUCTS, JSON_PARTS, PART_IDENTIFIER import json from freecad import active_document @@ -61,7 +61,7 @@ def create_or_update_part(self, json_object): # should be careful in case the name already exists. # thus it is combined with the uuid. not really nice # but definitely efficient - part_file_name = get_part_name_uuid(json_object) + part_file_name = PART_IDENTIFIER + get_part_name_uuid(json_object) active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(part_file_name) diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py index 33c4a77..b78f02a 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly_tree_traverser.py @@ -24,7 +24,7 @@ # SPDX-License-Identifier: LGPL-3.0-or-later from json_io.products.json_product_assembly import JsonProductAssembly -from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME, PRODUCT_IDENTIFIER +from json_io.json_definitions import JSON_ELEMNT_CHILDREN, JSON_ELEMENT_NAME from freecad.active_document import ActiveDocument import FreeCAD Log = FreeCAD.Console.PrintLog @@ -79,7 +79,7 @@ def parse_from_json(self): # the last json_product is the root of the assembly, open it again for the UI if(json_product is not None): - active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_unique_name()) + active_document = ActiveDocument(self.working_output_directory).open_set_and_get_document(json_product.get_product_unique_name()) return json_product, active_document diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index efeb5fa..5b03066 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -32,11 +32,10 @@ import FreeCAD import FreeCADGui from test.test_setup import AWorkingDirectoryTest -from freecad.active_document import FREECAD_FILE_EXTENSION +from freecad.active_document import FREECAD_FILE_EXTENSION, ActiveDocument from module.environment import Environment -from json_io.json_definitions import JSON_ELEMENT_STL_PATH +from json_io.json_definitions import JSON_ELEMENT_STL_PATH, PART_IDENTIFIER, PRODUCT_IDENTIFIER import unittest -from freecad.active_document import ActiveDocument App = FreeCAD Gui = FreeCADGui @@ -73,7 +72,7 @@ def test_create_part(self): json_importer.create_or_update_part(json_object) # Check the file got created - test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION + test_file_name = self._WORKING_DIRECTORY + PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") App.open(test_file_name) @@ -128,7 +127,7 @@ def test_create_part_update_uuid(self): json_importer.create_or_update_part(json_object) # Check the file got created - test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION + test_file_name = self._WORKING_DIRECTORY + PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") App.open(test_file_name) @@ -151,7 +150,7 @@ def test_create_part_update_uuid(self): json_importer.create_or_update_part(json_object) # Check the file got created - test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a0666dfe022" + FREECAD_FILE_EXTENSION + test_file_name = self._WORKING_DIRECTORY + PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a0666dfe022" + FREECAD_FILE_EXTENSION App.open(test_file_name) self.assertEquals(len(App.ActiveDocument.RootObjects), TEST_ALLOWED_AMOUNT_OF_PART_OBJECTS, "Correct amount of objects in file") @@ -178,7 +177,7 @@ def test_create_part_update_value(self): json_importer.create_or_update_part(json_object) # Check the file got created - test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION + test_file_name = self._WORKING_DIRECTORY + PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") App.open(test_file_name) @@ -202,7 +201,7 @@ def test_create_part_update_value(self): json_importer.create_or_update_part(json_object) # Check the file got created - test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION + test_file_name = self._WORKING_DIRECTORY + PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a0581dfe022" + FREECAD_FILE_EXTENSION self.assertTrue(os.path.isfile(test_file_name), "File exists on drive") App.open(test_file_name) @@ -234,12 +233,12 @@ def test_create_part_change_shape(self): json_importer.create_or_update_part(json_object) # Check the file got created - test_file_name = self._WORKING_DIRECTORY + "Beam_6201a731_d703_43f8_ab37_6a7171dfe022" + FREECAD_FILE_EXTENSION + test_file_name = self._WORKING_DIRECTORY + PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a7171dfe022" + FREECAD_FILE_EXTENSION App.open(test_file_name) # Check that there is the correct object inside self.assertIsNotNone(App.ActiveDocument.getObject("Box"), "Got correct object") - App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") + App.closeDocument(PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a7171dfe022") # Now start cyling the objects json_object["shape"] = "CYLINDER" @@ -249,7 +248,7 @@ def test_create_part_change_shape(self): App.open(test_file_name) self.assertIsNone(App.ActiveDocument.getObject("Box"), "Removed previous object") self.assertIsNotNone(App.ActiveDocument.getObject("Cylinder"), "Got correct object") - App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") + App.closeDocument(PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a7171dfe022") # Next object json_object["shape"] = "SPHERE" @@ -259,7 +258,7 @@ def test_create_part_change_shape(self): App.open(test_file_name) self.assertIsNone(App.ActiveDocument.getObject("Cylinder"), "Removed previous object") self.assertIsNotNone(App.ActiveDocument.getObject("Sphere"), "Got correct object") - App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") + App.closeDocument(PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a7171dfe022") # Next object json_object["shape"] = "GEOMETRY" @@ -269,7 +268,7 @@ def test_create_part_change_shape(self): App.open(test_file_name) self.assertIsNone(App.ActiveDocument.getObject("Sphere"), "Removed previous object") self.assertIsNotNone(App.ActiveDocument.getObject("Geometry"), "Got correct object") - App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") + App.closeDocument(PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a7171dfe022") # Next object json_object["shape"] = "CONE" @@ -279,9 +278,8 @@ def test_create_part_change_shape(self): App.open(test_file_name) self.assertIsNone(App.ActiveDocument.getObject("Geometry"), "Removed previous object") self.assertIsNotNone(App.ActiveDocument.getObject("Cone"), "Got correct object") - App.closeDocument("Beam_6201a731_d703_43f8_ab37_6a7171dfe022") + App.closeDocument(PART_IDENTIFIER + "Beam_6201a731_d703_43f8_ab37_6a7171dfe022") - @unittest.SkipTest def test_full_import(self): """ Full JSON import test @@ -311,7 +309,8 @@ def test_full_import(self): self.assertEquals(len(json_product.children), 5, "Correct amount of children") self.assertEquals(len(active_document.app_active_document.RootObjects), 10, "Found correct amount of root objects 5 plus 5 sheets") - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BeamStructure_2afb23c9_f458_4bdb_a4e7_fc863364644f") + active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document( + PRODUCT_IDENTIFIER + "BeamStructure_2afb23c9_f458_4bdb_a4e7_fc863364644f") self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") @unittest.SkipTest From 2709748f08370ceff7d3c0048f28f31d92e9935e Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Tue, 3 Dec 2019 15:43:51 +0100 Subject: [PATCH 19/20] Refactor command_import and resolve TODOs - (Task #5) --- Task #5: Implement Import Functionality --- .../commands/command_import.py | 17 +++++-- VirtualSatelliteCAD/json_io/json_importer.py | 15 +----- .../json_io/products/json_product.py | 2 +- .../json_io/products/json_product_child.py | 46 ------------------- VirtualSatelliteCAD/module/environment.py | 1 + .../products/test_json_product_assembly.py | 1 - ...st_json_product_assembly_tree_traverser.py | 27 +---------- .../test/json_io/test_json_importer.py | 5 +- 8 files changed, 22 insertions(+), 92 deletions(-) diff --git a/VirtualSatelliteCAD/commands/command_import.py b/VirtualSatelliteCAD/commands/command_import.py index 7846929..62bdc40 100644 --- a/VirtualSatelliteCAD/commands/command_import.py +++ b/VirtualSatelliteCAD/commands/command_import.py @@ -31,11 +31,14 @@ from PySide2.QtWidgets import QFileDialog from json_io.json_importer import JsonImporter import os +import json + +Log = FreeCAD.Console.PrintMessage class CommandImport: def Activated(self): - FreeCAD.Console.PrintMessage("Calling the importer\n") + Log("Calling the importer\n") # call pyqt dialog: returns (filename, filter) filename = QFileDialog.getOpenFileName( @@ -45,10 +48,18 @@ def Activated(self): "JSON(*.json)")[0] # filter if filename != '': - FreeCAD.Console.PrintMessage(f"Selected file '{filename}'\n") + (f"Selected file '{filename}'\n") + + with open(filename, 'r') as f: + try: + json_object = json.load(f) + except ValueError as error: + Log(f"ERROR: Invalid JSON found: '{error}'\n") + Log("Please provide a valid JSON\n") + return json_importer = JsonImporter(Environment.get_appdata_module_path() + os.sep) - json_importer.full_import(filename) + json_importer.full_import(json_object) def IsActive(self): return True diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index 837ce61..ef953e7 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -31,7 +31,6 @@ from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser from json_io.json_definitions import get_part_name_uuid, JSON_PRODUCTS, JSON_PARTS, PART_IDENTIFIER -import json from freecad import active_document App = FreeCAD @@ -74,21 +73,11 @@ def create_or_update_part(self, json_object): return part_file_name - # TODO: give json - def full_import(self, filepath): + def full_import(self, json_object): ''' Import a whole json file's products and parts into a FreeCAD document ''' - Log(f"Importing JSON file '{filepath}'\n") - - # TODO: refactor in command - with open(filepath, 'r') as f: - try: - json_object = json.load(f) - except ValueError as error: - Log(f"ERROR: Invalid JSON found: '{error}'\n") - Log("Please provide a valid JSON\n") - return + Log(f"Calling the importer'\n") json_parts = json_object[JSON_PARTS] diff --git a/VirtualSatelliteCAD/json_io/products/json_product.py b/VirtualSatelliteCAD/json_io/products/json_product.py index 0ace4f1..61033e5 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product.py +++ b/VirtualSatelliteCAD/json_io/products/json_product.py @@ -108,7 +108,7 @@ def _create_or_update_freecad_part(self, active_document): import_part_ref = active_document.app_active_document.getObjectsByLabel(import_part_name_in_product) # print(f"Called with '{import_part_name_in_product}'") - # TODO: + # TODO: CRUD # If the part doesn't exists (the returned list is not empty) update (delete and recreate) it if import_part_ref: active_document.app_active_document.removeObject(import_part_ref[0].Name) diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index 6f5acdc..4ddcfab 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -42,49 +42,3 @@ def get_part_unique_name(self): else: return PART_IDENTIFIER + _get_combined_name_uuid(self.part_name, self.part_uuid) -# def parse_from_json(self, json_object): -# -# super().parse_from_json(json_object) -# -# if self.has_children: -# # Get all children from the json and try to parse them -# # into JsonProductChild objects -# json_object_children = list(json_object[JSON_ELEMNT_CHILDREN]) -# -# self.children = [] -# for json_object_child in json_object_children: -# -# # TODO: child to check it has children? -> differ -# -# json_product_child = JsonProductChild().parse_from_json(json_object_child) -# # json_product_child.propagate_pos_and_rot_from_parent(self) -# self.children.append(json_product_child) -# -# return self -# -# def write_to_freecad(self, active_document): -# # This assembly may refer to a part as well -# # hence if there is a partUuid and if there is a part name, than -# # it should be written to the FreeCAD document as well. -# if self.is_part_reference(): -# super().write_to_freecad(active_document) -# -# # And now write the children, they decide on their own if they reference -# # part or a product -# if self.has_children: -# for child in self.children: -# child.write_to_freecad(active_document) - -# def propagate_pos_and_rot_from_parent(self, parent): -# """ -# This function propagates position and rotation parameters from the parent: -# Doing this the pos and rot of an object become absolute not relative, which could result -# in overhead when parsing back. -# """ -# self.pos_x += parent.pos_x -# self.pos_y += parent.pos_y -# self.pos_z += parent.pos_z -# -# self.rot_x += parent.rot_x -# self.rot_y += parent.rot_y -# self.rot_z += parent.rot_z diff --git a/VirtualSatelliteCAD/module/environment.py b/VirtualSatelliteCAD/module/environment.py index e8e078d..4fe05d1 100644 --- a/VirtualSatelliteCAD/module/environment.py +++ b/VirtualSatelliteCAD/module/environment.py @@ -91,6 +91,7 @@ def get_test_resource_path(cls, test_resource_name): path = os.path.join(cls.get_tests_resource_path(), test_resource_name) return path + # TODO: Update user file handling @classmethod def get_appdata_module_path(cls): ''' diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py index 063e8f1..b94431a 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly.py @@ -62,7 +62,6 @@ def test_parse_with_children(self): self.assertEqual(json_product.part_uuid, "3d3708fd_5c6c_4af9_b710_d68778466084", "Property is correctly set") # Properties have to be 0 since an assembly itself has no position and orientation - # TODO: check self.assertEqual(json_product.pos_x, 0, "Property is correctly set") self.assertEqual(json_product.pos_y, 0, "Property is correctly set") self.assertEqual(json_product.pos_z, 0, "Property is correctly set") diff --git a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py index 7c597e0..ec593c9 100644 --- a/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py +++ b/VirtualSatelliteCAD/test/json_io/products/test_json_product_assembly_tree_traverser.py @@ -20,13 +20,11 @@ # from test.test_setup import AWorkingDirectoryTest from test.json_io.test_json_data import TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD, TEST_JSON_PRODUCT_WITHOUT_CHILDREN, \ - TEST_JSON_FULL_VISCUBE, TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD_SUBASSEMBLY_IS_NO_PART, \ - TEST_JSON_PRODUCT_SUBASSEMBLY_WITH_SAME_PART + TEST_JSON_PRODUCT_WITH_CHILDREN_WITH_CHILD_SUBASSEMBLY_IS_NO_PART, TEST_JSON_PRODUCT_SUBASSEMBLY_WITH_SAME_PART import json from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser from json_io.json_definitions import JSON_ELEMENT_NAME, PRODUCT_IDENTIFIER from freecad.active_document import ActiveDocument -import unittest import glob import os @@ -139,29 +137,6 @@ def test_traverse_and_parse_json_tree_subassembly_same_part(self): PRODUCT_IDENTIFIER + "BasePlateBottom1_e8794f3d_86ec_44c5_9618_8b7170c45484") self.assertEquals(len(active_document.app_active_document.RootObjects), 4, "Found correct amount of root objects 2 objects plus 2 sheets") - # TODO remove/adapt - @unittest.SkipTest - def test_traverse_and_parse_json_tree3(self): - json_data = TEST_JSON_FULL_VISCUBE - self.create_Test_Part() - - json_object = json.loads(json_data) - - traverser = JsonProductAssemblyTreeTraverser(self._WORKING_DIRECTORY) - traverser.traverse(json_object) - lst_of_depths = traverser._lst_of_depths - - self.assertEqual(len(lst_of_depths), 2, "Found the right amount of 2 assemblies") - - traverser.parse_from_json() - - # this should have similar results to test_create_part_product_assembly_and_subassembly_with_root_part_manual in TestJsonProductAssembly - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("BeamStructure_2afb23c9_f458_4bdb_a4e7_fc863364644f") - self.assertEquals(len(active_document.app_active_document.RootObjects), 6, "Found correct amount of root objects 3 objects plus 3 sheets") - - active_document = ActiveDocument(self._WORKING_DIRECTORY).open_set_and_get_document("SpaceCube_a3533e02_125c_4066_bffe_d046d8d8342a") - self.assertEquals(len(active_document.app_active_document.RootObjects), 10, "Found correct amount of root objects 5 objects plus 5 sheets") - def test_traverse_and_parse_json_tree_rootassembly_without_children(self): json_data = TEST_JSON_PRODUCT_WITHOUT_CHILDREN self.create_Test_Part() diff --git a/VirtualSatelliteCAD/test/json_io/test_json_importer.py b/VirtualSatelliteCAD/test/json_io/test_json_importer.py index 5b03066..c95c1d4 100644 --- a/VirtualSatelliteCAD/test/json_io/test_json_importer.py +++ b/VirtualSatelliteCAD/test/json_io/test_json_importer.py @@ -36,6 +36,7 @@ from module.environment import Environment from json_io.json_definitions import JSON_ELEMENT_STL_PATH, PART_IDENTIFIER, PRODUCT_IDENTIFIER import unittest +from test.json_io.test_json_data import TEST_JSON_FULL_VISCUBE App = FreeCAD Gui = FreeCADGui @@ -285,9 +286,9 @@ def test_full_import(self): Full JSON import test """ - json_test_resource_path = Environment.get_test_resource_path("VisCube2.json") json_importer = JsonImporter(self._WORKING_DIRECTORY) - part_file_names, json_product, active_document = json_importer.full_import(json_test_resource_path) + json_object = json.loads(TEST_JSON_FULL_VISCUBE) + part_file_names, json_product, active_document = json_importer.full_import(json_object) # ========================= # Check parts From d0e7fa605c262374e966bbd091ad9381164ba2f7 Mon Sep 17 00:00:00 2001 From: JAmmermann-DLR Date: Tue, 3 Dec 2019 15:58:04 +0100 Subject: [PATCH 20/20] Remove unused import and blank lines at EOF - (Task #5) --- Task #5: Implement Import Functionality --- VirtualSatelliteCAD/json_io/json_importer.py | 2 -- VirtualSatelliteCAD/json_io/products/json_product_assembly.py | 1 - VirtualSatelliteCAD/json_io/products/json_product_child.py | 1 - 3 files changed, 4 deletions(-) diff --git a/VirtualSatelliteCAD/json_io/json_importer.py b/VirtualSatelliteCAD/json_io/json_importer.py index ef953e7..f31ba9e 100644 --- a/VirtualSatelliteCAD/json_io/json_importer.py +++ b/VirtualSatelliteCAD/json_io/json_importer.py @@ -31,8 +31,6 @@ from json_io.products.json_product_assembly_tree_traverser import JsonProductAssemblyTreeTraverser from json_io.json_definitions import get_part_name_uuid, JSON_PRODUCTS, JSON_PARTS, PART_IDENTIFIER -from freecad import active_document - App = FreeCAD Gui = FreeCADGui Log = FreeCAD.Console.PrintLog diff --git a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py index d4b3008..fdca5d5 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_assembly.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_assembly.py @@ -93,4 +93,3 @@ def write_to_freecad(self, active_document): def get_product_unique_name(self): return PRODUCT_IDENTIFIER + _get_combined_name_uuid(self.name, self.uuid) - diff --git a/VirtualSatelliteCAD/json_io/products/json_product_child.py b/VirtualSatelliteCAD/json_io/products/json_product_child.py index 4ddcfab..13e5994 100644 --- a/VirtualSatelliteCAD/json_io/products/json_product_child.py +++ b/VirtualSatelliteCAD/json_io/products/json_product_child.py @@ -41,4 +41,3 @@ def get_part_unique_name(self): return PRODUCT_IDENTIFIER + _get_combined_name_uuid(self.name, self.uuid) else: return PART_IDENTIFIER + _get_combined_name_uuid(self.part_name, self.part_uuid) -