Adds rough first version of rating stars

It's not fully polished just yet

CURA-6013
This commit is contained in:
Jaime van Kessel 2018-12-07 15:31:33 +01:00
parent ab8a2a9b2b
commit f4220da550
8 changed files with 180 additions and 17 deletions

View file

@ -44,7 +44,7 @@ class Account(QObject):
OAUTH_SERVER_URL= self._oauth_root, OAUTH_SERVER_URL= self._oauth_root,
CALLBACK_PORT=self._callback_port, CALLBACK_PORT=self._callback_port,
CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port), CALLBACK_URL="http://localhost:{}/callback".format(self._callback_port),
CLIENT_ID="um---------------ultimaker_cura_drive_plugin", CLIENT_ID="um----------------------------ultimaker_cura",
CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write", CLIENT_SCOPES="account.user.read drive.backup.read drive.backup.write packages.download packages.rating.read packages.rating.write",
AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data", AUTH_DATA_PREFERENCE_KEY="general/ultimaker_auth_data",
AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root), AUTH_SUCCESS_REDIRECT="{}/app/auth-success".format(self._oauth_root),

View file

@ -0,0 +1,101 @@
import QtQuick 2.2
import QtQuick.Controls 2.0
import UM 1.0 as UM
Item
{
id: ratingWidget
property real rating: 0
property int indexHovered: -1
property string packageId: ""
property int numRatings: 0
property int userRating: 0
width: contentRow.width
height: contentRow.height
MouseArea
{
id: mouseArea
anchors.fill: parent
hoverEnabled: ratingWidget.enabled
acceptedButtons: Qt.NoButton
onExited:
{
ratingWidget.indexHovered = -1
}
Row
{
id: contentRow
height: childrenRect.height
Repeater
{
model: 5 // We need to get 5 stars
Button
{
id: control
hoverEnabled: true
onHoveredChanged:
{
if(hovered)
{
indexHovered = index
}
}
property bool isStarFilled:
{
// If the entire widget is hovered, override the actual rating.
if(ratingWidget.indexHovered >= 0)
{
return indexHovered >= index
}
if(ratingWidget.userRating > 0)
{
return userRating >= index +1
}
return rating >= index + 1
}
contentItem: Item {}
height: UM.Theme.getSize("rating_star").height
width: UM.Theme.getSize("rating_star").width
background: UM.RecolorImage
{
source: UM.Theme.getIcon(control.isStarFilled ? "star_filled" : "star_empty")
// Unfilled stars should always have the default color. Only filled stars should change on hover
color:
{
if(!enabled)
{
return "#5a5a5a"
}
if((ratingWidget.indexHovered >= 0 || ratingWidget.userRating > 0) && isStarFilled)
{
return UM.Theme.getColor("primary")
}
return "#5a5a5a"
}
}
onClicked:
{
if(userRating == 0)
{
//User didn't vote yet, locally fake it
numRatings += 1
}
userRating = index + 1 // Fake local data
toolbox.ratePackage(ratingWidget.packageId, index + 1)
}
}
}
Label
{
text: "(" + numRatings + ")"
}
}
}
}

View file

@ -84,6 +84,16 @@ Item
color: UM.Theme.getColor("text_medium") color: UM.Theme.getColor("text_medium")
font: UM.Theme.getFont("default") font: UM.Theme.getFont("default")
} }
RatingWidget
{
visible: model.type == "plugin"
packageId: model.id
rating: model.average_rating != undefined ? model.average_rating : 0
numRatings: model.num_ratings != undefined ? model.num_ratings : 0
userRating: model.user_rating
enabled: installedPackages != 0
}
} }
} }
MouseArea MouseArea

View file

