Esempio n. 1
0
    def _loadAll(self) -> None:
        container_registry = ContainerRegistry.getInstance()

        if not self.machine.has_materials:
            self.materials["empty_material"] = MaterialNode("empty_material", variant = self)
            return  # There should not be any materials loaded for this printer.

        # Find all the materials for this variant's name.
        else:  # Printer has its own material profiles. Look for material profiles with this printer's definition.
            base_materials = container_registry.findInstanceContainersMetadata(type = "material", definition = "fdmprinter")
            printer_specific_materials = container_registry.findInstanceContainersMetadata(type = "material", definition = self.machine.container_id, variant_name = None)
            variant_specific_materials = container_registry.findInstanceContainersMetadata(type = "material", definition = self.machine.container_id, variant_name = self.variant_name)  # If empty_variant, this won't return anything.
            materials_per_base_file = {material["base_file"]: material for material in base_materials}
            materials_per_base_file.update({material["base_file"]: material for material in printer_specific_materials})  # Printer-specific profiles override global ones.
            materials_per_base_file.update({material["base_file"]: material for material in variant_specific_materials})  # Variant-specific profiles override all of those.
            materials = list(materials_per_base_file.values())

        # Filter materials based on the exclude_materials property.
        filtered_materials = [material for material in materials if material["id"] not in self.machine.exclude_materials]

        for material in filtered_materials:
            base_file = material["base_file"]
            if base_file not in self.materials:
                self.materials[base_file] = MaterialNode(material["id"], variant = self)
                self.materials[base_file].materialChanged.connect(self.materialsChanged)
        if not self.materials:
            self.materials["empty_material"] = MaterialNode("empty_material", variant = self)
def test_onMetadataChanged_wrongContainer(container_registry):
    variant_node = MagicMock()
    variant_node.variant_name = "variant_1"
    variant_node.machine.has_machine_quality = True
    variant_node.machine.quality_definition = "machine_1"
    with patch("cura.Machines.MaterialNode.QualityNode"):
        with patch(
                "UM.Settings.ContainerRegistry.ContainerRegistry.getInstance",
                MagicMock(return_value=container_registry)):
            node = MaterialNode("material_1", variant_node)

    # We only do this now since we do want it to be constructed but not actually re-evaluated.
    node._loadAll = MagicMock()

    container = createMockedInstanceContainer("material_2")
    container.getMetaData = MagicMock(
        return_value={
            "base_file": "new_base_file",
            "material": "new_material_type",
            "GUID": "new_guid"
        })

    node._onMetadataChanged(container)

    assert node.material_type == "test_material_type"
    assert node.guid == "omg zomg"
    assert node.base_file == "material_1"
Esempio n. 3
0
    def _materialRemoved(self, container: ContainerInterface) -> None:
        if container.getMetaDataEntry("type") != "material":
            return  # Only interested in materials.
        base_file = container.getMetaDataEntry("base_file")
        if base_file not in self.materials:
            return  # We don't track this material anyway. No need to remove it.

        original_node = self.materials[base_file]
        del self.materials[base_file]
        self.materialsChanged.emit(original_node)

        # Now a different material from the same base file may have been hidden because it was not as specific as the one we deleted.
        # Search for any submaterials from that base file that are still left.
        materials_same_base_file = ContainerRegistry.getInstance().findContainersMetadata(base_file = base_file)
        if materials_same_base_file:
            most_specific_submaterial = None
            for submaterial in materials_same_base_file:
                if submaterial["definition"] == self.machine.container_id:
                    if submaterial.get("variant_name", "empty") == self.variant_name:
                        most_specific_submaterial = submaterial
                        break  # most specific match possible
                    if submaterial.get("variant_name", "empty") == "empty":
                        most_specific_submaterial = submaterial

            if most_specific_submaterial is None:
                Logger.log("w", "Material %s removed, but no suitable replacement found", base_file)
            else:
                Logger.log("i", "Material %s (%s) overridden by %s", base_file, self.variant_name, most_specific_submaterial.get("id"))
                self.materials[base_file] = MaterialNode(most_specific_submaterial["id"], variant = self)
                self.materialsChanged.emit(self.materials[base_file])

        if not self.materials:  # The last available material just got deleted and there is nothing with the same base file to replace it.
            self.materials["empty_material"] = MaterialNode("empty_material", variant = self)
            self.materialsChanged.emit(self.materials["empty_material"])
