From 922e98af67f25151e2dbcb377742c21ef401caf8 Mon Sep 17 00:00:00 2001 From: Fabio Bas Date: Sun, 29 Jun 2014 23:30:32 +0200 Subject: [PATCH] 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(); }