feat: set prioritization by set type (#5097)

* feat: prefer 'Core' and 'Expansion' sets for prioritization

* rework set prioritization

* clean up priority enum

* formatting

* revert changes to CockatriceXml3Parser

* re-add missing null check

* remove priority fallback ternary from CardSet model

* make defaultSort logic easier to follow

* revert changes to v3 card database xsd

* remove unused invisible priority col from sets dialog

* move draft innovation and duel deck sets to secondary prio

* minor fixes

* change PriorityFallback to 1

* make priority optional in xml

* remove PriorityUndefined and set PriorityFallback to 0

* set priority when not found to PriorityOther

in case a new set type is added it's unlikey we want it sorted first,
it'll probably be a new product so it's probably best to sort it with
the funny things

* simplify sort function

---------

Co-authored-by: tooomm <tooomm@users.noreply.github.com>
Co-authored-by: ebbit1q <ebbit1q@gmail.com>
This commit is contained in:
J. Cameron McDonald
2024-12-01 18:59:00 -06:00
committed by GitHub
parent 5156495b47
commit b9ed9a6c0b
11 changed files with 128 additions and 36 deletions

View File

@@ -195,6 +195,13 @@ void SetsModel::swapRows(int oldRow, int newRow)
emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
}
void SetsModel::restoreOriginalOrder()
{
int numRows = rowCount();
sets.defaultSort();
emit dataChanged(index(0, 0), index(numRows - 1, columnCount() - 1));
}
void SetsModel::sort(int column, Qt::SortOrder order)
{
QMultiMap<QString, CardSetPtr> setMap;

View File

@@ -49,7 +49,8 @@ public:
LongNameCol,
ShortNameCol,
SetTypeCol,
ReleaseDateCol
ReleaseDateCol,
PriorityCol
};
enum Role
{
@@ -80,6 +81,7 @@ public:
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
void save(CardDatabase *db);
void restore(CardDatabase *db);
void restoreOriginalOrder();
};
class SetsDisplayModel : public QSortFilterProxyModel

View File

@@ -102,6 +102,7 @@ WndSets::WndSets(QWidget *parent) : QMainWindow(parent)
view->sortByColumn(SetsModel::SortKeyCol, Qt::AscendingOrder);
view->setColumnHidden(SetsModel::SortKeyCol, true);
view->setColumnHidden(SetsModel::IsKnownCol, true);
view->setColumnHidden(SetsModel::PriorityCol, true);
view->setRootIsDecorated(false);
connect(view->header(), SIGNAL(sectionClicked(int)), this, SLOT(actSort(int)));
@@ -254,7 +255,7 @@ void WndSets::actRestore()
void WndSets::actRestoreOriginalOrder()
{
view->header()->setSortIndicator(SORT_RESET, Qt::DescendingOrder);
model->sort(model->ReleaseDateCol, Qt::DescendingOrder);
model->restoreOriginalOrder();
sortWarning->setVisible(false);
}

View File