@ -41,6 +41,9 @@ class PackagesModel(ListModel):
self.addRoleName(Qt.UserRole + 20, "links") self.addRoleName(Qt.UserRole + 20, "links")
self.addRoleName(Qt.UserRole + 21, "website") self.addRoleName(Qt.UserRole + 21, "website")
self.addRoleName(Qt.UserRole + 22, "login_required") self.addRoleName(Qt.UserRole + 22, "login_required")
self.addRoleName(Qt.UserRole + 23, "average_rating")
self.addRoleName(Qt.UserRole + 24, "num_ratings")
self.addRoleName(Qt.UserRole + 25, "user_rating")
# List of filters for queries. The result is the union of the each list of results. # List of filters for queries. The result is the union of the each list of results.
self._filter = {} # type: Dict[str, str] self._filter = {} # type: Dict[str, str]
@ -101,7 +104,10 @@ class PackagesModel(ListModel):
"tags": package["tags"] if "tags" in package else [], "tags": package["tags"] if "tags" in package else [],
"links": links_dict, "links": links_dict,
"website": package["website"] if "website" in package else None, "website": package["website"] if "website" in package else None,
"login_required": "login-required" in package.get("tags", []) "login_required": "login-required" in package.get("tags", []),
"average_rating": package.get("rating", {}).get("average", 0),
"num_ratings": package.get("rating", {}).get("count", 0),
"user_rating": package.get("rating", {}).get("user", 0)
}) })
# Filter on all the key-word arguments. # Filter on all the key-word arguments.

View file

