From 77f8836508a4c28c7c6c1352ff891c5d36d00237 Mon Sep 17 00:00:00 2001
From: Steru <jerrydream111@gmail.com>
Date: Sun, 4 Aug 2024 22:56:15 +0200
Subject: Added method to get medal winners. Adjusted sort result method to
 also work with the medal winners.

---
 src/discipline/Sport.cpp | 144 +++++++++++++++++++++++++++++++++++++++++++----
 src/discipline/Sport.h   |  22 ++++++--
 2 files changed, 150 insertions(+), 16 deletions(-)

(limited to 'src/discipline')

diff --git a/src/discipline/Sport.cpp b/src/discipline/Sport.cpp
index 42e82aa..1ea8019 100644
--- a/src/discipline/Sport.cpp
+++ b/src/discipline/Sport.cpp
@@ -1,6 +1,7 @@
 
 #include "Sport.h"
 #include <set>
+#include <map>
 #include <algorithm>
 #include <regex>
 
@@ -8,6 +9,21 @@
 #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) {
@@ -20,21 +36,44 @@ function<bool (const QJsonValue &left, const QJsonValue &right)> genCompare(QStr
 
 // 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;
 
-    float lMark = l["results"].toObject()["mark"].toString().toFloat();
-    float rMark = r["results"].toObject()["mark"].toString().toFloat();
-    return lMark < rMark;
+    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) {
+void Sport::lastName(QJsonArray &competitors) {
     for (int i = 0; i < competitors.size(); ++i) {
         string fullName = competitors[i].toObject()["name"].toString().toUtf8().constData();
 
@@ -64,7 +103,7 @@ void Sport::lastName(QJsonArray& competitors) {
 set<QString> Sport::getCategories() {
     set<QString> categoryNames;
 
-    for (const QJsonValueRef& unit : this->discipline["units"].toArray()) {
+    for (const QJsonValueRef &unit : this->discipline["units"].toArray()) {
         categoryNames.insert(unit.toObject()["eventUnitName"].toString());
     }
 
@@ -79,21 +118,97 @@ set<QString> Sport::getCategories() {
 QJsonArray Sport::getCompetitorsByCategory(QString category) {
     QJsonArray competitors;
 
-    for (const QJsonValueRef& unitRef : this->discipline["units"].toArray()) {
+    for (const QJsonValueRef &unitRef : this->discipline["units"].toArray()) {
         QJsonObject unit = unitRef.toObject();
 
         // search all units with the same category
-        if (QString::compare(unit["eventUnitName"].toString(), category, Qt::CaseSensitive) == 0) {
+        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;
 
-            // add all competitors from one unit
-            for (const QJsonValueRef& comp : unit["competitors"].toArray()) {
-                competitors.push_back(comp.toObject());
+            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;
         }
     }
 
-    return QJsonArray(competitors);
+    // 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;
 }
 
 /**
@@ -147,7 +262,12 @@ void Sport::sortByCountry(QJsonArray &competitors) {
  * @param competitors The competitors of one category.
  */
 void Sport::sortByResult(QJsonArray &competitors) {
-    sortCompetitors(competitors, compareMark);
+    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) {
diff --git a/src/discipline/Sport.h b/src/discipline/Sport.h
index 78968fe..9156b82 100644
--- a/src/discipline/Sport.h
+++ b/src/discipline/Sport.h
@@ -22,11 +22,12 @@ public:
 
     set<QString> getCategories();
     QJsonArray getCompetitorsByCategory(QString category);
+    QJsonArray getCompetitorsWithMedal();
 
     // filter to change the current competitor array
-    void lastName(QJsonArray& competitors);
-    void filterByName(QJsonArray& competitors, QString name);
-    void filterByCountry(QJsonArray& competitors, QString nocShort);
+    void lastName(QJsonArray &competitors);
+    void filterByName(QJsonArray &competitors, QString name);
+    void filterByCountry(QJsonArray &competitors, QString nocShort);
 
     // sort functions to change the order of the current competitor array
     void sortByName(QJsonArray &competitors);
@@ -58,11 +59,24 @@ private:
      * - medalType
      * - irk
      * [- winnerLoserTie] (only if provided in the discipline?)
+     *
+     * Analysis of where to find the medal winners:
+     *
+     * Search for ... in category name.
+     * - "Bronze"
+     * - "Gold"
+     * - "Final"
+     *
+     * ! ATTENTION !
+     * When searching for "Final" there might be "Final A", "Final B", etc.
+     * The results will only be in ONE of these categories!
+     * -> which is good... cause then we can count occurences.
      */
     QJsonObject discipline;
 
-    void filterCompetitors(QJsonArray& competitors, QString attribute, QString filter);
+    void filterCompetitors(QJsonArray &competitors, QString attribute, QString filter);
     void sortCompetitors(QJsonArray &competitors,  function<bool (const QJsonValue &left, const QJsonValue &right)> compare);
+    QJsonObject createCompetitorWithMedals(QJsonObject medalComp);
 
 };
 
-- 
cgit v1.2.3