Esempio n. 4
0
    def _materialAdded(self, container: ContainerInterface) -> None:
        if container.getMetaDataEntry("type") != "material":
            return  # Not interested.
        if not self.machine.has_materials:
            return  # We won't add any materials.
        material_definition = container.getMetaDataEntry("definition")

        base_file = container.getMetaDataEntry("base_file")
        if base_file in self.machine.exclude_materials:
            return  # Material is forbidden for this printer.
        if base_file not in self.materials:  # Completely new base file. Always better than not having a file as long as it matches our set-up.
            if material_definition != "fdmprinter" and material_definition != self.machine.container_id:
                return
            material_variant = container.getMetaDataEntry("variant_name", "empty")
            if material_variant != "empty" and material_variant != self.variant_name:
                return
        else:  # We already have this base profile. Replace the base profile if the new one is more specific.
            new_definition = container.getMetaDataEntry("definition")
            if new_definition == "fdmprinter":
                return  # Just as unspecific or worse.
            if new_definition != self.machine.container_id:
                return  # Doesn't match this set-up.
            original_metadata = ContainerRegistry.getInstance().findContainersMetadata(id = self.materials[base_file].container_id)[0]
            original_variant = original_metadata.get("variant_name", "empty")
            if original_variant != "empty" or container.getMetaDataEntry("variant_name", "empty") == "empty":
                return  # Original was already specific or just as unspecific as the new one.

        if "empty_material" in self.materials:
            del self.materials["empty_material"]
        self.materials[base_file] = MaterialNode(container.getId(), variant = self)
        self.materials[base_file].materialChanged.connect(self.materialsChanged)
        self.materialsChanged.emit(self.materials[base_file])
def test_onRemoved_rightContainer(container_registry):
    variant_node = MagicMock()
    variant_node.variant_name = "variant_1"
    variant_node.machine.has_machine_quality = True
    variant_node.machine.quality_definition = "machine_1"
    with patch("cura.Machines.MaterialNode.QualityNode"):
        with patch(
                "UM.Settings.ContainerRegistry.ContainerRegistry.getInstance",
                MagicMock(return_value=container_registry)):
            node = MaterialNode("material_1", variant_node)

            container = createMockedInstanceContainer("material_1")
            variant_node.materials = {"material_1": MagicMock()}
            node._onRemoved(container)

    assert "material_1" not in variant_node.materials
def test_materialNodeInit_noMachineQuality(container_registry):
    variant_node = MagicMock()
    variant_node.variant_name = "variant_1"
    variant_node.machine.has_machine_quality = False
    with patch("cura.Machines.MaterialNode.QualityNode"):
        with patch(
                "UM.Settings.ContainerRegistry.ContainerRegistry.getInstance",
                MagicMock(return_value=container_registry)):
            node = MaterialNode("material_1", variant_node)

    assert len(node.qualities) == 1
    assert "quality_1" in node.qualities