@@ -23,8 +23,9 @@ const char *CardDatabase::TOKENS_SETNAME = "TK";
CardSet::CardSet(const QString &_shortName,
const QString &_longName,
const QString &_setType,
const QDate &_releaseDate)
: shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType)
const QDate &_releaseDate,
const CardSet::Priority _priority)
: shortName(_shortName), longName(_longName), releaseDate(_releaseDate), setType(_setType), priority(_priority)
{
loadSetOptions();
}
@@ -32,9 +33,10 @@ CardSet::CardSet(const QString &_shortName,
CardSetPtr CardSet::newInstance(const QString &_shortName,
const QString &_longName,
const QString &_setType,
const QDate &_releaseDate)
const QDate &_releaseDate,
const Priority _priority)
{
CardSetPtr ptr(new CardSet(_shortName, _longName, _setType, _releaseDate));
CardSetPtr ptr(new CardSet(_shortName, _longName, _setType, _releaseDate, _priority));
// ptr->setSmartPointer(ptr);
return ptr;
}
@@ -195,25 +197,31 @@ void SetList::markAllAsKnown()
void SetList::guessSortKeys()
{
// sort by release date DESC; invalid dates to the bottom.
QDate distantFuture(2050, 1, 1);
int aHundredYears = 36500;
defaultSort();
for (int i = 0; i < size(); ++i) {
CardSetPtr set = at(i);
if (set.isNull()) {
qDebug() << "guessSortKeys set is null";
continue;
}
QDate date = set->getReleaseDate();
if (date.isNull()) {
set->setSortKey(static_cast<unsigned int>(aHundredYears));
} else {
set->setSortKey(static_cast<unsigned int>(date.daysTo(distantFuture)));
}
set->setSortKey(i);
}
}
void SetList::defaultSort()
{
std::sort(begin(), end(), [](const CardSetPtr &a, const CardSetPtr &b) {
// Sort by priority, then by release date, then by short name
if (a->getPriority() != b->getPriority()) {
return a->getPriority() < b->getPriority(); // lowest first
} else if (a->getReleaseDate() != b->getReleaseDate()) {
return a->getReleaseDate() > b->getReleaseDate(); // most recent first
} else {
return a->getShortName() < b->getShortName(); // alphabetically
}
});
}
CardInfoPerSet::CardInfoPerSet(const CardSetPtr &_set) : set(_set)
{
}

View File

@@ -29,22 +29,36 @@ Q_DECLARE_METATYPE(CardInfoPtr)
class CardSet : public QList<CardInfoPtr>
{
public:
enum Priority
{
PriorityFallback = 0,
PriorityPrimary = 10,
PrioritySecondary = 20,
PriorityReprint = 30,
PriorityOther = 40,
PriorityLowest = 100,
};
private:
QString shortName, longName;
unsigned int sortKey;
QDate releaseDate;
QString setType;
Priority priority;
bool enabled, isknown;
public:
explicit CardSet(const QString &_shortName = QString(),
const QString &_longName = QString(),
const QString &_setType = QString(),
const QDate &_releaseDate = QDate());
const QDate &_releaseDate = QDate(),
const Priority _priority = PriorityFallback);
static CardSetPtr newInstance(const QString &_shortName = QString(),
const QString &_longName = QString(),
const QString &_setType = QString(),
const QDate &_releaseDate = QDate());
const QDate &_releaseDate = QDate(),
const Priority _priority = PriorityFallback);
QString getCorrectedShortName() const;
QString getShortName() const
{
@@ -62,6 +76,10 @@ public:
{
return releaseDate;
}
Priority getPriority() const
{
return priority;
}
void setLongName(const QString &_longName)
{
longName = _longName;
@@ -74,6 +92,10 @@ public:
{
releaseDate = _releaseDate;
}
void setPriority(const Priority _priority)
{
priority = _priority;
}
void loadSetOptions();
int getSortKey() const
@@ -113,6 +135,7 @@ public:
int getEnabledSetsNum();
int getUnknownSetsNum();
QStringList getUnknownSetsNames();
void defaultSort();
};
class CardInfoPerSet

View File

@@ -10,7 +10,8 @@ void ICardDatabaseParser::clearSetlist()
CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName,
const QString &longName,
const QString &setType,
const QDate &releaseDate)
const QDate &releaseDate,
const CardSet::Priority priority)
{
if (sets.contains(setName)) {
return sets.value(setName);
@@ -20,8 +21,9 @@ CardSetPtr ICardDatabaseParser::internalAddSet(const QString &setName,
newSet->setLongName(longName);
newSet->setSetType(setType);
newSet->setReleaseDate(releaseDate);
newSet->setPriority(priority);
sets.insert(setName, newSet);
emit addSet(newSet);
return newSet;
}
}

View File

@@ -32,7 +32,8 @@ protected:
CardSetPtr internalAddSet(const QString &setName,
const QString &longName = "",
const QString &setType = "",
const QDate &releaseDate = QDate());
const QDate &releaseDate = QDate(),
const CardSet::Priority priority = CardSet::PriorityFallback);
signals:
virtual void addCard(CardInfoPtr card) = 0;
virtual void addSet(CardSetPtr set) = 0;
@@ -40,4 +41,4 @@ signals:
Q_DECLARE_INTERFACE(ICardDatabaseParser, "ICardDatabaseParser")
#endif
#endif

