From 922e98af67f25151e2dbcb377742c21ef401caf8 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 29 Jun 2014 23:30:32 +0200 Subject: [PATCH 1/9] Add pricing from deckbrew.com * the previous PriceUpdater class has become abstract * BLPPriceUpdater inherits the old code for blacklotusproject.com * DBPriceUpdater is a new implementation for deckbrew.com * add a setting to choose between the two --- cockatrice/src/dlg_settings.cpp | 39 ++++++++- cockatrice/src/dlg_settings.h | 4 + cockatrice/src/priceupdater.cpp | 136 ++++++++++++++++++++++++++++- cockatrice/src/priceupdater.h | 32 +++++-- cockatrice/src/settingscache.cpp | 7 ++ cockatrice/src/settingscache.h | 3 + cockatrice/src/tab_deck_editor.cpp | 13 ++- 7 files changed, 222 insertions(+), 12 deletions(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 14115824..cf8fa846 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "carddatabase.h" #include "dlg_settings.h" @@ -531,9 +532,27 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() priceTagsCheckBox = new QCheckBox; priceTagsCheckBox->setChecked(settingsCache->getPriceTagFeature()); connect(priceTagsCheckBox, SIGNAL(stateChanged(int)), settingsCache, SLOT(setPriceTagFeature(int))); - + + priceTagSource0 = new QRadioButton; + priceTagSource1 = new QRadioButton; + + switch(settingsCache->getPriceTagSource()) + { + case 0: + priceTagSource0->setChecked(true); + break; + case 1: + priceTagSource1->setChecked(true); + break; + } + + connect(priceTagSource0, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); + connect(priceTagSource1, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); + QGridLayout *generalGrid = new QGridLayout; generalGrid->addWidget(priceTagsCheckBox, 0, 0); + generalGrid->addWidget(priceTagSource0, 1, 0); + generalGrid->addWidget(priceTagSource1, 2, 0); generalGroupBox = new QGroupBox; generalGroupBox->setLayout(generalGrid); @@ -546,10 +565,26 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() void DeckEditorSettingsPage::retranslateUi() { - priceTagsCheckBox->setText(tr("Enable &price tag feature (using data from blacklotusproject.com)")); + priceTagsCheckBox->setText(tr("Enable &price tag feature")); + priceTagSource0->setText(tr("using data from blacklotusproject.com")); + priceTagSource1->setText(tr("using data from deckbrew.com")); generalGroupBox->setTitle(tr("General")); } +void DeckEditorSettingsPage::radioPriceTagSourceClicked(bool checked) +{ + if(!checked) + return; + + int source=0; + if(priceTagSource0->isChecked()) + source=0; + if(priceTagSource1->isChecked()) + source=1; + + QMetaObject::invokeMethod( settingsCache, "setPriceTagSource", Qt::QueuedConnection, Q_ARG(int, source)); +} + MessagesSettingsPage::MessagesSettingsPage() { aAdd = new QAction(this); diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 8462f612..803e0dfc 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -15,6 +15,7 @@ class QCheckBox; class QLabel; class QCloseEvent; class QSpinBox; +class QRadioButton; class AbstractSettingsPage : public QWidget { public: @@ -100,8 +101,11 @@ class DeckEditorSettingsPage : public AbstractSettingsPage { public: DeckEditorSettingsPage(); void retranslateUi(); +private slots: + void radioPriceTagSourceClicked(bool checked); private: QCheckBox *priceTagsCheckBox; + QRadioButton *priceTagSource0, *priceTagSource1; QGroupBox *generalGroupBox; }; diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index a0d32a32..e5b359df 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -4,25 +4,43 @@ */ #include #include +#include #include "qt-json/json.h" #include "priceupdater.h" +#if QT_VERSION < 0x050000 + // for Qt::escape() + #include +#endif + /** * Constructor. * * @param _deck deck. */ -PriceUpdater::PriceUpdater(const DeckList *_deck) +AbstractPriceUpdater::AbstractPriceUpdater(const DeckList *_deck) { nam = new QNetworkAccessManager(this); deck = _deck; } +// blacklotusproject.com + +/** + * Constructor. + * + * @param _deck deck. + */ +BLPPriceUpdater::BLPPriceUpdater(const DeckList *_deck) +: AbstractPriceUpdater(_deck) +{ +} + /** * Update the prices of the cards in deckList. */ -void PriceUpdater::updatePrices() +void BLPPriceUpdater::updatePrices() { QString q = "http://blacklotusproject.com/json/?cards="; QStringList cards = deck->getCardList(); @@ -38,7 +56,7 @@ void PriceUpdater::updatePrices() /** * Called when the download of the json file with the prices is finished. */ -void PriceUpdater::downloadFinished() +void BLPPriceUpdater::downloadFinished() { QNetworkReply *reply = static_cast(sender()); bool ok; @@ -82,3 +100,115 @@ void PriceUpdater::downloadFinished() deleteLater(); emit finishedUpdate(); } + +// deckbrew.com + +/** + * Constructor. + * + * @param _deck deck. + */ +DBPriceUpdater::DBPriceUpdater(const DeckList *_deck) +: AbstractPriceUpdater(_deck) +{ +} + +/** + * Update the prices of the cards in deckList. + */ +void DBPriceUpdater::updatePrices() +{ + QString q = "https://api.deckbrew.com/mtg/cards"; + QStringList cards = deck->getCardList(); + for (int i = 0; i < cards.size(); ++i) { + q += (i ? "&" : "?") + QString("name=") + cards[i].toLower(); + } + QUrl url(q.replace(' ', '+')); + + QNetworkReply *reply = nam->get(QNetworkRequest(url)); + connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); +} + +/** + * Called when the download of the json file with the prices is finished. + */ +void DBPriceUpdater::downloadFinished() +{ + QNetworkReply *reply = static_cast(sender()); + bool ok; + QString tmp = QString(reply->readAll()); + + // Errors are incapsulated in an object, check for them first + QVariantMap resultMap = QtJson::Json::parse(tmp, ok).toMap(); + if (!ok) { + QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); + reply->deleteLater(); + deleteLater(); + return; + } + + if(resultMap.contains("errors")) + { + QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices:") + + "
" + +#if QT_VERSION < 0x050000 + Qt::escape(resultMap["errors"].toList().first().toString()) +#else + resultMap["errors"].toList().first().toString().toHtmlEscaped() +#endif + ); + reply->deleteLater(); + deleteLater(); + return; + } + + // Good results are a list + QVariantList resultList = QtJson::Json::parse(tmp, ok).toList(); + if (!ok) { + QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); + reply->deleteLater(); + deleteLater(); + return; + } + + QMap cardsPrice; + + QListIterator it(resultList); + while (it.hasNext()) { + QVariantMap map = it.next().toMap(); + QString name = map.value("name").toString().toLower(); + + QList editions = map.value("editions").toList(); + foreach (QVariant ed, editions) + { + QVariantMap edition = ed.toMap(); + QString set = edition.value("set_id").toString(); + // Prices are in USD cents + float price = edition.value("price").toMap().value("median").toString().toFloat() / 100; + //qDebug() << "card " << name << " set " << set << " price " << price << endl; + + /** + * Make sure Masters Edition (MED) isn't the set, as it doesn't + * physically exist. Also check the price to see that the cheapest set + * ends up as the final price. + */ + if (set != "MED" && (!cardsPrice.contains(name) || cardsPrice.value(name) > price)) + cardsPrice.insert(name, price); + } + } + + InnerDecklistNode *listRoot = deck->getRoot(); + for (int i = 0; i < listRoot->size(); i++) { + InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); + for (int j = 0; j < currentZone->size(); j++) { + DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); + if (!currentCard) + continue; + currentCard->setPrice(cardsPrice[currentCard->getName().toLower()]); + } + } + + reply->deleteLater(); + deleteLater(); + emit finishedUpdate(); +} diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index a95252eb..901471f5 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -11,18 +11,38 @@ class QNetworkAccessManager; * * @author Marcio Ribeiro */ -class PriceUpdater : public QObject +class AbstractPriceUpdater : public QWidget { Q_OBJECT -private: +protected: const DeckList *deck; QNetworkAccessManager *nam; signals: void finishedUpdate(); -private slots: - void downloadFinished(); +protected slots: + virtual void downloadFinished() = 0; public: - PriceUpdater(const DeckList *deck); - void updatePrices(); + AbstractPriceUpdater(const DeckList *deck); + virtual void updatePrices() = 0; +}; + +class BLPPriceUpdater : public AbstractPriceUpdater +{ + Q_OBJECT +protected: + virtual void downloadFinished(); +public: + BLPPriceUpdater(const DeckList *deck); + virtual void updatePrices(); +}; + +class DBPriceUpdater : public AbstractPriceUpdater +{ + Q_OBJECT +protected: + virtual void downloadFinished(); +public: + DBPriceUpdater(const DeckList *deck); + virtual void updatePrices(); }; #endif diff --git a/cockatrice/src/settingscache.cpp b/cockatrice/src/settingscache.cpp index 892630f5..5f504aa3 100644 --- a/cockatrice/src/settingscache.cpp +++ b/cockatrice/src/settingscache.cpp @@ -45,6 +45,7 @@ SettingsCache::SettingsCache() soundPath = settings->value("sound/path").toString(); priceTagFeature = settings->value("deckeditor/pricetags", false).toBool(); + priceTagSource = settings->value("deckeditor/pricetagsource", 0).toInt(); ignoreUnregisteredUsers = settings->value("chat/ignore_unregistered", false).toBool(); } @@ -247,6 +248,12 @@ void SettingsCache::setPriceTagFeature(int _priceTagFeature) emit priceTagFeatureChanged(priceTagFeature); } +void SettingsCache::setPriceTagSource(int _priceTagSource) +{ + priceTagSource = _priceTagSource; + settings->setValue("deckeditor/pricetagsource", priceTagSource); +} + void SettingsCache::setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers) { ignoreUnregisteredUsers = _ignoreUnregisteredUsers; diff --git a/cockatrice/src/settingscache.h b/cockatrice/src/settingscache.h index 68b5129e..3b4d612d 100644 --- a/cockatrice/src/settingscache.h +++ b/cockatrice/src/settingscache.h @@ -53,6 +53,7 @@ private: bool soundEnabled; QString soundPath; bool priceTagFeature; + int priceTagSource; bool ignoreUnregisteredUsers; QString picUrl; QString picUrlHq; @@ -87,6 +88,7 @@ public: bool getSoundEnabled() const { return soundEnabled; } QString getSoundPath() const { return soundPath; } bool getPriceTagFeature() const { return priceTagFeature; } + int getPriceTagSource() const { return priceTagSource; } bool getIgnoreUnregisteredUsers() const { return ignoreUnregisteredUsers; } QString getPicUrl() const { return picUrl; } QString getPicUrlHq() const { return picUrlHq; } @@ -121,6 +123,7 @@ public slots: void setSoundEnabled(int _soundEnabled); void setSoundPath(const QString &_soundPath); void setPriceTagFeature(int _priceTagFeature); + void setPriceTagSource(int _priceTagSource); void setIgnoreUnregisteredUsers(bool _ignoreUnregisteredUsers); void setPicUrl(const QString &_picUrl); void setPicUrlHq(const QString &_picUrlHq); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 0c1d44e1..5706ffd6 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -644,7 +644,18 @@ void TabDeckEditor::setPriceTagFeatureEnabled(int enabled) void TabDeckEditor::actUpdatePrices() { aUpdatePrices->setDisabled(true); - PriceUpdater *up = new PriceUpdater(deckModel->getDeckList()); + AbstractPriceUpdater *up; + + switch(settingsCache->getPriceTagSource()) + { + case 0: + up = new BLPPriceUpdater(deckModel->getDeckList()); + break; + case 1: + up = new DBPriceUpdater(deckModel->getDeckList()); + break; + } + connect(up, SIGNAL(finishedUpdate()), this, SLOT(finishedUpdatingPrices())); up->updatePrices(); } From a9ffbf5d2db104aabbc7ea7ec27cfe45813ccafb Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 19:03:12 +0200 Subject: [PATCH 2/9] use a signal instead of calling QMetaObject::invokeMethod --- cockatrice/src/dlg_settings.cpp | 4 +++- cockatrice/src/dlg_settings.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index cf8fa846..0d36dd18 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -549,6 +549,8 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() connect(priceTagSource0, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); connect(priceTagSource1, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); + connect(this, SIGNAL(priceTagSourceChanged(int)), settingsCache, SLOT(setPriceTagSource(int))); + QGridLayout *generalGrid = new QGridLayout; generalGrid->addWidget(priceTagsCheckBox, 0, 0); generalGrid->addWidget(priceTagSource0, 1, 0); @@ -582,7 +584,7 @@ void DeckEditorSettingsPage::radioPriceTagSourceClicked(bool checked) if(priceTagSource1->isChecked()) source=1; - QMetaObject::invokeMethod( settingsCache, "setPriceTagSource", Qt::QueuedConnection, Q_ARG(int, source)); + emit priceTagSourceChanged(source); } MessagesSettingsPage::MessagesSettingsPage() diff --git a/cockatrice/src/dlg_settings.h b/cockatrice/src/dlg_settings.h index 803e0dfc..55685e1e 100644 --- a/cockatrice/src/dlg_settings.h +++ b/cockatrice/src/dlg_settings.h @@ -103,6 +103,8 @@ public: void retranslateUi(); private slots: void radioPriceTagSourceClicked(bool checked); +signals: + void priceTagSourceChanged(int _priceTagSource); private: QCheckBox *priceTagsCheckBox; QRadioButton *priceTagSource0, *priceTagSource1; From 9ca5908de5507de5a7c30e767d80c4dca6994844 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 19:13:05 +0200 Subject: [PATCH 3/9] Create an enum for price sources Probably settingscache would have been the correct place for the enum, but keeping it in the same class/file where price sources are defined sounds cleaner to me --- cockatrice/src/dlg_settings.cpp | 11 ++++++----- cockatrice/src/priceupdater.h | 2 ++ cockatrice/src/tab_deck_editor.cpp | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 0d36dd18..757c719a 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -24,6 +24,7 @@ #include "dlg_settings.h" #include "main.h" #include "settingscache.h" +#include "priceupdater.h" GeneralSettingsPage::GeneralSettingsPage() { @@ -538,10 +539,10 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() switch(settingsCache->getPriceTagSource()) { - case 0: + case AbstractPriceUpdater::BLPPriceSource: priceTagSource0->setChecked(true); break; - case 1: + case AbstractPriceUpdater::DBPriceSource: priceTagSource1->setChecked(true); break; } @@ -578,11 +579,11 @@ void DeckEditorSettingsPage::radioPriceTagSourceClicked(bool checked) if(!checked) return; - int source=0; + int source=AbstractPriceUpdater::BLPPriceSource; if(priceTagSource0->isChecked()) - source=0; + source=AbstractPriceUpdater::BLPPriceSource; if(priceTagSource1->isChecked()) - source=1; + source=AbstractPriceUpdater::DBPriceSource; emit priceTagSourceChanged(source); } diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index 901471f5..642c75c7 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -14,6 +14,8 @@ class QNetworkAccessManager; class AbstractPriceUpdater : public QWidget { Q_OBJECT +public: + enum PriceSource { BLPPriceSource, DBPriceSource }; protected: const DeckList *deck; QNetworkAccessManager *nam; diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 5706ffd6..71fd9cbd 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -648,10 +648,10 @@ void TabDeckEditor::actUpdatePrices() switch(settingsCache->getPriceTagSource()) { - case 0: + case AbstractPriceUpdater::BLPPriceSource: up = new BLPPriceUpdater(deckModel->getDeckList()); break; - case 1: + case AbstractPriceUpdater::DBPriceSource: up = new DBPriceUpdater(deckModel->getDeckList()); break; } From d102733826b636d3683395a12ab1b91807adf50d Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 19:24:51 +0200 Subject: [PATCH 4/9] Added default cases to switch --- cockatrice/src/dlg_settings.cpp | 7 ++++--- cockatrice/src/tab_deck_editor.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cockatrice/src/dlg_settings.cpp b/cockatrice/src/dlg_settings.cpp index 757c719a..0950ec60 100644 --- a/cockatrice/src/dlg_settings.cpp +++ b/cockatrice/src/dlg_settings.cpp @@ -539,12 +539,13 @@ DeckEditorSettingsPage::DeckEditorSettingsPage() switch(settingsCache->getPriceTagSource()) { - case AbstractPriceUpdater::BLPPriceSource: - priceTagSource0->setChecked(true); - break; case AbstractPriceUpdater::DBPriceSource: priceTagSource1->setChecked(true); break; + case AbstractPriceUpdater::BLPPriceSource: + default: + priceTagSource0->setChecked(true); + break; } connect(priceTagSource0, SIGNAL(toggled(bool)), this, SLOT(radioPriceTagSourceClicked(bool))); diff --git a/cockatrice/src/tab_deck_editor.cpp b/cockatrice/src/tab_deck_editor.cpp index 71fd9cbd..5a3d5e77 100644 --- a/cockatrice/src/tab_deck_editor.cpp +++ b/cockatrice/src/tab_deck_editor.cpp @@ -648,12 +648,13 @@ void TabDeckEditor::actUpdatePrices() switch(settingsCache->getPriceTagSource()) { - case AbstractPriceUpdater::BLPPriceSource: - up = new BLPPriceUpdater(deckModel->getDeckList()); - break; case AbstractPriceUpdater::DBPriceSource: up = new DBPriceUpdater(deckModel->getDeckList()); break; + case AbstractPriceUpdater::BLPPriceSource: + default: + up = new BLPPriceUpdater(deckModel->getDeckList()); + break; } connect(up, SIGNAL(finishedUpdate()), this, SLOT(finishedUpdatingPrices())); From 06def7f46ae74fbce5020dc7317e9459ef28cada Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 2 Jul 2014 21:40:41 +0200 Subject: [PATCH 5/9] Ensure all card prices are retrieved correctly Query card database to get a multiverseis for a card. Get card price using that id instead of card name. Tested Aether, split cards, question elemental?, _____ --- cockatrice/src/priceupdater.cpp | 32 ++++++++++++++++++++++++-------- cockatrice/src/priceupdater.h | 5 +++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index e5b359df..1cf2b064 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -9,6 +9,9 @@ #include "qt-json/json.h" #include "priceupdater.h" +#include "main.h" +#include "carddatabase.h" + #if QT_VERSION < 0x050000 // for Qt::escape() #include @@ -120,10 +123,16 @@ void DBPriceUpdater::updatePrices() { QString q = "https://api.deckbrew.com/mtg/cards"; QStringList cards = deck->getCardList(); + muidMap.clear(); + CardInfo * card; + int muid; for (int i = 0; i < cards.size(); ++i) { - q += (i ? "&" : "?") + QString("name=") + cards[i].toLower(); + card = db->getCard(cards[i], false); + muid=card->getPreferredMuId(); + q += (i ? "&" : "?") + QString("multiverseid=%1").arg(muid); + muidMap.insert(muid, cards[i]); } - QUrl url(q.replace(' ', '+')); + QUrl url(q); QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); @@ -172,17 +181,24 @@ void DBPriceUpdater::downloadFinished() } QMap cardsPrice; - QListIterator it(resultList); while (it.hasNext()) { - QVariantMap map = it.next().toMap(); - QString name = map.value("name").toString().toLower(); + QVariantMap cardMap = it.next().toMap(); - QList editions = map.value("editions").toList(); + + // get sets list + QList editions = cardMap.value("editions").toList(); foreach (QVariant ed, editions) { + // retrieve card name "as we know it" from the muid QVariantMap edition = ed.toMap(); QString set = edition.value("set_id").toString(); + + int muid = edition.value("multiverse_id").toString().toInt(); + if(!muidMap.contains(muid)) + continue; + + QString name=muidMap.value(muid); // Prices are in USD cents float price = edition.value("price").toMap().value("median").toString().toFloat() / 100; //qDebug() << "card " << name << " set " << set << " price " << price << endl; @@ -196,7 +212,7 @@ void DBPriceUpdater::downloadFinished() cardsPrice.insert(name, price); } } - + InnerDecklistNode *listRoot = deck->getRoot(); for (int i = 0; i < listRoot->size(); i++) { InnerDecklistNode *currentZone = dynamic_cast(listRoot->at(i)); @@ -204,7 +220,7 @@ void DBPriceUpdater::downloadFinished() DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); if (!currentCard) continue; - currentCard->setPrice(cardsPrice[currentCard->getName().toLower()]); + currentCard->setPrice(cardsPrice[currentCard->getName()]); } } diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index 642c75c7..076d9663 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -6,6 +6,9 @@ class QNetworkAccessManager; +// If we don't typedef this, won't compile on OS X < 10.9 +typedef QMap MuidStringMap; + /** * Price Updater. * @@ -41,6 +44,8 @@ public: class DBPriceUpdater : public AbstractPriceUpdater { Q_OBJECT +protected: + MuidStringMap muidMap; protected: virtual void downloadFinished(); public: From 106e9d15383b96076ce89ea14f040c8bff1eac49 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 9 Jul 2014 09:43:01 +0200 Subject: [PATCH 6/9] Updated for new API * Use the new, shortest API m= * for a card, grab prices for all sets * use the lower price for a card from all sets, but avoid prices=0 (e.g.: some M15 cards have no price yet) --- cockatrice/src/priceupdater.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index 1cf2b064..98d3043d 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -126,14 +126,20 @@ void DBPriceUpdater::updatePrices() muidMap.clear(); CardInfo * card; int muid; + SetList sets; for (int i = 0; i < cards.size(); ++i) { card = db->getCard(cards[i], false); - muid=card->getPreferredMuId(); - q += (i ? "&" : "?") + QString("multiverseid=%1").arg(muid); - muidMap.insert(muid, cards[i]); + sets = card->getSets(); + for(int j = 0; j < sets.size(); ++j) + { + muid=card->getMuId(sets[j]->getShortName()); + //qDebug() << "muid " << muid << " card: " << cards[i] << endl; + q += (i ? "&" : "?") + QString("m=%1").arg(muid); + muidMap.insert(muid, cards[i]); + } } QUrl url(q); - + //qDebug() << "request prices from: " << url.toString() << endl; QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); } @@ -208,7 +214,7 @@ void DBPriceUpdater::downloadFinished() * physically exist. Also check the price to see that the cheapest set * ends up as the final price. */ - if (set != "MED" && (!cardsPrice.contains(name) || cardsPrice.value(name) > price)) + if (set != "MED" && price > 0 && (!cardsPrice.contains(name) || cardsPrice.value(name) > price)) cardsPrice.insert(name, price); } } From bf92e670022d21cacfcea4e11b077303d1c161ec Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 23 Jul 2014 22:11:26 +0200 Subject: [PATCH 7/9] Ensure the whole table gets refreshed when updating prices --- cockatrice/src/decklistmodel.cpp | 10 ++-------- cockatrice/src/decklistmodel.h | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/cockatrice/src/decklistmodel.cpp b/cockatrice/src/decklistmodel.cpp index ec10ff05..c55af013 100644 --- a/cockatrice/src/decklistmodel.cpp +++ b/cockatrice/src/decklistmodel.cpp @@ -472,13 +472,7 @@ void DeckListModel::printDeckList(QPrinter *printer) doc.print(printer); } -void DeckListModel::pricesUpdated(InnerDecklistNode *node) +void DeckListModel::pricesUpdated() { - if (!node) - node = root; - - if (node->isEmpty()) - return; - - emit dataChanged(createIndex(0, 2, node->at(0)), createIndex(node->size() - 1, 2, node->last())); + emit layoutChanged(); } diff --git a/cockatrice/src/decklistmodel.h b/cockatrice/src/decklistmodel.h index 2873c818..f4e93e3b 100644 --- a/cockatrice/src/decklistmodel.h +++ b/cockatrice/src/decklistmodel.h @@ -51,7 +51,7 @@ public: void cleanList(); DeckLoader *getDeckList() const { return deckList; } void setDeckList(DeckLoader *_deck); - void pricesUpdated(InnerDecklistNode *node = 0); + void pricesUpdated(); private: DeckLoader *deckList; InnerDecklistNode *root; From b40abfa1adcca92adb20e07223dfa153e2e570e2 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 23 Jul 2014 23:00:58 +0200 Subject: [PATCH 8/9] Split price retrieval on multiple request --- cockatrice/src/priceupdater.cpp | 73 +++++++++++++++++++++++++++------ cockatrice/src/priceupdater.h | 4 +- 2 files changed, 64 insertions(+), 13 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index 98d3043d..1a84880d 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -121,12 +121,15 @@ DBPriceUpdater::DBPriceUpdater(const DeckList *_deck) */ void DBPriceUpdater::updatePrices() { - QString q = "https://api.deckbrew.com/mtg/cards"; + QString base = "https://api.deckbrew.com/mtg/cards", q = ""; QStringList cards = deck->getCardList(); muidMap.clear(); + urls.clear(); CardInfo * card; int muid; SetList sets; + bool bNotFirst=false; + for (int i = 0; i < cards.size(); ++i) { card = db->getCard(cards[i], false); sets = card->getSets(); @@ -134,11 +137,35 @@ void DBPriceUpdater::updatePrices() { muid=card->getMuId(sets[j]->getShortName()); //qDebug() << "muid " << muid << " card: " << cards[i] << endl; - q += (i ? "&" : "?") + QString("m=%1").arg(muid); + if(bNotFirst) + { + q += QString("&m=%1").arg(muid); + } else { + q += QString("?m=%1").arg(muid); + bNotFirst = true; + } muidMap.insert(muid, cards[i]); } + + if(q.length() > 240) + { + urls.append(base + q); + bNotFirst=false; + q = ""; + } } - QUrl url(q); + if(q.length() > 0) + urls.append(base + q); + + requestNext(); +} + +void DBPriceUpdater::requestNext() +{ + if(urls.empty()) + return; + + QUrl url(urls.takeFirst(), QUrl::TolerantMode); //qDebug() << "request prices from: " << url.toString() << endl; QNetworkReply *reply = nam->get(QNetworkRequest(url)); connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished())); @@ -158,8 +185,13 @@ void DBPriceUpdater::downloadFinished() if (!ok) { QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); reply->deleteLater(); - deleteLater(); - return; + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } if(resultMap.contains("errors")) @@ -173,8 +205,13 @@ void DBPriceUpdater::downloadFinished() #endif ); reply->deleteLater(); - deleteLater(); - return; + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } // Good results are a list @@ -182,8 +219,13 @@ void DBPriceUpdater::downloadFinished() if (!ok) { QMessageBox::critical(this, tr("Error"), tr("A problem has occured while fetching card prices.")); reply->deleteLater(); - deleteLater(); - return; + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } QMap cardsPrice; @@ -226,11 +268,18 @@ void DBPriceUpdater::downloadFinished() DecklistCardNode *currentCard = dynamic_cast(currentZone->at(j)); if (!currentCard) continue; - currentCard->setPrice(cardsPrice[currentCard->getName()]); + float price = cardsPrice[currentCard->getName()]; + if(price > 0) + currentCard->setPrice(price); } } reply->deleteLater(); - deleteLater(); - emit finishedUpdate(); + if(urls.isEmpty()) + { + deleteLater(); + emit finishedUpdate(); + } else { + requestNext(); + } } diff --git a/cockatrice/src/priceupdater.h b/cockatrice/src/priceupdater.h index 076d9663..afe582b6 100644 --- a/cockatrice/src/priceupdater.h +++ b/cockatrice/src/priceupdater.h @@ -1,7 +1,7 @@ #ifndef PRICEUPDATER_H #define PRICEUPDATER_H -#include +#include #include "decklist.h" class QNetworkAccessManager; @@ -46,8 +46,10 @@ class DBPriceUpdater : public AbstractPriceUpdater Q_OBJECT protected: MuidStringMap muidMap; + QList urls; protected: virtual void downloadFinished(); + void requestNext(); public: DBPriceUpdater(const DeckList *deck); virtual void updatePrices(); From 49b3568b53fe4d1060eb9b4b543c37b7a4fcad86 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Wed, 23 Jul 2014 23:10:48 +0200 Subject: [PATCH 9/9] small fix: moving the url length check in the inner loop --- cockatrice/src/priceupdater.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cockatrice/src/priceupdater.cpp b/cockatrice/src/priceupdater.cpp index 1a84880d..2a1a012e 100644 --- a/cockatrice/src/priceupdater.cpp +++ b/cockatrice/src/priceupdater.cpp @@ -145,13 +145,13 @@ void DBPriceUpdater::updatePrices() bNotFirst = true; } muidMap.insert(muid, cards[i]); - } - if(q.length() > 240) - { - urls.append(base + q); - bNotFirst=false; - q = ""; + if(q.length() > 240) + { + urls.append(base + q); + bNotFirst=false; + q = ""; + } } } if(q.length() > 0)