CuraProfileReader.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # Copyright (c) 2019 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. import configparser
  4. from typing import List, Optional, Tuple
  5. from UM.Logger import Logger
  6. from UM.Settings.ContainerFormatError import ContainerFormatError
  7. from UM.Settings.InstanceContainer import InstanceContainer # The new profile to make.
  8. from cura.CuraApplication import CuraApplication
  9. from cura.Machines.ContainerTree import ContainerTree
  10. from cura.ReaderWriters.ProfileReader import ProfileReader
  11. import zipfile
  12. class CuraProfileReader(ProfileReader):
  13. """A plugin that reads profile data from Cura profile files.
  14. It reads a profile from a .curaprofile file, and returns it as a profile
  15. instance.
  16. """
  17. def __init__(self) -> None:
  18. """Initialises the cura profile reader.
  19. This does nothing since the only other function is basically stateless.
  20. """
  21. super().__init__()
  22. def read(self, file_name: str) -> List[Optional[InstanceContainer]]:
  23. """Reads a cura profile from a file and returns it.
  24. :param file_name: The file to read the cura profile from.
  25. :return: The cura profiles that were in the file, if any. If the file
  26. could not be read or didn't contain a valid profile, ``None`` is
  27. returned.
  28. """
  29. try:
  30. with zipfile.ZipFile(file_name, "r") as archive:
  31. results = [] # type: List[Optional[InstanceContainer]]
  32. for profile_id in archive.namelist():
  33. with archive.open(profile_id) as f:
  34. serialized = f.read()
  35. upgraded_profiles = self._upgradeProfile(serialized.decode("utf-8"), profile_id) #After upgrading it may split into multiple profiles.
  36. for upgraded_profile in upgraded_profiles:
  37. serialization, new_id = upgraded_profile
  38. profile = self._loadProfile(serialization, new_id)
  39. if profile is not None:
  40. results.append(profile)
  41. return results
  42. except zipfile.BadZipFile:
  43. # It must be an older profile from Cura 2.1.
  44. with open(file_name, encoding = "utf-8") as fhandle:
  45. serialized_bytes = fhandle.read()
  46. return [self._loadProfile(serialized, profile_id) for serialized, profile_id in self._upgradeProfile(serialized_bytes, file_name)]
  47. def _upgradeProfile(self, serialized: str, profile_id: str) -> List[Tuple[str, str]]:
  48. """Convert a profile from an old Cura to this Cura if needed.
  49. :param serialized: The profile data to convert in the serialized on-disk format.
  50. :param profile_id: The name of the profile.
  51. :return: List of serialized profile strings and matching profile names.
  52. """
  53. parser = configparser.ConfigParser(interpolation = None)
  54. parser.read_string(serialized)
  55. if "general" not in parser:
  56. Logger.log("w", "Missing required section 'general'.")
  57. return []
  58. if "version" not in parser["general"]:
  59. Logger.log("w", "Missing required 'version' property")
  60. return []
  61. version = int(parser["general"]["version"])
  62. setting_version = int(parser["metadata"].get("setting_version", "0"))
  63. if InstanceContainer.Version != version:
  64. name = parser["general"]["name"]
  65. return self._upgradeProfileVersion(serialized, name, version, setting_version)
  66. else:
  67. return [(serialized, profile_id)]
  68. def _loadProfile(self, serialized: str, profile_id: str) -> Optional[InstanceContainer]:
  69. """Load a profile from a serialized string.
  70. :param serialized: The profile data to read.
  71. :param profile_id: The name of the profile.
  72. :return: The profile that was stored in the string.
  73. """
  74. # Create an empty profile.
  75. profile = InstanceContainer(profile_id)
  76. profile.setMetaDataEntry("type", "quality_changes")
  77. try:
  78. profile.deserialize(serialized, file_name = profile_id)
  79. except ContainerFormatError as e:
  80. Logger.log("e", "Error in the format of a container: %s", str(e))
  81. return None
  82. except Exception as e:
  83. Logger.log("e", "Error while trying to parse profile: %s", str(e))
  84. return None
  85. global_stack = CuraApplication.getInstance().getGlobalContainerStack()
  86. if global_stack is None:
  87. return None
  88. active_quality_definition = ContainerTree.getInstance().machines[global_stack.definition.getId()].quality_definition
  89. if profile.getMetaDataEntry("definition") != active_quality_definition:
  90. profile.setMetaDataEntry("definition", active_quality_definition)
  91. return profile
  92. def _upgradeProfileVersion(self, serialized: str, profile_id: str, main_version: int, setting_version: int) -> List[Tuple[str, str]]:
  93. """Upgrade a serialized profile to the current profile format.
  94. :param serialized: The profile data to convert.
  95. :param profile_id: The name of the profile.
  96. :param source_version: The profile version of 'serialized'.
  97. :return: List of serialized profile strings and matching profile names.
  98. """
  99. source_version = main_version * 1000000 + setting_version
  100. from UM.VersionUpgradeManager import VersionUpgradeManager
  101. results = VersionUpgradeManager.getInstance().updateFilesData("quality_changes", source_version, [serialized], [profile_id])
  102. if results is None:
  103. return []
  104. serialized = results.files_data[0]
  105. parser = configparser.ConfigParser(interpolation = None)
  106. parser.read_string(serialized)
  107. if "general" not in parser:
  108. Logger.log("w", "Missing required section 'general'.")
  109. return []
  110. new_source_version = results.version
  111. if int(new_source_version / 1000000) != InstanceContainer.Version or new_source_version % 1000000 != CuraApplication.SettingVersion:
  112. Logger.log("e", "Failed to upgrade profile [%s]", profile_id)
  113. if int(parser["general"]["version"]) != InstanceContainer.Version:
  114. Logger.log("e", "Failed to upgrade profile [%s]", profile_id)
  115. return []
  116. return [(serialized, profile_id)]