]> git.sesse.net Git - pkanalytics/blob - events.cpp
Connect insertions/type changes to the database.
[pkanalytics] / events.cpp
1 #include <algorithm>
2 #include <string>
3 #include <map>
4 #include <vector>
5 #include <optional>
6 #include <sqlite3.h>
7 #include "events.h"
8
9 using namespace std;
10
11 string format_timestamp(uint64_t pos);
12
13 QVariant EventsModel::headerData(int section, Qt::Orientation orientation, int role) const
14 {
15         if (role != Qt::DisplayRole) {
16                 return QVariant();
17         }
18         if (orientation == Qt::Horizontal) {
19                 if (section == 0) {
20                         return "Time";
21                 } else if (section == 1) {
22                         return "Player";
23                 } else {
24                         return "Type";
25                 }
26         } else {
27                 return "";
28         }
29 }
30
31 QVariant EventsModel::data(const QModelIndex &index, int role) const
32 {
33         if (role != Qt::DisplayRole) {
34                 return QVariant();
35         }
36         refresh_if_needed();
37         if (index.column() == 0) {
38                 return QString::fromUtf8(format_timestamp(events[index.row()].t));
39         } else if (index.column() == 1) {
40                 optional<int> player_id = events[index.row()].player_id;
41                 if (player_id) {
42                         const Player &p = players[*player_id];
43                         return QString::fromUtf8(p.name + " (" + p.number + ")");
44                 } else {
45                         return QVariant();
46                 }
47         } else if (index.column() == 2) {
48                 return QString::fromUtf8(events[index.row()].type);
49         }
50         return QVariant();
51 }
52
53 void EventsModel::refresh_if_needed() const
54 {
55         if (!stale) {
56                 return;
57         }
58
59         players.clear();
60         events.clear();
61         stale = false;
62
63         // Read the players.
64         sqlite3_stmt *stmt;
65         int ret = sqlite3_prepare_v2(db, "SELECT player, number, name FROM player", -1, &stmt, 0);
66         if (ret != SQLITE_OK) {
67                 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
68                 abort();
69         }
70         for ( ;; ) {
71                 ret = sqlite3_step(stmt);
72                 if (ret == SQLITE_ROW) {
73                         Player p;
74                         p.player_id = sqlite3_column_int(stmt, 0);
75                         p.number = (const char *)sqlite3_column_text(stmt, 1);
76                         p.name = (const char *) sqlite3_column_text(stmt, 2);
77                         players[p.player_id] = std::move(p);
78                 } else if (ret == SQLITE_DONE) {
79                         break;
80                 } else {
81                         fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
82                         abort();
83                 }
84         }
85         ret = sqlite3_finalize(stmt);
86         if (ret != SQLITE_OK) {
87                 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
88                 abort();
89         }
90
91         // Read the events.
92         ret = sqlite3_prepare_v2(db, "SELECT event, t, player, type FROM event", -1, &stmt, 0);
93         if (ret != SQLITE_OK) {
94                 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
95                 abort();
96         }
97         for ( ;; ) {
98                 ret = sqlite3_step(stmt);
99                 if (ret == SQLITE_ROW) {
100                         Event e;
101                         e.event_id = sqlite3_column_int(stmt, 0);
102                         e.t = sqlite3_column_int(stmt, 1);
103                         e.player_id = sqlite3_column_int(stmt, 2);
104                         e.type = (const char *)sqlite3_column_text(stmt, 3);
105                         events.push_back(std::move(e));
106                 } else if (ret == SQLITE_DONE) {
107                         break;
108                 } else {
109                         fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
110                         abort();
111                 }
112         }
113         ret = sqlite3_finalize(stmt);
114         if (ret != SQLITE_OK) {
115                 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
116                 abort();
117         }
118
119         // TODO what if data changes externally?
120         //emit dataChanged(QModelIndex(
121 }
122
123 int EventsModel::insert_event(uint64_t t, int player_id)
124 {
125         auto it = lower_bound(events.begin(), events.end(), t,
126                 [](const Event &e, uint64_t t) { return e.t < t; });
127         int pos = distance(events.begin(), it);
128         beginInsertRows(QModelIndex(), pos, pos);
129
130         Event e;
131         e.t = t;
132         e.player_id = player_id;
133         e.type = "unknown";
134         events.insert(events.begin() + pos, e);
135
136         endInsertRows();
137
138         // Insert the new row into the database.
139         sqlite3_stmt *stmt;
140         int ret = sqlite3_prepare_v2(db, "INSERT INTO event (t, player, type) VALUES (?, ?, ?)", -1, &stmt, 0);
141         if (ret != SQLITE_OK) {
142                 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
143                 abort();
144         }
145
146         sqlite3_bind_int64(stmt, 1, t);
147         sqlite3_bind_int64(stmt, 2, player_id);
148         sqlite3_bind_text(stmt, 3, e.type.data(), e.type.size(), SQLITE_STATIC);
149
150         ret = sqlite3_step(stmt);
151         if (ret == SQLITE_ROW) {
152                 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
153                 abort();
154         }
155
156         ret = sqlite3_finalize(stmt);
157         if (ret != SQLITE_OK) {
158                 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
159                 abort();
160         }
161
162         events[pos].event_id = sqlite3_last_insert_rowid(db);
163         return pos;
164 }
165
166 void EventsModel::set_event_type(unsigned pos, const string &type)
167 {
168         events[pos].type = type;
169         emit dataChanged(createIndex(pos, 0), createIndex(pos, 2));
170
171         sqlite3_stmt *stmt;
172         int ret = sqlite3_prepare_v2(db, "UPDATE event SET type=? WHERE event=?", -1, &stmt, 0);
173         if (ret != SQLITE_OK) {
174                 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
175                 abort();
176         }
177
178         sqlite3_bind_text(stmt, 1, type.data(), type.size(), SQLITE_STATIC);
179         sqlite3_bind_int64(stmt, 2, events[pos].event_id);
180
181         ret = sqlite3_step(stmt);
182         if (ret == SQLITE_ROW) {
183                 fprintf(stderr, "UPDATE step: %s\n", sqlite3_errmsg(db));
184                 abort();
185         }
186
187         ret = sqlite3_finalize(stmt);
188         if (ret != SQLITE_OK) {
189                 fprintf(stderr, "UPDATE finalize: %s\n", sqlite3_errmsg(db));
190                 abort();
191         }
192 }