summaryrefslogtreecommitdiff
path: root/bayrate/db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'bayrate/db.cpp')
-rw-r--r--bayrate/db.cpp331
1 files changed, 331 insertions, 0 deletions
diff --git a/bayrate/db.cpp b/bayrate/db.cpp
new file mode 100644
index 0000000..14e1762
--- /dev/null
+++ b/bayrate/db.cpp
@@ -0,0 +1,331 @@
+/*************************************************************************************
+
+ Copyright 2010 Philip Waldron
+
+ This file is part of BayRate.
+
+ BayRate is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ BayRate is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with BayRate. If not, see <http://www.gnu.org/licenses/>.
+
+***************************************************************************************/
+
+#include <vector>
+#include <string>
+#include <map>
+#include <iostream>
+#include <sstream>
+#include <assert.h>
+#include <mysql++/mysql++.h>
+#include <ctime>
+#include <boost/date_time/gregorian/gregorian.hpp>
+#include "db.h"
+#include "collection.h"
+#include "player.h"
+#include "game.h"
+
+using namespace std;
+
+databaseConnection::databaseConnection() {
+
+}
+
+databaseConnection::~databaseConnection() {
+ db.disconnect();
+}
+
+/****************************************************************
+
+makeConnection ()
+
+Establish a connection to the ratings server(s).
+
+*****************************************************************/
+bool databaseConnection::makeConnection() {
+ db = mysqlpp::Connection(false);
+
+ return db.connect("database", "localhost", "user", "password");;
+}
+
+// Set exclude flags on any games for which player rank data is bogus
+// Bogus ranks are anything that isn't a kyu/dan indicator
+// Blank ranks will be dealt with later
+void databaseConnection::excludeBogusGameData() {
+ mysqlpp::Query query(&db, false);
+
+ query.exec("UPDATE games SET exclude = 1 WHERE NOT (rank_1 LIKE '%k%' OR rank_1 LIKE '%K%' OR rank_1 LIKE '%d%' OR rank_1 LIKE '%D%')");
+ query.exec("UPDATE games SET exclude = 1 WHERE NOT (rank_2 LIKE '%k%' OR rank_2 LIKE '%K%' OR rank_2 LIKE '%d%' OR rank_2 LIKE '%D%')");
+ query.exec("UPDATE games SET exclude = 1 WHERE (rank_1 = '0k' OR rank_1 = '0K' OR rank_1 = '0d' OR rank_1 = '0D')");
+ query.exec("UPDATE games SET exclude = 1 WHERE (rank_2 = '0k' OR rank_2 = '0K' OR rank_2 = '0d' OR rank_2 = '0D')");
+ query.exec("UPDATE games SET exclude = 1 WHERE handicap>9");
+ query.exec("UPDATE games SET exclude = 1 WHERE handicap>=2 and komi>=10");
+ query.exec("UPDATE games SET exclude = 1 WHERE handicap>=2 and komi<=-10");
+ query.exec("UPDATE games SET exclude = 1 WHERE (handicap=0 or handicap=1) and komi<=-20");
+ query.exec("UPDATE games SET exclude = 1 WHERE (handicap=0 or handicap=1) and komi<=-20");
+ query.exec("UPDATE games SET exclude = 1 WHERE (game_date < '1900-01-01')");
+ query.exec("UPDATE games SET exclude = 1 WHERE pin_player_1 = 0 or pin_player_2 = 0");
+}
+
+/****************************************************************
+
+bool getTournamentUpdateList (vector<string> tournamentUpdateList, date tournamentCascadeDate)
+
+Get the list of tournaments that need (re)rating. List of
+tournament codes is placed in parameter TournamentUpdateList.
+Date of first tournament that need rerating placed in parameter
+tournamentCascadeDate.
+
+Returns true if the operation was successful
+
+*****************************************************************/
+bool databaseConnection::getTournamentUpdateList(vector<string> &tournamentUpdateList, boost::gregorian::date &tournamentCascadeDate) {
+ mysqlpp::Query query = db.query("SELECT MIN(Game_Date) AS date FROM games WHERE Game_Date>'1900-01-01' AND NOT (Online OR Exclude OR Rated)");
+
+ mysqlpp::StoreQueryResult res = query.store();
+
+ if (!res)
+ return false;
+
+ mysqlpp::Date tempDate = res[0]["date"];
+
+ // Check if the response was NULL. The date gets converted to 0000-00-00 if it is.
+ if (tempDate == mysqlpp::Date("0000-00-00")) {
+ return true;
+ }
+ tournamentCascadeDate = boost::gregorian::date(boost::gregorian::from_simple_string(tempDate.str()));
+
+ mysqlpp::Query query2 = db.query("SELECT Tournament_Code FROM tournaments WHERE Tournament_Date>=%0q ORDER BY Tournament_Date");
+ query2.parse();
+ res = query2.store(tempDate);
+
+ for (size_t i=0; i<res.num_rows(); i++) {
+ tournamentUpdateList.push_back(static_cast<string>(res[i]["Tournament_Code"]));
+ }
+
+ return true;
+}
+
+
+/****************************************************************
+
+bool getTDList ()
+
+Gets the last valid TDList prior to the date given by parameter
+tournamentCascadeDate. Data placed into map parameter TdList.
+
+Returns true if the operation is successful
+
+*****************************************************************/
+bool databaseConnection::getTDList(boost::gregorian::date &tournamentCascadeDate, map<int, tdListEntry> &tdList) {
+ tdListEntry entry;
+
+ mysqlpp::Query query = db.query("SELECT name, x.pin_player, x.rating, x.sigma, x.elab_date FROM ratings x, players, (SELECT MAX(elab_date) AS maxdate, pin_player FROM ratings WHERE elab_date < %0q GROUP BY pin_player) AS maxresults WHERE x.pin_player=maxresults.pin_player AND x.elab_date=maxresults.maxdate and x.pin_player=players.pin_player and x.pin_player!=0");
+ query.parse();
+ mysqlpp::StoreQueryResult res = query.store(mysqlpp::Date(boost::gregorian::to_iso_extended_string(tournamentCascadeDate)));
+
+ if (!res)
+ return false;
+
+ for (size_t i=0; i<res.num_rows(); ++i) {
+ entry.id = res[i]["pin_player"];
+ entry.rating = res[i]["rating"];
+ entry.sigma = res[i]["sigma"];
+ entry.name = string(res[i]["name"]);
+
+ mysqlpp::Date tempDate = res[i]["elab_date"];
+ if (tempDate == mysqlpp::Date("0000-00-00")) {
+ entry.lastRatingDate = boost::gregorian::date(1900, 1, 1);
+ }
+ else {
+ entry.lastRatingDate = boost::gregorian::date(boost::gregorian::from_simple_string(tempDate.str()));
+ }
+ entry.ratingUpdated = false;
+
+ tdList[entry.id] = entry;
+ }
+
+ return true;
+}
+
+/****************************************************************
+
+bool getTournamentInfo (string &tournamentCode, collection &c)
+
+Gets players and games from tournament indexed by parameters
+tournamentCode, and place the information in parameter collection.
+
+Returns true if operation is successful.
+
+*****************************************************************/
+
+bool databaseConnection::getTournamentInfo (string &tournamentCode, collection &c) {
+ player p;
+ game g;
+ stringstream ss(stringstream::in | stringstream::out);
+ int rankPartNumber;
+ char rankPartKyuDan;
+
+ std::string test;
+
+ mysqlpp::Query dateQuery = db.query("SELECT Tournament_Date, Tournament_Descr FROM tournaments WHERE tournament_code=%0q LIMIT 1");
+ dateQuery.parse();
+ mysqlpp::StoreQueryResult dateResult = dateQuery.store(tournamentCode);
+ mysqlpp::Date tempDate = dateResult[0]["Tournament_Date"];
+
+ if (tempDate == mysqlpp::Date("0000-00-00")) {
+ return false;
+ }
+
+ c.tournamentCode = tournamentCode;
+ dateResult[0]["Tournament_Descr"].to_string(c.tournamentName);
+ c.tournamentDate = boost::gregorian::date(boost::gregorian::from_simple_string(tempDate.str()));
+
+ cout << c.tournamentCode << '\t' << c.tournamentDate << '\t' << c.tournamentName << endl;
+
+ mysqlpp::Query gameQuery = db.query("SELECT pin_player_1, rank_1, color_1, pin_player_2, rank_2, color_2, handicap, komi, result FROM games WHERE Tournament_Code=%0q AND NOT (Online OR Exclude)");
+ gameQuery.parse();
+
+ mysqlpp::StoreQueryResult gameRes=gameQuery.store(tournamentCode);
+
+ for (size_t i=0; i<gameRes.num_rows(); i++) {
+ // Process and locally store the game information
+ if (string(gameRes[i]["color_1"]) == string("W")) {
+ g.white = gameRes[i]["pin_player_1"];
+ g.black = gameRes[i]["pin_player_2"];
+ }
+ else if (string(gameRes[i]["color_1"]) == string("B")) {
+ g.white = gameRes[i]["pin_player_2"];
+ g.black = gameRes[i]["pin_player_1"];
+ }
+ else {
+ cout << "Fatal error: unknown player colour " << gameRes[i]["color_1"] << endl;
+ exit (1);
+ }
+
+ if (string(gameRes[i]["result"]) == string("W")) {
+ g.whiteWins = true;
+ }
+ else if (string(gameRes[i]["result"]) == string("B")) {
+ g.whiteWins = false;
+ }
+ else {
+ cout << "Fatal error: unknown game result " << gameRes[i]["result"] << endl;
+ exit (1);
+ }
+
+ g.handicap = gameRes[i]["handicap"];
+ g.komi = gameRes[i]["komi"];
+
+ c.gameList.push_back(g);
+
+ // Process and locally store the player information
+ p.id = gameRes[i]["pin_player_1"];
+
+ if (c.playerHash.find(p.id) == c.playerHash.end()) {
+
+ ss.str(string(gameRes[i]["rank_1"]));
+ ss >> rankPartNumber >> rankPartKyuDan;
+
+ if ( (rankPartKyuDan == 'k') || (rankPartKyuDan == 'K') ) {
+ p.seed = -(rankPartNumber+0.5);
+ }
+ else if ( (rankPartKyuDan == 'd') || (rankPartKyuDan == 'd') ) {
+ p.seed = rankPartNumber+0.5;
+ }
+ else {
+ cout << "Fatal error: player " << gameRes[i]["pin_player_1"] << " unknown rank format: " << gameRes[i]["rank_1"] << endl;
+ exit(1);
+ }
+
+ c.playerHash[p.id] = p;
+ }
+
+ p.id = gameRes[i]["pin_player_2"];
+
+ if (c.playerHash.find(p.id) == c.playerHash.end()) {
+
+ ss.str(string(gameRes[i]["rank_2"]));
+ ss >> rankPartNumber >> rankPartKyuDan;
+
+ if ( (rankPartKyuDan == 'k') || (rankPartKyuDan == 'K') ) {
+ p.seed = -(rankPartNumber+0.5);
+ }
+ else if ( (rankPartKyuDan == 'd') || (rankPartKyuDan == 'd') ) {
+ p.seed = rankPartNumber+0.5;
+ }
+ else {
+ cout << "Fatal error: player " << gameRes[i]["pin_player_2"] << " unknown rank format: " << gameRes[i]["rank_2"] << endl;
+ exit(1);
+ }
+
+ c.playerHash[p.id] = p;
+ }
+ }
+
+ return true;
+}
+
+/****************************************************************
+
+void syncNewRatings (collection &c)
+
+Pushes new ratings into the ratings database and update appropriate indexes.
+
+*****************************************************************/
+void databaseConnection::syncNewRatings (collection &c) {
+
+ map<int, player>::iterator playerIt = c.playerHash.begin();
+
+ mysqlpp::Transaction trans(db);
+
+ mysqlpp::Query query1 = db.query("INSERT INTO ratings (pin_player, rating, sigma, elab_date) VALUES (%0q, %1q, %2q, %3q) ON DUPLICATE KEY UPDATE rating=%4q, sigma=%5q");
+ query1.parse();
+
+ mysqlpp::Query query4 = db.query("UPDATE players SET rating=%0q, sigma=%1q, elab_date=%2q WHERE pin_player=%3q");
+ query4.parse();
+
+ for (playerIt = c.playerHash.begin(); playerIt != c.playerHash.end(); playerIt++) {
+ query1.execute(playerIt->second.id, playerIt->second.rating, playerIt->second.sigma, to_iso_extended_string(c.tournamentDate), playerIt->second.rating, playerIt->second.sigma);
+ if (query1.errnum() != 0) {
+ cerr << "Query failure in query1 (db.cpp). Rolling back transaction and exiting program" << endl;
+ trans.rollback();
+ exit(1);
+ }
+
+ query4.execute(playerIt->second.rating, playerIt->second.sigma, to_iso_extended_string(c.tournamentDate), playerIt->second.id);
+ if (query4.errnum() != 0) {
+ cerr << "Query failure in query4 (db.cpp). Rolling back transaction and exiting program" << endl;
+ trans.rollback();
+ exit(1);
+ }
+ }
+
+ mysqlpp::Query query2 = db.query("UPDATE games SET elab_date = %0q WHERE tournament_code=%1q AND Online=0");
+ query2.parse();
+ query2.execute(to_iso_extended_string(c.tournamentDate), c.tournamentCode);
+ if (query2.errnum() != 0) {
+ cerr << "Query failure in query2 (db.cpp). Rolling back transaction and exiting program" << endl;
+ trans.rollback();
+ exit(1);
+ }
+
+ mysqlpp::Query query3 = db.query("UPDATE games SET Rated=1 WHERE tournament_code=%0q AND Online=0");
+ query3.parse();
+ query3.execute(c.tournamentCode);
+ if (query3.errnum() != 0) {
+ cerr << "Query failure in (db.cpp). Rolling back transaction and exiting program" << endl;
+ trans.rollback();
+ exit(1);
+ }
+
+ trans.commit();
+}