]> git.sesse.net Git - pkanalytics/blob - main.cpp
Support filtering passes by thrower and receiver.
[pkanalytics] / main.cpp
1 #include <QMainWindow>
2 #include <QApplication>
3 #include <QGridLayout>
4 #include <QShortcut>
5 #include <QInputDialog>
6 #include <QTimer>
7 #include <algorithm>
8 #include <string>
9 #include <map>
10 #include <vector>
11 #include <optional>
12 #include <sqlite3.h>
13 #include "mainwindow.h"
14 #include "events.h"
15 #include "players.h"
16 #include "formations.h"
17 #include "json.h"
18
19 using namespace std;
20
21 sqlite3 *open_db(const char *filename)
22 {
23         sqlite3 *db;
24         int ret = sqlite3_open(filename, &db);
25         if (ret != SQLITE_OK) {
26                 fprintf(stderr, "%s: %s\n", filename, sqlite3_errmsg(db));
27                 exit(1);
28         }
29
30         sqlite3_exec(db, R"(
31                 CREATE TABLE IF NOT EXISTS player (player INTEGER PRIMARY KEY, number VARCHAR, name VARCHAR, gender VARCHAR(1));
32         )", nullptr, nullptr, nullptr);  // Ignore errors.
33
34         sqlite3_exec(db, R"(
35                 CREATE TABLE IF NOT EXISTS match (match INTEGER PRIMARY KEY, description VARCHAR, video_filename VARCHAR, gender_rule_a BOOLEAN DEFAULT false, gender_pull_rule BOOLEAN DEFAULT false);
36         )", nullptr, nullptr, nullptr);  // Ignore errors.
37
38         sqlite3_exec(db, R"(
39                 CREATE TABLE IF NOT EXISTS formation (formation INTEGER PRIMARY KEY, name VARCHAR, offense BOOLEAN NOT NULL);
40         )", nullptr, nullptr, nullptr);  // Ignore errors.
41
42         sqlite3_exec(db, R"(
43                 CREATE TABLE IF NOT EXISTS event (event INTEGER PRIMARY KEY, match INTEGER, t INTEGER, player INTEGER, type VARCHAR, formation INTEGER, FOREIGN KEY (player) REFERENCES player(player), FOREIGN KEY (match) REFERENCES match (match), FOREIGN KEY (formation) REFERENCES formation (formation));
44         )", nullptr, nullptr, nullptr);  // Ignore errors.
45
46         sqlite3_exec(db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr);  // Ignore errors.
47         sqlite3_exec(db, "PRAGMA synchronous=NORMAL", nullptr, nullptr, nullptr);  // Ignore errors.
48         sqlite3_exec(db, "PRAGMA foreign_keys=ON", nullptr, nullptr, nullptr);  // Ignore errors.
49         return db;
50 }
51 int get_match_id(sqlite3 *db, QWidget *parent, int requested_match)
52 {
53         QStringList items;
54         vector<int> ids;
55         bool requested_match_ok = false;
56
57         // Read the list of matches already in the database.
58         sqlite3_stmt *stmt;
59         int ret = sqlite3_prepare_v2(db, "SELECT match, description FROM match ORDER BY match", -1, &stmt, 0);
60         if (ret != SQLITE_OK) {
61                 fprintf(stderr, "SELECT prepare: %s\n", sqlite3_errmsg(db));
62                 abort();
63         }
64         for ( ;; ) {
65                 ret = sqlite3_step(stmt);
66                 if (ret == SQLITE_ROW) {
67                         char buf[256];
68                         snprintf(buf, sizeof(buf), "%s (%d)", sqlite3_column_text(stmt, 1), sqlite3_column_int(stmt, 0));
69                         ids.push_back(sqlite3_column_int(stmt, 0));
70                         if (ids.back() == requested_match) {
71                                 requested_match_ok = true;
72                         }
73                         items.push_back(buf);
74                 } else if (ret == SQLITE_DONE) {
75                         break;
76                 } else {
77                         fprintf(stderr, "SELECT step: %s\n", sqlite3_errmsg(db));
78                         abort();
79                 }
80         }
81         ret = sqlite3_finalize(stmt);
82         if (ret != SQLITE_OK) {
83                 fprintf(stderr, "SELECT finalize: %s\n", sqlite3_errmsg(db));
84                 abort();
85         }
86         items.push_back("Add new…");
87
88         if (requested_match_ok) {
89                 return requested_match;
90         }
91
92         QString chosen_str;
93         {
94                 QInputDialog dialog(parent, Qt::WindowFlags());
95                 dialog.setWindowTitle("Open game");
96                 dialog.setLabelText("Choose game to analyze:");
97                 dialog.setComboBoxItems(items);
98                 if (items.size() >= 2) {
99                         dialog.setTextValue(items[items.size() - 2]);
100                 }
101                 dialog.setOption(QInputDialog::UseListViewForComboBoxItems, true);
102                 if (!dialog.exec()) {
103                         return -1;
104                 }
105                 chosen_str = dialog.textValue();
106         }
107
108         for (unsigned i = 0; i < ids.size(); ++i) {
109                 if (chosen_str == items[i]) {
110                         return ids[i];
111                 }
112         }
113
114         // Must be a new game. Get its name and insert it into the database.
115         bool ok;
116         QString new_game_str = QInputDialog::getText(parent, "New game", "Choose name for new game:", QLineEdit::Normal, "", &ok);
117         if (!ok || new_game_str.isEmpty()) {
118                 return -1;
119         }
120
121         // Insert the new row into the database.
122         ret = sqlite3_prepare_v2(db, "INSERT INTO match (description, gender_rule_a, gender_pull_rule) VALUES (?, COALESCE((SELECT gender_rule_a FROM match ORDER BY match DESC LIMIT 1),false), COALESCE((SELECT gender_pull_rule FROM match ORDER BY match DESC LIMIT 1),false))", -1, &stmt, 0);
123         if (ret != SQLITE_OK) {
124                 fprintf(stderr, "INSERT prepare: %s\n", sqlite3_errmsg(db));
125                 abort();
126         }
127
128         QByteArray new_game_utf8 = new_game_str.toUtf8();
129         sqlite3_bind_text(stmt, 1, (const char *)new_game_utf8.data(), new_game_utf8.size(), SQLITE_STATIC);
130
131         ret = sqlite3_step(stmt);
132         if (ret == SQLITE_ROW) {
133                 fprintf(stderr, "INSERT step: %s\n", sqlite3_errmsg(db));
134                 abort();
135         }
136
137         ret = sqlite3_finalize(stmt);
138         if (ret != SQLITE_OK) {
139                 fprintf(stderr, "INSERT finalize: %s\n", sqlite3_errmsg(db));
140                 abort();
141         }
142
143         return sqlite3_last_insert_rowid(db);
144 }
145
146 int main(int argc, char *argv[])
147 {
148         QApplication app(argc, argv);
149         sqlite3 *db = open_db("ultimate.db");
150
151         int requested_match = -1;
152         if (argc >= 2) {
153                 requested_match = atoi(argv[1]);
154         }
155
156         int match_id = get_match_id(db, nullptr, requested_match);
157         if (match_id <= 0) {  // Cancel.
158                 return 0;
159         }
160
161         MainWindow mainWindow(new EventsModel(db, match_id), new PlayersModel(db),
162                               new FormationsModel(db, true), new FormationsModel(db, false),
163                               db, match_id);
164         mainWindow.resize(QSize(1280, 720));
165         mainWindow.show();
166
167         int ret = app.exec();
168
169         return ret;
170 }