View File

@@ -77,6 +77,7 @@ void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
if (xmlName == "set") {
QString shortName, longName, setType;
QDate releaseDate;
short priority;
while (!xml.atEnd()) {
if (xml.readNext() == QXmlStreamReader::EndElement) {
break;
@@ -92,6 +93,8 @@ void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
} else if (xmlName == "releasedate") {
releaseDate =
QDate::fromString(xml.readElementText(QXmlStreamReader::IncludeChildElements), Qt::ISODate);
} else if (xmlName == "priority") {
priority = xml.readElementText(QXmlStreamReader::IncludeChildElements).toShort();
} else if (!xmlName.isEmpty()) {
qDebug() << "[CockatriceXml4Parser] Unknown set property" << xmlName
<< ", trying to continue anyway";
@@ -99,7 +102,7 @@ void CockatriceXml4Parser::loadSetsFromXml(QXmlStreamReader &xml)
}
}
internalAddSet(shortName, longName, setType, releaseDate);
internalAddSet(shortName, longName, setType, releaseDate, static_cast<CardSet::Priority>(priority));
}
}
}
@@ -249,6 +252,7 @@ static QXmlStreamWriter &operator<<(QXmlStreamWriter &xml, const CardSetPtr &set
xml.writeTextElement("longname", set->getLongName());
xml.writeTextElement("settype", set->getSetType());
xml.writeTextElement("releasedate", set->getReleaseDate().toString(Qt::ISODate));
xml.writeTextElement("priority", QString::number(set->getPriority()));
xml.writeEndElement();
return xml;

View File

@@ -15,6 +15,7 @@
<xs:element type="xs:string" name="longname" minOccurs="0" maxOccurs="1" />
<xs:element type="xs:string" name="settype" minOccurs="0" maxOccurs="1" />
<xs:element type="xs:string" name="releasedate" minOccurs="0" maxOccurs="1" />
<xs:element type="xs:integer" name="priority" minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>
<xs:complexType name="cardInSetType">

View File

@@ -19,6 +19,18 @@ OracleImporter::OracleImporter(const QString &_dataDir, QObject *parent) : CardD
{
}
CardSet::Priority OracleImporter::getSetPriority(QString &setType, QString &shortName)
{
if (!setTypePriorities.contains(setType.toLower())) {
qDebug() << "warning: Set type" << setType << "unrecognized for prioritization";
}
CardSet::Priority priority = setTypePriorities.value(setType.toLower(), CardSet::PriorityOther);
if (nonEnglishSets.contains(shortName)) {
priority = CardSet::PriorityLowest;
}
return priority;
}
bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
{
QList<SetToDownload> newSetList;
@@ -38,6 +50,7 @@ bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
QList<QVariant> setCards;
QString setType;
QDate releaseDate;
CardSet::Priority priority;
while (it.hasNext()) {
map = it.next().toMap();
@@ -45,6 +58,8 @@ bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
longName = map.value("name").toString();
setCards = map.value("cards").toList();
setType = map.value("type").toString();
releaseDate = map.value("releaseDate").toDate();
priority = getSetPriority(setType, shortName);
// capitalize set type
if (setType.length() > 0) {
// basic grammar for words that aren't capitalized, like in "From the Vault"
@@ -62,12 +77,7 @@ bool OracleImporter::readSetsFromByteArray(const QByteArray &data)
}
setType = setType.trimmed();
}
if (!nonEnglishSets.contains(shortName)) {
releaseDate = map.value("releaseDate").toDate();
} else {
releaseDate = QDate();
}
newSetList.append(SetToDownload(shortName, longName, setCards, setType, releaseDate));
newSetList.append(SetToDownload(shortName, longName, setCards, priority, setType, releaseDate));
}
std::sort(newSetList.begin(), newSetList.end());
@@ -493,8 +503,9 @@ int OracleImporter::startImport()
sets.insert(TOKENS_SETNAME, tokenSet);
for (const SetToDownload &curSetToParse : allSets) {
CardSetPtr newSet = CardSet::newInstance(curSetToParse.getShortName(), curSetToParse.getLongName(),
curSetToParse.getSetType(), curSetToParse.getReleaseDate());
CardSetPtr newSet =
CardSet::newInstance(curSetToParse.getShortName(), curSetToParse.getLongName(), curSetToParse.getSetType(),
curSetToParse.getReleaseDate(), curSetToParse.getPriority());
if (!sets.contains(newSet->getShortName()))
sets.insert(newSet->getShortName(), newSet);

View File

@@ -7,11 +7,36 @@
#include <utility>
// many users prefer not to see these sets with non english arts
// as a solution we remove the date property on these sets
// that way they will be sorted last by default
// this will cause their art to not get priority over english cards
// users will still be able to find these sets and prioritize them manually
// they will given priority PriorityLowest
const QStringList nonEnglishSets = {"4BB", "FBB", "PS11", "PSAL", "REN", "RIN"};
const QMap<QString, CardSet::Priority> setTypePriorities{
{"core", CardSet::PriorityPrimary},
{"expansion", CardSet::PriorityPrimary},
{"commander", CardSet::PrioritySecondary},
{"starter", CardSet::PrioritySecondary},
{"draft_innovation", CardSet::PrioritySecondary},
{"duel_deck", CardSet::PrioritySecondary},
{"archenemy", CardSet::PriorityReprint},
{"arsenal", CardSet::PriorityReprint},
{"box", CardSet::PriorityReprint},
{"from_the_vault", CardSet::PriorityReprint},
{"masterpiece", CardSet::PriorityReprint},
{"masters", CardSet::PriorityReprint},
{"memorabilia", CardSet::PriorityReprint},
{"planechase", CardSet::PriorityReprint},
{"premium_deck", CardSet::PriorityReprint},
{"promo", CardSet::PriorityReprint},
{"spellbook", CardSet::PriorityReprint},
{"token", CardSet::PriorityReprint},
{"treasure_chest", CardSet::PriorityReprint},
{"alchemy", CardSet::PriorityOther},
{"funny", CardSet::PriorityOther},
{"minigame", CardSet::PriorityOther},
{"vanguard", CardSet::PriorityOther},
};
class SetToDownload
{
@@ -20,6 +45,7 @@ private:
QList<QVariant> cards;
QDate releaseDate;
QString setType;
CardSet::Priority priority;
public:
const QString &getShortName() const
@@ -42,13 +68,18 @@ public:
{
return releaseDate;
}
CardSet::Priority getPriority() const
{
return priority;
}
SetToDownload(QString _shortName,
QString _longName,
QList<QVariant> _cards,
CardSet::Priority _priority,
QString _setType = QString(),
const QDate &_releaseDate = QDate())
: shortName(std::move(_shortName)), longName(std::move(_longName)), cards(std::move(_cards)),
releaseDate(_releaseDate), setType(std::move(_setType))
releaseDate(_releaseDate), setType(std::move(_setType)), priority(_priority)
{
}
bool operator<(const SetToDownload &set) const
@@ -108,6 +139,7 @@ signals:
public:
explicit OracleImporter(const QString &_dataDir, QObject *parent = nullptr);
CardSet::Priority getSetPriority(QString &setType, QString &shortName);
bool readSetsFromByteArray(const QByteArray &data);
int startImport();
bool saveToFile(const QString &fileName, const QString &sourceUrl, const QString &sourceVersion);