PackageModel.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. # Copyright (c) 2021 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from PyQt5.QtCore import pyqtProperty, QObject
  4. import re
  5. from typing import Any, Dict, Optional
  6. from UM.i18n import i18nCatalog # To translate placeholder names if data is not present.
  7. catalog = i18nCatalog("cura")
  8. class PackageModel(QObject):
  9. """
  10. Represents a package, containing all the relevant information to be displayed about a package.
  11. Effectively this behaves like a glorified named tuple, but as a QObject so that its properties can be obtained from
  12. QML. The model can also be constructed directly from a response received by the API.
  13. """
  14. def __init__(self, package_data: Dict[str, Any], installation_status: str, section_title: Optional[str] = None, parent: Optional[QObject] = None) -> None:
  15. """
  16. Constructs a new model for a single package.
  17. :param package_data: The data received from the Marketplace API about the package to create.
  18. :param installation_status: Whether the package is `not_installed`, `installed` or `bundled`.
  19. :param section_title: If the packages are to be categorized per section provide the section_title
  20. :param parent: The parent QML object that controls the lifetime of this model (normally a PackageList).
  21. """
  22. super().__init__(parent)
  23. self._package_id = package_data.get("package_id", "UnknownPackageId")
  24. self._package_type = package_data.get("package_type", "")
  25. self._icon_url = package_data.get("icon_url", "")
  26. self._display_name = package_data.get("display_name", catalog.i18nc("@label:property", "Unknown Package"))
  27. tags = package_data.get("tags", [])
  28. self._is_checked_by_ultimaker = (self._package_type == "plugin" and "verified" in tags) or (self._package_type == "material" and "certified" in tags)
  29. self._package_version = package_data.get("package_version", "") # Display purpose, no need for 'UM.Version'.
  30. self._package_info_url = package_data.get("website", "") # Not to be confused with 'download_url'.
  31. self._download_count = package_data.get("download_count", 0)
  32. self._description = package_data.get("description", "")
  33. self._formatted_description = self._format(self._description)
  34. self._download_url = package_data.get("download_url", "")
  35. self._release_notes = package_data.get("release_notes", "") # Not used yet, propose to add to description?
  36. author_data = package_data.get("author", {})
  37. self._author_name = author_data.get("display_name", catalog.i18nc("@label:property", "Unknown Author"))
  38. self._author_info_url = author_data.get("website", "")
  39. if not self._icon_url or self._icon_url == "":
  40. self._icon_url = author_data.get("icon_url", "")
  41. self._installation_status = installation_status
  42. self._section_title = section_title
  43. # Note that there's a lot more info in the package_data than just these specified here.
  44. def _format(self, text: str) -> str:
  45. """
  46. Formats a user-readable block of text for display.
  47. :return: A block of rich text with formatting embedded.
  48. """
  49. # Turn all in-line hyperlinks into actual links.
  50. url_regex = re.compile(r"(((http|https)://)[a-zA-Z0-9@:%._+~#?&/=]{2,256}\.[a-z]{2,12}(/[a-zA-Z0-9@:%.-_+~#?&/=]*)?)")
  51. text = re.sub(url_regex, r'<a href="\1">\1</a>', text)
  52. # Turn newlines into <br> so that they get displayed as newlines when rendering as rich text.
  53. text = text.replace("\n", "<br>")
  54. return text
  55. @pyqtProperty(str, constant = True)
  56. def packageId(self) -> str:
  57. return self._package_id
  58. @pyqtProperty(str, constant = True)
  59. def packageType(self) -> str:
  60. return self._package_type
  61. @pyqtProperty(str, constant=True)
  62. def iconUrl(self):
  63. return self._icon_url
  64. @pyqtProperty(str, constant = True)
  65. def displayName(self) -> str:
  66. return self._display_name
  67. @pyqtProperty(bool, constant = True)
  68. def isCheckedByUltimaker(self):
  69. return self._is_checked_by_ultimaker
  70. @pyqtProperty(str, constant=True)
  71. def packageVersion(self):
  72. return self._package_version
  73. @pyqtProperty(str, constant=True)
  74. def packageInfoUrl(self):
  75. return self._package_info_url
  76. @pyqtProperty(int, constant=True)
  77. def downloadCount(self):
  78. return self._download_count
  79. @pyqtProperty(str, constant=True)
  80. def description(self):
  81. return self._description
  82. @pyqtProperty(str, constant = True)
  83. def formattedDescription(self) -> str:
  84. return self._formatted_description
  85. @pyqtProperty(str, constant=True)
  86. def authorName(self):
  87. return self._author_name
  88. @pyqtProperty(str, constant=True)
  89. def authorInfoUrl(self):
  90. return self._author_info_url
  91. @pyqtProperty(str, constant = True)
  92. def installationStatus(self) -> str:
  93. return self._installation_status
  94. @pyqtProperty(str, constant = True)
  95. def sectionTitle(self) -> Optional[str]:
  96. return self._section_title