@ -22,7 +22,8 @@ from cura.CuraApplication import CuraApplication
from .AuthorsModel import AuthorsModel from .AuthorsModel import AuthorsModel
from .PackagesModel import PackagesModel from .PackagesModel import PackagesModel
from cura.CuraVersion import CuraVersion
from cura.API import CuraAPI
if TYPE_CHECKING: if TYPE_CHECKING:
from cura.Settings.GlobalStack import GlobalStack from cura.Settings.GlobalStack import GlobalStack
@ -50,17 +51,10 @@ class Toolbox(QObject, Extension):
self._download_progress = 0 # type: float self._download_progress = 0 # type: float
self._is_downloading = False # type: bool self._is_downloading = False # type: bool
self._network_manager = None # type: Optional[QNetworkAccessManager] self._network_manager = None # type: Optional[QNetworkAccessManager]
self._request_header = [ self._request_headers = [] # type: List[Tuple(bytes, bytes)]
b"User-Agent", self._updateRequestHeader()
str.encode(
"%s/%s (%s %s)" % (
self._application.getApplicationName(),
self._application.getVersion(),
platform.system(),
platform.machine(),
)
)
]
self._request_urls = {} # type: Dict[str, QUrl] self._request_urls = {} # type: Dict[str, QUrl]
self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated self._to_update = [] # type: List[str] # Package_ids that are waiting to be updated
self._old_plugin_ids = set() # type: Set[str] self._old_plugin_ids = set() # type: Set[str]
@ -115,6 +109,7 @@ class Toolbox(QObject, Extension):
self._restart_dialog_message = "" # type: str self._restart_dialog_message = "" # type: str
self._application.initializationFinished.connect(self._onAppInitialized) self._application.initializationFinished.connect(self._onAppInitialized)
self._application.getCuraAPI().account.loginStateChanged.connect(self._updateRequestHeader)
# Signals: # Signals:
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
@ -134,12 +129,38 @@ class Toolbox(QObject, Extension):
showLicenseDialog = pyqtSignal() showLicenseDialog = pyqtSignal()
uninstallVariablesChanged = pyqtSignal() uninstallVariablesChanged = pyqtSignal()
def _updateRequestHeader(self):
self._request_headers = [
(b"User-Agent",
str.encode(
"%s/%s (%s %s)" % (
self._application.getApplicationName(),
self._application.getVersion(),
platform.system(),
platform.machine(),
)
))
]
access_token = self._application.getCuraAPI().account.accessToken
if access_token:
self._request_headers.append((b"Authorization", "Bearer {}".format(access_token).encode()))
def _resetUninstallVariables(self) -> None: def _resetUninstallVariables(self) -> None:
self._package_id_to_uninstall = None # type: Optional[str] self._package_id_to_uninstall = None # type: Optional[str]
self._package_name_to_uninstall = "" self._package_name_to_uninstall = ""
self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]] self._package_used_materials = [] # type: List[Tuple[GlobalStack, str, str]]
self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]] self._package_used_qualities = [] # type: List[Tuple[GlobalStack, str, str]]
@pyqtSlot(str, int)
def ratePackage(self, package_id: str, rating: int) -> None:
url = QUrl("{base_url}/packages/{package_id}/ratings".format(base_url=self._api_url, package_id = package_id))
self._rate_request = QNetworkRequest(url)
for header_name, header_value in self._request_headers:
cast(QNetworkRequest, self._rate_request).setRawHeader(header_name, header_value)
data = "{\"data\": {\"cura_version\": \"%s\", \"rating\": %i}}" % (Version(CuraVersion), rating)
self._rate_reply = cast(QNetworkAccessManager, self._network_manager).put(self._rate_request, data.encode())
@pyqtSlot(result = str) @pyqtSlot(result = str)
def getLicenseDialogPluginName(self) -> str: def getLicenseDialogPluginName(self) -> str:
return self._license_dialog_plugin_name return self._license_dialog_plugin_name
@ -563,7 +584,8 @@ class Toolbox(QObject, Extension):
def _makeRequestByType(self, request_type: str) -> None: def _makeRequestByType(self, request_type: str) -> None:
Logger.log("i", "Requesting %s metadata from server.", request_type) Logger.log("i", "Requesting %s metadata from server.", request_type)
request = QNetworkRequest(self._request_urls[request_type]) request = QNetworkRequest(self._request_urls[request_type])
request.setRawHeader(*self._request_header) for header_name, header_value in self._request_headers:
request.setRawHeader(header_name, header_value)
if self._network_manager: if self._network_manager:
self._network_manager.get(request) self._network_manager.get(request)
@ -578,7 +600,8 @@ class Toolbox(QObject, Extension):
if hasattr(QNetworkRequest, "RedirectPolicyAttribute"): if hasattr(QNetworkRequest, "RedirectPolicyAttribute"):
# Patch for Qt 5.9+ # Patch for Qt 5.9+
cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True) cast(QNetworkRequest, self._download_request).setAttribute(QNetworkRequest.RedirectPolicyAttribute, True)
cast(QNetworkRequest, self._download_request).setRawHeader(*self._request_header) for header_name, header_value in self._request_headers:
cast(QNetworkRequest, self._download_request).setRawHeader(header_name, header_value)
self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request) self._download_reply = cast(QNetworkAccessManager, self._network_manager).get(self._download_request)
self.setDownloadProgress(0) self.setDownloadProgress(0)
self.setIsDownloading(True) self.setIsDownloading(True)
@ -660,7 +683,7 @@ class Toolbox(QObject, Extension):
else: else:
self.setViewPage("errored") self.setViewPage("errored")
self.resetDownload() self.resetDownload()
else: elif reply.operation() == QNetworkAccessManager.PutOperation:
# Ignore any operation that is not a get operation # Ignore any operation that is not a get operation
pass pass

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="13px" viewBox="0 0 14 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Star Copy 8</title>
<desc>Created with Sketch.</desc>
<g id="Toolbox-VIP-material" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Marketplace-hover-" transform="translate(-140.000000, -457.000000)" stroke="#666666">
<path d="M150.450431,468.749111 L149.791458,464.907 L152.582915,462.186001 L148.725216,461.625444 L147,458.129776 L145.274784,461.625444 L141.417085,462.186001 L144.208542,464.907 L143.549569,468.749111 L147,466.935112 L150.450431,468.749111 Z" id="Star-Copy-8"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 844 B

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="13px" viewBox="0 0 14 13" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Star Copy 7</title>
<desc>Created with Sketch.</desc>
<g id="Toolbox-VIP-material" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Marketplace-hover-" transform="translate(-127.000000, -457.000000)" fill="#666666" stroke="#666666">
<path d="M137.450431,468.749111 L136.791458,464.907 L139.582915,462.186001 L135.725216,461.625444 L134,458.129776 L132.274784,461.625444 L128.417085,462.186001 L131.208542,464.907 L130.549569,468.749111 L134,466.935112 L137.450431,468.749111 Z" id="Star-Copy-7"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 859 B

View file

@ -394,6 +394,7 @@
"section": [0.0, 2.2], "section": [0.0, 2.2],
"section_icon": [1.6, 1.6], "section_icon": [1.6, 1.6],
"section_icon_column": [2.8, 0.0], "section_icon_column": [2.8, 0.0],
"rating_star": [1.0, 1.0],
"setting": [25.0, 1.8], "setting": [25.0, 1.8],
"setting_control": [10.0, 2.0], "setting_control": [10.0, 2.0],