]> git.sesse.net Git - pkanalytics/blobdiff - events.cpp
Add the beginnings of formation support.
[pkanalytics] / events.cpp
index e6ed5796340ff3521ab051512f25920d1d04c022..bb915b273b072ef4c4e5ec5eb9c348ac4d562f6b 100644 (file)
@@ -24,7 +24,7 @@ QVariant EventsModel::headerData(int section, Qt::Orientation orientation, int r
                if (section == 0) {
                        return "Time";
                } else if (section == 1) {
-                       return "Player";
+                       return "Who/what";
                } else {
                        return "Type";
                }
@@ -42,10 +42,15 @@ QVariant EventsModel::data(const QModelIndex &index, int role) const
                return QString::fromUtf8(format_timestamp(events[index.row()].t));
        } else if (index.column() == 1) {
                optional<int> player_id = events[index.row()].player_id;
+               optional<int> formation_id = events[index.row()].formation_id;
                if (player_id) {
                        auto p_it = players.find(*player_id);
                        const Player &p = p_it->second;
                        return QString::fromUtf8(p.name + " (" + p.number + ")");
+               } else if (formation_id) {
+                       auto f_it = formations.find(*formation_id);
+                       const Formation &p = f_it->second;
+                       return QString::fromUtf8(p.name);
                } else {
                        return QVariant();
                }
@@ -90,8 +95,34 @@ void EventsModel::load_data()
                abort();
        }
 
+       // Read the formations.
+       ret = sqlite3_prepare_v2(db, "SELECT formation, name FROM formation", -1, &stmt, 0);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+       for ( ;; ) {
+               ret = sqlite3_step(stmt);
+               if (ret == SQLITE_ROW) {
+                       Formation f;
+                       f.formation_id = sqlite3_column_int(stmt, 0);
+                       f.name = (const char *) sqlite3_column_text(stmt, 1);
+                       formations[f.formation_id] = std::move(f);
+               } else if (ret == SQLITE_DONE) {
+                       break;
+               } else {
+                       fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
+                       abort();
+               }
+       }
+       ret = sqlite3_finalize(stmt);
+       if (ret != SQLITE_OK) {
+               fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
+               abort();
+       }
+
        // Read the events.
-       ret = sqlite3_prepare_v2(db, "SELECT event, t, player, type FROM event WHERE match=? ORDER BY t", -1, &stmt, 0);
+       ret = sqlite3_prepare_v2(db, "SELECT event, t, player, formation, type FROM event WHERE match=? ORDER BY t", -1, &stmt, 0);
        if (ret != SQLITE_OK) {
                fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
                abort();
@@ -106,7 +137,10 @@ void EventsModel::load_data()
                        if (sqlite3_column_type(stmt, 2) == SQLITE_INTEGER) {  // Non-NULL.
                                e.player_id = sqlite3_column_int(stmt, 2);
                        }
-                       e.type = (const char *)sqlite3_column_text(stmt, 3);
+                       if (sqlite3_column_type(stmt, 3) == SQLITE_INTEGER) {  // Non-NULL.
+                               e.formation_id = sqlite3_column_int(stmt, 3);
+                       }
+                       e.type = (const char *)sqlite3_column_text(stmt, 4);
                        events.push_back(std::move(e));
                } else if (ret == SQLITE_DONE) {
                        break;
@@ -122,7 +156,7 @@ void EventsModel::load_data()
        }
 }
 
-unsigned EventsModel::insert_event(uint64_t t, optional<int> player_id, const string &type)
+unsigned EventsModel::insert_event(uint64_t t, optional<int> player_id, optional<int> formation_id, const string &type)
 {
        auto it = lower_bound(events.begin(), events.end(), t,
                [](const Event &e, uint64_t t) { return e.t < t; });
@@ -139,7 +173,7 @@ unsigned EventsModel::insert_event(uint64_t t, optional<int> player_id, const st
 
        // Insert the new row into the database.
        sqlite3_stmt *stmt;
-       int ret = sqlite3_prepare_v2(db, "INSERT INTO event (match, t, player, type) VALUES (?, ?, ?, ?)", -1, &stmt, 0);
+       int ret = sqlite3_prepare_v2(db, "INSERT INTO event (match, t, player, formation, type) VALUES (?, ?, ?, ?, ?)", -1, &stmt, 0);
        if (ret != SQLITE_OK) {
                fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
                abort();
@@ -152,7 +186,12 @@ unsigned EventsModel::insert_event(uint64_t t, optional<int> player_id, const st
        } else {
                sqlite3_bind_null(stmt, 3);
        }
-       sqlite3_bind_text(stmt, 4, type.data(), type.size(), SQLITE_STATIC);
+       if (formation_id) {
+               sqlite3_bind_int64(stmt, 4, *formation_id);
+       } else {
+               sqlite3_bind_null(stmt, 4);
+       }
+       sqlite3_bind_text(stmt, 5, type.data(), type.size(), SQLITE_STATIC);
 
        ret = sqlite3_step(stmt);
        if (ret == SQLITE_ROW) {
@@ -385,16 +424,44 @@ void EventsModel::set_team_at(uint64_t t, const set<int> &new_team)
        set<int> old_team = get_team_at(backdate_point);
        for (int player_id : old_team) {
                if (!new_team.count(player_id)) {
-                       insert_event(backdate_point, player_id, "out");
+                       insert_event(backdate_point, player_id, nullopt, "out");
                }
        }
        for (int player_id : new_team) {
                if (!old_team.count(player_id)) {
-                       insert_event(backdate_point, player_id, "in");
+                       insert_event(backdate_point, player_id, nullopt, "in");
                }
        }
 }
 
+void EventsModel::set_formation_at(uint64_t t, bool offense, unsigned formation)
+{
+       // If there's another goal/stoppage no more than 20 seconds ago,
+       // we assume that the formation started at that point (it just took
+       // the operator a bit of time to see it). If not, we assume we
+       // changed in the middle of a point.
+       uint64_t backdate_point = 0;
+       for (const Event &e : events) {
+               if (e.t > t) {
+                       break;
+               }
+               if (e.type == "goal" || e.type == "their_goal" || e.type == "stoppage" || e.type == "reset" || e.type == "set_offense" || e.type == "set_defense" || e.type == "in" || e.type == "out") {
+                       backdate_point = e.t + 1;
+               }
+               if (e.type == "formation_offense" || e.type == "formation_defense") {
+                       backdate_point = 0;
+               }
+       }
+       if (backdate_point != 0 && t - backdate_point < 20000) {
+               t = backdate_point;
+       }
+       if (offense) {
+               insert_event(t, nullopt, formation, "formation_offense");
+       } else {
+               insert_event(t, nullopt, formation, "formation_defense");
+       }
+}
+
 vector<int> EventsModel::sort_team(const set<int> &team) const
 {
        vector<int> ret(team.begin(), team.end());