itat_challenge/src/discipline/Sport.cpp

278 lines
9.8 KiB
C++
Raw Normal View History

#include "Sport.h"
#include <set>
#include <map>
#include <algorithm>
#include <regex>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValueRef>
#include <QString>
#include <QTime>
// QJsonArray filter function, provide with input array and evaluation function
QJsonArray filter(QJsonArray input, function<bool (QJsonObject)> eval) {
QJsonArray output;
for (const QJsonValueRef &elemRef :input) {
QJsonObject elem = elemRef.toObject();
if(eval(elem)) {
output.append(elem);
}
}
return output;
}
// static compare function for specific attribute in competitors
function<bool (const QJsonValue &left, const QJsonValue &right)> genCompare(QString attribute) {
return [attribute](const QJsonValue &left, const QJsonValue &right) {
QString l = left.toObject()[attribute].toString();
QString r = right.toObject()[attribute].toString();
return l.compare(r) < 0;
};
}
// static compare function for the results of a competitor in a specific competition (also called mark)
bool compareMark(const QJsonValue &left, const QJsonValue &right) {
// check if one competitor has no mark
QJsonObject l = left.toObject();
if (!l.contains("results")) return true;
QJsonObject r = right.toObject();
if (!r.contains("results")) return false;
QString lMark = l["results"].toObject()["mark"].toString();
QString rMark = r["results"].toObject()["mark"].toString();
// check if the marks are numerical values
if (!lMark.contains(":") && !rMark.contains(":")) return lMark.toFloat() < rMark.toFloat();
// compare time values if not numerical
QString lTime(""), rTime("");
for (QChar c : lMark) if (c.isDigit()) lTime.append(c);
for (QChar c : rMark) if (c.isDigit()) rTime.append(c);
return lTime.compare(rTime) < 0;
}
// static compare function for the amount of medals a competitor has gotten
bool compareMedals(const QJsonValue &left, const QJsonValue &right) {
QJsonObject lMedals = left.toObject()["medals"].toObject();
QJsonObject rMedals = right.toObject()["medals"].toObject();
int gold = lMedals["ME_GOLD"].toInt() - rMedals["ME_GOLD"].toInt();
int silver = lMedals["ME_SILVER"].toInt() - rMedals["ME_SILVER"].toInt();
int bronze = lMedals["ME_BRONZE"].toInt() - rMedals["ME_BRONZE"].toInt();
return gold < 0 || (gold == 0 && (silver < 0 || (silver == 0 && bronze < 0)));
}
/**
* @brief Sport::lastName Reduce the full name to the part that is marked in capital letters (probably last name).
* @param competitors The competitors of one category.
*/
void Sport::lastName(QJsonArray &competitors) {
for (int i = 0; i < competitors.size(); ++i) {
string fullName = competitors[i].toObject()["name"].toString().toUtf8().constData();
regex r("[A-Z']{2,}");
smatch m;
regex_search(fullName, m, r);
string lastName = "";
for (string s : m) {
lastName = lastName + s + " ";
}
QJsonValue nameValue = QJsonValue(QString(lastName.substr(0, lastName.size() - 1).c_str()));
QJsonObject comp(competitors[i].toObject());
comp.remove("name");
comp.insert("name", nameValue);
competitors[i] = comp;
}
}
/**
* @brief Sport::getCategories Reads all possible categories (also called units).
* @return A set of all category names.
*/
set<QString> Sport::getCategories() {
set<QString> categoryNames;
for (const QJsonValueRef &unit : this->discipline["units"].toArray()) {
categoryNames.insert(unit.toObject()["eventUnitName"].toString());
}
return categoryNames;
}
/**
* @brief Sport::getCompetitorsByCategory Searches for all competitors, who took part in the given category.
* @param category The category to search in.
* @return An QJsonArray with all competitors as QJsonValueRef, which can be casted to QJsonObject.
*/
QJsonArray Sport::getCompetitorsByCategory(QString category) {
QJsonArray competitors;
for (const QJsonValueRef &unitRef : this->discipline["units"].toArray()) {
QJsonObject unit = unitRef.toObject();
// search all units with the same category
if (unit["eventUnitName"].toString().compare(category, Qt::CaseSensitive) != 0) continue;
// add all competitors from one unit
for (const QJsonValueRef &comp : unit["competitors"].toArray()) {
competitors.push_back(comp.toObject());
}
}
return QJsonArray(competitors);
}
/**
* @brief Sport::getCompetitorsWithMedal Filters all competitors, who have at least one medal. These objects are different from getCompetitorsByCategory !!!
* @return All competitors, who won at least one medal. Structure of one competitor: {code, name, noc, medals{ME_GOLD, ME_SILVER, ME_BRONZE}}
*/
QJsonArray Sport::getCompetitorsWithMedal() {
map<QString, QJsonObject> competitors;
// filter all units, which have medal events
QJsonArray units = filter(this->discipline["units"].toArray(), [](QJsonObject unit){
// search all units with Final, Gold or Bronze in their name, because these are the categories with the medal winners
QString unitName = unit["eventUnitName"].toString();
return unitName.contains("Bronze", Qt::CaseSensitive)
|| unitName.contains("Gold", Qt::CaseSensitive)
|| unitName.contains("Final", Qt::CaseSensitive);
});
for (const QJsonValueRef &unitRef : units) {
QJsonObject unit = unitRef.toObject();
// filter all competitors, who won medals
QJsonArray medalComps = filter(unit["competitors"].toArray(), [](QJsonObject comp) {
if (!comp.contains("results")) return false;
QString medalType = comp["results"].toObject()["medalType"].toString();
return !medalType.isEmpty();
});
for (const QJsonValueRef &medalCompRef : medalComps) {
QJsonObject medalComp = medalCompRef.toObject();
QString name = medalComp["name"].toString();
QString medalType = medalComp["results"].toObject()["medalType"].toString();
// check if competitor has other medal(s)
if (competitors.find(name) == competitors.end()) {
competitors.insert({name, createCompetitorWithMedals(medalComp)});
}
// update the medal count
QJsonObject updatedMedalCount = QJsonObject(competitors.find(name)->second["medals"].toObject());
int amount = updatedMedalCount[medalType].toInt() + 1;
updatedMedalCount.remove(medalType);
updatedMedalCount.insert(medalType, amount);
// create new medals QJsonObject and set it in the map
competitors.find(name)->second["medals"] = updatedMedalCount;
}
}
// convert map to QJsonArray
QJsonArray output;
for (const pair<QString, QJsonObject> &competitor : competitors) {
output.append(competitor.second);
}
return output;
}
QJsonObject Sport::createCompetitorWithMedals(QJsonObject comp) {
// create new competitor QJsonObject and add it to the competitor map
QJsonObject medals {
{"ME_GOLD", 0},
{"ME_SILVER", 0},
{"ME_BRONZE", 0}
};
QJsonObject medalComp {
{"code", comp["code"].toString()},
{"name", comp["name"].toString()},
{"noc", comp["noc"].toString()},
{"medals", medals}
};
return medalComp;
}
/**
* @brief Sport::filterByName Filter the competitors by name (case insensitive).
* @param competitors The competitors of one category.
* @param name The (part of the) name to search for.
*/
void Sport::filterByName(QJsonArray &competitors, QString name) {
filterCompetitors(competitors, QString("name"), name);
}
/**
* @brief Sport::filterByCountry Filter the competitors by their national olympics comittee (case insensitive, short form).
* @param competitors The competitors of one category.
* @param nocShort The (part of the) national olympics comittee short name to search for.
*/
void Sport::filterByCountry(QJsonArray &competitors, QString nocShort) {
filterCompetitors(competitors, QString("noc"), nocShort);
}
void Sport::filterCompetitors(QJsonArray &competitors, QString attribute, QString filter) {
for (int i = 0; i < competitors.size(); i++) {
if (!competitors[i].toObject()[attribute].toString().contains(filter, Qt::CaseInsensitive)) {
// remove the competitor, if the attribute does not fit the filter string
competitors.removeAt(i);
i--;
}
}
}
/**
* @brief Sport::sortByName Sort the competitors by their name (alphabetical, ascending).
* @param competitors The competitors of one category.
*/
void Sport::sortByName(QJsonArray &competitors) {
sortCompetitors(competitors, genCompare( QString("name") ));
}
/**
* @brief Sport::sortByCountry Sort the competitors by their national olympic comittee short name (alphabetical, ascending).
* @param competitors The competitors of one category.
*/
void Sport::sortByCountry(QJsonArray &competitors) {
sortCompetitors(competitors, genCompare( QString("noc") ));
}
/**
* @brief Sport::sortByResult Sort the competitors by their results in one specific category (numerical, ascending).
* @param competitors The competitors of one category.
*/
void Sport::sortByResult(QJsonArray &competitors) {
if (competitors.isEmpty()) return;
QJsonObject comp = competitors[0].toObject();
if (comp.contains("results")) sortCompetitors(competitors, compareMark);
else if (comp.contains("medals")) sortCompetitors(competitors, compareMedals);
}
void Sport::sortCompetitors(QJsonArray &competitors, function<bool (const QJsonValue &left, const QJsonValue &right)> compare) {
make_heap(competitors.begin(), competitors.end(), compare);
sort_heap(competitors.begin(), competitors.end(), compare);
}