HaikuDepot: Use web app bulk transfers.

Collect up to 50 packages and get information about them in a bulk transfer.
If that fails, do it in two smaller transfers, until only one package is
left, fall back to use the getPkg method than.

Remember packages for which there is an icon on the server. After the
first round of reading icons from the cache, only try to fetch icons from
the server for which we know one should be there.

Added quite a bit of command line output to see what is going on.
This commit is contained in:
Stephan Aßmus
2014-08-30 23:42:07 +02:00
parent 630c253b03
commit e3fdc8940c
2 changed files with 223 additions and 53 deletions
+213 -53
View File
@@ -17,6 +17,7 @@
#include <Entry.h>
#include <FindDirectory.h>
#include <File.h>
#include <Message.h>
#include <Path.h>
#include "WebAppInterface.h"
@@ -598,6 +599,8 @@ Model::_PopulateAllPackagesThread(bool fromCacheOnly)
{
int32 depotIndex = 0;
int32 packageIndex = 0;
PackageList bulkPackageList;
PackageList packagesWithIconsList;
while (!fStopPopulatingAllPackages) {
// Obtain PackageInfoRef while keeping the depot and package lists
@@ -625,11 +628,120 @@ Model::_PopulateAllPackagesThread(bool fromCacheOnly)
if (package.Get() == NULL)
continue;
_PopulatePackageInfo(package, fromCacheOnly);
_PopulatePackageIcon(package, fromCacheOnly);
//_PopulatePackageInfo(package, fromCacheOnly);
bulkPackageList.Add(package);
if (bulkPackageList.CountItems() == 50) {
_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
packagesWithIconsList);
bulkPackageList.Clear();
}
if (fromCacheOnly)
_PopulatePackageIcon(package, fromCacheOnly);
// TODO: Average user rating. It needs to be shown in the
// list view, so without the user clicking the package.
}
if (!fStopPopulatingAllPackages && bulkPackageList.CountItems() > 0) {
_PopulatePackageInfos(bulkPackageList, fromCacheOnly,
packagesWithIconsList);
}
if (!fromCacheOnly) {
for (int i = packagesWithIconsList.CountItems() - 1; i >= 0; i--) {
if (fStopPopulatingAllPackages)
break;
const PackageInfoRef& package = packagesWithIconsList.ItemAtFast(i);
printf("Getting/Updating native icon for %s\n",
package->Title().String());
_PopulatePackageIcon(package, fromCacheOnly);
}
}
}
void
Model::_PopulatePackageInfos(PackageList& packages, bool fromCacheOnly,
PackageList& packagesWithIcons)
{
if (fromCacheOnly)
return;
// Retrieve info from web-app
WebAppInterface interface;
BMessage info;
StringList packageNames;
for (int i = 0; i < packages.CountItems(); i++) {
const PackageInfoRef& package = packages.ItemAtFast(i);
packageNames.Add(package->Title());
}
status_t status = interface.RetrieveBulkPackageInfo(packageNames, info);
if (status == B_OK) {
// Parse message
// info.PrintToStream();
BMessage result;
BMessage pkgs;
if (info.FindMessage("result", &result) == B_OK
&& result.FindMessage("pkgs", &pkgs) == B_OK) {
int32 index = 0;
while (true) {
BString name;
name << index++;
BMessage pkgInfo;
if (pkgs.FindMessage(name, &pkgInfo) != B_OK)
break;
BString pkgName;
if (pkgInfo.FindString("name", &pkgName) != B_OK)
continue;
// Find the PackageInfoRef
bool found = false;
for (int i = 0; i < packages.CountItems(); i++) {
const PackageInfoRef& package = packages.ItemAtFast(i);
if (pkgName == package->Title()) {
_PopulatePackageInfo(package, pkgInfo);
if (_HasNativeIcon(pkgInfo))
packagesWithIcons.Add(package);
packages.Remove(i);
found = true;
break;
}
}
if (!found)
printf("No matching package for %s\n", pkgName.String());
}
}
} else {
printf("Error sending request: %s\n", strerror(status));
int count = packages.CountItems();
if (count >= 4) {
// Retry in smaller chunks
PackageList firstHalf;
PackageList secondHalf;
for (int i = 0; i < count / 2; i++)
firstHalf.Add(packages.ItemAtFast(i));
for (int i = count / 2; i < count; i++)
secondHalf.Add(packages.ItemAtFast(i));
packages.Clear();
_PopulatePackageInfos(firstHalf, fromCacheOnly, packagesWithIcons);
_PopulatePackageInfos(secondHalf, fromCacheOnly, packagesWithIcons);
} else {
while (packages.CountItems() > 0) {
const PackageInfoRef& package = packages.ItemAtFast(0);
_PopulatePackageInfo(package, fromCacheOnly);
packages.Remove(0);
}
}
}
if (packages.CountItems() > 0) {
for (int i = 0; i < packages.CountItems(); i++) {
const PackageInfoRef& package = packages.ItemAtFast(i);
printf("No package info for %s\n", package->Title().String());
}
}
}
@@ -646,61 +758,80 @@ Model::_PopulatePackageInfo(const PackageInfoRef& package, bool fromCacheOnly)
status_t status = interface.RetrievePackageInfo(package->Title(), info);
if (status == B_OK) {
// Parse message
info.PrintToStream();
// info.PrintToStream();
BMessage result;
if (info.FindMessage("result", &result) == B_OK) {
BMessage categories;
if (result.FindMessage("pkgCategoryCodes", &categories) == B_OK) {
int32 index = 0;
while (true) {
BString name;
name << index++;
BString category;
if (categories.FindString(name, &category) != B_OK)
break;
if (info.FindMessage("result", &result) == B_OK)
_PopulatePackageInfo(package, result);
}
}
if (category == "AUDIO")
package->AddCategory(CategoryAudio());
else if (category == "BUSINESS")
package->AddCategory(CategoryBusiness());
else if (category == "DEVELOPMENT")
package->AddCategory(CategoryDevelopment());
else if (category == "EDUCATION")
package->AddCategory(CategoryEducation());
else if (category == "GAMES")
package->AddCategory(CategoryGames());
else if (category == "GRAPHICS")
package->AddCategory(CategoryGraphics());
else if (category == "INTERNETANDNETWORK")
package->AddCategory(CategoryInternetAndNetwork());
else if (category == "PRODUCTIVITY")
package->AddCategory(CategoryProductivity());
else if (category == "SCIENCEANDMATHEMATICS")
package->AddCategory(CategoryScienceAndMathematics());
else if (category == "SYSTEMANDUTILITIES")
package->AddCategory(CategorySystemAndUtilities());
else if (category == "VIDEO")
package->AddCategory(CategoryVideo());
// TODO: The server will eventually support an API to
// get the defined categories and their translated names.
// This should then be used instead of hard-coded
// categories and translations in the app.
}
}
double derivedRating;
double derivedRatingSampleSize;
if (result.FindDouble("derivedRating", &derivedRating) == B_OK
&& result.FindDouble("derivedRatingSampleSize",
&derivedRatingSampleSize) == B_OK) {
if (derivedRatingSampleSize > 0) {
RatingSummary summary;
summary.averageRating = derivedRating;
summary.ratingCount = (int)derivedRatingSampleSize;
package->SetRatingSummary(summary);
}
}
void
Model::_PopulatePackageInfo(const PackageInfoRef& package,
const BMessage& data)
{
const char* categoriesDebug = "";
const char* ratingDebug = "";
BMessage categories;
if (data.FindMessage("pkgCategoryCodes", &categories) == B_OK) {
int32 index = 0;
while (true) {
BString name;
name << index++;
BString category;
if (categories.FindString(name, &category) != B_OK)
break;
if (category == "AUDIO")
package->AddCategory(CategoryAudio());
else if (category == "BUSINESS")
package->AddCategory(CategoryBusiness());
else if (category == "DEVELOPMENT")
package->AddCategory(CategoryDevelopment());
else if (category == "EDUCATION")
package->AddCategory(CategoryEducation());
else if (category == "GAMES")
package->AddCategory(CategoryGames());
else if (category == "GRAPHICS")
package->AddCategory(CategoryGraphics());
else if (category == "INTERNETANDNETWORK")
package->AddCategory(CategoryInternetAndNetwork());
else if (category == "PRODUCTIVITY")
package->AddCategory(CategoryProductivity());
else if (category == "SCIENCEANDMATHEMATICS")
package->AddCategory(CategoryScienceAndMathematics());
else if (category == "SYSTEMANDUTILITIES")
package->AddCategory(CategorySystemAndUtilities());
else if (category == "VIDEO")
package->AddCategory(CategoryVideo());
// TODO: The server will eventually support an API to
// get the defined categories and their translated names.
// This should then be used instead of hard-coded
// categories and translations in the app.
categoriesDebug = "categories";
}
}
double derivedRating;
double derivedRatingSampleSize;
if (data.FindDouble("derivedRating", &derivedRating) == B_OK
&& data.FindDouble("derivedRatingSampleSize",
&derivedRatingSampleSize) == B_OK) {
if (derivedRatingSampleSize > 0) {
RatingSummary summary;
summary.averageRating = derivedRating;
summary.ratingCount = (int)derivedRatingSampleSize;
package->SetRatingSummary(summary);
if (strlen(categoriesDebug) > 0)
ratingDebug = ", rating";
else
ratingDebug = "rating";
}
}
printf("Populated package info for %s: %s%s\n",
package->Title().String(), categoriesDebug, ratingDebug);
}
@@ -753,3 +884,32 @@ Model::_PopulatePackageIcon(const PackageInfoRef& package, bool fromCacheOnly)
}
}
}
bool
Model::_HasNativeIcon(const BMessage& message) const
{
BMessage pkgIcons;
if (message.FindMessage("pkgIcons", &pkgIcons) != B_OK)
return false;
if (!pkgIcons.IsEmpty())
pkgIcons.PrintToStream();
int32 index = 0;
while (true) {
BString name;
name << index++;
BMessage typeCodeInfo;
if (pkgIcons.FindMessage(name, &typeCodeInfo) != B_OK)
break;
BString mediaTypeCode;
if (typeCodeInfo.FindString("mediaTypeCode", &mediaTypeCode) == B_OK
&& mediaTypeCode == "application/x-vnd.haiku-icon") {
return true;
}
}
return false;
}
+10
View File
@@ -9,6 +9,8 @@
#include "PackageInfo.h"
class BMessage;
class PackageFilter : public BReferenceable {
public:
@@ -101,12 +103,20 @@ private:
static int32 _PopulateAllPackagesEntry(void* cookie);
void _PopulateAllPackagesThread(bool fromCacheOnly);
void _PopulatePackageInfos(
PackageList& packages,
bool fromCacheOnly,
PackageList& packagesWithIcons);
void _PopulatePackageInfo(
const PackageInfoRef& package,
bool fromCacheOnly);
void _PopulatePackageInfo(
const PackageInfoRef& package,
const BMessage& data);
void _PopulatePackageIcon(
const PackageInfoRef& package,
bool fromCacheOnly);
bool _HasNativeIcon(const BMessage& message) const;
private:
BLocker fLock;