LocalPackageList.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. # Copyright (c) 2021 Ultimaker B.V.
  2. # Cura is released under the terms of the LGPLv3 or higher.
  3. from typing import Any, Dict, List, Optional, TYPE_CHECKING
  4. from operator import attrgetter
  5. from PyQt5.QtCore import pyqtSlot, QObject
  6. if TYPE_CHECKING:
  7. from PyQt5.QtCore import QObject
  8. from UM.i18n import i18nCatalog
  9. from UM.TaskManagement.HttpRequestManager import HttpRequestManager
  10. from UM.Logger import Logger
  11. from .PackageList import PackageList
  12. from .PackageModel import PackageModel
  13. from .Constants import PACKAGE_UPDATES_URL
  14. catalog = i18nCatalog("cura")
  15. class LocalPackageList(PackageList):
  16. PACKAGE_CATEGORIES = {
  17. "installed":
  18. {
  19. "plugin": catalog.i18nc("@label", "Installed Plugins"),
  20. "material": catalog.i18nc("@label", "Installed Materials")
  21. },
  22. "bundled":
  23. {
  24. "plugin": catalog.i18nc("@label", "Bundled Plugins"),
  25. "material": catalog.i18nc("@label", "Bundled Materials")
  26. }
  27. } # The section headers to be used for the different package categories
  28. def __init__(self, parent: Optional["QObject"] = None) -> None:
  29. super().__init__(parent)
  30. self._has_footer = False
  31. @pyqtSlot()
  32. def updatePackages(self) -> None:
  33. """Update the list with local packages, these are materials or plugin, either bundled or user installed. The list
  34. will also contain **to be removed** or **to be installed** packages since the user might still want to interact
  35. with these.
  36. """
  37. self.setErrorMessage("") # Clear any previous errors.
  38. self.setIsLoading(True)
  39. # Obtain and sort the local packages
  40. self.setItems([{"package": p} for p in [self._makePackageModel(p) for p in self._manager.local_packages]])
  41. self.sort(attrgetter("sectionTitle", "can_update", "displayName"), key = "package", reverse = True)
  42. self.checkForUpdates(self._manager.local_packages)
  43. self.setIsLoading(False)
  44. self.setHasMore(False) # All packages should have been loaded at this time
  45. def _makePackageModel(self, package_info: Dict[str, Any]) -> PackageModel:
  46. """ Create a PackageModel from the package_info and determine its section_title"""
  47. bundled_or_installed = "bundled" if self._manager.isBundledPackage(package_info["package_id"]) else "installed"
  48. package_type = package_info["package_type"]
  49. section_title = self.PACKAGE_CATEGORIES[bundled_or_installed][package_type]
  50. package = PackageModel(package_info, section_title = section_title, parent = self)
  51. if package_info["package_id"] in self._manager.getPackagesToRemove() or package_info["package_id"] in self._manager.getPackagesToInstall():
  52. package.is_recently_managed = True
  53. self._connectManageButtonSignals(package)
  54. return package
  55. def checkForUpdates(self, packages: List[Dict[str, Any]]):
  56. if self._account.isLoggedIn:
  57. installed_packages = "installed_packages=".join([f"{package['package_id']}:{package['package_version']}&" for package in packages])
  58. request_url = f"{PACKAGE_UPDATES_URL}?installed_packages={installed_packages[:-1]}"
  59. self._ongoing_request = HttpRequestManager.getInstance().get(
  60. request_url,
  61. scope = self._scope,
  62. callback = self._parseResponse
  63. )
  64. def _parseResponse(self, reply: "QNetworkReply") -> None:
  65. """
  66. Parse the response from the package list API request which can update.
  67. :param reply: A reply containing information about a number of packages.
  68. """
  69. response_data = HttpRequestManager.readJSON(reply)
  70. if "data" not in response_data:
  71. Logger.error(
  72. f"Could not interpret the server's response. Missing 'data' from response data. Keys in response: {response_data.keys()}")
  73. return
  74. if len(response_data["data"]) == 0:
  75. return
  76. for package_data in response_data["data"]:
  77. package = self.getPackageModel(package_data["package_id"])
  78. package.download_url = package_data.get("download_url", "")
  79. package.canUpdate = True
  80. self.sort(attrgetter("sectionTitle", "can_update", "displayName"), key = "package", reverse = True)