Browse Source

CURA-5035 Separate API calls!

Ian Paschal 6 years ago
parent
commit
01007946b4

+ 3 - 2
cura/CuraPackageManager.py

@@ -142,7 +142,7 @@ class CuraPackageManager(QObject):
             if package_id in Application.getInstance().getRequiredPlugins():
                 continue
 
-            plugin_package_info["is_bundled"] = True if plugin_package_info["author"]["name"] == "Ultimaker B.V." else False
+            plugin_package_info["is_bundled"] = True if plugin_package_info["author"]["display_name"] == "Ultimaker B.V." else False
             plugin_package_info["is_active"] = self._plugin_registry.isActivePlugin(package_id)
             package_type = "plugin"
             if package_type not in installed_packages_dict:
@@ -160,7 +160,8 @@ class CuraPackageManager(QObject):
                             "cura_version": int(plugin_metadata["plugin"]["api"]),
                             "website": "",
                             "author": {
-                                "name": plugin_metadata["plugin"].get("author", ""),
+                                "author_id": plugin_metadata["plugin"].get("author", ""),
+                                "display_name": plugin_metadata["plugin"].get("author", ""),
                                 "email": "",
                                 "website": "",
                             },

+ 0 - 1
plugins/Toolbox/resources/qml/ToolboxBackColumn.qml