Esempio n. 7
0
    def _materialAdded(self, container: ContainerInterface) -> None:
        if container.getMetaDataEntry("type") != "material":
            return  # Not interested.
        if not ContainerRegistry.getInstance().findContainersMetadata(
                id=container.getId()):
            # CURA-6889
            # containerAdded and removed signals may be triggered in the next event cycle. If a container gets added
            # and removed in the same event cycle, in the next cycle, the connections should just ignore the signals.
            # The check here makes sure that the container in the signal still exists.
            Logger.log(
                "d",
                "Got container added signal for container [%s] but it no longer exists, do nothing.",
                container.getId())
            return
        if not self.machine.has_materials:
            return  # We won't add any materials.
        material_definition = container.getMetaDataEntry("definition")

        base_file = container.getMetaDataEntry("base_file")
        if base_file in self.machine.exclude_materials:
            return  # Material is forbidden for this printer.
        if base_file not in self.materials:  # Completely new base file. Always better than not having a file as long as it matches our set-up.
            if material_definition != "fdmprinter" and material_definition != self.machine.container_id:
                return
            material_variant = container.getMetaDataEntry("variant_name")
            if material_variant is not None and material_variant != self.variant_name:
                return
        else:  # We already have this base profile. Replace the base profile if the new one is more specific.
            new_definition = container.getMetaDataEntry("definition")
            if new_definition == "fdmprinter":
                return  # Just as unspecific or worse.
            material_variant = container.getMetaDataEntry("variant_name")
            if new_definition != self.machine.container_id or material_variant != self.variant_name:
                return  # Doesn't match this set-up.
            original_metadata = ContainerRegistry.getInstance(
            ).findContainersMetadata(
                id=self.materials[base_file].container_id)[0]
            if "variant_name" in original_metadata or material_variant is None:
                return  # Original was already specific or just as unspecific as the new one.

        if "empty_material" in self.materials:
            del self.materials["empty_material"]
        self.materials[base_file] = MaterialNode(container.getId(),
                                                 variant=self)
        self.materials[base_file].materialChanged.connect(
            self.materialsChanged)
        self.materialsChanged.emit(self.materials[base_file])