@@ -48,7 +48,6 @@ Item
         {
             toolbox.viewPage = "overview"
             toolbox.filterModelByProp("packages", "type", toolbox.viewCategory)
-            toolbox.filterModelByProp("authors", "type", toolbox.viewCategory)
         }
         style: ButtonStyle
         {

+ 3 - 2
plugins/Toolbox/resources/qml/ToolboxDetailList.qml

@@ -6,10 +6,9 @@ import QtQuick.Controls 1.4
 import QtQuick.Controls.Styles 1.4
 import UM 1.1 as UM
 
-Rectangle
+Item
 {
     id: detailList
-    // color: "green"
     ScrollView
     {
         frameVisible: false
@@ -24,6 +23,8 @@ Rectangle
                 bottomMargin: UM.Theme.getSize("wide_margin").height
                 top: parent.top
             }
+            // TODO: Sometimes the height is not the childrenRect.height. Lord
+            // knows why. Probably because QT is garbage.
             height: childrenRect.height
             spacing: UM.Theme.getSize("default_margin").height
             Repeater

+ 84 - 5
plugins/Toolbox/resources/qml/ToolboxDetailTile.qml

@@ -11,9 +11,14 @@ Item
     id: tile
     property bool installed: toolbox.isInstalled(model.id)
     width: detailList.width - UM.Theme.getSize("wide_margin").width
-    height: UM.Theme.getSize("toolbox_detail_tile").height
-    Column
+    // TODO: Without this line, every instance of this object has 0 height. With
+    // it, QML spits out tons of bugs claiming a binding loop (not true). Why?
+    // Because QT is garbage.
+    height: Math.max( UM.Theme.getSize("toolbox_detail_tile").height, childrenRect.height + UM.Theme.getSize("default_margin").height)
+    Item
     {
+        id: normalData
+        height: childrenRect.height
         anchors
         {
             left: parent.left
@@ -23,15 +28,17 @@ Item
         }
         Label
         {
+            id: packageName
             width: parent.width
             height: UM.Theme.getSize("toolbox_property_label").height
             text: model.name
             wrapMode: Text.WordWrap
             color: UM.Theme.getColor("text")
-            font: UM.Theme.getFont("default_bold")
+            font: UM.Theme.getFont("medium_bold")
         }
         Label
         {
+            anchors.top: packageName.bottom
             width: parent.width
             text:
             {
@@ -53,13 +60,13 @@ Item
             font: UM.Theme.getFont("default")
         }
     }
-    Rectangle
+    Item
     {
         id: controls
         anchors.right: tile.right
         anchors.top: tile.top
         width: childrenRect.width
-        color: "blue"
+        height: childrenRect.height
         Button
         {
             id: installButton
@@ -179,6 +186,78 @@ Item
             }
         }
     }
+
+    Item
+    {
+        anchors.top: normalData.bottom
+        anchors.topMargin: UM.Theme.getSize("default_margin").height
+        height: model.type == "material" ? childrenRect.height : 0
+        width: normalData.width
+        visible: model.type == "material"
+        Label
+        {
+            id: compatibilityHeading
+            anchors.topMargin: UM.Theme.getSize("default_margin").height
+            width: parent.width
+            text: catalog.i18nc("@label", "Compatibility")
+            wrapMode: Text.WordWrap
+            color: UM.Theme.getColor("text_medium")
+            font: UM.Theme.getFont("default")
+        }
+        Column
+        {
+            id: compatibilityLabels
+            anchors
+            {
+                top: compatibilityHeading.bottom
+                topMargin: UM.Theme.getSize("default_margin").height
+                bottomMargin: UM.Theme.getSize("default_margin").height
+            }
+            width: childrenRect.width
+            Label
+            {
+                text: catalog.i18nc("@label", "Machines") + ":"
+                font: UM.Theme.getFont("small")
+            }
+            Label
+            {
+                text: catalog.i18nc("@label", "Print Cores") + ":"
+                font: UM.Theme.getFont("small")
+            }
+            Label
+            {
+                text: catalog.i18nc("@label", "Quality Profiles") + ":"
+                font: UM.Theme.getFont("small")
+            }
+        }
+        Column
+        {
+            id: compatibilityValues
+            anchors
+            {
+                left: compatibilityLabels.right
+                leftMargin: UM.Theme.getSize("default_margin").height
+                top: compatibilityLabels.top
+                bottom: compatibilityLabels.bottom
+            }
+            Label
+            {
+                text: "Thingy"
+                font: UM.Theme.getFont("very_small")
+            }
+            Label
+            {
+                text: "Thingy"
+                font: UM.Theme.getFont("very_small")
+            }
+            Label
+            {
+                text: "Thingy"
+                font: UM.Theme.getFont("very_small")
+            }
+        }
+    }
+
     Rectangle
     {
         color: UM.Theme.getColor("lining")

+ 1 - 1
plugins/Toolbox/resources/qml/ToolboxDownloadsGridTile.qml

@@ -92,7 +92,7 @@ Item
             {
                 case "material":
                     toolbox.viewPage = "author"
-                    toolbox.filterModelByProp("packages", "author_name", model.name)
+                    toolbox.filterModelByProp("packages", "author_id", model.id)
                     break
                 default:
                     toolbox.viewPage = "detail"

+ 0 - 11
plugins/Toolbox/resources/qml/ToolboxDownloadsShowcaseTile.qml

@@ -10,17 +10,6 @@ Item
 {
     width: UM.Theme.getSize("toolbox_thumbnail_large").width
     height: childrenRect.height
-    visible:
-    {
-        if (toolbox.viewCategory == "material" && model.packages_count)
-        {
-            return model.packages_count > 0
-        }
-        else
-        {
-            return true
-        }
-    }
     Rectangle
     {
         id: highlight

+ 1 - 1
plugins/Toolbox/resources/qml/ToolboxHeader.qml

@@ -38,7 +38,7 @@ Item
             active: toolbox.viewCategory == "material"
             onClicked:
             {
-                toolbox.filterModelByProp("authors", "type", "material")
+                toolbox.filterModelByProp("authors", "package_types", "material")
                 toolbox.viewCategory = "material"
                 toolbox.viewPage = "overview"
             }

+ 21 - 17
plugins/Toolbox/src/AuthorsModel.py

@@ -15,13 +15,14 @@ class AuthorsModel(ListModel):
 
         self._metadata = None
 
-        self.addRoleName(Qt.UserRole + 1, "name")
-        self.addRoleName(Qt.UserRole + 2, "email")
-        self.addRoleName(Qt.UserRole + 3, "website")
-        self.addRoleName(Qt.UserRole + 4, "type")
-        self.addRoleName(Qt.UserRole + 5, "icon_url")
-        self.addRoleName(Qt.UserRole + 6, "packages_count")
-        self.addRoleName(Qt.UserRole + 7, "description")
+        self.addRoleName(Qt.UserRole + 1, "id")
+        self.addRoleName(Qt.UserRole + 2, "name")
+        self.addRoleName(Qt.UserRole + 3, "email")
+        self.addRoleName(Qt.UserRole + 4, "website")
+        self.addRoleName(Qt.UserRole + 5, "package_count")
+        self.addRoleName(Qt.UserRole + 6, "package_types")
+        self.addRoleName(Qt.UserRole + 7, "icon_url")
+        self.addRoleName(Qt.UserRole + 8, "description")
 
         # List of filters for queries. The result is the union of the each list of results.
         self._filter = {}  # type: Dict[str,str]
@@ -35,21 +36,24 @@ class AuthorsModel(ListModel):
 
         for author in self._metadata:
             items.append({
-                "name": author["name"],
-                "email": author["email"] if "email" in author else None,
-                "website": author["website"],
-                "type": author["type"] if "type" in author else None,
-                "icon_url": author["icon_url"] if "icon_url" in author else None,
-                "packages_count": author["packages_count"] if "packages_count" in author else 0,
-                "description": "Material and quality profiles from {author_name}".format( author_name = author["name"])
+                "id":             author["author_id"],
+                "name":           author["display_name"],
+                "email":          author["email"] if "email" in author else None,
+                "website":        author["website"],
+                "package_count":  author["package_count"] if "package_count" in author else 0,
+                "package_types":  author["package_types"],
+                "icon_url":       author["icon_url"] if "icon_url" in author else None,
+                "description":    "Material and quality profiles from {author_name}".format( author_name = author["display_name"])
             })
 
         # Filter on all the key-word arguments.
         for key, value in self._filter.items():
-            if "*" in value:
-                key_filter = lambda candidate, key = key, value = value: self._matchRegExp(candidate, key, value)
+            if key is "package_types":
+                key_filter = lambda item, value = value: value in item["package_types"]
+            elif "*" in value:
+                key_filter = lambda item, key = key, value = value: self._matchRegExp(item, key, value)
             else:
-                key_filter = lambda candidate, key = key, value = value: self._matchString(candidate, key, value)
+                key_filter = lambda item, key = key, value = value: self._matchString(item, key, value)
             items = filter(key_filter, items)
 
         # Execute all filters.

+ 12 - 10
plugins/Toolbox/src/PackagesModel.py

@@ -21,14 +21,15 @@ class PackagesModel(ListModel):
         self.addRoleName(Qt.UserRole + 2, "type")
         self.addRoleName(Qt.UserRole + 3, "name")
         self.addRoleName(Qt.UserRole + 4, "version")
-        self.addRoleName(Qt.UserRole + 5, "author_name")
-        self.addRoleName(Qt.UserRole + 6, "author_email")
-        self.addRoleName(Qt.UserRole + 7, "description")
-        self.addRoleName(Qt.UserRole + 8, "icon_url")
-        self.addRoleName(Qt.UserRole + 9, "image_urls")
-        self.addRoleName(Qt.UserRole + 10, "download_url")
-        self.addRoleName(Qt.UserRole + 11, "last_updated")
-        self.addRoleName(Qt.UserRole + 12, "is_bundled")
+        self.addRoleName(Qt.UserRole + 5, "author_id")
+        self.addRoleName(Qt.UserRole + 6, "author_name")
+        self.addRoleName(Qt.UserRole + 7, "author_email")
+        self.addRoleName(Qt.UserRole + 8, "description")
+        self.addRoleName(Qt.UserRole + 9, "icon_url")
+        self.addRoleName(Qt.UserRole + 10, "image_urls")
+        self.addRoleName(Qt.UserRole + 11, "download_url")
+        self.addRoleName(Qt.UserRole + 12, "last_updated")
+        self.addRoleName(Qt.UserRole + 13, "is_bundled")
 
         # List of filters for queries. The result is the union of the each list of results.
         self._filter = {}  # type: Dict[str, str]
@@ -46,8 +47,9 @@ class PackagesModel(ListModel):
                 "type": package["package_type"],
                 "name": package["display_name"],
                 "version": package["package_version"],
-                "author_name": package["author"]["name"],
-                "author_email": package["author"]["email"] if "email" in package["author"] else None,
+                "author_id": package["author"]["author_id"],
+                "author_name": package["author"]["display_name"],
+                "author_email": package["author"]["email"] if "email" in package["author"] else "None",
                 "description": package["description"],
                 "icon_url": package["icon_url"] if "icon_url" in package else None,
                 "image_urls": package["image_urls"] if "image_urls" in package else None,

+ 61 - 137
plugins/Toolbox/src/Toolbox.py

@@ -56,10 +56,10 @@ class Toolbox(QObject, Extension):
             )
         ]
         self._request_urls = {
-            "authors": None,
-            "packages": QUrl("{base_url}/packages".format(base_url = self._api_url)),
-            "plugins_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url)),
-            "materials_showcase": None
+            "authors":            QUrl("{base_url}/authors".format(base_url = self._api_url)),
+            "packages":           QUrl("{base_url}/packages".format(base_url = self._api_url)),
+            "plugins_showcase":   QUrl("{base_url}/showcase".format(base_url = self._api_url)),
+            "materials_showcase": QUrl("{base_url}/showcase".format(base_url = self._api_url))
         }
 
         # Data:
@@ -68,33 +68,7 @@ class Toolbox(QObject, Extension):
             "packages": [],
             "plugins_showcase": [],
             "plugins_installed": [],
-            # TODO: Replace this with a proper API call:
-            "materials_showcase": [
-                {
-                    "name": "Ultimaker",
-                    "email": "ian.paschal@gmail.com",
-                    "website": "ultimaker.com",
-                    "type": "material",
-                    "icon": None,
-                    "packages_count": 7
-                },
-                {
-                    "name": "DSM",
-                    "email": "contact@dsm.nl",
-                    "website": "www.dsm.nl",
-                    "type": "material",
-                    "icon": None,
-                    "packages_count": 0
-                },
-                {
-                    "name": "BASF",
-                    "email": "contact@basf.de",
-                    "website": "www.basf.de",
-                    "type": "material",
-                    "icon": None,
-                    "packages_count": 0
-                }
-            ],
+            "materials_showcase": [],
             "materials_installed": []
         }
 
@@ -103,8 +77,10 @@ class Toolbox(QObject, Extension):
             "authors": AuthorsModel(self),
             "packages": PackagesModel(self),
             "plugins_showcase": PackagesModel(self),
+            "plugins_available": PackagesModel(self),
             "plugins_installed": PackagesModel(self),
             "materials_showcase": AuthorsModel(self),
+            "materials_available": PackagesModel(self),
             "materials_installed": PackagesModel(self)
         }
 
@@ -190,7 +166,9 @@ class Toolbox(QObject, Extension):
 
         # Make remote requests:
         self._makeRequestByType("packages")
+        self._makeRequestByType("authors")
         self._makeRequestByType("plugins_showcase")
+        self._makeRequestByType("materials_showcase")
 
         # Gather installed packages:
         self._updateInstalledModels()
@@ -298,6 +276,15 @@ class Toolbox(QObject, Extension):
             return True
         return False
 
+    def loadingComplete(self) -> bool:
+        populated = 0
+        for list in self._metadata.items():
+            if len(list) > 0:
+                populated += 1
+        if populated == len(self._metadata.items()):
+            return True
+        return False
+
 
 
     # Make API Calls
@@ -362,111 +349,50 @@ class Toolbox(QObject, Extension):
             return
 
         if reply.operation() == QNetworkAccessManager.GetOperation:
-            # TODO: In the future use the following to build any model from any
-            # request. Right now this doesn't work because the packages request
-            # is also responsible for populating other models.
-            # for type, url in self._request_urls.items():
-            #     if reply.url() == url:
-            #         try:
-            #             json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
-            #
-            #             # Check for errors:
-            #             if "errors" in json_data:
-            #                 for error in json_data["errors"]:
-            #                     Logger.log("e", "%s", error["title"])
-            #                 return
-            #
-            #             # Create model and apply metadata:
-            #             if not self._models[type]:
-            #                 Logger.log("e", "Could not find the %s model.", type)
-            #                 break
-            #             self._metadata[type] = json_data["data"]
-            #             self._models[type].setMetadata(self._metadata[type])
-            #             self.metadataChanged.emit()
-            #             self.setViewPage("overview")
-            #             return
-            #         except json.decoder.JSONDecodeError:
-            #             Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
-            #             break
-
-            if reply.url() == self._request_urls["packages"]:
-                try:
-                    json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
-
-                    # Check for errors:
-                    if "errors" in json_data:
-                        for error in json_data["errors"]:
-                            Logger.log("e", "%s", error["title"])
-                        return
+            for type, url in self._request_urls.items():
+                if reply.url() == url:
+                    try:
+                        json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
+
+                        # Check for errors:
+                        if "errors" in json_data:
+                            for error in json_data["errors"]:
+                                Logger.log("e", "%s", error["title"])
+                            return
+
+                        # Create model and apply metadata:
+                        if not self._models[type]:
+                            Logger.log("e", "Could not find the %s model.", type)
+                            break
+
+                        # HACK: Eventually get rid of the code from here...
+                        if type is "plugins_showcase" or type is "materials_showcase":
+                            self._metadata["plugins_showcase"] = json_data["data"]["plugin"]["packages"]
+                            self._metadata["materials_showcase"] = json_data["data"]["material"]["authors"]
+                        else:
+                            # ...until here.
+                            # This hack arises for multiple reasons but the main
+                            # one is because there are not separate API calls
+                            # for different kinds of showcases.
+                            self._metadata[type] = json_data["data"]
+                        self._models[type].setMetadata(self._metadata[type])
+
+                        # Do some auto filtering
+                        # TODO: Make multiple API calls in the future to handle this
+                        if type is "packages":
+                            self._models[type].setFilter({"type": "plugin"})
+                        if type is "authors":
+                            self._models[type].setFilter({"package_types": "material"})
+
+                        self.metadataChanged.emit()
+
+                        if self.loadingComplete() is True:
+                            self.setViewPage("overview")
 
-                    # Create packages model with all packages:
-                    if not self._models["packages"]:
-                        self._models["packages"] = PackagesModel(self)
-                    self._metadata["packages"] = json_data["data"]
-                    self._models["packages"].setMetadata(self._metadata["packages"])
-                    self.metadataChanged.emit()
-
-                    # Create authors model with all authors:
-                    if not self._models["authors"]:
-                        self._models["authors"] = AuthorsModel()
-                    # TODO: Replace this with a proper API call:
-                    for package in self._metadata["packages"]:
-                        if package["author"] not in self._metadata["authors"]:
-                            self._metadata["authors"].append(package["author"])
-
-                    for author in self._metadata["authors"]:
-                        if "package_count" not in author:
-                            author["package_count"] = 0
-
-                        for package in self._metadata["packages"]:
-                            if package["author"]["name"] == author["name"]:
-                                author["package_count"] += 1
-                                author["type"] = package["package_type"]
-                                if "icon_url" in package:
-                                    author["icon_url"] = package["icon_url"]
-
-                    self._models["authors"].setMetadata(self._metadata["authors"])
-                    self.metadataChanged.emit()
-
-                    if not self._models["materials_showcase"]:
-                        self._models["materials_showcase"] = AuthorsModel(self)
-                    # TODO: Replace this with a proper API call:
-                    self._models["materials_showcase"].setMetadata(self._metadata["materials_showcase"])
-
-                    # This part is also needed for comparing downloaded packages to
-                    # installed packages.
-                    self._models["packages"].setMetadata(self._metadata["packages"])
-                    self._models["packages"].setFilter({"type": "plugin"})
-
-                    self.metadataChanged.emit()
-
-                    self.setViewPage("overview")
-                    return
-
-                except json.decoder.JSONDecodeError:
-                    Logger.log("w", "Toolbox: Received invalid JSON for package list.")
-                    return
-
-            if reply.url() == self._request_urls["plugins_showcase"]:
-                try:
-                    json_data = json.loads(bytes(reply.readAll()).decode("utf-8"))
-
-                    # Check for errors:
-                    if "errors" in json_data:
-                        for error in json_data["errors"]:
-                            Logger.log("e", "%s", error["title"])
                         return
-
-                    self._metadata["plugins_showcase"] = json_data["data"]
-                    self._models["plugins_showcase"].setMetadata(self._metadata["plugins_showcase"])
-                    self.metadataChanged.emit()
-
-                    self.setViewPage("overview")
-                    return
-
-                except json.decoder.JSONDecodeError:
-                    Logger.log("w", "Toolbox: Received invalid JSON for showcase.")
-                    return
+                    except json.decoder.JSONDecodeError:
+                        Logger.log("w", "Toolbox: Received invalid JSON for %s.", type)
+                        break
 
         else:
             # Ignore any operation that is not a get operation
@@ -531,7 +457,7 @@ class Toolbox(QObject, Extension):
     def activePackage(self) -> dict:
         return self._active_package
 
-    def setViewCategory(self, category: str = "plugins"):
+    def setViewCategory(self, category: str = "plugin"):
         self._view_category = category
         self.viewChanged.emit()
     @pyqtProperty(str, fset = setViewCategory, notify = viewChanged)
@@ -549,8 +475,6 @@ class Toolbox(QObject, Extension):
 
     # Expose Models:
     # --------------------------------------------------------------------------
-    # TODO: Maybe replace this with simply exposing self._models to Qt and then
-    # setting model: toolbox.models.foobar instead of toolbox.foobarModel
     @pyqtProperty(QObject, notify = metadataChanged)
     def authorsModel(self) -> AuthorsModel:
         return self._models["authors"]