Esempio n. 8
0
class TestSendMaterialJob(TestCase):
    # version 1
    _LOCAL_MATERIAL_WHITE = {
        "type": "material",
        "status": "unknown",
        "id": "generic_pla_white",
        "base_file": "generic_pla_white",
        "setting_version": "5",
        "name": "White PLA",
        "brand": "Generic",
        "material": "PLA",
        "color_name": "White",
        "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce",
        "version": "1",
        "color_code": "#ffffff",
        "description": "Test PLA White",
        "adhesion_info": "Use glue.",
        "approximate_diameter": "3",
        "properties": {
            "density": "1.00",
            "diameter": "2.85",
            "weight": "750"
        },
        "definition": "fdmprinter",
        "compatible": True
    }

    # version 2
    _LOCAL_MATERIAL_WHITE_NEWER = {
        "type": "material",
        "status": "unknown",
        "id": "generic_pla_white",
        "base_file": "generic_pla_white",
        "setting_version": "5",
        "name": "White PLA",
        "brand": "Generic",
        "material": "PLA",
        "color_name": "White",
        "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce",
        "version": "2",
        "color_code": "#ffffff",
        "description": "Test PLA White",
        "adhesion_info": "Use glue.",
        "approximate_diameter": "3",
        "properties": {
            "density": "1.00",
            "diameter": "2.85",
            "weight": "750"
        },
        "definition": "fdmprinter",
        "compatible": True
    }

    # invalid version: "one"
    _LOCAL_MATERIAL_WHITE_INVALID_VERSION = {
        "type": "material",
        "status": "unknown",
        "id": "generic_pla_white",
        "base_file": "generic_pla_white",
        "setting_version": "5",
        "name": "White PLA",
        "brand": "Generic",
        "material": "PLA",
        "color_name": "White",
        "GUID": "badb0ee7-87c8-4f3f-9398-938587b67dce",
        "version": "one",
        "color_code": "#ffffff",
        "description": "Test PLA White",
        "adhesion_info": "Use glue.",
        "approximate_diameter": "3",
        "properties": {
            "density": "1.00",
            "diameter": "2.85",
            "weight": "750"
        },
        "definition": "fdmprinter",
        "compatible": True
    }

    _LOCAL_MATERIAL_WHITE_ALL_RESULT = {
        "generic_pla_white":
        MaterialGroup("generic_pla_white", MaterialNode(_LOCAL_MATERIAL_WHITE))
    }

    _LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT = {
        "generic_pla_white":
        MaterialGroup("generic_pla_white",
                      MaterialNode(_LOCAL_MATERIAL_WHITE_NEWER))
    }

    _LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT = {
        "generic_pla_white":
        MaterialGroup("generic_pla_white",
                      MaterialNode(_LOCAL_MATERIAL_WHITE_INVALID_VERSION))
    }

    _LOCAL_MATERIAL_BLACK = {
        "type": "material",
        "status": "unknown",
        "id": "generic_pla_black",
        "base_file": "generic_pla_black",
        "setting_version": "5",
        "name": "Yellow CPE",
        "brand": "Ultimaker",
        "material": "CPE",
        "color_name": "Black",
        "GUID": "5fbb362a-41f9-4818-bb43-15ea6df34aa4",
        "version": "1",
        "color_code": "#000000",
        "description": "Test PLA Black",
        "adhesion_info": "Use glue.",
        "approximate_diameter": "3",
        "properties": {
            "density": "1.01",
            "diameter": "2.85",
            "weight": "750"
        },
        "definition": "fdmprinter",
        "compatible": True
    }

    _LOCAL_MATERIAL_BLACK_ALL_RESULT = {
        "generic_pla_black":
        MaterialGroup("generic_pla_black", MaterialNode(_LOCAL_MATERIAL_BLACK))
    }

    _REMOTE_MATERIAL_WHITE = {
        "guid": "badb0ee7-87c8-4f3f-9398-938587b67dce",
        "material": "PLA",
        "brand": "Generic",
        "version": 1,
        "color": "White",
        "density": 1.00
    }

    _REMOTE_MATERIAL_BLACK = {
        "guid": "5fbb362a-41f9-4818-bb43-15ea6df34aa4",
        "material": "PLA",
        "brand": "Generic",
        "version": 2,
        "color": "Black",
        "density": 1.00
    }

    def test_run(self):
        device_mock = MagicMock()
        job = SendMaterialJob(device_mock)
        job.run()

        # We expect the materials endpoint to be called when the job runs.
        device_mock.get.assert_called_with(
            "materials/", on_finished=job._onGetRemoteMaterials)

    def test__onGetRemoteMaterials_withFailedRequest(self):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        reply_mock.attribute.return_value = 404
        job = SendMaterialJob(device_mock)
        job._onGetRemoteMaterials(reply_mock)

        # We expect the device not to be called for any follow up.
        self.assertEqual(0, device_mock.createFormPart.call_count)

    def test__onGetRemoteMaterials_withWrongEncoding(self):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        reply_mock.attribute.return_value = 200
        reply_mock.readAll.return_value = QByteArray(
            json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("cp500"))
        job = SendMaterialJob(device_mock)
        job._onGetRemoteMaterials(reply_mock)

        # Given that the parsing fails we do no expect the device to be called for any follow up.
        self.assertEqual(0, device_mock.createFormPart.call_count)

    def test__onGetRemoteMaterials_withBadJsonAnswer(self):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        reply_mock.attribute.return_value = 200
        reply_mock.readAll.return_value = QByteArray(
            b"Six sick hicks nick six slick bricks with picks and sticks.")
        job = SendMaterialJob(device_mock)
        job._onGetRemoteMaterials(reply_mock)

        # Given that the parsing fails we do no expect the device to be called for any follow up.
        self.assertEqual(0, device_mock.createFormPart.call_count)

    def test__onGetRemoteMaterials_withMissingGuidInRemoteMaterial(self):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        reply_mock.attribute.return_value = 200
        remote_material_without_guid = self._REMOTE_MATERIAL_WHITE.copy()
        del remote_material_without_guid["guid"]
        reply_mock.readAll.return_value = QByteArray(
            json.dumps([remote_material_without_guid]).encode("ascii"))
        job = SendMaterialJob(device_mock)
        job._onGetRemoteMaterials(reply_mock)

        # Given that parsing fails we do not expect the device to be called for any follow up.
        self.assertEqual(0, device_mock.createFormPart.call_count)

    @patch("cura.Machines.MaterialManager.MaterialManager")
    @patch("cura.Settings.CuraContainerRegistry")
    @patch("UM.Application")
    def test__onGetRemoteMaterials_withInvalidVersionInLocalMaterial(
            self, application_mock, container_registry_mock,
            material_manager_mock):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        application_mock.getContainerRegistry.return_value = container_registry_mock
        application_mock.getMaterialManager.return_value = material_manager_mock

        reply_mock.attribute.return_value = 200
        reply_mock.readAll.return_value = QByteArray(
            json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))

        material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_INVALID_VERSION_ALL_RESULT.copy(
        )

        with mock.patch.object(Application,
                               "getInstance",
                               new=lambda: application_mock):
            job = SendMaterialJob(device_mock)
            job._onGetRemoteMaterials(reply_mock)

        self.assertEqual(0, device_mock.createFormPart.call_count)

    @patch("UM.Application.Application.getInstance")
    def test__onGetRemoteMaterials_withNoUpdate(self, application_mock):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        container_registry_mock = application_mock.getContainerRegistry.return_value
        material_manager_mock = application_mock.getMaterialManager.return_value

        device_mock.createFormPart.return_value = "_xXx_"

        material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy(
        )

        reply_mock.attribute.return_value = 200
        reply_mock.readAll.return_value = QByteArray(
            json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))

        with mock.patch.object(Application,
                               "getInstance",
                               new=lambda: application_mock):
            job = SendMaterialJob(device_mock)
            job._onGetRemoteMaterials(reply_mock)

        self.assertEqual(0, device_mock.createFormPart.call_count)
        self.assertEqual(0, device_mock.postFormWithParts.call_count)

    @patch("UM.Application.Application.getInstance")
    def test__onGetRemoteMaterials_withUpdatedMaterial(self,
                                                       get_instance_mock):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        application_mock = get_instance_mock.return_value
        container_registry_mock = application_mock.getContainerRegistry.return_value
        material_manager_mock = application_mock.getMaterialManager.return_value

        container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(
            x)

        device_mock.createFormPart.return_value = "_xXx_"

        material_manager_mock.getAllMaterialGroups.return_value = self._LOCAL_MATERIAL_WHITE_NEWER_ALL_RESULT.copy(
        )

        reply_mock.attribute.return_value = 200
        reply_mock.readAll.return_value = QByteArray(
            json.dumps([self._REMOTE_MATERIAL_WHITE]).encode("ascii"))

        job = SendMaterialJob(device_mock)
        job._onGetRemoteMaterials(reply_mock)

        self.assertEqual(1, device_mock.createFormPart.call_count)
        self.assertEqual(1, device_mock.postFormWithParts.call_count)
        self.assertEqual([
            call.createFormPart(
                "name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"",
                "<xml></xml>"),
            call.postFormWithParts(target="materials/",
                                   parts=["_xXx_"],
                                   on_finished=job.sendingFinished)
        ], device_mock.method_calls)

    @patch("UM.Application.Application.getInstance")
    def test__onGetRemoteMaterials_withNewMaterial(self, application_mock):
        reply_mock = MagicMock()
        device_mock = MagicMock()
        container_registry_mock = application_mock.getContainerRegistry.return_value
        material_manager_mock = application_mock.getMaterialManager.return_value

        container_registry_mock.getContainerFilePathById = lambda x: _FILES_MAP.get(
            x)

        device_mock.createFormPart.return_value = "_xXx_"

        all_results = self._LOCAL_MATERIAL_WHITE_ALL_RESULT.copy()
        for key, value in self._LOCAL_MATERIAL_BLACK_ALL_RESULT.items():
            all_results[key] = value
        material_manager_mock.getAllMaterialGroups.return_value = all_results

        reply_mock.attribute.return_value = 200
        reply_mock.readAll.return_value = QByteArray(
            json.dumps([self._REMOTE_MATERIAL_BLACK]).encode("ascii"))

        with mock.patch.object(Application,
                               "getInstance",
                               new=lambda: application_mock):
            job = SendMaterialJob(device_mock)
            job._onGetRemoteMaterials(reply_mock)

        self.assertEqual(1, device_mock.createFormPart.call_count)
        self.assertEqual(1, device_mock.postFormWithParts.call_count)
        self.assertEqual([
            call.createFormPart(
                "name=\"file\"; filename=\"generic_pla_white.xml.fdm_material\"",
                "<xml></xml>"),
            call.postFormWithParts(target="materials/",
                                   parts=["_xXx_"],
                                   on_finished=job.sendingFinished)
        ], device_mock.method_calls)